From 710e9cdc69d9464f284c9c83bd68a7a9847c9493 Mon Sep 17 00:00:00 2001 From: Marius David Wieschollek Date: Sat, 7 Mar 2020 23:29:41 +0100 Subject: Added custom theme section Signed-off-by: Marius David Wieschollek --- package-lock.json | 32 ++- package.json | 2 +- src/js/App/Background.js | 3 + src/js/Controller/Theme/List.js | 7 +- src/js/Controller/Theme/Save.js | 16 ++ src/js/Controller/Theme/Show.js | 13 + src/js/Converter/ThemeConverter.js | 45 +++ src/js/Definition/Theme.json | 12 + src/js/Manager/BadgeManager.js | 20 +- src/js/Manager/ControllerManager.js | 14 + src/js/Manager/ConverterManager.js | 11 +- src/js/Models/Theme/Theme.js | 183 +++++++++++- src/js/Queue/Client/MiningClient.js | 10 + src/js/Queue/FeedbackQueue.js | 5 +- src/js/Repositories/ThemeRepository.js | 141 +++++++++- src/js/Services/SettingsService.js | 5 + src/js/Services/SystemService.js | 8 + src/js/Services/ThemeService.js | 185 +++++++++++- src/js/Services/ToastService.js | 30 +- src/js/Themes/Dark.json | 9 + src/js/Themes/Light.json | 13 +- src/js/preview.js | 1 - src/platform/generic/_locales/de/messages.json | 242 ++++++++++++++-- src/platform/generic/_locales/en/messages.json | 326 +++++++++++++--------- src/scss/_base.scss | 10 +- src/scss/_fonts.scss | 13 + src/scss/_theme.scss | 7 + src/scss/includes.scss | 1 + src/scss/preview.scss | 4 - src/vue/App/Options.vue | 5 +- src/vue/App/Popup.vue | 13 +- src/vue/Components/Collected/MinedProperty.vue | 17 +- src/vue/Components/Form/InputField.vue | 49 ++-- src/vue/Components/Form/SelectField.vue | 29 +- src/vue/Components/Options/Theming.vue | 79 ++++-- src/vue/Components/Popup/Collected.vue | 18 +- src/vue/Components/Tabs.vue | 2 +- src/vue/Components/Theming/BadgeIcon.vue | 61 ++++ src/vue/Components/Theming/CustomColorAlpha.vue | 81 ------ src/vue/Components/Theming/CustomColorInherit.vue | 87 ++++++ src/vue/Components/Theming/CustomColorSet.vue | 31 +- src/vue/Components/Theming/CustomColorToast.vue | 77 +++++ src/vue/Components/Theming/CustomFontFamily.vue | 52 ++++ src/vue/Components/Theming/CustomFontSize.vue | 59 ++++ src/vue/Components/Theming/CustomTheme.vue | 117 ++++++-- src/vue/Components/Theming/PreviewTheme.vue | 80 +++++- src/vue/Components/Toast/Toast.vue | 123 ++++---- webpack.config.js | 17 +- 48 files changed, 1860 insertions(+), 505 deletions(-) create mode 100644 src/js/Controller/Theme/Save.js create mode 100644 src/js/Controller/Theme/Show.js create mode 100644 src/js/Converter/ThemeConverter.js create mode 100644 src/scss/_fonts.scss delete mode 100644 src/scss/preview.scss create mode 100644 src/vue/Components/Theming/BadgeIcon.vue delete mode 100644 src/vue/Components/Theming/CustomColorAlpha.vue create mode 100644 src/vue/Components/Theming/CustomColorInherit.vue create mode 100644 src/vue/Components/Theming/CustomColorToast.vue create mode 100644 src/vue/Components/Theming/CustomFontFamily.vue create mode 100644 src/vue/Components/Theming/CustomFontSize.vue diff --git a/package-lock.json b/package-lock.json index 9df7c0b..dcee739 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6799,9 +6799,9 @@ } }, "terser": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", - "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz", + "integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==", "dev": true, "requires": { "commander": "^2.20.0", @@ -6959,9 +6959,9 @@ } }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", "dev": true }, "tty-browserify": { @@ -7355,9 +7355,9 @@ "dev": true }, "webpack": { - "version": "4.41.6", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.6.tgz", - "integrity": "sha512-yxXfV0Zv9WMGRD+QexkZzmGIh54bsvEs+9aRWxnN8erLWEOehAKUTeNBoUbA6HPEZPlRo7KDi2ZcNveoZgK9MA==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz", + "integrity": "sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==", "dev": true, "requires": { "@webassemblyjs/ast": "1.8.5", @@ -7397,6 +7397,12 @@ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -7407,13 +7413,13 @@ } }, "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "dev": true, "requires": { "big.js": "^5.2.2", - "emojis-list": "^2.0.0", + "emojis-list": "^3.0.0", "json5": "^1.0.1" } }, diff --git a/package.json b/package.json index 28bde59..a86ab81 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "vue-style-loader": "^4.1.2", "vue-template-compiler": "^2.6.11", "webextension-polyfill": "^0.6.0", - "webpack": "^4.41.6", + "webpack": "^4.42.0", "webpack-cli": "^3.3.11" }, "repository": { diff --git a/src/js/App/Background.js b/src/js/App/Background.js index 9a4c7ee..ae6c1d9 100644 --- a/src/js/App/Background.js +++ b/src/js/App/Background.js @@ -12,6 +12,8 @@ import BadgeManager from '@js/Manager/BadgeManager'; import ContextMenuManager from '@js/Manager/ContextMenuManager'; import MiningManager from '@js/Manager/MiningManager'; import NotificationService from '@js/Services/NotificationService'; +import ThemeService from '@js/Services/ThemeService'; +import ThemeRepository from '@js/Repositories/ThemeRepository'; class Background { async init() { @@ -27,6 +29,7 @@ class Background { TabManager.init(); NotificationService.init(); RecommendationManager.init(); + ThemeService.init(ThemeRepository); BadgeManager.init(); ContextMenuManager.init(); MiningManager.init(); diff --git a/src/js/Controller/Theme/List.js b/src/js/Controller/Theme/List.js index 4273d00..1a42419 100644 --- a/src/js/Controller/Theme/List.js +++ b/src/js/Controller/Theme/List.js @@ -4,11 +4,8 @@ import ThemeRepository from '@js/Repositories/ThemeRepository'; export default class List extends AbstractController { async execute(message, reply) { - let themes = await ThemeRepository.findAll(), - list = {}; + let themes = await ThemeRepository.findAll(); - for(let theme of themes) list[theme.getId()] = theme.getLabel(); - - reply.setType('theme.info').setPayload(list); + reply.setType('theme.items').setPayload(themes); } } \ No newline at end of file diff --git a/src/js/Controller/Theme/Save.js b/src/js/Controller/Theme/Save.js new file mode 100644 index 0000000..2e809a4 --- /dev/null +++ b/src/js/Controller/Theme/Save.js @@ -0,0 +1,16 @@ +import AbstractController from '@js/Controller/AbstractController'; +import ThemeRepository from '@js/Repositories/ThemeRepository'; +import ErrorManager from '@js/Manager/ErrorManager'; + +export default class Save extends AbstractController { + + async execute(message, reply) { + try { + await ThemeRepository.update(message.getPayload()); + reply.setPayload({success: true}); + } catch(e) { + ErrorManager.logError(e); + reply.setType('error').setPayload({success: false, message: e.message}); + } + } +} \ No newline at end of file diff --git a/src/js/Controller/Theme/Show.js b/src/js/Controller/Theme/Show.js new file mode 100644 index 0000000..9cb7a62 --- /dev/null +++ b/src/js/Controller/Theme/Show.js @@ -0,0 +1,13 @@ +import AbstractController from '@js/Controller/AbstractController'; +import ThemeRepository from '@js/Repositories/ThemeRepository'; + +export default class Show extends AbstractController { + async execute(message, reply) { + try { + let theme = await ThemeRepository.findById(message.getPayload()); + reply.setType('theme.item').setPayload(theme); + } catch(e) { + reply.setType('error'); + } + } +} \ No newline at end of file diff --git a/src/js/Converter/ThemeConverter.js b/src/js/Converter/ThemeConverter.js new file mode 100644 index 0000000..77133a6 --- /dev/null +++ b/src/js/Converter/ThemeConverter.js @@ -0,0 +1,45 @@ +import Theme from '@js/Models/Theme/Theme'; + +export default class ThemeConverter { + /** + * + * @param {Message} message + * @returns {Message} + */ + convert(message) { + if(message.getType() === 'theme.items') { + return this._covertItems(message); + } else { + return this._covertItem(message); + } + } + + /** + * + * @param {Message} message + * @returns {Message} + */ + _covertItems(message) { + let payload = message.getPayload(), + servers = []; + + if(payload !== null) { + for(let data of payload) { + servers.push(new Theme(data)); + } + } + + return message.setPayload(servers); + } + + /** + * + * @param {Message} message + * @returns {Message} + */ + _covertItem(message) { + let payload = message.getPayload(); + + return message.setPayload(new Theme(payload)); + } +} \ No newline at end of file diff --git a/src/js/Definition/Theme.json b/src/js/Definition/Theme.json index c160dc8..2b166fc 100644 --- a/src/js/Definition/Theme.json +++ b/src/js/Definition/Theme.json @@ -5,7 +5,19 @@ "label": { "type": "string" }, + "font": { + "type": "object" + }, + "variables": { + "type": "object" + }, + "badge": { + "type": "object" + }, "colors": { "type": "object" + }, + "style": { + "type": "boolean" } } \ No newline at end of file diff --git a/src/js/Manager/BadgeManager.js b/src/js/Manager/BadgeManager.js index 793f636..0bf8c6d 100644 --- a/src/js/Manager/BadgeManager.js +++ b/src/js/Manager/BadgeManager.js @@ -4,6 +4,7 @@ import TabManager from '@js/Manager/TabManager'; import ServerManager from '@js/Manager/ServerManager'; import ErrorManager from '@js/Manager/ErrorManager'; import LocalisationService from '@js/Services/LocalisationService'; +import ThemeService from '@js/Services/ThemeService'; class BadgeManager { @@ -64,11 +65,7 @@ class BadgeManager { await this._api.browserAction.setBadgeText({text: '', tabId}); } - if(SystemService.getBrowserPlatform() === 'firefox') { - await this._api.browserAction.setBadgeTextColor({color: '#fff'}); - } - - await this._api.browserAction.setBadgeBackgroundColor({color: '#0082c9'}); + await this._setBadgeTheme(); } catch(e) { ErrorManager.logError(e); } @@ -96,6 +93,19 @@ class BadgeManager { ErrorManager.logError(e); } } + + async _setBadgeTheme() { + if(SystemService.getBrowserPlatform() === 'firefox') { + let color = await ThemeService.getBadgeTextColor(); + await this._api.browserAction.setBadgeTextColor({color}); + } + + let color = await ThemeService.getBadgeBackgroundColor(); + await this._api.browserAction.setBadgeBackgroundColor({color}); + + let icon = await ThemeService.getBadgeIcon(); + await this._api.browserAction.setIcon({path:icon}); + } } export default new BadgeManager(); \ No newline at end of file diff --git a/src/js/Manager/ControllerManager.js b/src/js/Manager/ControllerManager.js index 9bcf97c..20e4ec8 100644 --- a/src/js/Manager/ControllerManager.js +++ b/src/js/Manager/ControllerManager.js @@ -165,6 +165,20 @@ class ControllerManager { await this._executeController(module, message, reply); } ); + MessageService.listen( + 'theme.show', + async (message, reply) => { + let module = await import(/* webpackChunkName: "ThemeShow" */ '@js/Controller/Theme/Show'); + await this._executeController(module, message, reply); + } + ); + MessageService.listen( + 'theme.save', + async (message, reply) => { + let module = await import(/* webpackChunkName: "ThemeSave" */ '@js/Controller/Theme/Save'); + await this._executeController(module, message, reply); + } + ); } /** diff --git a/src/js/Manager/ConverterManager.js b/src/js/Manager/ConverterManager.js index 5ccd273..61df209 100644 --- a/src/js/Manager/ConverterManager.js +++ b/src/js/Manager/ConverterManager.js @@ -1,8 +1,9 @@ +import ErrorManager from '@js/Manager/ErrorManager'; import MessageService from '@js/Services/MessageService'; +import ThemeConverter from '@js/Converter/ThemeConverter'; +import FolderConverter from '@js/Converter/FolderConverter'; import ServerConverter from '@js/Converter/ServerConverter'; import PasswordConverter from '@js/Converter/PasswordConverter'; -import ErrorManager from '@js/Manager/ErrorManager'; -import FolderConverter from '@js/Converter/FolderConverter'; class ConverterManager { @@ -25,6 +26,12 @@ class ConverterManager { await this._executeConverter(FolderConverter, message); } ); + MessageService.convert( + ['theme.items', 'theme.item', 'theme.save', 'theme.preview'], + async (message) => { + await this._executeConverter(ThemeConverter, message); + } + ); } diff --git a/src/js/Models/Theme/Theme.js b/src/js/Models/Theme/Theme.js index b69e10e..6567197 100644 --- a/src/js/Models/Theme/Theme.js +++ b/src/js/Models/Theme/Theme.js @@ -1,5 +1,5 @@ import AbstractModel from 'passwords-client/src/Model/AbstractModel'; -import Properties from '@js/Definition/Theme' +import Properties from '@js/Definition/Theme'; export default class Theme extends AbstractModel { @@ -11,44 +11,207 @@ export default class Theme extends AbstractModel { * @return {Object} */ getId() { - return this.getProperty('id') + return this.getProperty('id'); } - + /** * @param {String} value * @return {this} */ setId(value) { - return this.setProperty('id', value) + return this.setProperty('id', value); } /** * @return {Object} */ getLabel() { - return this.getProperty('label') + return this.getProperty('label'); } - + /** * @param {String} value * @return {this} */ setLabel(value) { - return this.setProperty('label', value) + return this.setProperty('label', value); } /** * @return {Object} */ getColors() { - return this.getProperty('colors') + return this.getProperty('colors'); } - + /** * @param {Object} value * @return {this} */ setColors(value) { - return this.setProperty('colors', value) + return this.setProperty('colors', value); + } + + /** + * @return {Object} + */ + getVariables() { + return this.getProperty('variables'); + } + + /** + * @param {Object} value + * @return {this} + */ + setVariables(value) { + return this.setProperty('variables', value); + } + + /** + * @return {Object} + */ + getBadge() { + return this.getProperty('badge'); + } + + /** + * @param {Object} value + * @return {this} + */ + setBadge(value) { + return this.setProperty('badge', value); + } + + /** + * @return {String} + */ + getBadgeIcon() { + return this._getSubProperty('badge', 'icon'); + } + + /** + * @param {String} value + * @return {this} + */ + setBadgeIcon(value) { + return this._setSubProperty('badge', 'icon', value); + } + + /** + * @return {String} + */ + getBadgeBackgroundColor() { + return this._getSubProperty('badge', 'color-bg'); + } + + /** + * @param {String} value + * @return {this} + */ + setBadgeBackgroundColor(value) { + return this._setSubProperty('badge', 'color-bg', value); + } + + /** + * @return {String} + */ + getBadgeForegroundColor() { + return this._getSubProperty('badge', 'color-fg'); + } + + /** + * @param {String} value + * @return {this} + */ + setBadgeForegroundColor(value) { + return this._setSubProperty('badge', 'color-fg', value); + } + + /** + * @return {Object} + */ + getFont() { + return this.getProperty('font'); + } + + /** + * @param {Object} value + * @return {this} + */ + setFont(value) { + return this.setProperty('font', value); + } + + /** + * @return {String} + */ + getFontFamily() { + return this._getSubProperty('font', 'family'); + } + + /** + * @param {String} value + * @return {this} + */ + setFontFamily(value) { + return this._setSubProperty('font', 'family', value); + } + + /** + * @return {String} + */ + getFontSize() { + return this._getSubProperty('font', 'size'); + } + + /** + * @param {String} value + * @return {this} + */ + setFontSize(value) { + return this._setSubProperty('font', 'size', value); + } + + /** + * @return {Boolean} + */ + getStyle() { + return this.getProperty('style'); + } + + /** + * @param {Boolean} value + * @return {this} + */ + setStyle(value) { + return this.setProperty('style', value); + } + + /** + * @param {String} property + * @param {String} key + * @return {(null|String)} + * @private + */ + _getSubProperty(property, key) { + let data = this.getProperty(property); + if(data !== null && data !== undefined && data.hasOwnProperty(key)) return data[key]; + + return null; + } + + /** + * @param {String} property + * @param {String} key + * @param {String} value + * @return {this} + * @private + */ + _setSubProperty(property, key, value) { + let data = this.getProperty(property); + if(data === null || data === undefined) data = {}; + data[key] = value; + + return this.setProperty(property, data); } } \ No newline at end of file diff --git a/src/js/Queue/Client/MiningClient.js b/src/js/Queue/Client/MiningClient.js index 630bd23..66e6666 100644 --- a/src/js/Queue/Client/MiningClient.js +++ b/src/js/Queue/Client/MiningClient.js @@ -1,14 +1,21 @@ import FeedbackClient from '@js/Queue/Client/FeedbackClient'; import MiningItem from '@js/Models/Queue/MiningItem'; +import EventQueue from '@js/Event/EventQueue'; +import ErrorManager from '@js/Manager/ErrorManager'; class MiningClient extends FeedbackClient { + get update() { + return this._event; + } + constructor() { let worker = (i) => { return this._worker(i); }; let feedbackWorker = (i) => { return this._feedbackWorker(i); }; super('mining', worker, feedbackWorker, MiningItem); this._items = {}; this._solvedItems = {}; + this._event = new EventQueue(); } /** @@ -54,6 +61,9 @@ class MiningClient extends FeedbackClient { resolve, reject }; + + this._event.emit(item) + .catch(ErrorManager.catch); }); } diff --git a/src/js/Queue/FeedbackQueue.js b/src/js/Queue/FeedbackQueue.js index 2a89852..98250e7 100644 --- a/src/js/Queue/FeedbackQueue.js +++ b/src/js/Queue/FeedbackQueue.js @@ -1,14 +1,13 @@ import Queue from '@js/Queue/Queue'; -import MessageService from '@js/Services/MessageService'; import FeedbackItem from '@js/Models/Queue/FeedbackItem'; export default class FeedbackQueue extends Queue { /** * - * @param {String} name + * @param {String} name * @param {(String|null)} [area=null] - * @param {QueueItem} [type=FeedbackItem] + * @param {QueueItem} [type=FeedbackItem] */ constructor(name, area, type = FeedbackItem) { super(name, area, type); diff --git a/src/js/Repositories/ThemeRepository.js b/src/js/Repositories/ThemeRepository.js index 2c4cd6f..3446ed5 100644 --- a/src/js/Repositories/ThemeRepository.js +++ b/src/js/Repositories/ThemeRepository.js @@ -1,12 +1,58 @@ import Theme from '@js/Models/Theme/Theme'; import DarkTheme from '@js/Themes/Dark'; import LightTheme from '@js/Themes/Light'; -import LocalisationService from '@js/Services/LocalisationService'; +import RGB from '@js/Themes/RGB'; +import Hacker from '@js/Themes/Hacker'; +import ArcDark from '@js/Themes/ArcDark'; +import ArcLight from '@js/Themes/ArcLight'; +import OledDarkTheme from '@js/Themes/OledDark'; +import AdaptaTealTheme from '@js/Themes/AdaptaTeal'; +import AdaptaLightTheme from '@js/Themes/AdaptaLight'; +import SettingsService from '@js/Services/SettingsService'; +import StorageService from '@js/Services/StorageService'; +import uuid from 'uuidv4'; +import BooleanState from 'passwords-client/src/State/BooleanState'; class ThemeRepository { + get STORAGE_KEY() { + return 'themes'; + } + constructor() { this._themes = null; + this._customThemes = null; + this._loading = new BooleanState(false); + } + + /** + * + * @param {Theme} theme + */ + async create(theme) { + if(theme.getId() === null || theme.getId() === undefined) { + theme.setId(uuid()); + } + + await this._saveCustomTheme(theme); + if(this._themes !== null) { + this._themes[theme.getId()] = theme; + } + } + + /** + * + * @param {Theme} theme + */ + async update(theme) { + if(theme.getId() === null || theme.getId() === undefined) { + return await this.create(theme); + } + + await this._saveCustomTheme(theme); + if(this._themes !== null) { + this._themes[theme.getId()] = theme; + } } /** @@ -44,29 +90,102 @@ class ThemeRepository { * @private */ async _listThemes() { + if(this._loading.get()) await this._loading.awaitFalse(); if(this._themes !== null) return this._themes; + this._loading.set(true); - this._themes = {}; - let systemThemes = [LightTheme, DarkTheme]; + let themes = {}; + let systemThemes = [LightTheme, DarkTheme, OledDarkTheme, AdaptaLightTheme, AdaptaTealTheme, ArcLight, ArcDark, Hacker, RGB]; for(let data of systemThemes) { - let theme = this._makeSystemTheme(data); + let theme = new Theme(data); - this._themes[theme.getId()] = theme; + themes[theme.getId()] = theme; + } + + let customThemes = await this._loadCustomThemes(); + if(customThemes.length === 0) { + themes.custom = await this._makeCustomTheme(); + } else { + for(let theme of customThemes) { + themes[theme.getId()] = theme; + } } - return this._themes; + this._themes = themes; + this._loading.set(false); + return themes; + } + + async _makeCustomTheme() { + let theme = await SettingsService.getValue('theme.custom'); + + if(theme === null) { + theme = LightTheme; + theme.label = 'ThemeCustom'; + } + + theme.id = 'custom'; + + return new Theme(theme); + } + + /** + * + * @returns {Promise} + * @private + */ + async _loadCustomThemes() { + if(this._customThemes !== null) { + return this._customThemes; + } + + let themes = []; + if(await StorageService.has(this.STORAGE_KEY)) { + let data = await StorageService.get(this.STORAGE_KEY); + + for(let element of data) { + themes.push(new Theme(element)); + } + } + + this._customThemes = themes; + return themes; + } + + /** + * + * @param theme + * @returns {Promise} + * @private + */ + async _saveCustomTheme(theme) { + let themes = await this._loadCustomThemes(); + + for(let i = 0; i < themes.length; i++) { + if(themes[i].getId() === theme.getId()) { + themes[i] = theme; + await this._saveCustomThemeList(themes); + return; + } + } + + themes.push(theme); + await this._saveCustomThemeList(themes); } /** * - * @param {Object} data - * @return {Theme} + * @param {Theme[]} themes + * @return {Promise} * @private */ - _makeSystemTheme(data) { - data.label = LocalisationService.translate(data.label); + async _saveCustomThemeList(themes) { + let objects = []; - return new Theme(data); + for(let theme of themes) { + objects.push(theme.getProperties()); + } + await StorageService.set(this.STORAGE_KEY, objects); } } diff --git a/src/js/Services/SettingsService.js b/src/js/Services/SettingsService.js index 09880cc..717e0f6 100644 --- a/src/js/Services/SettingsService.js +++ b/src/js/Services/SettingsService.js @@ -31,9 +31,14 @@ class SettingsService { 'client.ext.theme.current', 'local.theme.current', 'sync.theme.current' + ], + 'theme.custom' : [ + 'sync.theme.custom', + 'local.theme.custom' ] }; this._defaults = { + 'theme.custom' : null, 'theme.current' : 'light', 'server.default' : null, 'password.autosubmit' : true, diff --git a/src/js/Services/SystemService.js b/src/js/Services/SystemService.js index ee8a836..32aac1f 100644 --- a/src/js/Services/SystemService.js +++ b/src/js/Services/SystemService.js @@ -93,6 +93,14 @@ class SystemService { return this.getBrowserApi().runtime.id; } + /** + * @param {String} path + * @return {Promise} + */ + async getFileUrl(path) { + return await this.getBrowserApi().runtime.getURL(path); + } + /** * @returns {Boolean} */ diff --git a/src/js/Services/ThemeService.js b/src/js/Services/ThemeService.js index 37770df..105230f 100644 --- a/src/js/Services/ThemeService.js +++ b/src/js/Services/ThemeService.js @@ -1,17 +1,72 @@ import SettingsService from '@js/Services/SettingsService'; -import ThemeRepository from '@js/Repositories/ThemeRepository'; +import MessageService from '@js/Services/MessageService'; +import SystemService from '@js/Services/SystemService'; import ErrorManager from '@js/Manager/ErrorManager'; class ThemeService { + get FONT_MAPPING() { + return { + default : '-apple-system, BlinkMacSystemFont, Ubuntu, Calibri, "Helvetica Neue", sans-serif', + mono : 'FreeMono, "Courier New", monospace', + sans : 'Ubuntu, Calibri, "Helvetica Neue", sans-serif', + serif : '"Times New Roman", Numbus, serif', + light : '"Comfortaa Light","Lato Light","Corbel Light","Gill Sans Light", sans-serif', + nextcloud: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', + dyslexic : 'OpenDyslexic, Dyslexie, sans-serif' + }; + } + + constructor() { + /** @type {(ThemeRepository|null)} **/ + this._repository = null; + this._style = null; + } + + /** + * + * @param {ThemeRepository} repository + */ + init(repository) { + this._repository = repository; + } + + + async getBadgeIcon() { + let theme = await this.getCurrentTheme(), + icon = theme.getBadgeIcon(); + + return await SystemService.getBrowserApi().runtime.getURL(`img/${icon}.svg`); + } + + async getBadgeTextColor() { + let theme = await this.getCurrentTheme(); + + return theme.getBadgeForegroundColor(); + } + + async getBadgeBackgroundColor() { + let theme = await this.getCurrentTheme(); + + return theme.getBadgeBackgroundColor(); + } + async apply() { - let theme = await this.getCurrentTheme(), - colors = theme.getColors(); + let theme = await this.getCurrentTheme(); + this.applyTheme(theme); + } - for(let color in colors) { - if(!colors.hasOwnProperty(color)) continue; - document.documentElement.style.setProperty(`--${color}-color`, colors[color]); - } + /** + * + * @param {Theme} theme + */ + applyTheme(theme) { + this._createStyleSheet( + theme, + this._applyFont(theme.getFont()), + this._applyColors(theme.getColors()), + this._applyVariables(theme.getVariables()) + ); } /** @@ -21,12 +76,118 @@ class ThemeService { async getCurrentTheme() { let current = await SettingsService.getValue('theme.current'); - try { - return await ThemeRepository.findById(current); - } catch(e) { - ErrorManager.logError(e); - return await ThemeRepository.findById('light'); + if(this._repository !== null) { + try { + return await this._repository.findById(current); + } catch(e) { + ErrorManager.logError(e); + return await this._repository.findById('light'); + } + } + + let reply = await MessageService.send({type: 'theme.show', payload: current}); + if(reply.getType() === 'theme.item') return reply.getPayload(); + + reply = await MessageService.send({type: 'theme.show', payload: 'light'}); + return reply.getPayload(); + } + + /** + * + * @param {Object} colors + * @return {{}} + * @private + */ + _applyColors(colors) { + if(!colors) return {}; + + let css = {}; + for(let color in colors) { + if(!colors.hasOwnProperty(color)) continue; + css[`--${color}-color`] = colors[color]; + } + + for(let toast of ['info', 'success', 'warning', 'error']) { + let color = `${toast}-fg`; + if(colors.hasOwnProperty(color)) { + css[`--${toast}-hv-color`] = `${colors[color]}40`; + } + } + + return css; + } + + /** + * + * @param {Object} font + * @return {{}} + * @private + */ + _applyFont(font) { + if(!font) return {}; + + let css = {}; + if(font.hasOwnProperty('family') && font.family) { + let mapping = this.FONT_MAPPING; + + if(mapping.hasOwnProperty(font.family)) { + css['--font-family'] = mapping[font.family]; + } else { + css['--font-family'] = font.family; + } } + + if(font.hasOwnProperty('size') && font.size) css['--font-size'] = font.size; + + return css; + } + + /** + * + * @param {Object} variables + * @return {{}} + * @private + */ + _applyVariables(variables) { + if(!variables) return {}; + + let css = {}; + for(let variable in variables) { + if(!variables.hasOwnProperty(variable)) continue; + css[`--${variable}`] = variables[variable]; + } + + return css; + } + + /** + * + * @param {Theme} theme + * @param {Object} variables + * @private + */ + _createStyleSheet(theme, ...variables) { + let css = ''; + variables = Object.assign(...variables); + for(let variable in variables) { + if(!variables.hasOwnProperty(variable)) continue; + + let value = variables[variable].replace(';', ''), + key = variable.replace(';', ''); + + css += `${key}: ${value};`; + } + + css = `:root { ${css} }`; + if(theme.getStyle()) css = `@import url("/css/themes/${theme.getId()}.css");\n${css}`; + + if(this._style === null) { + this._style = document.createElement('style'); + this._style.setAttribute('type', 'text/css'); + document.body.appendChild(this._style); + } + + this._style.innerHTML = css; } } diff --git a/src/js/Services/ToastService.js b/src/js/Services/ToastService.js index bb2bacc..088722b 100644 --- a/src/js/Services/ToastService.js +++ b/src/js/Services/ToastService.js @@ -40,15 +40,26 @@ class ToastService { } } + /** + * + * @param {(String|String[])} message The text of the toast + * @param {(String|String[])} [title=null] The title of the toast + * @param {Number} [ttl=3] Time before the toast is closed + * @return {Promise} + */ + success(message, title = null, ttl = 3) { + return this.create({type: 'success', title, message, closeable: true, ttl}); + } /** * * @param {(String|String[])} message The text of the toast * @param {(String|String[])} [title=null] The title of the toast + * @param {Number} [ttl=10] Time before the toast is closed * @return {Promise} */ - warning(message, title = null) { - return this.create({type: 'warning', title, message, closeable: true, ttl: 10}); + warning(message, title = null, ttl = 10) { + return this.create({type: 'warning', title, message, closeable: true, ttl}); } @@ -56,10 +67,11 @@ class ToastService { * * @param {(String|String[])} message The text of the toast * @param {(String|String[])} [title=null] The title of the toast + * @param {Number} [ttl=10] Time before the toast is closed * @return {Promise} */ - error(message, title = null) { - return this.create({type: 'error', title, message, closeable: true, ttl: 10}); + error(message, title = null, ttl = 10) { + return this.create({type: 'error', title, message, closeable: true, ttl}); } /** @@ -80,16 +92,6 @@ class ToastService { return this.create(config); } - /** - * - * @param {(String|String[])} message The text of the toast - * @param {(String|String[])} [title=null] The title of the toast - * @return {Promise} - */ - success(message, title = null) { - return this.create({type: 'success', title, message, closeable: true, ttl: 3}); - } - /** * diff --git a/src/js/Themes/Dark.json b/src/js/Themes/Dark.json index 0de7c67..5dc7752 100644 --- a/src/js/Themes/Dark.json +++ b/src/js/Themes/Dark.json @@ -1,6 +1,15 @@ { "id": "dark", "label": "ThemeDark", + "badge" : { + "color-bg": "#0c63db", + "color-fg": "#d8d8d8", + "icon" : "passwords-light" + }, + "font" : { + "family": "default", + "size" : "11pt" + }, "colors": { "element-bg" : "#181818", "element-fg" : "#d8d8d8", diff --git a/src/js/Themes/Light.json b/src/js/Themes/Light.json index bd3269f..cd4a39a 100644 --- a/src/js/Themes/Light.json +++ b/src/js/Themes/Light.json @@ -1,6 +1,15 @@ { "id" : "light", "label" : "ThemeLight", + "badge" : { + "color-bg": "#0652dd", + "color-fg": "#ffffff", + "icon" : "passwords-dark" + }, + "font" : { + "family": "default", + "size" : "11pt" + }, "colors": { "element-bg" : "#ffffff", "element-fg" : "#000000", @@ -10,8 +19,8 @@ "element-active-fg" : "#0996f8", "element-active-hover-bg": "#ededed", "element-active-hover-fg": "#0670cc", - "button-bg" : "#00000000", - "button-fg" : "#00000000", + "button-bg" : "inherit", + "button-fg" : "inherit", "button-hover-bg" : "#0996f8", "button-hover-fg" : "#ffffff", "info-bg" : "#0652dd", diff --git a/src/js/preview.js b/src/js/preview.js index 6297ff6..3bead7b 100644 --- a/src/js/preview.js +++ b/src/js/preview.js @@ -1,6 +1,5 @@ import Preview from '@js/App/Preview'; import '@js/Prototype/prototype'; -import '@scss/preview.scss' // noinspection JSUnresolvedVariable __webpack_public_path__ = `/`; diff --git a/src/platform/generic/_locales/de/messages.json b/src/platform/generic/_locales/de/messages.json index a2fcfc4..6d175d5 100644 --- a/src/platform/generic/_locales/de/messages.json +++ b/src/platform/generic/_locales/de/messages.json @@ -489,7 +489,27 @@ } } }, - "SettingsCurrentTheme" : { + "DemoInfoNotification" : { + "message" : "Infomeldung", + "description": "" + }, + "DemoSuccessNotification" : { + "message" : "Erfolgsmeldung", + "description": "" + }, + "DemoWarningNotification" : { + "message" : "Warnmeldung", + "description": "" + }, + "DemoErrorNotification" : { + "message" : "Fehlermeldung", + "description": "" + }, + "DemoText" : { + "message" : "Demo", + "description": "" + }, + "SettingsThemeId" : { "message" : "Aktives Thema", "description": "" }, @@ -497,64 +517,236 @@ "message" : "Dunkles Thema", "description": "" }, + "ThemeOledDark" : { + "message" : "Dunkles OLED Thema", + "description": "" + }, "ThemeLight" : { - "message" : "Helles Thema", + "message" : "Standard", "description": "" }, - "DemoInfoNotification" : { - "message" : "Info Meldung", + "ThemeAdaptaLight" : { + "message" : "Adapta", "description": "" }, - "DemoSuccessNotification" : { - "message" : "Erfolgsmeldung", + "ThemeAdaptaTeal" : { + "message" : "Adapta teal", "description": "" }, - "DemoWarningNotification" : { - "message" : "Warnmeldung", + "ThemeArcDark" : { + "message" : "Arc dark", "description": "" }, - "DemoErrorNotification" : { - "message" : "Fehlermeldung", + "ThemeArcLight" : { + "message" : "Arc", "description": "" }, - "DemoDataInsert" : { - "message" : "Demo-Daten einfügen", + "ThemeHacker" : { + "message" : "Hacker", "description": "" }, - "DemoText" : { - "message" : "Demo", + "ThemeRGB" : { + "message" : "Gaming RGB", "description": "" }, - "SettingsCustomTheme" : { + "ThemeCustom" : { "message" : "Eigenes Thema", "description": "" }, + "CustomFont" : { + "message" : "Schriftart", + "description": "" + }, "CustomDefaultElement" : { - "message" : "Normales Element", + "message" : "Normale Elemente", "description": "" }, "CustomActiveElement" : { - "message" : "Aktives Element", + "message" : "Aktive Elemente", "description": "" }, "CustomButtons" : { "message" : "Schaltflächen", "description": "" }, - "CustomColorsBackground" : { - "message" : "Farbe Hintergrund", + "CustomToasts" : { + "message" : "Mini-Benachrichtigungen", + "description": "" + }, + "CustomBadge" : { + "message" : "Browser-Symbol", + "description": "" + }, + "BackgroundColorLabel" : { + "message" : "Hintergrundfarbe", + "description": "" + }, + "ForegroundColorLabel" : { + "message" : "Vordergrundfarbe", + "description": "" + }, + "BackgroundInheritLabel" : { + "message" : "Hintergrund erben", + "description": "" + }, + "ForegroundInheritLabel" : { + "message" : "Vordergrund erben", + "description": "" + }, + "BackgroundColorBaseTitle" : { + "message" : "Diese Farbe wird im Normalfall für den Hintergrund verwendet", + "description": "" + }, + "BackgroundColorHoverTitle" : { + "message" : "Diese Farbe wird für den Hintergrund verwendet wenn das Element mit der Maus aktiviert wird. Diese Farbe kann auch für Umrandungen verwendet werden wenn das Element nicht aktiviert ist.", + "description": "" + }, + "ForegroundColorBaseTitle" : { + "message" : "Diese Farbe wird im Normalfall für den Vordergrund verwendet", + "description": "" + }, + "ForegroundColorHoverTitle" : { + "message" : "Diese Farbe wird für den Vordergrund und Umrandungen verwendet wenn das Element mit der Maus überfahren wird", + "description": "" + }, + "BackgroundInheritTitle": { + "message" : "Falls aktiviert wird die Farbe des übergeordneten Elements übernommen", + "description": "" + }, + "ForegroundInheritTitle": { + "message" : "Falls aktiviert wird die Farbe des übergeordneten Elements übernommen", + "description": "" + }, + "SettingsCustomFont" : { + "message" : "Schriftart", + "description": "" + }, + "FontDefault" : { + "message" : "Standard", + "description": "" + }, + "FontMono" : { + "message" : "Monospace-Schrift", + "description": "" + }, + "FontSerif" : { + "message" : "Serifenschrift", + "description": "" + }, + "FontSans" : { + "message" : "Serifenlose Schrift", + "description": "" + }, + "FontLight" : { + "message" : "Dünne Schrift", + "description": "" + }, + "FontNextcloud" : { + "message" : "Nextcloud", + "description": "" + }, + "FontOpenDyslexic" : { + "message" : "Open Dyslexic", + "description": "" + }, + "FontCustom" : { + "message" : "Eigene Schrift", + "description": "" + }, + "SettingsCustomFontSize" : { + "message" : "Schriftgröße", + "description": "" + }, + "FontSizeVerySmall" : { + "message" : "Sehr klein", + "description": "" + }, + "FontSizeSmall" : { + "message" : "Klein", + "description": "" + }, + "FontSizeDefault" : { + "message" : "Standard", + "description": "" + }, + "FontSizeMedium" : { + "message" : "Medium", + "description": "" + }, + "FontSizeLarge" : { + "message" : "Groß", + "description": "" + }, + "FontSizeVeryLarge" : { + "message" : "Sehr groß", + "description": "" + }, + "ToastInfoColors" : { + "message" : "Infomeldung", + "description": "" + }, + "ToastSuccessColors" : { + "message" : "Erfolgsmeldung", + "description": "" + }, + "ToastWarningColors" : { + "message" : "Warnmeldung", + "description": "" + }, + "ToastErrorColors" : { + "message" : "Fehlermeldung", + "description": "" + }, + "ToastBackgroundTitle" : { + "message" : "Hintergrundfarbe für die Mini-Benachrichtigung", + "description": "" + }, + "ToastForegroundTitle" : { + "message" : "Farbe für Texte und Schaltflächen", + "description": "" + }, + "SettingsBadgeIcon" : { + "message" : "Symbolleisten-Symbol", + "description": "" + }, + "BadgeIconAuto" : { + "message" : "Automatisch", + "description": "" + }, + "BadgeIconLight" : { + "message" : "Helles Symbol", + "description": "" + }, + "BadgeIconMedium" : { + "message" : "Graues Symbol", + "description": "" + }, + "BadgeIconDark" : { + "message" : "Dunkles Symbol", + "description": "" + }, + "BadgeIconNewLight" : { + "message" : "Helles modernes Symbol", + "description": "" + }, + "BadgeIconNewMedium" : { + "message" : "Graues modernes Symbol", + "description": "" + }, + "BadgeIconNewDark" : { + "message" : "Dunkles modernes Symbol", "description": "" }, - "CustomColorsForeground" : { - "message" : "Farbe Vordergrund", + "CustomBadgeColors" : { + "message" : "Abzeichenfarbe", "description": "" }, - "CustomAlphaBackground" : { - "message" : "Transparenz Hintergrund", + "BadgeBackgroundTitle" : { + "message" : "Hintergrundfarbe des Abzeichens", "description": "" }, - "CustomAlphaForeground" : { - "message" : "Transparenz Vordergrund", + "BadgeForegroundTitle" : { + "message" : "Textfarbe des Abzeichens", "description": "" } } \ No newline at end of file diff --git a/src/platform/generic/_locales/en/messages.json b/src/platform/generic/_locales/en/messages.json index 5491456..29ad693 100644 --- a/src/platform/generic/_locales/en/messages.json +++ b/src/platform/generic/_locales/en/messages.json @@ -503,7 +503,27 @@ } } }, - "SettingsCurrentTheme" : { + "DemoInfoNotification" : { + "message" : "Info notification", + "description": "" + }, + "DemoSuccessNotification" : { + "message" : "Success notification", + "description": "" + }, + "DemoWarningNotification" : { + "message" : "Warning notification", + "description": "" + }, + "DemoErrorNotification" : { + "message" : "Error notification", + "description": "" + }, + "DemoText" : { + "message" : "Demo", + "description": "" + }, + "SettingsThemeId" : { "message" : "Active theme", "description": "" }, @@ -511,38 +531,46 @@ "message" : "Dark theme", "description": "" }, + "ThemeOledDark" : { + "message" : "Dark OLED", + "description": "" + }, "ThemeLight" : { - "message" : "Light theme", + "message" : "Default", "description": "" }, - "DemoInfoNotification" : { - "message" : "Info notification", + "ThemeAdaptaLight" : { + "message" : "Adapta", "description": "" }, - "DemoSuccessNotification" : { - "message" : "Success notification", + "ThemeAdaptaTeal" : { + "message" : "Adapta teal", "description": "" }, - "DemoWarningNotification" : { - "message" : "Warning notification", + "ThemeArcDark" : { + "message" : "Arc dark", "description": "" }, - "DemoErrorNotification" : { - "message" : "Error notification", + "ThemeArcLight" : { + "message" : "Arc", "description": "" }, - "DemoDataInsert" : { - "message" : "Insert demo data", + "ThemeHacker" : { + "message" : "Hacker", "description": "" }, - "DemoText" : { - "message" : "Demo", + "ThemeRGB" : { + "message" : "Gaming RGB", "description": "" }, - "SettingsCustomTheme" : { + "ThemeCustom" : { "message" : "Custom theme", "description": "" }, + "CustomFont" : { + "message" : "Font", + "description": "" + }, "CustomDefaultElement" : { "message" : "Default element", "description": "" @@ -555,156 +583,184 @@ "message" : "Buttons", "description": "" }, - "CustomColorsBackground" : { + "CustomToasts" : { + "message" : "Toast notifications", + "description": "" + }, + "CustomBadge" : { + "message" : "Browser icon", + "description": "" + }, + "BackgroundColorLabel" : { "message" : "Background color", "description": "" }, - "CustomColorsForeground" : { + "ForegroundColorLabel" : { "message" : "Foreground color", "description": "" }, - "CustomAlphaBackground" : { - "message" : "Background transparent", + "BackgroundInheritLabel" : { + "message" : "Inherit background", "description": "" }, - "CustomAlphaForeground" : { - "message" : "Foreground transparent", + "ForegroundInheritLabel" : { + "message" : "Inherit foreground", "description": "" }, - - - - - - - - - - - "PasswordRequestFailedTitle" : { - "message" : "Nextcloud password request failed", - "description": "Notification title when the password request fails" + "BackgroundColorBaseTitle" : { + "message" : "This color is usually used for the background", + "description": "" }, - "PasswordRequestFailedText" : { - "message" : "The password list could not be retrieved.\nError: $ERROR$", - "description" : "Notification text when the password request fails", - "placeholders": { - "error": { - "content": "$1", - "example": "Unknown Error" - } - } + "BackgroundColorHoverTitle" : { + "message" : "This color is used for the background if the element is active. It can also be used for the border of an inactive element", + "description": "" }, - "PasswordEncodingFailedTitle" : { - "message" : "Could not parse password properties", - "description": "Notification title when the password properties can not be parsed" + "ForegroundColorBaseTitle" : { + "message" : "This color is usually used for texts and borders", + "description": "" }, - "PasswordEncodingFailedText" : { - "message" : "The properties of password #$ID$ do not contain valid JSON data and could not be processed", - "description" : "Notification text when the password properties can not be parsed", - "placeholders": { - "id": { - "content": "$1", - "example": "0" - } - } + "ForegroundColorHoverTitle" : { + "message" : "This color is used for texts and borders if the element is active", + "description": "" }, - "PasswordCreatedTitle" : { - "message" : "Password saved", - "description": "Notification title when a password was saved" + "BackgroundInheritTitle": { + "message" : "If activated, the value will be inherited from the parent element", + "description": "" }, - "PasswordCreatedText" : { - "message" : "The password was stored sucessfully", - "description": "Notification text when a password was saved" + "ForegroundInheritTitle": { + "message" : "If activated, the value will be inherited from the parent element", + "description": "" }, - "CreatePasswordFailedTitle" : { - "message" : "Save password failed", - "description": "Notification title when saving a password failed" + "SettingsCustomFont" : { + "message" : "Font family", + "description": "" }, - "CreatePasswordFailedText" : { - "message" : "The password could not be saved", - "description": "Notification text when saving a password failed" + "FontDefault" : { + "message" : "Default", + "description": "" }, - "UpdatedLoginDetectedTitle" : { - "message" : "Updated login found", - "description": "Notification title when an updated login is detected" + "FontMono" : { + "message" : "Monospace", + "description": "" }, - "UpdatedLoginDetectedText" : { - "message" : "Click here to update the password for \"$USER$\"", - "description" : "Notification text when an updated login is detected", - "placeholders": { - "user": { - "content": "$1", - "example": "username" - } - } + "FontSerif" : { + "message" : "Serif", + "description": "" }, - "PasswordUpdatedTitle" : { - "message" : "Password updated", - "description": "Notification title when a password was updated" + "FontSans" : { + "message" : "Sans serif", + "description": "" }, - "PasswordUpdatedText" : { - "message" : "The password was updated sucessfully", - "description": "Notification text when a password was updated" + "FontLight" : { + "message" : "Light", + "description": "" }, - "UpdatePasswordFailedTitle" : { - "message" : "Update password failed", - "description": "Notification title when updating a password failed" + "FontNextcloud" : { + "message" : "Nextcloud", + "description": "" }, - "UpdatePasswordFailedText" : { - "message" : "The password could not be updated", - "description": "Notification text when updating a password failed" + "FontOpenDyslexic" : { + "message" : "Open Dyslexic", + "description": "" }, - "ApiLogin200" : { - "message" : "URL invalid or API disabled", - "description": "Error text when an api request could not be decoded" + "FontCustom" : { + "message" : "Custom", + "description": "" }, - "ApiLogin401" : { - "message" : "Invalid user credentials", - "description": "Error text when an api request got a 401 Unauthorized response" + "SettingsCustomFontSize" : { + "message" : "Font size", + "description": "" }, - "ApiLogin403" : { - "message" : "Access denied", - "description": "Error text when an api request got a 403 Forbidden response" + "FontSizeVerySmall" : { + "message" : "Very small", + "description": "" }, - "ApiLogin404" : { - "message" : "URL is invalid", - "description": "Error text when an api request got a 404 Not Found response" + "FontSizeSmall" : { + "message" : "Small", + "description": "" }, - "ApiLogin500" : { - "message" : "Server crashed during request", - "description": "Error text when an api request got a 50x Internal Server Error response" + "FontSizeDefault" : { + "message" : "Default", + "description": "" }, - "ApiLoginGeneric" : { - "message" : "Http Error $STATUS$: $MESSAGE$", - "description" : "Generic error message for failed requests", - "placeholders": { - "status" : { - "content": "$1", - "example": "HTTP status code" - }, - "message": { - "content": "$2", - "example": "HTTP status text" - } - } + "FontSizeMedium" : { + "message" : "Medium", + "description": "" }, - "SettingsInvalidUrl" : { - "message" : "The url $URL$ seems to be invalid.\n\n If the url of the app is\nhttps://example.com/index.php/apps/passwords\nyou need to enter\nhttps://example.com\n\nIf the url of the app is\nhttps://example.com/nextcloud/index.php/apps/passwords\nyou need to enter\nhttps://example.com/nextcloud", - "description" : "Generic error message for failed requests", - "placeholders": { - "url": { - "content": "$1", - "example": "The user input" - } - } + "FontSizeLarge" : { + "message" : "Large", + "description": "" + }, + "FontSizeVeryLarge" : { + "message" : "Very large", + "description": "" + }, + "ToastInfoColors" : { + "message" : "Info toast", + "description": "" + }, + "ToastSuccessColors" : { + "message" : "Success toast", + "description": "" + }, + "ToastWarningColors" : { + "message" : "Warning toast", + "description": "" + }, + "ToastErrorColors" : { + "message" : "Error toast", + "description": "" + }, + "ToastBackgroundTitle" : { + "message" : "Background color for popup messages", + "description": "" + }, + "ToastForegroundTitle" : { + "message" : "Color for text and button backgrounds", + "description": "" }, - "DarkMode" : { - "message" : "Dark Mode", - "description": "Lable of the Dark Mode setting" + "SettingsBadgeIcon" : { + "message" : "Toolbar icon", + "description": "" + }, + "BadgeIconAuto" : { + "message" : "Automatic", + "description": "" + }, + "BadgeIconLight" : { + "message" : "Light icon", + "description": "" + }, + "BadgeIconMedium" : { + "message" : "Grey icon", + "description": "" }, - "Url" : { "message": "Url" }, - "Password" : { "message": "Password" }, - "User" : { "message": "User" }, - "Save" : { "message": "Save" } + "BadgeIconDark" : { + "message" : "Dark icon", + "description": "" + }, + "BadgeIconNewLight" : { + "message" : "Light modern icon", + "description": "" + }, + "BadgeIconNewMedium" : { + "message" : "Grey modern icon", + "description": "" + }, + "BadgeIconNewDark" : { + "message" : "Dark modern icon", + "description": "" + }, + "CustomBadgeColors" : { + "message" : "Badge colors", + "description": "" + }, + "BadgeBackgroundTitle" : { + "message" : "Background color of the badge", + "description": "" + }, + "BadgeForegroundTitle" : { + "message" : "Text color of the badge", + "description": "" + } } \ No newline at end of file diff --git a/src/scss/_base.scss b/src/scss/_base.scss index 1f625a8..cd15ae8 100644 --- a/src/scss/_base.scss +++ b/src/scss/_base.scss @@ -1,4 +1,11 @@ +:root { + font-family : var(--font-family); + font-size : var(--font-size); +} + body { + font-family : var(--font-family); + font-size : var(--font-size); background-color : var(--element-bg-color); color : var(--element-fg-color); margin : 0; @@ -10,6 +17,7 @@ body { input, select, button, { - font-size : 1rem; + font-family : var(--font-family); + font-size : 1rem; } } \ No newline at end of file diff --git a/src/scss/_fonts.scss b/src/scss/_fonts.scss new file mode 100644 index 0000000..9a2365c --- /dev/null +++ b/src/scss/_fonts.scss @@ -0,0 +1,13 @@ +@font-face { + font-family : "OpenDyslexic"; + src : url('../../fonts/OpenDyslexic/OpenDyslexic-Regular.woff'); + font-weight : normal; + font-style : normal; +} + +@font-face { + font-family : "OpenDyslexic"; + src : url('../../fonts/OpenDyslexic/OpenDyslexic-Bold.woff'); + font-weight : bold; + font-style : normal; +} \ No newline at end of file diff --git a/src/scss/_theme.scss b/src/scss/_theme.scss index 4d00cdb..b837253 100644 --- a/src/scss/_theme.scss +++ b/src/scss/_theme.scss @@ -1,4 +1,7 @@ :root { + --font-family : initial; + --font-size : 11pt; + /** * Generic content elements */ @@ -39,10 +42,14 @@ */ --info-bg-color : #0652dd; --info-fg-color : #fff; + --info-hv-color : #ffffff40; --warning-bg-color : #ffc312; --warning-fg-color : #fff; + --warning-hv-color : #ffffff40; --error-bg-color : #ff3f34; --error-fg-color : #fff; + --error-hv-color : #ffffff40; --success-bg-color : #05c46b; --success-fg-color : #fff; + --success-hv-color : #ffffff40; } \ No newline at end of file diff --git a/src/scss/includes.scss b/src/scss/includes.scss index 8c7dde7..8ff7dde 100644 --- a/src/scss/includes.scss +++ b/src/scss/includes.scss @@ -1,2 +1,3 @@ @import "base"; +@import "fonts"; @import "theme"; \ No newline at end of file diff --git a/src/scss/preview.scss b/src/scss/preview.scss deleted file mode 100644 index 9b7c637..0000000 --- a/src/scss/preview.scss +++ /dev/null @@ -1,4 +0,0 @@ -body { - font-family : Ubuntu, Verdana, sans-serif; - font-size : 11pt; -} \ No newline at end of file diff --git a/src/vue/App/Options.vue b/src/vue/App/Options.vue index f85cb4b..fb69700 100644 --- a/src/vue/App/Options.vue +++ b/src/vue/App/Options.vue @@ -11,9 +11,9 @@ \ No newline at end of file diff --git a/src/vue/Components/Theming/CustomColorInherit.vue b/src/vue/Components/Theming/CustomColorInherit.vue new file mode 100644 index 0000000..092c763 --- /dev/null +++ b/src/vue/Components/Theming/CustomColorInherit.vue @@ -0,0 +1,87 @@ + + + + + \ No newline at end of file diff --git a/src/vue/Components/Theming/CustomColorSet.vue b/src/vue/Components/Theming/CustomColorSet.vue index 3960acd..8d0dbe8 100644 --- a/src/vue/Components/Theming/CustomColorSet.vue +++ b/src/vue/Components/Theming/CustomColorSet.vue @@ -1,16 +1,17 @@ + + \ No newline at end of file diff --git a/src/vue/Components/Theming/CustomFontFamily.vue b/src/vue/Components/Theming/CustomFontFamily.vue new file mode 100644 index 0000000..4e521e0 --- /dev/null +++ b/src/vue/Components/Theming/CustomFontFamily.vue @@ -0,0 +1,52 @@ + + + \ No newline at end of file diff --git a/src/vue/Components/Theming/CustomFontSize.vue b/src/vue/Components/Theming/CustomFontSize.vue new file mode 100644 index 0000000..9414057 --- /dev/null +++ b/src/vue/Components/Theming/CustomFontSize.vue @@ -0,0 +1,59 @@ + + + \ No newline at end of file diff --git a/src/vue/Components/Theming/CustomTheme.vue b/src/vue/Components/Theming/CustomTheme.vue index 8013b0a..f06b9e3 100644 --- a/src/vue/Components/Theming/CustomTheme.vue +++ b/src/vue/Components/Theming/CustomTheme.vue @@ -1,38 +1,113 @@