fc5aedcedd
This commit makes it so that a user's theme will only be pulled from the database rather than from a weird mix of cookies and the database. This fixes some issues with themes rendering weirdly.
199 lines
7.1 KiB
TypeScript
199 lines
7.1 KiB
TypeScript
import { schema } from '#triplit/schema';
|
|
import { type Entity } from '@triplit/client';
|
|
import { computed, watch } from 'vue';
|
|
|
|
// TODO: most of this code is generated by gemini 3 flash with fixups,
|
|
// but its still bad code, so I'm going to clean it up later
|
|
export const useUserSettings = async () => {
|
|
const { user, loggedIn } = useAuth();
|
|
const triplit = useTriplitClient();
|
|
|
|
const accent = useState('theme:accent', () => 'violet');
|
|
const neutral = useState('theme:neutral', () => 'zinc');
|
|
const hinting = useState('theme:hinting', () => '0');
|
|
|
|
const colorScheme: { preference: Ref<'light' | 'dark' | 'system'>; value: Ref<'light' | 'dark'>; class: Ref<'light' | 'dark' | undefined> } = {
|
|
preference: useState('theme:colorScheme', () => 'system' as 'light' | 'dark' | 'system'),
|
|
value: computed(() => {
|
|
if (colorScheme.preference.value === 'system') {
|
|
if (import.meta.client) {
|
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
|
|
return prefersDark ? 'dark' : 'light';
|
|
}
|
|
|
|
return 'dark';
|
|
}
|
|
|
|
return colorScheme.preference.value as 'light' | 'dark';
|
|
}),
|
|
class: ref<undefined | 'light' | 'dark'>(undefined)
|
|
}
|
|
|
|
let listeningToColorScheme = false;
|
|
const changeSystemColorScheme = (e: MediaQueryListEvent) => {
|
|
if (colorScheme.preference.value !== 'system') return;
|
|
|
|
colorScheme.class.value = e.matches ? 'dark' : 'light';
|
|
}
|
|
watch(colorScheme.preference, () => {
|
|
if (colorScheme.preference.value === 'system') {
|
|
if (import.meta.client) {
|
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
|
|
if (!listeningToColorScheme) {
|
|
listeningToColorScheme = true;
|
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', changeSystemColorScheme);
|
|
}
|
|
|
|
colorScheme.class.value = prefersDark ? 'dark' : 'light';
|
|
return;
|
|
}
|
|
|
|
if (listeningToColorScheme) {
|
|
listeningToColorScheme = false;
|
|
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', changeSystemColorScheme);
|
|
}
|
|
|
|
// fallback to dark on the server
|
|
colorScheme.class.value = 'dark';
|
|
return;
|
|
}
|
|
|
|
colorScheme.class.value = colorScheme.preference.value as 'light' | 'dark';
|
|
}, { immediate: true });
|
|
|
|
const start = Date.now();
|
|
const remoteSettings = useState<Entity<typeof schema, 'settings'> | null>('user:settings', () => null);
|
|
const { results } = await useQuery('settings', triplit, triplit.query('settings'))
|
|
console.log("fetching settings took", Date.now() - start);
|
|
|
|
watch(results, (val) => {
|
|
if (!val) {
|
|
remoteSettings.value = null;
|
|
return;
|
|
}
|
|
|
|
remoteSettings.value = val[0] as any;
|
|
}, { immediate: true, deep: true })
|
|
|
|
watch(remoteSettings, (newSettings) => {
|
|
if (!newSettings?.appearance) return;
|
|
const { appearance } = newSettings;
|
|
|
|
if (appearance.colorScheme) {
|
|
colorScheme.preference.value = appearance.colorScheme as 'light' | 'dark' | 'system';
|
|
}
|
|
|
|
if (appearance.accent) {
|
|
accent.value = appearance.accent;
|
|
}
|
|
|
|
if (appearance.neutral) {
|
|
neutral.value = appearance.neutral;
|
|
}
|
|
|
|
if (appearance.hinting !== undefined) {
|
|
hinting.value = String(appearance.hinting);
|
|
}
|
|
}, { immediate: true, deep: true });
|
|
|
|
// Effective settings with defaults for backwards compatibility
|
|
const settings = computed(() => {
|
|
const remote = remoteSettings.value;
|
|
return {
|
|
appearance: {
|
|
colorScheme: remote?.appearance?.colorScheme ?? colorScheme.preference.value,
|
|
accent: remote?.appearance?.accent ?? accent.value,
|
|
neutral: remote?.appearance?.neutral ?? neutral.value,
|
|
hinting: remote?.appearance?.hinting ?? Number(hinting.value),
|
|
fontSize: remote?.appearance?.fontSize ?? 'medium',
|
|
},
|
|
systemAssistants: {
|
|
rename: {
|
|
enabled: remote?.systemAssistants?.rename?.enabled ?? false,
|
|
prompt: remote?.systemAssistants?.rename?.prompt ?? null,
|
|
modelId: remote?.systemAssistants?.rename?.modelId ?? null,
|
|
}
|
|
}
|
|
};
|
|
});
|
|
|
|
/**
|
|
* Update settings both locally and on the server
|
|
*/
|
|
const updateSettings = async (updates: {
|
|
appearance?: {
|
|
colorScheme?: 'light' | 'dark' | 'system';
|
|
accent?: string;
|
|
neutral?: string;
|
|
hinting?: number;
|
|
fontSize?: string;
|
|
};
|
|
systemAssistants?: {
|
|
rename?: {
|
|
enabled?: boolean;
|
|
prompt?: string | null;
|
|
modelId?: string | null;
|
|
};
|
|
};
|
|
}) => {
|
|
if (updates.appearance) {
|
|
if (updates.appearance.colorScheme) colorScheme.preference.value = updates.appearance.colorScheme;
|
|
if (updates.appearance.accent) accent.value = updates.appearance.accent;
|
|
if (updates.appearance.neutral) neutral.value = updates.appearance.neutral;
|
|
if (updates.appearance.hinting !== undefined) hinting.value = String(updates.appearance.hinting);
|
|
}
|
|
|
|
if (!loggedIn.value || !user.value?.id) return;
|
|
|
|
const current = remoteSettings.value;
|
|
if (!current) {
|
|
await triplit.insert('settings', {
|
|
userId: user.value.id,
|
|
appearance: {
|
|
colorScheme: colorScheme.preference.value,
|
|
accent: accent.value,
|
|
neutral: neutral.value,
|
|
hinting: Number(hinting.value),
|
|
...(updates.appearance || {})
|
|
},
|
|
systemAssistants: {
|
|
rename: {
|
|
enabled: updates.systemAssistants?.rename?.enabled ?? true,
|
|
prompt: updates.systemAssistants?.rename?.prompt ?? null,
|
|
modelId: updates.systemAssistants?.rename?.modelId ?? null,
|
|
}
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
await triplit.update('settings', current.id, (s) => {
|
|
if (updates.appearance) {
|
|
s.appearance = {
|
|
...(s.appearance || {}),
|
|
...updates.appearance
|
|
};
|
|
}
|
|
if (updates.systemAssistants) {
|
|
// @ts-expect-error
|
|
s.systemAssistants = {
|
|
...(s.systemAssistants || {}),
|
|
...updates.systemAssistants
|
|
};
|
|
}
|
|
});
|
|
};
|
|
|
|
return {
|
|
settings,
|
|
remoteSettings,
|
|
updateSettings,
|
|
accent,
|
|
neutral,
|
|
hinting,
|
|
colorScheme
|
|
};
|
|
};
|