Added ablity to export/import themes
This commit is contained in:
@@ -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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3>{$_('layout.theme.theme')}</h3>
|
||||
<div class="space"></div>
|
||||
<button
|
||||
class="no-margin surface-container-highest"
|
||||
onclick={async () => {
|
||||
await Clipboard.write({
|
||||
string: bookmarkletSaveToUrl([
|
||||
{
|
||||
name: 'advancedTheming',
|
||||
store: interfaceAdvancedThemingStore,
|
||||
schema: zThemeColors,
|
||||
serialize: JSON.stringify
|
||||
},
|
||||
{
|
||||
name: 'borderRadius',
|
||||
store: interfaceBorderRadiusStore,
|
||||
schema: zNumber
|
||||
}
|
||||
])
|
||||
});
|
||||
|
||||
addToast({
|
||||
data: {
|
||||
text: $_('player.share.copiedSuccess')
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<i>content_copy</i>
|
||||
<span>{$_('layout.export.exportToShareUrl')}</span>
|
||||
</button>
|
||||
|
||||
<div class="space"></div>
|
||||
<button
|
||||
class="no-margin surface-container-highest"
|
||||
onclick={async () => {
|
||||
downloadStringAsFile(
|
||||
settingsToJson([
|
||||
{
|
||||
name: 'advancedTheming',
|
||||
store: interfaceAdvancedThemingStore,
|
||||
schema: zThemeColors,
|
||||
serialize: JSON.stringify
|
||||
},
|
||||
{
|
||||
name: 'borderRadius',
|
||||
store: interfaceBorderRadiusStore,
|
||||
schema: zNumber
|
||||
}
|
||||
]),
|
||||
'materialious-theme.json'
|
||||
);
|
||||
}}
|
||||
>
|
||||
<i>file_export</i>
|
||||
<span>{$_('layout.export.exportToFile')}</span>
|
||||
</button>
|
||||
|
||||
<div class="space"></div>
|
||||
<div>
|
||||
<button class="surface-container-highest">
|
||||
<i>attach_file</i>
|
||||
<span>{$_('layout.export.importFromFile')}</span>
|
||||
</button>
|
||||
<input
|
||||
onchange={async (event: Event) => {
|
||||
const files = (event.target as HTMLInputElement).files;
|
||||
if (files?.length === 0 || !files) return;
|
||||
|
||||
await loadSettingsFromFile(files[0]);
|
||||
}}
|
||||
accept=".json"
|
||||
type="file"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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<any>[] = 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<any>[] = persistedStores): string {
|
||||
const settings: Record<string, string> = {};
|
||||
|
||||
for (const { name, store, excludeFromBookmarklet } of persistedStores) {
|
||||
for (const { name, store, excludeFromBookmarklet } of stores) {
|
||||
const value = get(store);
|
||||
if (!excludeFromBookmarklet) {
|
||||
settings[name] = value;
|
||||
|
||||
@@ -60,7 +60,7 @@ import {
|
||||
import { isOwnBackend } from '$lib/shared';
|
||||
import { SUPPORTED_THEME_KEYS } from '$lib/shared/theme';
|
||||
|
||||
type PersistedStore<T> = {
|
||||
export type PersistedStore<T> = {
|
||||
name: string;
|
||||
store: Writable<T>;
|
||||
schema: z.ZodType<T>;
|
||||
@@ -69,18 +69,18 @@ type PersistedStore<T> = {
|
||||
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'
|
||||
}),
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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'}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -133,6 +133,8 @@
|
||||
// Should always be loaded after env settings
|
||||
// So user preferences overwrite instance preferences.
|
||||
bookmarkletLoadFromUrl();
|
||||
|
||||
setThemeColors($interfaceAdvancedThemingStore);
|
||||
});
|
||||
|
||||
let syncToSettingsInitialized = false;
|
||||
|
||||
Reference in New Issue
Block a user