•
diff --git a/package-lock.json b/package-lock.json
index 8c734e03b..1d32ccc1a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3248,6 +3248,11 @@
"tslib": "^2.0.3"
}
},
+ "hex-to-rgba": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/hex-to-rgba/-/hex-to-rgba-2.0.1.tgz",
+ "integrity": "sha512-5XqPJBpsEUMsseJUi2w2Hl7cHFFi3+OO10M2pzAvKB1zL6fc+koGMhmBqoDOCB4GemiRM/zvDMRIhVw6EkB8dQ=="
+ },
"hookable": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
diff --git a/package.json b/package.json
index 63880b684..0b79997d3 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,8 @@
"@vueuse/components": "^10.1.2",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.7",
- "focus-trap": "^7.4.3"
+ "focus-trap": "^7.4.3",
+ "hex-to-rgba": "^2.0.1"
},
"overrides": {
"vue": "latest"
diff --git a/store/callback.ts b/store/callback.ts
index 5661bae42..9b4f98200 100644
--- a/store/callback.ts
+++ b/store/callback.ts
@@ -29,18 +29,17 @@ export const useCallbackStore = defineStore('callback', () => {
...payload,
sender: window.location.href,
});
- // @todo don't save to store
encryptedMessage.value = AES.encrypt(stringifiedData, encryptKey).toString();
// build and go to url
const destinationUrl = new URL(url);
console.debug('[send]', encryptedMessage.value, url);
destinationUrl.searchParams.set('data', encryptedMessage.value);
- window.location.href = destinationUrl;
+ window.location.href = destinationUrl.toString();
};
const watcher = () => {
console.debug('[watcher]');
- const currentUrl = new URL(window.location);
+ const currentUrl = new URL(window.location.toString());
const callbackValue = currentUrl.searchParams.get('data');
console.debug('[watcher]', { callbackValue });
if (!callbackValue) {
diff --git a/store/server.ts b/store/server.ts
index ef111679e..e3cb659c3 100644
--- a/store/server.ts
+++ b/store/server.ts
@@ -4,6 +4,7 @@ import { ArrowRightOnRectangleIcon, GlobeAltIcon, KeyIcon } from '@heroicons/vue
import { useAccountStore } from './account';
import { usePurchaseStore } from "./purchase";
import { useTrialStore } from './trial';
+import { useThemeStore } from './theme';
import type {
Server,
ServerAccountCallbackSendPayload,
@@ -12,6 +13,7 @@ import type {
ServerStateData,
ServerStateDataAction,
} from '~/types/server';
+import type { Theme } from '~/types/theme';
/**
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
* @see https://github.com/vuejs/pinia/discussions/1085
@@ -21,6 +23,7 @@ setActivePinia(createPinia());
export const useServerStore = defineStore('server', () => {
const accountStore = useAccountStore();
const purchaseStore = usePurchaseStore();
+ const themeStore = useThemeStore();
const trialStore = useTrialStore();
/**
* State
@@ -47,6 +50,7 @@ export const useServerStore = defineStore('server', () => {
const regGuid = ref('');
const site = ref('');
const state = ref(''); // @todo implement ServerState ENUM
+ const theme = ref();
const uptime = ref(0);
const username = ref(''); // @todo potentially move to a user store
const wanFQDN = ref('');
@@ -83,6 +87,7 @@ export const useServerStore = defineStore('server', () => {
regGuid: regGuid.value,
site: site.value,
state: state.value,
+ theme: theme.value,
uptime: uptime.value,
username: username.value,
wanFQDN: wanFQDN.value,
@@ -409,12 +414,17 @@ export const useServerStore = defineStore('server', () => {
if (typeof data?.regGuid !== 'undefined') regGuid.value = data.regGuid;
if (typeof data?.site !== 'undefined') site.value = data.site;
if (typeof data?.state !== 'undefined') state.value = data.state;
+ if (typeof data?.theme !== 'undefined') theme.value = data.theme;
if (typeof data?.uptime !== 'undefined') uptime.value = data.uptime;
if (typeof data?.username !== 'undefined') username.value = data.username;
if (typeof data?.wanFQDN !== 'undefined') wanFQDN.value = data.wanFQDN;
console.debug('[setServer] server.value', server.value);
};
+ watch(theme, () => {
+ if (theme.value) themeStore.setTheme(theme.value);
+ });
+
return {
// state
apiKey,
diff --git a/store/theme.ts b/store/theme.ts
index e69de29bb..b5054b063 100644
--- a/store/theme.ts
+++ b/store/theme.ts
@@ -0,0 +1,59 @@
+import { defineStore, createPinia, setActivePinia } from "pinia";
+import hexToRgba from 'hex-to-rgba';
+import type { Theme } from "~/types/theme";
+/**
+ * @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
+ * @see https://github.com/vuejs/pinia/discussions/1085
+ */
+setActivePinia(createPinia());
+
+export const useThemeStore = defineStore('theme', () => {
+ // State
+ const serverTheme = ref();
+ // Getters
+ const darkMode = computed(() => (serverTheme.value?.name === 'black' || serverTheme.value?.name === 'azure') ?? false);
+ // Actions
+ const setTheme = (data: Theme) => {
+ console.debug('[setTheme]');
+ serverTheme.value = data;
+ };
+ const setCssVars = () => {
+ const body = document.body;
+ const defaultColors = {
+ darkTheme: {
+ alpha: '#1c1b1b',
+ beta: '#f2f2f2',
+ gamma: '#999999',
+ },
+ lightTheme: {
+ alpha: '#f2f2f2',
+ beta: '#1c1b1b',
+ gamma: '#999999',
+ },
+ };
+ let { alpha, beta, gamma } = darkMode.value ? defaultColors.darkTheme : defaultColors.lightTheme;
+ // overwrite with hex colors set in webGUI @ /Settings/DisplaySettings
+ if (serverTheme.value?.textColor) alpha = serverTheme.value?.textColor;
+ if (serverTheme.value?.bgColor) {
+ beta = serverTheme.value?.bgColor;
+ body.style.setProperty('--color-customgradient-start', hexToRgba(beta, 0));
+ body.style.setProperty('--color-customgradient-end', hexToRgba(beta, 0.9));
+ }
+ if (serverTheme.value?.metaColor) gamma = serverTheme.value?.metaColor;
+ body.style.setProperty('--color-alpha', alpha);
+ body.style.setProperty('--color-beta', beta);
+ body.style.setProperty('--color-gamma', gamma);
+ // box shadow
+ body.style.setProperty('--shadow-beta', `0 25px 50px -12px ${hexToRgba(beta, 0.15)}`);
+ body.style.setProperty('--ring-offset-shadow', `0 0 ${beta}`);
+ body.style.setProperty('--ring-shadow', `0 0 ${beta}`);
+ };
+
+ watch(serverTheme, () => {
+ setCssVars();
+ });
+
+ return {
+ setTheme,
+ };
+});
diff --git a/types/server.ts b/types/server.ts
index 6cd749990..788d7c10c 100644
--- a/types/server.ts
+++ b/types/server.ts
@@ -1,4 +1,5 @@
import { KeyIcon } from '@heroicons/vue/24/solid';
+import { Theme } from '~/types/theme';
import { UserProfileLink } from '~/types/userProfile';
export enum ServerState {
@@ -40,6 +41,7 @@ export interface Server {
site?: string;
// state?: ServerState;
state: string;
+ theme: Theme;
uptime?: number;
username?: string;
wanFQDN?: string;
diff --git a/types/theme.ts b/types/theme.ts
new file mode 100644
index 000000000..0711333d7
--- /dev/null
+++ b/types/theme.ts
@@ -0,0 +1,9 @@
+export interface Theme {
+ banner: string;
+ bannerGradient: string;
+ bgColor: string;
+ descriptionShow: boolean;
+ metaColor: string;
+ name: string;
+ textColor: string;
+}
\ No newline at end of file