added feature to select timezone for 'Written at'
authorPhiTux <redacted>
Sat, 21 Jun 2025 22:00:54 +0000 (00:00 +0200)
committerPhiTux <redacted>
Sat, 21 Jun 2025 22:00:54 +0000 (00:00 +0200)
backend/server/routers/users.py
frontend/package-lock.json
frontend/package.json
frontend/src/lib/SelectTimezone.svelte [new file with mode: 0644]
frontend/src/routes/+layout.svelte
frontend/src/routes/write/+page.svelte

index f61bf4e27d75458d03f400d09acc2343ba63ef5b..b543c6ff305bee655396e6352cd94c9fc36f4d3a 100644 (file)
@@ -127,7 +127,9 @@ def get_default_user_settings():
         "autoloadImagesByDefault": False,
         "setAutoloadImagesPerDevice": True,
         "useOnThisDay": True,
-        "onThisDayYears": [1,5,10]
+        "onThisDayYears": [1,5,10],
+        "useBrowserTimezone": True,
+        "timezone": "UTC"
     }
 
 @router.get("/getUserSettings")
@@ -180,35 +182,3 @@ async def save_user_settings(settings: dict, cookie = Depends(isLoggedIn)):
         raise HTTPException(status_code=500, detail="Internal Server Error when trying to write users.json") from e
     else:
         return {"success": True}
-
-
-"""
-export const defaultSettings = writable({
-  useTrianglify: true,
-  trianglifyOpacity: 0.4,
-  trianglifyColor: '',
-  backgroundColor: '',
-  autoloadImagesDefault: true,
-  saveAutoloadImagesPerDevice: true,
-});
-"""
-
-
-
-"""
-{
-      "user_id": 1,
-      "dailytxt-version": 2,
-      "username": "Marco",
-      "password": "...",
-      "salt": "...",
-      "enc_enc_key": "...",
-      "LOGIN_ON_EACH_LOAD": false,
-      "backup_codes": [
-        {
-          "password": "...",
-          "salt": "...",
-          "enc_orig_password": "..."
-        }
-      ]
-    }"""
\ No newline at end of file
index db5a44dd04f2a498ce81000d38903ed91c6bbef7..e8ceccff2286a7d351c73b3c98eea19804c8c33b 100644 (file)
@@ -15,6 +15,7 @@
                                "dayjs": "^1.11.13",
                                "emoji-picker-element": "^1.26.1",
                                "marked": "^15.0.6",
+                               "svelte-multiselect": "^11.1.1",
                                "svelte-outside": "^0.0.3",
                                "tiny-markdown-editor": "^0.1.31",
                                "trianglify": "^4.1.1",
@@ -45,7 +46,6 @@
                        "version": "2.3.0",
                        "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
                        "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
-                       "dev": true,
                        "license": "Apache-2.0",
                        "dependencies": {
                                "@jridgewell/gen-mapping": "^0.3.5",
                        "version": "0.3.8",
                        "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
                        "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
-                       "dev": true,
                        "license": "MIT",
                        "dependencies": {
                                "@jridgewell/set-array": "^1.2.1",
                        "version": "3.1.2",
                        "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
                        "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
-                       "dev": true,
                        "license": "MIT",
                        "engines": {
                                "node": ">=6.0.0"
                        "version": "1.2.1",
                        "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
                        "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
-                       "dev": true,
                        "license": "MIT",
                        "engines": {
                                "node": ">=6.0.0"
                        "version": "1.5.0",
                        "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
                        "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
-                       "dev": true,
                        "license": "MIT"
                },
                "node_modules/@jridgewell/trace-mapping": {
                        "version": "0.3.25",
                        "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
                        "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
-                       "dev": true,
                        "license": "MIT",
                        "dependencies": {
                                "@jridgewell/resolve-uri": "^3.1.0",
                        "version": "1.0.6",
                        "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
                        "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
-                       "dev": true,
                        "license": "MIT"
                },
                "node_modules/@types/json-schema": {
                        "version": "8.14.0",
                        "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
                        "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
-                       "dev": true,
                        "license": "MIT",
                        "bin": {
                                "acorn": "bin/acorn"
                        "version": "1.4.13",
                        "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz",
                        "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==",
-                       "dev": true,
                        "license": "MIT",
                        "peerDependencies": {
                                "acorn": ">=8.9.0"
                        "version": "5.3.2",
                        "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
                        "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
-                       "dev": true,
                        "license": "Apache-2.0",
                        "engines": {
                                "node": ">= 0.4"
                        "version": "4.1.0",
                        "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
                        "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
-                       "dev": true,
                        "license": "Apache-2.0",
                        "engines": {
                                "node": ">= 0.4"
                        "version": "2.1.1",
                        "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
                        "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
-                       "dev": true,
                        "license": "MIT",
                        "engines": {
                                "node": ">=6"
                        "version": "1.2.1",
                        "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.1.tgz",
                        "integrity": "sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==",
-                       "dev": true,
                        "license": "MIT"
                },
                "node_modules/espree": {
                        "version": "1.3.2",
                        "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.3.2.tgz",
                        "integrity": "sha512-C4PXusxYhFT98GjLSmb20k9PREuUdporer50dhzGuJu9IJXktbMddVCMLAERl5dAHyAi73GWWCE4FVHGP1794g==",
-                       "dev": true,
                        "license": "MIT",
                        "dependencies": {
                                "@jridgewell/sourcemap-codec": "^1.4.15"
                        "version": "3.0.3",
                        "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
                        "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
-                       "dev": true,
                        "license": "MIT",
                        "dependencies": {
                                "@types/estree": "^1.0.6"
                        "version": "3.0.0",
                        "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
                        "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
-                       "dev": true,
                        "license": "MIT"
                },
                "node_modules/locate-path": {
                        "version": "0.30.17",
                        "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
                        "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
-                       "dev": true,
                        "license": "MIT",
                        "dependencies": {
                                "@jridgewell/sourcemap-codec": "^1.5.0"
                        "version": "5.16.0",
                        "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.16.0.tgz",
                        "integrity": "sha512-Ygqsiac6UogVED2ruKclU+pOeMThxWtp9LG+li7BXeDKC2paVIsRTMkNmcON4Zejerd1s5sZHWx6ZtU85xklVg==",
-                       "dev": true,
                        "license": "MIT",
                        "dependencies": {
                                "@ampproject/remapping": "^2.3.0",
                                "svelte": "^4.0.0 || ^5.0.0"
                        }
                },
+               "node_modules/svelte-multiselect": {
+                       "version": "11.1.1",
+                       "resolved": "https://registry.npmjs.org/svelte-multiselect/-/svelte-multiselect-11.1.1.tgz",
+                       "integrity": "sha512-G9rhfpMVYxnfT1XMqVsmAV91TYhUwKgAIPXxe5QSVwNGmr6po8L/Vjmqrd8nmANYdM6anmPqi3eRX9W1KrgXLw==",
+                       "license": "MIT",
+                       "peerDependencies": {
+                               "svelte": "^5.8.0"
+                       }
+               },
                "node_modules/svelte-outside": {
                        "version": "0.0.3",
                        "resolved": "https://registry.npmjs.org/svelte-outside/-/svelte-outside-0.0.3.tgz",
                        "version": "1.1.2",
                        "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz",
                        "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
-                       "dev": true,
                        "license": "MIT"
                }
        }
index dcef1be9d3c3c209bc7e5e8ae8e53dc81ace0660..61ef0b5f9e9cfff45e87a8f4b5f4d6ae4518e154 100644 (file)
@@ -39,6 +39,7 @@
                "dayjs": "^1.11.13",
                "emoji-picker-element": "^1.26.1",
                "marked": "^15.0.6",
+               "svelte-multiselect": "^11.1.1",
                "svelte-outside": "^0.0.3",
                "tiny-markdown-editor": "^0.1.31",
                "trianglify": "^4.1.1",
diff --git a/frontend/src/lib/SelectTimezone.svelte b/frontend/src/lib/SelectTimezone.svelte
new file mode 100644 (file)
index 0000000..d644d6d
--- /dev/null
@@ -0,0 +1,37 @@
+<script>
+       import { MultiSelect } from 'svelte-multiselect';
+       import { tempSettings } from '$lib/settingsStore';
+
+       let value = $state(null);
+
+       function formatTimezone(tz) {
+               return tz.replace(/_/g, ' ');
+       }
+
+       let timezones = Intl.supportedValuesOf('timeZone').map((tz) => ({
+               label: formatTimezone(tz),
+               value: tz
+       }));
+
+       $effect(() => {
+               if (value !== null) {
+                       $tempSettings.timezone = value.value;
+               }
+       });
+</script>
+
+<MultiSelect
+       bind:value
+       options={timezones}
+       maxSelect={1}
+       placeholder="Zeitzone auswählen"
+       disabled={$tempSettings.useBrowserTimezone}
+       id="selectTimezone"
+/>
+
+<style>
+       :global(.multiselect) {
+               background-color: white !important;
+               border-radius: 0.375rem !important;
+       }
+</style>
index 95126897feb035e24a6decfd6b6cc969bf4d8f04..7812a3a545972da355876af14f00b335d397c040 100644 (file)
@@ -31,6 +31,7 @@
                faTrash
        } from '@fortawesome/free-solid-svg-icons';
        import Tag from '$lib/Tag.svelte';
+       import SelectTimezone from '$lib/SelectTimezone.svelte';
 
        let { children } = $props();
        let inDuration = 150;
                                                                                blub <br />
                                                                        </div>
                                                                        <div id="timezone">
+                                                                               {#if $tempSettings.useBrowserTimezone !== $settings.useBrowserTimezone || ($tempSettings.timezone !== undefined && $tempSettings.timezone?.value !== $settings.timezone?.value)}
+                                                                                       <div class="unsaved-changes" transition:slide></div>
+                                                                               {/if}
                                                                                <h5>Zeitzone</h5>
-                                                                               Bla<br />
-                                                                               blub <br />
-                                                                               bla <br />
-                                                                               blub <br />
+                                                                               Stelle die Zeitzone ein, die für den Timestamp ("Geschrieben am") genutzt werden
+                                                                               soll.
+
+                                                                               <div class="form-check mt-2">
+                                                                                       <input
+                                                                                               class="form-check-input"
+                                                                                               type="radio"
+                                                                                               name="timezone"
+                                                                                               id="timezone1"
+                                                                                               value={true}
+                                                                                               bind:group={$tempSettings.useBrowserTimezone}
+                                                                                       />
+                                                                                       <label class="form-check-label" for="timezone1">
+                                                                                               Zeitzone automatisch anhand des Browsers ermitteln.
+                                                                                       </label>
+                                                                                       <br />
+                                                                                       Aktuell: <code>{new Intl.DateTimeFormat().resolvedOptions().timeZone}</code>
+                                                                               </div>
+                                                                               <div class="form-check">
+                                                                                       <input
+                                                                                               class="form-check-input"
+                                                                                               type="radio"
+                                                                                               name="timezone"
+                                                                                               id="timezone2"
+                                                                                               value={false}
+                                                                                               bind:group={$tempSettings.useBrowserTimezone}
+                                                                                       />
+                                                                                       <label class="form-check-label" for="timezone2">
+                                                                                               Für diesen Account immer die folgende Zeitzone verwenden:
+                                                                                       </label>
+                                                                                       <br />
+                                                                                       <SelectTimezone />
+                                                                                       {#if !$tempSettings.useBrowserTimezone}
+                                                                                               <span transition:fade>
+                                                                                                       Ausgewählt: <code>{$tempSettings.timezone}</code>
+                                                                                               </span>
+                                                                                       {/if}
+                                                                               </div>
+
+                                                                               <div class="form-text mt-3">
+                                                                                       Wenn man auf Reisen ist, kann es sinnvoll sein, die Zeitzone anhand des
+                                                                                       Browsers zu ermitteln. Dann werden Datum und Uhrzeit am Zielort
+                                                                                       vorraussichtlich besser erkannt.<br />
+                                                                                       Wenn man hingegen zuhause im privaten Browser teils andere Zeitzonen (z. B. immer
+                                                                                       UTC) verwendet, kann es sinnvoll sein, hier eine bestimmte Zeitzone festzulegen.
+                                                                               </div>
                                                                        </div>
+
                                                                        <div id="onthisday">
                                                                                {#if $tempSettings.useOnThisDay !== $settings.useOnThisDay || JSON.stringify(onThisDayYears
                                                                                                        .trim()
                                                                                bla <br />
                                                                                blub <br />
                                                                        </div>
+                                                               </div>
 
-                                                                       <h3 id="tags" class="text-primary">Tags</h3>
+                                                               <div id="tags">
+                                                                       <h3 class="text-primary">Tags</h3>
                                                                        <div>
                                                                                Hier können Tags bearbeitet oder auch vollständig aus DailyTxT gelöscht werden.
                                                                                <div class="d-flex flex-column tagColumn mt-1">
                                                                                        {/each}
                                                                                </div>
                                                                        </div>
+                                                               </div>
 
-                                                                       <div id="templates">
-                                                                               <h3 class="text-primary">Vorlagen</h3>
-                                                                               <div>
-                                                                                       {#if oldTemplateName !== templateName || oldTemplateText !== templateText}
-                                                                                               <div class="unsaved-changes" transition:slide></div>
-                                                                                       {/if}
+                                                               <div id="templates">
+                                                                       <h3 class="text-primary">Vorlagen</h3>
+                                                                       <div>
+                                                                               {#if oldTemplateName !== templateName || oldTemplateText !== templateText}
+                                                                                       <div class="unsaved-changes" transition:slide></div>
+                                                                               {/if}
 
-                                                                                       <div class="d-flex flex-column">
-                                                                                               <select
-                                                                                                       bind:value={selectedTemplate}
-                                                                                                       class="form-select"
-                                                                                                       aria-label="Select template"
-                                                                                                       onchange={updateSelectedTemplate}
-                                                                                               >
-                                                                                                       <option value="-1" selected={selectedTemplate === '-1'}>
-                                                                                                               Neue Vorlage erstellen...
+                                                                               <div class="d-flex flex-column">
+                                                                                       <select
+                                                                                               bind:value={selectedTemplate}
+                                                                                               class="form-select"
+                                                                                               aria-label="Select template"
+                                                                                               onchange={updateSelectedTemplate}
+                                                                                       >
+                                                                                               <option value="-1" selected={selectedTemplate === '-1'}>
+                                                                                                       Neue Vorlage erstellen...
+                                                                                               </option>
+                                                                                               {#each $templates as template, index}
+                                                                                                       <option value={index} selected={index === selectedTemplate}>
+                                                                                                               {template.name}
                                                                                                        </option>
-                                                                                                       {#each $templates as template, index}
-                                                                                                               <option value={index} selected={index === selectedTemplate}>
-                                                                                                                       {template.name}
-                                                                                                               </option>
-                                                                                                       {/each}
-                                                                                               </select>
-                                                                                       </div>
+                                                                                               {/each}
+                                                                                       </select>
+                                                                               </div>
 
-                                                                                       <hr />
+                                                                               <hr />
 
-                                                                                       {#if confirmDeleteTemplate}
-                                                                                               <div transition:slide class="d-flex flex-row align-items-center mb-2">
-                                                                                                       <span
-                                                                                                               >Vorlage <b>{$templates[selectedTemplate].name}</b> wirklich löschen?</span
-                                                                                                       >
-                                                                                                       <button
-                                                                                                               type="button"
-                                                                                                               class="btn btn-secondary ms-2"
-                                                                                                               onclick={() => (confirmDeleteTemplate = false)}>Abbrechen</button
-                                                                                                       >
-                                                                                                       <button
-                                                                                                               type="button"
-                                                                                                               class="btn btn-danger ms-2"
-                                                                                                               onclick={() => {
-                                                                                                                       deleteTemplate();
-                                                                                                               }}
-                                                                                                               disabled={isDeletingTemplate}
-                                                                                                               >Löschen
-                                                                                                               {#if isDeletingTemplate}
-                                                                                                                       <span
-                                                                                                                               class="spinner-border spinner-border-sm ms-2"
-                                                                                                                               role="status"
-                                                                                                                               aria-hidden="true"
-                                                                                                                       ></span>
-                                                                                                               {/if}
-                                                                                                       </button>
-                                                                                               </div>
-                                                                                       {/if}
-                                                                                       <div class="d-flex flex-row">
-                                                                                               <input
-                                                                                                       disabled={selectedTemplate === null}
-                                                                                                       type="text"
-                                                                                                       bind:value={templateName}
-                                                                                                       class="form-control"
-                                                                                                       placeholder="Name der Vorlage"
-                                                                                               />
+                                                                               {#if confirmDeleteTemplate}
+                                                                                       <div transition:slide class="d-flex flex-row align-items-center mb-2">
+                                                                                               <span
+                                                                                                       >Vorlage <b>{$templates[selectedTemplate].name}</b> wirklich löschen?</span
+                                                                                               >
                                                                                                <button
-                                                                                                       disabled={selectedTemplate === '-1' || selectedTemplate === null}
                                                                                                        type="button"
-                                                                                                       class="btn btn-outline-danger ms-5"
-                                                                                                       onclick={() => {
-                                                                                                               confirmDeleteTemplate = !confirmDeleteTemplate;
-                                                                                                       }}><Fa fw icon={faTrash} /></button
+                                                                                                       class="btn btn-secondary ms-2"
+                                                                                                       onclick={() => (confirmDeleteTemplate = false)}>Abbrechen</button
                                                                                                >
-                                                                                       </div>
-                                                                                       <textarea
-                                                                                               disabled={selectedTemplate === null}
-                                                                                               bind:value={templateText}
-                                                                                               class="form-control mt-2"
-                                                                                               rows="10"
-                                                                                               placeholder="Inhalt der Vorlage"
-                                                                                       >
-                                                                                       </textarea>
-                                                                                       <div class="d-flex justify-content-end">
                                                                                                <button
-                                                                                                       disabled={(oldTemplateName === templateName &&
-                                                                                                               oldTemplateText === templateText) ||
-                                                                                                               isSavingTemplate}
                                                                                                        type="button"
-                                                                                                       class="btn btn-primary mt-2"
-                                                                                                       onclick={saveTemplate}
-                                                                                               >
-                                                                                                       Vorlage speichern
-                                                                                                       {#if isSavingTemplate}
+                                                                                                       class="btn btn-danger ms-2"
+                                                                                                       onclick={() => {
+                                                                                                               deleteTemplate();
+                                                                                                       }}
+                                                                                                       disabled={isDeletingTemplate}
+                                                                                                       >Löschen
+                                                                                                       {#if isDeletingTemplate}
                                                                                                                <span
                                                                                                                        class="spinner-border spinner-border-sm ms-2"
                                                                                                                        role="status"
                                                                                                        {/if}
                                                                                                </button>
                                                                                        </div>
+                                                                               {/if}
+                                                                               <div class="d-flex flex-row">
+                                                                                       <input
+                                                                                               disabled={selectedTemplate === null}
+                                                                                               type="text"
+                                                                                               bind:value={templateName}
+                                                                                               class="form-control"
+                                                                                               placeholder="Name der Vorlage"
+                                                                                       />
+                                                                                       <button
+                                                                                               disabled={selectedTemplate === '-1' || selectedTemplate === null}
+                                                                                               type="button"
+                                                                                               class="btn btn-outline-danger ms-5"
+                                                                                               onclick={() => {
+                                                                                                       confirmDeleteTemplate = !confirmDeleteTemplate;
+                                                                                               }}><Fa fw icon={faTrash} /></button
+                                                                                       >
+                                                                               </div>
+                                                                               <textarea
+                                                                                       disabled={selectedTemplate === null}
+                                                                                       bind:value={templateText}
+                                                                                       class="form-control mt-2"
+                                                                                       rows="10"
+                                                                                       placeholder="Inhalt der Vorlage"
+                                                                               >
+                                                                               </textarea>
+                                                                               <div class="d-flex justify-content-end">
+                                                                                       <button
+                                                                                               disabled={(oldTemplateName === templateName &&
+                                                                                                       oldTemplateText === templateText) ||
+                                                                                                       isSavingTemplate}
+                                                                                               type="button"
+                                                                                               class="btn btn-primary mt-2"
+                                                                                               onclick={saveTemplate}
+                                                                                       >
+                                                                                               Vorlage speichern
+                                                                                               {#if isSavingTemplate}
+                                                                                                       <span
+                                                                                                               class="spinner-border spinner-border-sm ms-2"
+                                                                                                               role="status"
+                                                                                                               aria-hidden="true"
+                                                                                                       ></span>
+                                                                                               {/if}
+                                                                                       </button>
                                                                                </div>
                                                                        </div>
+                                                               </div>
 
-                                                                       <div id="data">
-                                                                               <h4>Daten</h4>
-                                                                               <div id="export"><h5>Export</h5></div>
-                                                                               <div id="import"><h5>Import</h5></div>
-                                                                       </div>
+                                                               <div id="data">
+                                                                       <h4>Daten</h4>
+                                                                       <div id="export"><h5>Export</h5></div>
+                                                                       <div id="import"><h5>Import</h5></div>
+                                                               </div>
 
-                                                                       <div id="security">
-                                                                               <h4>Sicherheit</h4>
-                                                                               <div id="password"><h5>Password ändern</h5></div>
-                                                                               <div id="backupkeys"><h5>Backup-Keys</h5></div>
-                                                                               <div id="username"><h5>Username ändern</h5></div>
-                                                                               <div id="deleteaccount"><h5>Konto löschen</h5></div>
-                                                                       </div>
+                                                               <div id="security">
+                                                                       <h4>Sicherheit</h4>
+                                                                       <div id="password"><h5>Password ändern</h5></div>
+                                                                       <div id="backupkeys"><h5>Backup-Keys</h5></div>
+                                                                       <div id="username"><h5>Username ändern</h5></div>
+                                                                       <div id="deleteaccount"><h5>Konto löschen</h5></div>
+                                                               </div>
 
-                                                                       <div id="about">
-                                                                               <h4>About</h4>
-                                                                               Version:<br />
-                                                                               Changelog: <br />
-                                                                               Link zu github
-                                                                       </div>
+                                                               <div id="about">
+                                                                       <h4>About</h4>
+                                                                       Version:<br />
+                                                                       Changelog: <br />
+                                                                       Link zu github
                                                                </div>
                                                        </div>
                                                </div>
                                                {/if}
                                        </button>
                                </div>
-                               <!-- </div> -->
                        </div>
-               </div>
 
-               <div class="toast-container position-fixed bottom-0 end-0 p-3">
-                       <div
-                               id="toastSuccessEditTag"
-                               class="toast align-items-center text-bg-success"
-                               role="alert"
-                               aria-live="assertive"
-                               aria-atomic="true"
-                       >
-                               <div class="d-flex">
-                                       <div class="toast-body">Änderungen wurden gespeichert!</div>
+                       <div class="toast-container position-fixed bottom-0 end-0 p-3">
+                               <div
+                                       id="toastSuccessEditTag"
+                                       class="toast align-items-center text-bg-success"
+                                       role="alert"
+                                       aria-live="assertive"
+                                       aria-atomic="true"
+                               >
+                                       <div class="d-flex">
+                                               <div class="toast-body">Änderungen wurden gespeichert!</div>
+                                       </div>
                                </div>
-                       </div>
 
-                       <div
-                               id="toastErrorEditTag"
-                               class="toast align-items-center text-bg-danger"
-                               role="alert"
-                               aria-live="assertive"
-                               aria-atomic="true"
-                       >
-                               <div class="d-flex">
-                                       <div class="toast-body">Fehler beim Speichern der Änderungen!</div>
+                               <div
+                                       id="toastErrorEditTag"
+                                       class="toast align-items-center text-bg-danger"
+                                       role="alert"
+                                       aria-live="assertive"
+                                       aria-atomic="true"
+                               >
+                                       <div class="d-flex">
+                                               <div class="toast-body">Fehler beim Speichern der Änderungen!</div>
+                                       </div>
                                </div>
-                       </div>
 
-                       <div
-                               id="toastErrorDeleteTag"
-                               class="toast align-items-center text-bg-danger"
-                               role="alert"
-                               aria-live="assertive"
-                               aria-atomic="true"
-                       >
-                               <div class="d-flex">
-                                       <div class="toast-body">Fehler beim Löschen des Tags!</div>
+                               <div
+                                       id="toastErrorDeleteTag"
+                                       class="toast align-items-center text-bg-danger"
+                                       role="alert"
+                                       aria-live="assertive"
+                                       aria-atomic="true"
+                               >
+                                       <div class="d-flex">
+                                               <div class="toast-body">Fehler beim Löschen des Tags!</div>
+                                       </div>
                                </div>
-                       </div>
 
-                       <div
-                               id="toastSuccessSaveSettings"
-                               class="toast align-items-center text-bg-success"
-                               role="alert"
-                               aria-live="assertive"
-                               aria-atomic="true"
-                       >
-                               <div class="d-flex">
-                                       <div class="toast-body">Einstellungen gespeichert!</div>
+                               <div
+                                       id="toastSuccessSaveSettings"
+                                       class="toast align-items-center text-bg-success"
+                                       role="alert"
+                                       aria-live="assertive"
+                                       aria-atomic="true"
+                               >
+                                       <div class="d-flex">
+                                               <div class="toast-body">Einstellungen gespeichert!</div>
+                                       </div>
                                </div>
-                       </div>
 
-                       <div
-                               id="toastErrorSaveSettings"
-                               class="toast align-items-center text-bg-danger"
-                               role="alert"
-                               aria-live="assertive"
-                               aria-atomic="true"
-                       >
-                               <div class="d-flex">
-                                       <div class="toast-body">Fehler beim Speichern der Einstellungen!</div>
+                               <div
+                                       id="toastErrorSaveSettings"
+                                       class="toast align-items-center text-bg-danger"
+                                       role="alert"
+                                       aria-live="assertive"
+                                       aria-atomic="true"
+                               >
+                                       <div class="d-flex">
+                                               <div class="toast-body">Fehler beim Speichern der Einstellungen!</div>
+                                       </div>
                                </div>
-                       </div>
 
-                       <div
-                               id="toastErrorInvalidTemplateEmpty"
-                               class="toast align-items-center text-bg-danger"
-                               role="alert"
-                               aria-live="assertive"
-                               aria-atomic="true"
-                       >
-                               <div class="d-flex">
-                                       <div class="toast-body">Name oder Inhalt einer Vorlage dürfen nicht leer sein!</div>
+                               <div
+                                       id="toastErrorInvalidTemplateEmpty"
+                                       class="toast align-items-center text-bg-danger"
+                                       role="alert"
+                                       aria-live="assertive"
+                                       aria-atomic="true"
+                               >
+                                       <div class="d-flex">
+                                               <div class="toast-body">Name oder Inhalt einer Vorlage dürfen nicht leer sein!</div>
+                                       </div>
                                </div>
-                       </div>
 
-                       <div
-                               id="toastErrorInvalidTemplateDouble"
-                               class="toast align-items-center text-bg-danger"
-                               role="alert"
-                               aria-live="assertive"
-                               aria-atomic="true"
-                       >
-                               <div class="d-flex">
-                                       <div class="toast-body">Name der Vorlage existiert bereits</div>
+                               <div
+                                       id="toastErrorInvalidTemplateDouble"
+                                       class="toast align-items-center text-bg-danger"
+                                       role="alert"
+                                       aria-live="assertive"
+                                       aria-atomic="true"
+                               >
+                                       <div class="d-flex">
+                                               <div class="toast-body">Name der Vorlage existiert bereits</div>
+                                       </div>
                                </div>
-                       </div>
 
-                       <div
-                               id="toastSuccessSaveTemplate"
-                               class="toast align-items-center text-bg-success"
-                               role="alert"
-                               aria-live="assertive"
-                               aria-atomic="true"
-                       >
-                               <div class="d-flex">
-                                       <div class="toast-body">Vorlage gespeichert</div>
+                               <div
+                                       id="toastSuccessSaveTemplate"
+                                       class="toast align-items-center text-bg-success"
+                                       role="alert"
+                                       aria-live="assertive"
+                                       aria-atomic="true"
+                               >
+                                       <div class="d-flex">
+                                               <div class="toast-body">Vorlage gespeichert</div>
+                                       </div>
                                </div>
-                       </div>
 
-                       <div
-                               id="toastErrorDeletingTemplate"
-                               class="toast align-items-center text-bg-danger"
-                               role="alert"
-                               aria-live="assertive"
-                               aria-atomic="true"
-                       >
-                               <div class="d-flex">
-                                       <div class="toast-body">Fehler beim Löschen der Vorlage</div>
+                               <div
+                                       id="toastErrorDeletingTemplate"
+                                       class="toast align-items-center text-bg-danger"
+                                       role="alert"
+                                       aria-live="assertive"
+                                       aria-atomic="true"
+                               >
+                                       <div class="d-flex">
+                                               <div class="toast-body">Fehler beim Löschen der Vorlage</div>
+                                       </div>
                                </div>
-                       </div>
 
-                       <div
-                               id="toastSuccessDeletingTemplate"
-                               class="toast align-items-center text-bg-success"
-                               role="alert"
-                               aria-live="assertive"
-                               aria-atomic="true"
-                       >
-                               <div class="d-flex">
-                                       <div class="toast-body">Vorlage gelöscht</div>
+                               <div
+                                       id="toastSuccessDeletingTemplate"
+                                       class="toast align-items-center text-bg-success"
+                                       role="alert"
+                                       aria-live="assertive"
+                                       aria-atomic="true"
+                               >
+                                       <div class="d-flex">
+                                               <div class="toast-body">Vorlage gelöscht</div>
+                                       </div>
                                </div>
                        </div>
                </div>
                background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba(255, 255, 255, 1)'/></svg>");
        }
 
-       .settings-content {
+       .settings-content > div {
                padding: 0.5rem;
        }
 
 
        h3.text-primary {
                font-weight: 700;
+               position: sticky;
+               top: 0;
+               backdrop-filter: blur(10px) saturate(150%);
+               background-color: rgba(240, 240, 240, 0.9);
+               padding: 4px;
+               border-radius: 5px;
        }
 
        #trianglifyOpacity {
index 5ec77d25b6efc4d5076563444ad0121231283d11..2ac0d6f8e69ffaeed1e9f37631524fa8d8674b50 100644 (file)
                }
 
                // axios to backend
+               let timezone = $settings.useBrowserTimezone
+                       ? Intl.DateTimeFormat().resolvedOptions().timeZone
+                       : $settings.timezone;
                let date_written = new Date().toLocaleString('de-DE', {
-                       timeZone: 'Europe/Berlin',
+                       timeZone: timezone,
                        year: 'numeric',
                        month: '2-digit',
                        day: '2-digit',
git clone https://git.99rst.org/PROJECT