mirror of
https://github.com/nasa/openmct.git
synced 2025-06-01 15:10:50 +00:00
Notebook saved link (#2998)
* https://github.com/nasa/openmct/issues/2859 * create and store link to default notebook in storage and pass it to notification. * [Notebook] Add link to notebook inside 'Saved to Notebook' notification #2860 * Added custom autoDismissTimeout for into notifications. * Backwards compatibility fix for old notebook model without link in metadata. * lint fixes * added JS Doc description for API changes + changed property names to appropriate function. * fixed bug due to merging. * fixed url update loop Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
parent
55c851873c
commit
b7085f7f62
@ -75,13 +75,20 @@ export default class NotificationAPI extends EventEmitter {
|
|||||||
* Info notifications are low priority informational messages for the user. They will be auto-destroy after a brief
|
* Info notifications are low priority informational messages for the user. They will be auto-destroy after a brief
|
||||||
* period of time.
|
* period of time.
|
||||||
* @param {string} message The message to display to the user
|
* @param {string} message The message to display to the user
|
||||||
|
* @param {Object} [options] object with following properties
|
||||||
|
* autoDismissTimeout: {number} in miliseconds to automatically dismisses notification
|
||||||
|
* link: {Object} Add a link to notifications for navigation
|
||||||
|
* onClick: callback function
|
||||||
|
* cssClass: css class name to add style on link
|
||||||
|
* text: text to display for link
|
||||||
* @returns {InfoNotification}
|
* @returns {InfoNotification}
|
||||||
*/
|
*/
|
||||||
info(message) {
|
info(message, options = {}) {
|
||||||
let notificationModel = {
|
let notificationModel = {
|
||||||
message: message,
|
message: message,
|
||||||
autoDismiss: true,
|
autoDismiss: true,
|
||||||
severity: "info"
|
severity: "info",
|
||||||
|
options
|
||||||
};
|
};
|
||||||
|
|
||||||
return this._notify(notificationModel);
|
return this._notify(notificationModel);
|
||||||
@ -90,12 +97,19 @@ export default class NotificationAPI extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
* Present an alert to the user.
|
* Present an alert to the user.
|
||||||
* @param {string} message The message to display to the user.
|
* @param {string} message The message to display to the user.
|
||||||
|
* @param {Object} [options] object with following properties
|
||||||
|
* autoDismissTimeout: {number} in miliseconds to automatically dismisses notification
|
||||||
|
* link: {Object} Add a link to notifications for navigation
|
||||||
|
* onClick: callback function
|
||||||
|
* cssClass: css class name to add style on link
|
||||||
|
* text: text to display for link
|
||||||
* @returns {Notification}
|
* @returns {Notification}
|
||||||
*/
|
*/
|
||||||
alert(message) {
|
alert(message, options = {}) {
|
||||||
let notificationModel = {
|
let notificationModel = {
|
||||||
message: message,
|
message: message,
|
||||||
severity: "alert"
|
severity: "alert",
|
||||||
|
options
|
||||||
};
|
};
|
||||||
|
|
||||||
return this._notify(notificationModel);
|
return this._notify(notificationModel);
|
||||||
@ -104,12 +118,19 @@ export default class NotificationAPI extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
* Present an error message to the user
|
* Present an error message to the user
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
|
* @param {Object} [options] object with following properties
|
||||||
|
* autoDismissTimeout: {number} in miliseconds to automatically dismisses notification
|
||||||
|
* link: {Object} Add a link to notifications for navigation
|
||||||
|
* onClick: callback function
|
||||||
|
* cssClass: css class name to add style on link
|
||||||
|
* text: text to display for link
|
||||||
* @returns {Notification}
|
* @returns {Notification}
|
||||||
*/
|
*/
|
||||||
error(message) {
|
error(message, options = {}) {
|
||||||
let notificationModel = {
|
let notificationModel = {
|
||||||
message: message,
|
message: message,
|
||||||
severity: "error"
|
severity: "error",
|
||||||
|
options
|
||||||
};
|
};
|
||||||
|
|
||||||
return this._notify(notificationModel);
|
return this._notify(notificationModel);
|
||||||
@ -325,9 +346,11 @@ export default class NotificationAPI extends EventEmitter {
|
|||||||
this.emit('notification', notification);
|
this.emit('notification', notification);
|
||||||
|
|
||||||
if (notification.model.autoDismiss || this._selectNextNotification()) {
|
if (notification.model.autoDismiss || this._selectNextNotification()) {
|
||||||
|
const autoDismissTimeout = notification.model.options.autoDismissTimeout
|
||||||
|
|| DEFAULT_AUTO_DISMISS_TIMEOUT;
|
||||||
this.activeTimeout = setTimeout(() => {
|
this.activeTimeout = setTimeout(() => {
|
||||||
this._dismissOrMinimize(notification);
|
this._dismissOrMinimize(notification);
|
||||||
}, DEFAULT_AUTO_DISMISS_TIMEOUT);
|
}, autoDismissTimeout);
|
||||||
} else {
|
} else {
|
||||||
delete this.activeTimeout;
|
delete this.activeTimeout;
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,7 @@ import { addNotebookEntry, createNewEmbed, getNotebookEntries, mutateObject } fr
|
|||||||
import objectUtils from 'objectUtils';
|
import objectUtils from 'objectUtils';
|
||||||
|
|
||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
|
import objectLink from '../../../ui/mixins/object-link';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -182,7 +183,9 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
|
this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
|
||||||
this.formatSidebar();
|
this.formatSidebar();
|
||||||
|
|
||||||
window.addEventListener('orientationchange', this.formatSidebar);
|
window.addEventListener('orientationchange', this.formatSidebar);
|
||||||
|
window.addEventListener("hashchange", this.navigateToSectionPage, false);
|
||||||
|
|
||||||
this.navigateToSectionPage();
|
this.navigateToSectionPage();
|
||||||
},
|
},
|
||||||
@ -190,6 +193,9 @@ export default {
|
|||||||
if (this.unlisten) {
|
if (this.unlisten) {
|
||||||
this.unlisten();
|
this.unlisten();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.removeEventListener('orientationchange', this.formatSidebar);
|
||||||
|
window.removeEventListener("hashchange", this.navigateToSectionPage);
|
||||||
},
|
},
|
||||||
updated: function () {
|
updated: function () {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@ -225,15 +231,17 @@ export default {
|
|||||||
},
|
},
|
||||||
createNotebookStorageObject() {
|
createNotebookStorageObject() {
|
||||||
const notebookMeta = {
|
const notebookMeta = {
|
||||||
identifier: this.internalDomainObject.identifier
|
name: this.internalDomainObject.name,
|
||||||
|
identifier: this.internalDomainObject.identifier,
|
||||||
|
link: this.getLinktoNotebook()
|
||||||
};
|
};
|
||||||
const page = this.getSelectedPage();
|
const page = this.getSelectedPage();
|
||||||
const section = this.getSelectedSection();
|
const section = this.getSelectedSection();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
notebookMeta,
|
notebookMeta,
|
||||||
section,
|
page,
|
||||||
page
|
section
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
dragOver(event) {
|
dragOver(event) {
|
||||||
@ -309,6 +317,20 @@ export default {
|
|||||||
|
|
||||||
return this.openmct.objects.get(oldNotebookStorage.notebookMeta.identifier);
|
return this.openmct.objects.get(oldNotebookStorage.notebookMeta.identifier);
|
||||||
},
|
},
|
||||||
|
getLinktoNotebook() {
|
||||||
|
const objectPath = this.openmct.router.path;
|
||||||
|
const link = objectLink.computed.objectLink.call({
|
||||||
|
objectPath,
|
||||||
|
openmct: this.openmct
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedSection = this.selectedSection;
|
||||||
|
const selectedPage = this.selectedPage;
|
||||||
|
const sectionId = selectedSection ? selectedSection.id : '';
|
||||||
|
const pageId = selectedPage ? selectedPage.id : '';
|
||||||
|
|
||||||
|
return `${link}?sectionId=${sectionId}&pageId=${pageId}`;
|
||||||
|
},
|
||||||
getPage(section, id) {
|
getPage(section, id) {
|
||||||
return section.pages.find(p => p.id === id);
|
return section.pages.find(p => p.id === id);
|
||||||
},
|
},
|
||||||
@ -393,6 +415,12 @@ export default {
|
|||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const selectedSectionId = this.selectedSection && this.selectedSection.id;
|
||||||
|
const selectedPageId = this.selectedPage && this.selectedPage.id;
|
||||||
|
if (selectedPageId === pageId && selectedSectionId === sectionId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.sectionsChanged({ sections });
|
this.sectionsChanged({ sections });
|
||||||
},
|
},
|
||||||
newEntry(embed = null) {
|
newEntry(embed = null) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { addNotebookEntry, createNewEmbed } from './utils/notebook-entries';
|
import { addNotebookEntry, createNewEmbed } from './utils/notebook-entries';
|
||||||
import { getDefaultNotebook } from './utils/notebook-storage';
|
import { getDefaultNotebook, getDefaultNotebookLink, setDefaultNotebook } from './utils/notebook-storage';
|
||||||
import { NOTEBOOK_DEFAULT } from '@/plugins/notebook/notebook-constants';
|
import { NOTEBOOK_DEFAULT } from '@/plugins/notebook/notebook-constants';
|
||||||
import SnapshotContainer from './snapshot-container';
|
import SnapshotContainer from './snapshot-container';
|
||||||
|
|
||||||
@ -45,12 +45,21 @@ export default class Snapshot {
|
|||||||
_saveToDefaultNoteBook(embed) {
|
_saveToDefaultNoteBook(embed) {
|
||||||
const notebookStorage = getDefaultNotebook();
|
const notebookStorage = getDefaultNotebook();
|
||||||
this.openmct.objects.get(notebookStorage.notebookMeta.identifier)
|
this.openmct.objects.get(notebookStorage.notebookMeta.identifier)
|
||||||
.then(domainObject => {
|
.then(async (domainObject) => {
|
||||||
addNotebookEntry(this.openmct, domainObject, notebookStorage, embed);
|
addNotebookEntry(this.openmct, domainObject, notebookStorage, embed);
|
||||||
|
|
||||||
|
let link = notebookStorage.notebookMeta.link;
|
||||||
|
|
||||||
|
// Backwards compatibility fix (old notebook model without link)
|
||||||
|
if (!link) {
|
||||||
|
link = await getDefaultNotebookLink(this.openmct, domainObject);
|
||||||
|
notebookStorage.notebookMeta.link = link;
|
||||||
|
setDefaultNotebook(this.openmct, notebookStorage);
|
||||||
|
}
|
||||||
|
|
||||||
const defaultPath = `${domainObject.name} - ${notebookStorage.section.name} - ${notebookStorage.page.name}`;
|
const defaultPath = `${domainObject.name} - ${notebookStorage.section.name} - ${notebookStorage.page.name}`;
|
||||||
const msg = `Saved to Notebook ${defaultPath}`;
|
const msg = `Saved to Notebook ${defaultPath}`;
|
||||||
this._showNotification(msg);
|
this._showNotification(msg, link);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,16 +67,29 @@ export default class Snapshot {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_saveToNotebookSnapshots(embed) {
|
_saveToNotebookSnapshots(embed) {
|
||||||
const saved = this.snapshotContainer.addSnapshot(embed);
|
this.snapshotContainer.addSnapshot(embed);
|
||||||
if (!saved) {
|
}
|
||||||
return;
|
|
||||||
|
_showNotification(msg, url) {
|
||||||
|
const options = {
|
||||||
|
autoDismissTimeout: 30000,
|
||||||
|
link: {
|
||||||
|
cssClass: '',
|
||||||
|
text: 'click to view',
|
||||||
|
onClick: this._navigateToNotebook(url)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.openmct.notifications.info(msg, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
_navigateToNotebook(url = null) {
|
||||||
|
if (!url) {
|
||||||
|
return () => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const msg = 'Saved to Notebook Snapshots - click to view.';
|
return () => {
|
||||||
this._showNotification(msg);
|
window.location.href = window.location.origin + url;
|
||||||
}
|
};
|
||||||
|
|
||||||
_showNotification(msg) {
|
|
||||||
this.openmct.notifications.info(msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,14 +48,29 @@ export function getDefaultNotebook() {
|
|||||||
return JSON.parse(notebookStorage);
|
return JSON.parse(notebookStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getDefaultNotebookLink(openmct, domainObject = null) {
|
||||||
|
if (!domainObject) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = await openmct.objects.getOriginalPath(domainObject.identifier)
|
||||||
|
.then(objectPath => objectPath
|
||||||
|
.map(o => o && openmct.objects.makeKeyString(o.identifier))
|
||||||
|
.reverse()
|
||||||
|
.join('/')
|
||||||
|
);
|
||||||
|
const { page, section } = getDefaultNotebook();
|
||||||
|
|
||||||
|
return `#/browse/${path}?sectionId=${section.id}&pageId=${page.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function setDefaultNotebook(openmct, notebookStorage, domainObject) {
|
export function setDefaultNotebook(openmct, notebookStorage, domainObject) {
|
||||||
observeDefaultNotebookObject(openmct, notebookStorage.notebookMeta, domainObject);
|
observeDefaultNotebookObject(openmct, notebookStorage, domainObject);
|
||||||
saveDefaultNotebook(notebookStorage);
|
saveDefaultNotebook(notebookStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setDefaultNotebookSection(section) {
|
export function setDefaultNotebookSection(section) {
|
||||||
const notebookStorage = getDefaultNotebook();
|
const notebookStorage = getDefaultNotebook();
|
||||||
|
|
||||||
notebookStorage.section = section;
|
notebookStorage.section = section;
|
||||||
saveDefaultNotebook(notebookStorage);
|
saveDefaultNotebook(notebookStorage);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,11 @@
|
|||||||
@click="maximize()"
|
@click="maximize()"
|
||||||
>
|
>
|
||||||
<span class="c-message-banner__message">{{ activeModel.message }}</span>
|
<span class="c-message-banner__message">{{ activeModel.message }}</span>
|
||||||
|
<span v-if="haslink"
|
||||||
|
class="c-message-banner__message"
|
||||||
|
:class="[haslink ? getLinkProps.cssClass : '']"
|
||||||
|
>{{ getLinkProps.text }}</span>
|
||||||
|
|
||||||
<progress-bar
|
<progress-bar
|
||||||
v-if="activeModel.progressPerc !== undefined"
|
v-if="activeModel.progressPerc !== undefined"
|
||||||
class="c-message-banner__progress-bar"
|
class="c-message-banner__progress-bar"
|
||||||
@ -43,6 +48,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ProgressBar from '../../components/ProgressBar.vue';
|
import ProgressBar from '../../components/ProgressBar.vue';
|
||||||
|
|
||||||
let activeNotification = undefined;
|
let activeNotification = undefined;
|
||||||
let maximizedDialog = undefined;
|
let maximizedDialog = undefined;
|
||||||
let minimizeButton = {
|
let minimizeButton = {
|
||||||
@ -78,11 +84,20 @@ export default {
|
|||||||
message: undefined,
|
message: undefined,
|
||||||
progressPerc: undefined,
|
progressPerc: undefined,
|
||||||
progressText: undefined,
|
progressText: undefined,
|
||||||
minimized: undefined
|
minimized: undefined,
|
||||||
|
options: undefined
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
haslink() {
|
||||||
|
const options = this.activeModel.options;
|
||||||
|
|
||||||
|
return options && options.link;
|
||||||
|
},
|
||||||
|
getLinkProps() {
|
||||||
|
return this.activeModel.options.link;
|
||||||
|
},
|
||||||
progressWidth() {
|
progressWidth() {
|
||||||
return {
|
return {
|
||||||
width: this.activeModel.progress + '%'
|
width: this.activeModel.progress + '%'
|
||||||
@ -145,6 +160,15 @@ export default {
|
|||||||
activeNotification.off('destroy', dismissMaximizedDialog);
|
activeNotification.off('destroy', dismissMaximizedDialog);
|
||||||
},
|
},
|
||||||
maximize() {
|
maximize() {
|
||||||
|
if (this.haslink) {
|
||||||
|
const linkProps = this.getLinkProps;
|
||||||
|
linkProps.onClick();
|
||||||
|
|
||||||
|
activeNotification.dismiss();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.activeModel.progressPerc !== undefined) {
|
if (this.activeModel.progressPerc !== undefined) {
|
||||||
maximizedDialog = this.openmct.overlays.progressDialog({
|
maximizedDialog = this.openmct.overlays.progressDialog({
|
||||||
buttons: [minimizeButton],
|
buttons: [minimizeButton],
|
||||||
@ -164,6 +188,5 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user