1 Commits
Author SHA1 Message Date
jakob.scheidandGitea 2ed8840fc9 fix(color-scheme): load color scheme initially
The color scheme is now loaded and set after registering the watcher to
ensure that the callback is also called initially.
2026-08-05 20:46:50 +02:00
9 changed files with 8 additions and 241 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ import { useRoute } from 'vue-router';
const route = useRoute();
const { getColorScheme, updateColorScheme } = useColorScheme();
const colorScheme = ref(null);
const colorScheme = ref('auto');
provide('colorScheme', colorScheme);
watch(colorScheme, (newValue) => {
updateColorScheme(newValue);
@@ -54,7 +54,6 @@ const copyrightPeriod =
.footer-segment {
display: flex;
justify-content: center;
align-items: center;
gap: 32px;
padding: var(--padding-y);
background-color: var(--light-bg);
+3 -11
View File
@@ -16,7 +16,7 @@ limitations under the License.
<script setup>
import { useSettingsStore } from '../stores/settingsStore';
import { ref, useId, watch } from 'vue';
import { useId } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
@@ -35,16 +35,8 @@ const switchId = useId();
const store = useSettingsStore();
const enabled = ref(null);
watch(store, (newStore) => {
enabled.value = newStore.get(props.path);
});
// set value after registering watcher to avoid immediate value change
enabled.value = store.get(props.path) ?? props.setting.default;
const toggle = function toggle () {
enabled.value = !enabled.value;
store.set(props.path, enabled.value);
store.set(props.path, !store.get(props.path));
};
</script>
@@ -55,7 +47,7 @@ const toggle = function toggle () {
</label>
<div
class="switch-wrapper"
:class="{ enabled }"
:class="{ enabled: store.get(props.path) }"
:title="t('settings.switch.title')"
:aria-label="t('settings.switch.ariaLabel')"
:aria-labelledby="labelId"
@@ -20,23 +20,17 @@ import { useSettingsStore } from '../../stores/settingsStore.js';
import { expect, describe, test } from 'vitest';
import { nextTick } from 'vue';
const getWrapper = function getWrapper ({
i18n = 'switch1',
defaultValue = undefined,
translations = {},
piniaOptions = {}
} = {}) {
const getWrapper = function getWrapper ({ i18n = 'switch1', translations = {} } = {}) {
return mountComponent(Switch, {
attrs: {
setting: {
type: 'bool',
name: 'switch',
i18n,
default: defaultValue
i18n
},
path: 'switch'
}
}, translations, [], piniaOptions);
}, translations);
};
describe('Switch', () => {
@@ -59,42 +53,6 @@ describe('Switch', () => {
expect(switchElementClasses2).not.toContain('enabled');
});
test('shows default value', () => {
const wrapper = getWrapper({
defaultValue: true
});
const switchElement = wrapper.find('.switch-wrapper');
expect(switchElement.classes('enabled')).toBeTruthy();
});
test('shows stored value instead of default value', () => {
const wrapper = getWrapper({
defaultValue: true,
piniaOptions: {
setupStores: () => {
const store = useSettingsStore();
store.set('switch', false);
}
}
});
const switchElement = wrapper.find('.switch-wrapper');
expect(switchElement.classes('enabled')).toBeFalsy();
});
test('reacts on store change', async () => {
const wrapper = getWrapper();
const store = useSettingsStore();
const switchElement = wrapper.find('.switch-wrapper');
expect(switchElement.classes('enabled')).toBeFalsy();
store.set('switch', true);
await nextTick();
expect(switchElement.classes('enabled')).toBeTruthy();
});
test('toggles value in store', async () => {
const wrapper = getWrapper();
@@ -1,94 +0,0 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { useSettingsConfigCacheStore } from '../../stores/settingsConfigCacheStore';
import { useSettingsStore } from '../../stores/settingsStore';
import { useSettings } from '../useSettings';
import { createTestingPinia } from '@pinia/testing';
import { setActivePinia } from 'pinia';
import { beforeEach, describe, expect, test, vi } from 'vitest';
const settingDefaultValue = 42;
const settingPath = ['test', 'number'];
describe('useSettings', () => {
describe('getSetting', () => {
test('returns stored value', () => {
const { getSetting } = useSettings();
const settings = useSettingsStore();
const key = 'setting';
const value = 42;
settings.set(key, value);
expect(getSetting(key)).toBe(value);
});
test('returns default value', () => {
const { getSetting } = useSettings();
expect(getSetting(settingPath)).toBe(settingDefaultValue);
});
test('returns stored value instead of default value', () => {
const { getSetting } = useSettings();
const settings = useSettingsStore();
const value = 43;
expect(getSetting(settingPath)).toBe(settingDefaultValue);
settings.set(settingPath, value);
expect(getSetting(settingPath)).toBe(value);
});
test('returns undefined if there is no value', () => {
const { getSetting } = useSettings();
expect(getSetting(['test', 'bool'])).toBeUndefined();
});
});
});
beforeEach(() => {
setActivePinia(
createTestingPinia({
createSpy: vi.fn,
stubActions: false
})
);
const settingsConfigCache = useSettingsConfigCacheStore();
settingsConfigCache.set({
contents: [
{
name: 'test',
i18n: '',
content: [
{
type: 'number',
i18n: '',
name: 'number',
default: settingDefaultValue
},
{
type: 'bool',
i18n: '',
name: 'bool'
}
]
}
]
});
});
@@ -1,54 +0,0 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { useSettingsConfigCacheStore } from '../stores/settingsConfigCacheStore';
import { useSettingsStore } from '../stores/settingsStore';
import { getSettingRecursively } from '../utils/getSetting';
import { loadSettingsConfig } from '../utils/settingsParser';
/**
* Provides access to the stored settings.
*/
export const useSettings = function useSettings () {
/**
* Returns the value of a specific setting.
* @param {string[]} key - The settings key.
* @return The value of the setting.
*/
const getSetting = function getSetting (key) {
const settingsStore = useSettingsStore();
const settings = useSettingsConfigCacheStore().get();
return (
settingsStore.get(key) ??
getSettingRecursively(
key, settings.contents ?? []
)?.default
);
};
/**
* Sets the value of a specific setting.
* @param {string[]} key - The setting key.
* @param value - The new value for the setting.
*/
const setSetting = function setSetting (key, value) {
const settingsStore = useSettingsStore();
settingsStore.set(key.join('.'), value);
};
return { getSetting, setSetting };
};
@@ -1,31 +0,0 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { defineStore } from 'pinia';
export const useSettingsConfigCacheStore = defineStore('settingsConfigCache', () => {
let settingsConfig = undefined;
const set = function set (value) {
settingsConfig = value;
};
const get = function get () {
return settingsConfig;
};
return { set, get };
});
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { useSettingsConfigCacheStore } from '../stores/settingsConfigCacheStore';
import { validateSettingsConfig } from './settingsValidator.js';
/**
@@ -22,12 +21,10 @@ import { validateSettingsConfig } from './settingsValidator.js';
* @returns {Promise<import('../types/settingsConfig').SettingsConfig>}
*/
export async function loadSettingsConfig() {
const settingsConfigCache = useSettingsConfigCacheStore();
let raw;
try {
raw = (await import('../settings.json')).default;
settingsConfigCache.set(raw);
} catch (e) {
throw new Error(`[settings] Failed to load settings.json: ${e.message}`);
}
+1 -1
View File
@@ -36,7 +36,7 @@ const toggleSidebar = function toggleSidebar () {
<div class="sidebar-controls">
<SidebarExpandButton @click="toggleSidebar" class="sidebar-expand-button" />
</div>
<div class="sidebar-content" v-show="props.expanded">
<div class="sidebar-content" v-if="props.expanded">
<slot />
</div>
</nav>