diff --git a/src/common/data/notifications.js b/src/common/data/notifications.js new file mode 100644 index 0000000..b80a791 --- /dev/null +++ b/src/common/data/notifications.js @@ -0,0 +1,14 @@ +let Notifications = Object.freeze({ + 'TEST_NOTIFICATION': { + icon: 'card-text', + text: 'This is a test notification.', + timeout: -1, + }, + 'AARD_DRM': { + icon: 'exclamation-triangle', + text: 'Autodetection cannot run on this video. This usually happens when sites use DRM. You will have to set aspect ratio manually.', + timeout: 5000, + } +}); + +export default Notifications; \ No newline at end of file diff --git a/src/csui/NotificationUi.vue b/src/csui/NotificationUi.vue index e4d4406..d72b24e 100644 --- a/src/csui/NotificationUi.vue +++ b/src/csui/NotificationUi.vue @@ -5,11 +5,10 @@ -
+
{{action.label}}
+
+ Never show again:  + +
-
+
@@ -50,20 +72,19 @@ export default { }, data() { return { - // notificationIcon: null, - // notificationText: null, - // notificationActions: null, - // showNotification: false, notificationTimeout: null, notificationIcon: "exclamation-triangle", - notificationText: "this is a test notification with some html for bold measure", + notificationText: "Sample text. This will be replaced with real notification later.", notificationActions: null, - showNotification: true, + hideActions: null, + showNotification: false, }; }, - ...mapState([ - 'notificationConfig' - ]), + computed: { + ...mapState([ + 'notificationConfig' + ]), + }, watch: { /** * Sets new notification config. Currently, we can only show one notification at a time. @@ -79,31 +100,39 @@ export default { * label: label of the button * icon: icon of the button * } + * ], + * hideOptions: [ + * // more of notificationActions except it's a special case * ] * } */ notificationConfig(newConfig) { + console.log('notificationConfig?'); if (newConfig) { this.notificationText = newConfig.text; this.notificationActions = newConfig.notificationActions; this.notificationIcon = newConfig.icon; + this.hideActions = newConfig.hideActions; this.showNotification = true; if (newConfig.timeout !== -1) { - this.notificationTimeout = setTimeout(() => this.closeNotification(), newConfig.timeout ?? 10000); + this.notificationTimeout = setTimeout(() => this.closeNotification(), newConfig.timeout ?? 5000); } } } }, methods: { closeNotification() { + console.log("close notification!") + clearTimeout(this.notificationTimeout); this.showNotification = false; this.notificationIcon = null; this.notificationText = null; this.notificationActions = null; + this.hideActions = null; } } } @@ -120,18 +149,62 @@ export default { position: relative; width: 100%; height: 100%; + pointer-events: none; + + font-size: 16px; .notification-popup { + pointer-events: auto !important; position: absolute; z-index: 99999999; + top: 2em; + right: 2em; + width: 32em; + + padding: 0.7em 0.5em; + + font-family: 'Overpass'; + background-color: rgba(108, 55, 12, 0.779); - top: 2rem; - left: 2rem; - width: 15rem; color: #fff; + + user-select: none; } + + .notifcation-content { + margin-left: 0.5em; + } + + .notification-text { + text-align: justify; + } + .notification-icon { - font-size: 3rem; + font-size: 3em; + line-height: 0.5; + } + .action-button { + pointer-events: auto; + cursor: pointer; + } + + .hide-actions { + color: #ccc; + font-size: 0.8em; + justify-self: flex-end; + align-self: flex-end; + margin-top: 1em; + margin-bottom: -1em; + } + + .hide-action-button { + color: #eee; + font-size: 0.9em; + text-decoration: underline; + text-decoration-color: rgba(255,255,255,0.5); + + pointer-events: auto; + cursor: pointer; } } \ No newline at end of file diff --git a/src/ext/lib/uwui/PlayerNotificationUI.js b/src/ext/lib/uwui/PlayerNotificationUI.js index 245592d..0b0afc5 100644 --- a/src/ext/lib/uwui/PlayerNotificationUI.js +++ b/src/ext/lib/uwui/PlayerNotificationUI.js @@ -1,23 +1,31 @@ import UI from './UI'; import VuexWebExtensions from 'vuex-webextensions'; import NotificationUi from '../../../csui/NotificationUi.vue'; +import Notifications from '../../../common/data/notifications'; if (process.env.CHANNEL !== 'stable'){ console.info("Loading: PlayerNotificationUi"); } +let MuteScope = Object.freeze({ + CurrentSite: 'current-site', + Global: 'global' +}); class PlayerNotificationUi extends UI { constructor ( - playerElement - ) { + playerElement, + settings + ) { super( 'notification', PlayerNotificationUi.getStoreConfig(), PlayerNotificationUi.getUiConfig(playerElement), PlayerNotificationUi.getCommsConfig() - ) + ); + + this.settings = settings; } @@ -42,11 +50,13 @@ class PlayerNotificationUi extends UI { }, mutations: { 'uw-set-notification'(state, payload) { + console.log('mutation!', state, payload); state['notificationConfig'] = payload; } }, actions: { 'uw-set-notification'({commit}, payload) { + console.log('action!', commit, payload); commit('uw-set-notification', payload); } } @@ -78,7 +88,7 @@ class PlayerNotificationUi extends UI { /** * Show notification on screen. * - * @param notificationConfig notification config + * @param notificationConfig notification config (or ID of notification config in /common/data/notifications.js) * * notificationConfig should resemble this: * { @@ -91,11 +101,79 @@ class PlayerNotificationUi extends UI { * label: label of the button * icon: icon of the button * } + * ], + * hideActions: [ + * // more of notificationActions but with special case * ] * } + * + * When notificationConfig is a string, the function will add two additional notifications on the notificationActionsPile + * * never show this notification ever again on any site + * * never show this notification again on this site */ showNotification(notificationConfig) { - this.vuexStore?.dispatch('uw-set-notification', notificationConfig); + if (typeof notificationConfig === 'string') { + try { + const config = Notifications[notificationConfig]; + + // this should _never_ appear on production version of the extension, but it should help with development. + if (!config) { + return this.vuexStore?.dispatch('uw-set-notification', { + icon: 'x-circle-fill', + text: `Notification for key ${notificationConfig} does not exist.`, + timeout: -1, + }); + } + + // don't show notification if it's muted + if (this.isNotificationMuted(notificationConfig)) { + return; + } + + this.vuexStore?.dispatch('uw-set-notification', { + ...config, + hideActions: [ + { + command: () => this.muteNotification(notificationConfig, MuteScope.CurrentSite), + label: 'this site' + }, + { + command: () => this.muteNotification(notificationConfig, MuteScope.Global), + label: 'never ever' + } + ] + }); + } catch (e) { + console.error('theres been an error:', e) + } + } else { + this.vuexStore?.dispatch('uw-set-notification', notificationConfig); + } + } + + muteNotification(notificationId, scope) { + // ensure objects we try to set exist + if (!this.settings.active.mutedNotifications) { + this.settings.active.mutedNotifications = {}; + } + if (!this.settings.active.mutedNotifications[notificationId]) { + this.settings.active.mutedNotifications[notificationId] = {}; + } + + // actually mute notification + if (scope === MuteScope.Global) { + this.settings.active.mutedNotifications[notificationId].$global = true; + } else { + this.settings.active.mutedNotifications[notificationId][window.location.hostname] = true; + } + + // persist settings + this.settings.saveWithoutReload(); + } + + isNotificationMuted(notificationId) { + return this.settings.active.mutedNotifications?.[notificationId]?.$global + || this.settings.active.mutedNotifications?.[notificationId]?.[window.location.hostname]; } } diff --git a/src/ext/lib/uwui/UI.js b/src/ext/lib/uwui/UI.js index 35eb9ff..4ba063a 100644 --- a/src/ext/lib/uwui/UI.js +++ b/src/ext/lib/uwui/UI.js @@ -38,19 +38,12 @@ class UI { async initUi() { const random = Math.round(Math.random() * 69420); - // const uwid = `uw-${this.interfaceId}-root-${random}` - const uwid = 'not-so-random-id' + const uwid = `uw-${this.interfaceId}-root-${random}` const rootDiv = document.createElement('div'); - try { - rootDiv.setAttribute('style', `position: ${this.uiConfig.style?.position ?? 'relative'}; width: ${this.uiConfig.style?.width ?? '100%'}; height: ${this.uiConfig.style?.height ?? '100%'}; ${this.uiConfig.additionalStyle ?? ''}`); - rootDiv.setAttribute('id', uwid); - } catch (e) { - console.error("ERROR:", e) - } - - console.warn('UI: init 3', this.uiConfig); + rootDiv.setAttribute('style', `position: ${this.uiConfig.style?.position ?? 'relative'}; width: ${this.uiConfig.style?.width ?? '100%'}; height: ${this.uiConfig.style?.height ?? '100%'}; ${this.uiConfig.additionalStyle ?? ''}`); + rootDiv.setAttribute('id', uwid); if (this.uiConfig?.parentElement) { diff --git a/src/ext/lib/video-data/PlayerData.js b/src/ext/lib/video-data/PlayerData.js index c6493e7..e7b54d3 100644 --- a/src/ext/lib/video-data/PlayerData.js +++ b/src/ext/lib/video-data/PlayerData.js @@ -44,7 +44,7 @@ class PlayerData { this.extensionMode = videoData.extensionMode; this.invalid = false; this.element = this.getPlayer(); - this.notificationService = new PlayerNotificationUi(this.element); + this.notificationService = new PlayerNotificationUi(this.element, this.settings); this.dimensions = undefined; this.overlayNode = undefined; @@ -65,6 +65,7 @@ class PlayerData { this.checkPlayerSizeChange(); } this.startChangeDetection(); + } catch (e) { console.error('[Ultrawidify::PlayerData::ctor] There was an error setting up player data. You should be never seeing this message. Error:', e); this.invalid = true; @@ -472,6 +473,10 @@ class PlayerData { return true; } + + showNotification(notificationId) { + this.notificationService?.showNotification(notificationId); + } } if (process.env.CHANNEL !== 'stable'){