diff --git a/materialious/src/lib/components/settings/ExportImport.svelte b/materialious/src/lib/components/settings/ExportImport.svelte index d72ef15b..050f4d6a 100644 --- a/materialious/src/lib/components/settings/ExportImport.svelte +++ b/materialious/src/lib/components/settings/ExportImport.svelte @@ -6,10 +6,21 @@ import { importSubscriptions } from '$lib/importExport'; import { postSubscribeInvidious } from '$lib/api/invidious/subscribe'; import { getSubscriptionsInvidious } from '$lib/api/invidious/feed'; - import { backendInUseStore, invidiousAuthStore, invidiousInstanceStore } from '$lib/store'; + import { + backendInUseStore, + interfaceAdvancedThemingStore, + interfaceBorderRadiusStore, + invidiousAuthStore, + invidiousInstanceStore + } from '$lib/store'; import { Clipboard } from '@capacitor/clipboard'; - import { loadSettingsFromFile, settingsToJson } from '$lib/externalSettings'; + import { + bookmarkletSaveToUrl, + loadSettingsFromFile, + settingsToJson + } from '$lib/externalSettings'; import { downloadStringAsFile } from '$lib/misc'; + import { zNumber, zThemeColors } from '$lib/externalSettings/settings'; async function importInvidiousSubs() { const importedSubs = await getSubscriptionsInvidious(); @@ -179,3 +190,79 @@ type="file" /> + +

{$_('layout.theme.theme')}

+
+ + +
+ + +
+
+ + { + const files = (event.target as HTMLInputElement).files; + if (files?.length === 0 || !files) return; + + await loadSettingsFromFile(files[0]); + }} + accept=".json" + type="file" + /> +
diff --git a/materialious/src/lib/externalSettings/index.ts b/materialious/src/lib/externalSettings/index.ts index ebe24c4c..a8a5514b 100644 --- a/materialious/src/lib/externalSettings/index.ts +++ b/materialious/src/lib/externalSettings/index.ts @@ -2,7 +2,7 @@ import { page } from '$app/state'; import { get } from 'svelte/store'; import { z } from 'zod'; -import { persistedStores } from './settings'; +import { persistedStores, type PersistedStore } from './settings'; import { isOwnBackend } from '$lib/shared'; import { addOrUpdateKeyValue, getKeyValue } from '$lib/api/backend/keyvalue'; @@ -121,10 +121,10 @@ export function loadSettingsFromEnv() { } } -export function bookmarkletSaveToUrl(): string { +export function bookmarkletSaveToUrl(stores: PersistedStore[] = persistedStores): string { const url = new URL(location.origin); - for (const { name, store, serialize, excludeFromBookmarklet } of persistedStores) { + for (const { name, store, serialize, excludeFromBookmarklet } of stores) { const value = get(store); const encoded = serialize ? serialize(value) : value?.toString(); @@ -136,10 +136,10 @@ export function bookmarkletSaveToUrl(): string { return url.toString(); } -export function settingsToJson(): string { +export function settingsToJson(stores: PersistedStore[] = persistedStores): string { const settings: Record = {}; - for (const { name, store, excludeFromBookmarklet } of persistedStores) { + for (const { name, store, excludeFromBookmarklet } of stores) { const value = get(store); if (!excludeFromBookmarklet) { settings[name] = value; diff --git a/materialious/src/lib/externalSettings/settings.ts b/materialious/src/lib/externalSettings/settings.ts index 8a9f5c59..2305095c 100644 --- a/materialious/src/lib/externalSettings/settings.ts +++ b/materialious/src/lib/externalSettings/settings.ts @@ -60,7 +60,7 @@ import { import { isOwnBackend } from '$lib/shared'; import { SUPPORTED_THEME_KEYS } from '$lib/shared/theme'; -type PersistedStore = { +export type PersistedStore = { name: string; store: Writable; schema: z.ZodType; @@ -69,18 +69,18 @@ type PersistedStore = { excludeFromBackendSync?: boolean; // Won't be sync'd to account cloud }; -const zBoolean = z.coerce.boolean(); -const zNumber = z.coerce.number(); -const zFloat = z.coerce.number().min(0).max(1); -const zString = z.string(); -const zArray = z.array(z.string()); -const zChapterMode = z.enum(['automatic', 'manual', 'timeline']); -const zChapterModeRecord = z.record(z.string(), zChapterMode.optional()); -const zAuth = z.object({ +export const zBoolean = z.coerce.boolean(); +export const zNumber = z.coerce.number(); +export const zFloat = z.coerce.number().min(0).max(1); +export const zString = z.string(); +export const zArray = z.array(z.string()); +export const zChapterMode = z.enum(['automatic', 'manual', 'timeline']); +export const zChapterModeRecord = z.record(z.string(), zChapterMode.optional()); +export const zAuth = z.object({ username: z.string(), token: z.string() }); -const zThemeColors = z.record( +export const zThemeColors = z.record( z.string().refine((val) => SUPPORTED_THEME_KEYS.includes(val), { message: 'CSS property not allowed' }), diff --git a/materialious/src/lib/i18n/locales/en.json b/materialious/src/lib/i18n/locales/en.json index 45c91124..d307a55b 100644 --- a/materialious/src/lib/i18n/locales/en.json +++ b/materialious/src/lib/i18n/locales/en.json @@ -295,7 +295,8 @@ "exportSub": "Export subscriptions", "exportToClipboard": "Export to clipboard", "exportToFile": "Export to file", - "importFromFile": "Import from file" + "importFromFile": "Import from file", + "exportToShareUrl": "Export to share URL" }, "deArrow": { "title": "DeArrow", diff --git a/materialious/src/lib/importExport.ts b/materialious/src/lib/importExport.ts index 52957d45..5d4a7593 100644 --- a/materialious/src/lib/importExport.ts +++ b/materialious/src/lib/importExport.ts @@ -221,7 +221,7 @@ export async function exportSubscriptionsAsFile(exportType: 'InvidiousJSON' | 'O if (data) { downloadStringAsFile( data, - `materialious-export-${new Date().toDateString().replaceAll(' ', '')}.${exportType === 'InvidiousJSON' ? 'json' : 'opml'}` + `materialious-export.${exportType === 'InvidiousJSON' ? 'json' : 'opml'}` ); } } diff --git a/materialious/src/lib/misc.ts b/materialious/src/lib/misc.ts index 9988a359..09aefa7f 100644 --- a/materialious/src/lib/misc.ts +++ b/materialious/src/lib/misc.ts @@ -125,7 +125,7 @@ export function downloadStringAsFile(content: string, filename: string) { const a = document.createElement('a'); a.href = url; - a.download = filename; + a.download = `${new Date().toDateString().replaceAll(' ', '')}-${filename}`; document.body.appendChild(a); a.click(); diff --git a/materialious/src/routes/+layout.svelte b/materialious/src/routes/+layout.svelte index 41093b79..24a9e7df 100644 --- a/materialious/src/routes/+layout.svelte +++ b/materialious/src/routes/+layout.svelte @@ -133,6 +133,8 @@ // Should always be loaded after env settings // So user preferences overwrite instance preferences. bookmarkletLoadFromUrl(); + + setThemeColors($interfaceAdvancedThemingStore); }); let syncToSettingsInitialized = false;