mirror of
https://github.com/unraid/api.git
synced 2026-01-03 15:09:48 -06:00
feat: theme setting
This commit is contained in:
@@ -4,6 +4,9 @@ Icon="icon-u-globe"
|
||||
Tag="globe"
|
||||
---
|
||||
<?php
|
||||
/**
|
||||
* @todo create web component env switcher liker upcEnv(). If we utilize manifest.json then we'll be switching its path.
|
||||
*/
|
||||
$myservers_flash_cfg_path='/boot/config/plugins/dynamix.my.servers/myservers.cfg';
|
||||
$myservers = file_exists($myservers_flash_cfg_path) ? @parse_ini_file($myservers_flash_cfg_path,true) : [];
|
||||
// print_r($mystatus);
|
||||
@@ -138,3 +141,16 @@ if ($display['theme'] === 'black' || $display['theme'] === 'azure') {
|
||||
<div class="ComponentWrapper">
|
||||
<connect-wan-ip-check php-wan-ip="<?=@file_get_contents('https://wanip4.unraid.net/')?>"></connect-wan-ip-check>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* So we're not needing to modify DefaultLayout with an additional include,
|
||||
* we'll add the Modals web component to the bottom of the body
|
||||
*/
|
||||
const modalsWebComponent = 'connect-modals';
|
||||
if (!document.getElementsByTagName(modalsWebComponent).length) {
|
||||
const $body = document.getElementsByTagName('body')[0];
|
||||
const $modals = document.createElement(modalsWebComponent);
|
||||
$body.appendChild($modals);
|
||||
}
|
||||
</script>
|
||||
@@ -81,7 +81,7 @@ onBeforeMount(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="UserProfile" class="text-alpha relative z-20 flex flex-col h-full gap-y-4px pl-40px rounded">
|
||||
<div id="UserProfile" class="text-alpha relative z-20 flex flex-col h-full gap-y-4px pt-4px pr-16px pl-40px">
|
||||
<div class="text-gamma text-12px text-right font-semibold leading-normal flex flex-row items-baseline justify-end gap-x-12px">
|
||||
<UpcUptimeExpire />
|
||||
<span>•</span>
|
||||
|
||||
5
package-lock.json
generated
5
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<string>('');
|
||||
const site = ref<string>('');
|
||||
const state = ref<string>(''); // @todo implement ServerState ENUM
|
||||
const theme = ref<Theme>();
|
||||
const uptime = ref<number>(0);
|
||||
const username = ref<string>(''); // @todo potentially move to a user store
|
||||
const wanFQDN = ref<string>('');
|
||||
@@ -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,
|
||||
|
||||
@@ -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<Theme|undefined>();
|
||||
// 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,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
9
types/theme.ts
Normal file
9
types/theme.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface Theme {
|
||||
banner: string;
|
||||
bannerGradient: string;
|
||||
bgColor: string;
|
||||
descriptionShow: boolean;
|
||||
metaColor: string;
|
||||
name: string;
|
||||
textColor: string;
|
||||
}
|
||||
Reference in New Issue
Block a user