add classes for notification UI.

The UI classes are split into "general UI" part — a base class that could potentially be used for proper in-player UI if we ever get to that point — and part that's specific to our notification requirements.
This commit is contained in:
Tamius Han 2020-12-03 00:34:50 +01:00
parent 55fbd30699
commit 38641df28e
2 changed files with 174 additions and 0 deletions

View File

@ -0,0 +1,98 @@
import UI from './UI';
import VuexWebExtensions from 'vuex-webextensions';
import VideoNotification from '../../../csui/VideoNotification';
class PlayerNotification extends UI {
constructor (
playerElement
) {
super(
'notification',
getStoreConfig(),
getUiConfig(playerElement),
getCommsConfig()
)
}
//#region constructor helpers
// we will move some things out of the constructor in order to keep things clean
getStoreConfig() {
return {
plugins: [
VuexWebExtensions({
persistentStates: [
'notificationConfig'
],
}),
],
state: {
// should be null by default!
notificationConfig: {
text: 'sample text <b>now with 100% more html formatting!</b>',
icon: 'exclamation-circle',
timeout: 5000,
}
},
mutations: {
'uw-set-notification'(state, payload) {
state['notificationConfig'] = payload;
}
},
actions: {
'uw-set-notification'({commit}, payload) {
commit('uw-set-notification', payload);
}
}
};
}
getUiConfig(playerElement) {
return {
parentElement: playerElement,
component: VideoNotification
}
}
getCommsConfig() {
return {
handlers = {
'show-notification': [(message) => this.showNotification(message)],
}
}
}
//#endregion
//#region lifecycle
replace(playerElement) {
super.replace(this.getUiConfig(playerElement));
}
//#endregion
/**
* Show notification on screen.
*
* @param notificationConfig notification config
*
* notificationConfig should resemble this:
* {
* timeout: number how long we'll be displaying the notification. If empty, 10s. -1: until user dismisses it
* icon: string what icon we're gonna show. We're using bootstrap icons. Can be empty.
* text: notification text. Supports HTML.
* notificationActions: [
* {
* command: function that gets executed upon clicking the button.
* label: label of the button
* icon: icon of the button
* }
* ]
* }
*/
showNotification(notificationConfig) {
this.vuexStore?.dispatch('uw-set-notification', notificationConfig);
}
}
export default PlayerNotification;

76
src/ext/lib/uwui/UI.js Normal file
View File

@ -0,0 +1,76 @@
import { createApp } from 'vue';
import { createStore } from 'vuex';
class UI {
constructor(
interfaceId,
storeConfig,
uiConfig, // {component, parentElement?}
commsConfig,
) {
this.interfaceId = interfaceId;
this.commsConfig = commsConfig;
}
async init() {
// If comms exist, we need to destroy it
if (this.comms) {
this.comms.destroy();
}
if (!this.settings) {
this.settings = new Settings({
onSettingsChanged: () => this.reloadSettings(),
logger: this.logger
});
await this.settings.init();
}
this.comms = new CommsClient('content-ui-port', null, this.commsConfig.handlers);
// initialize vuejs, but only once (check handled in initVue())
// we need to initialize this _after_ initializing comms.
this.initVue();
}
async initVue() {
this.vuexStore = createStore(this.storeConfig);
}
async initUi() {
const random = Math.round(Math.random() * 69420);
const uwid = `uw-${this.interfaceId}-root-${random}`
const rootDiv = document.createElement('div');
rootDiv.setAttribute('style', `position: ${uiConfig.style?.position ?? 'relative'}; width: ${uiConfig.style?.width ?? '100%'}; height: ${uiConfig.style?.height ?? '100%'}; ${uiConfig.additionalStyle}`);
rootDiv.setAttribute('id', uwid);
if (this.uiConfig.parentElement) {
this.uiConfig.parentElement.appendChild(rootDiv);
} else {
document.body.appendChild(rootDiv);
}
this.element = rootDiv;
createApp(this.uiConfig.component)
.use(this.vuexStore)
.mount(`${uwid}`);
}
/**
* Replaces ui config and re-inits the UI
* @param {*} newUiConfig
*/
replace(newUiConfig) {
this.element?.remove();
this.uiConfig = newUiConfig;
this.initUi();
}
destroy() {
this.comms?.destroy();
this.element?.remove();
}
}
export default UI;