}
};
+ $effect(() => {
+ if (window.location.href) {
+ setTimeout(() => {
+ oc = document.querySelector('.offcanvas');
+ oc.addEventListener('hidden.bs.offcanvas', () => {
+ $offcanvasIsOpen = false;
+ });
+ oc.addEventListener('shown.bs.offcanvas', () => {
+ $offcanvasIsOpen = true;
+ });
+ }, 500);
+ }
+ });
+
onMount(() => {
days = updateCalendar();
import * as bootstrap from 'bootstrap';
import Tag from './Tag.svelte';
import { offcanvasIsOpen } from '$lib/helpers.js';
+ import { API_URL } from '$lib/APIurl.js';
+ import axios from 'axios';
- let { searchForString, searchForTag } = $props();
+ let oc;
onMount(() => {
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
}
}
+ $effect(() => {
+ if (window.location.href) {
+ setTimeout(() => {
+ oc = document.querySelector('.offcanvas');
+ oc.addEventListener('hidden.bs.offcanvas', () => {
+ $offcanvasIsOpen = false;
+ });
+ oc.addEventListener('shown.bs.offcanvas', () => {
+ $offcanvasIsOpen = true;
+ });
+ }, 1000);
+ }
+ });
+
+ function searchForString() {
+ if ($isSearching) {
+ return;
+ }
+ $isSearching = true;
+
+ axios
+ .get(API_URL + '/logs/searchString', {
+ params: {
+ searchString: $searchString
+ }
+ })
+ .then((response) => {
+ $searchResults = [...response.data];
+ $isSearching = false;
+ })
+ .catch((error) => {
+ $searchResults = [];
+ console.error(error);
+ $isSearching = false;
+
+ // toast
+ const toast = new bootstrap.Toast(document.getElementById('toastErrorSearching'));
+ toast.show();
+ });
+ }
+
+ function searchForTag() {
+ $searchString = '';
+ if ($isSearching) {
+ return;
+ }
+ $isSearching = true;
+
+ axios
+ .get(API_URL + '/logs/searchTag', { params: { tag_id: $searchTag.id } })
+ .then((response) => {
+ $searchResults = [...response.data];
+ $isSearching = false;
+ })
+ .catch((error) => {
+ $isSearching = false;
+ $searchResults = [];
+
+ console.error(error);
+ // toast
+ const toast = new bootstrap.Toast(document.getElementById('toastErrorSearching'));
+ toast.show();
+ });
+ }
+
let showTagDropdown = $state(false);
let filteredTags = $state([]);
let selectedTagIndex = $state(0);
$searchResults = [];
}
- let oc;
+ // selects a search result
function selectDate(date) {
$selectedDate = date;
</div>
</div>
+<div class="toast-container position-fixed bottom-0 end-0 p-3">
+ <div
+ id="toastErrorSearching"
+ 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 Suchen!</div>
+ </div>
+ </div>
+</div>
+
<style>
.btnSearchPopover {
border-bottom-left-radius: 0px;
color: #ccc;
text-align: center;
padding: 1rem;
+ user-select: none;
}
:global(.kbd) {
import {writable} from 'svelte/store';
-export const readingMode = writable(false);
+export const readingMode = writable('');
// old, to be deleted
export const useTrianglify = writable(true);
import trianglify from 'trianglify';
import { tags } from '$lib/tagStore.js';
import TagModal from '$lib/TagModal.svelte';
- import { alwaysShowSidenav } from '$lib/helpers.js';
+ import { alwaysShowSidenav, offcanvasIsOpen } from '$lib/helpers.js';
import { templates } from '$lib/templateStore';
-
import {
faRightFromBracket,
faGlasses,
let inDuration = 150;
let outDuration = 150;
+ axios.interceptors.request.use((config) => {
+ config.withCredentials = true;
+ return config;
+ });
+
+ axios.interceptors.response.use(
+ (response) => {
+ return response;
+ },
+ (error) => {
+ if (
+ error.response &&
+ error.response.status &&
+ (error.response.status == 401 || error.response.status == 440)
+ ) {
+ // logout
+ axios
+ .get(API_URL + '/users/logout')
+ .then((response) => {
+ localStorage.removeItem('user');
+ goto(`/login?error=${error.response.status}`);
+ })
+ .catch((error) => {
+ console.error(error);
+ });
+ }
+ return Promise.reject(error);
+ }
+ );
+
$effect(() => {
- if ($readingMode) {
+ if ($readingMode === true && page.url.pathname !== '/read') {
goto('/read');
- } else {
+ } else if ($readingMode === false) {
goto('/write');
}
});
getUserSettings();
getTemplates();
+ if (page.url.pathname === '/read') {
+ $readingMode = true;
+ } else if (page.url.pathname === '/write') {
+ $readingMode = false;
+ }
+
document.getElementById('settingsModal').addEventListener('shown.bs.modal', function () {
const height = document.getElementById('modal-body').clientHeight;
document.getElementById('settings-content').style.height = 'calc(' + height + 'px - 2rem)';
id="settings-content"
>
<div id="appearance">
- <h3 id="" class="text-primary">Aussehen</h3>
+ <h3 class="text-primary">Aussehen</h3>
<div id="lightdark">
<h5>Light/Dark-Mode</h5>
Bla<br />
</div>
<div id="functions">
- <h3 id="" class="text-primary">Funktionen</h3>
+ <h3 class="text-primary">Funktionen</h3>
<div id="autoLoadImages">
{#if $tempSettings.setAutoloadImagesPerDevice !== $settings.setAutoloadImagesPerDevice || $tempSettings.autoloadImagesByDefault !== $settings.autoloadImagesByDefault}
import Tag from '$lib/Tag.svelte';
import { tags } from '$lib/tagStore.js';
import FileList from '$lib/FileList.svelte';
- import { autoLoadImages } from '$lib/settingsStore';
+ import { autoLoadImagesThisDevice, settings } from '$lib/settingsStore';
import { faCloudArrowDown } from '@fortawesome/free-solid-svg-icons';
import { Fa } from 'svelte-fa';
import { fade, slide } from 'svelte/transition';
import ImageViewer from '$lib/ImageViewer.svelte';
+ import { alwaysShowSidenav } from '$lib/helpers.js';
+
+ axios.interceptors.request.use((config) => {
+ config.withCredentials = true;
+ return config;
+ });
marked.use({
breaks: true,
) {
log.images = [...log.images, file];
- if ($autoLoadImages) {
+ if (autoLoadImages) {
loadImage(file.uuid_filename);
}
}
}
});
+ let autoLoadImages = $derived(
+ ($settings.setAutoloadImagesPerDevice && $autoLoadImagesThisDevice) ||
+ (!$settings.setAutoloadImagesPerDevice && $settings.autoloadImagesByDefault)
+ );
+
function loadImage(uuid) {
for (let i = 0; i < logs.length; i++) {
let log = logs[i];
<DatepickerLogic />
<!-- shown on small Screen, when triggered -->
-<div class="offcanvas-md d-md-none offcanvas-start p-3" id="sidenav" tabindex="-1">
+<div class="offcanvas offcanvas-start p-3" id="sidenav" tabindex="-1">
<div class="offcanvas-header">
<button
type="button"
aria-label="Close"
></button>
</div>
- <Sidenav {search} />
+ <Sidenav />
</div>
<div class="d-flex flex-row justify-content-between h-100">
<!-- shown on large Screen -->
- <div class="d-md-block d-none sidenav p-3">
- <Sidenav {search} />
- </div>
+ {#if $alwaysShowSidenav}
+ <div class="sidenav p-3">
+ <Sidenav />
+ </div>
+ {/if}
<!-- Center -->
<div class="d-flex flex-column my-4 ms-4 flex-fill overflow-y-auto" id="scrollArea">
{#each logs as log (log.day)}
<!-- Log-Area -->
- <div class="log mb-3 p-3 d-flex flex-row" data-log-day={log.day}>
- <div class="date me-3 d-flex flex-column align-items-center">
- <p class="dateNumber">{log.day}</p>
- <p class="dateDay">
- <b>
- {new Date($cal.currentYear, $cal.currentMonth, log.day).toLocaleDateString('locale', {
- weekday: 'long'
- })}
- </b>
- </p>
- </div>
- <div class="flex-grow-1 middle">
- {#if log.text && log.text !== ''}
- <div class="text">
- {@html marked.parse(log.text)}
- </div>
- {/if}
- {#if log.tags?.length > 0}
- <div class="tags d-flex flex-row flex-wrap">
- {#each log.tags as t}
- <Tag tag={$tags.find((tag) => tag.id === t)} />
- {/each}
- </div>
- {/if}
- {#if log.images?.length > 0}
- {#if !$autoLoadImages && log.images.find((image) => !image.src && !image.loading)}
- <div class="d-flex flex-row">
- <button type="button" class="loadImageBtn" onclick={() => loadImages()}>
- <Fa icon={faCloudArrowDown} class="me-2" size="2x" fw /><br />
- Bilder laden
- </button>
+ {#if ('text' in log && log.text !== '') || log.tags?.length > 0 || log.files?.length > 0}
+ <div class="log mb-3 p-3 d-flex flex-row" data-log-day={log.day}>
+ <div class="date me-3 d-flex flex-column align-items-center">
+ <p class="dateNumber">{log.day}</p>
+ <p class="dateDay">
+ <b>
+ {new Date($cal.currentYear, $cal.currentMonth, log.day).toLocaleDateString(
+ 'locale',
+ {
+ weekday: 'long'
+ }
+ )}
+ </b>
+ </p>
+ </div>
+ <div class="flex-grow-1 middle">
+ {#if log.text && log.text !== ''}
+ <div class="text">
+ {@html marked.parse(log.text)}
</div>
- {:else}
- <ImageViewer images={log.images} />
{/if}
+ {#if log.tags?.length > 0}
+ <div class="tags d-flex flex-row flex-wrap">
+ {#each log.tags as t}
+ <Tag tag={$tags.find((tag) => tag.id === t)} />
+ {/each}
+ </div>
+ {/if}
+ {#if log.images?.length > 0}
+ {#if !autoLoadImages && log.images.find((image) => !image.src && !image.loading)}
+ <div class="d-flex flex-row">
+ <button type="button" class="loadImageBtn" onclick={() => loadImages()}>
+ <Fa icon={faCloudArrowDown} class="me-2" size="2x" fw /><br />
+ Bilder laden
+ </button>
+ </div>
+ {:else}
+ <ImageViewer images={log.images} />
+ {/if}
+ {/if}
+ </div>
+
+ {#if log.files && log.files.length > 0}
+ <div class="d-flex flex-column ms-3 files">
+ <FileList files={log.files} {downloadFile} />
+ </div>
{/if}
</div>
-
- {#if log.files && log.files.length > 0}
- <div class="d-flex flex-column ms-3 files">
- <FileList files={log.files} {downloadFile} />
- </div>
- {/if}
- </div>
+ {/if}
{/each}
</div>
</div>
<style>
+ .sidenav {
+ width: 380px;
+ min-width: 380px;
+ }
+
.files {
max-width: 350px;
}
import axios from 'axios';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
- import { searchString, searchTag, searchResults, isSearching } from '$lib/searchStore.js';
+ import { searchString, searchResults } from '$lib/searchStore.js';
import * as TinyMDE from 'tiny-markdown-editor';
import '../../../node_modules/tiny-markdown-editor/dist/tiny-mde.css';
import { API_URL } from '$lib/APIurl.js';
return config;
});
- axios.interceptors.response.use(
- (response) => {
- return response;
- },
- (error) => {
- if (
- error.response &&
- error.response.status &&
- (error.response.status == 401 || error.response.status == 440)
- ) {
- // logout
- axios
- .get(API_URL + '/users/logout')
- .then((response) => {
- localStorage.removeItem('user');
- goto(`/login?error=${error.response.status}`);
- })
- .catch((error) => {
- console.error(error);
- });
- }
- return Promise.reject(error);
- }
- );
-
let cancelDownload = new AbortController();
let tinyMDE;
}
});
- function searchForString() {
- if ($isSearching) {
- return;
- }
- $isSearching = true;
-
- axios
- .get(API_URL + '/logs/searchString', {
- params: {
- searchString: $searchString
- }
- })
- .then((response) => {
- $searchResults = [...response.data];
- $isSearching = false;
- })
- .catch((error) => {
- $searchResults = [];
- console.error(error);
- $isSearching = false;
-
- // toast
- const toast = new bootstrap.Toast(document.getElementById('toastErrorSearching'));
- toast.show();
- });
- }
-
- function searchForTag() {
- $searchString = '';
- if ($isSearching) {
- return;
- }
- $isSearching = true;
-
- axios
- .get(API_URL + '/logs/searchTag', { params: { tag_id: $searchTag.id } })
- .then((response) => {
- $searchResults = [...response.data];
- $isSearching = false;
- })
- .catch((error) => {
- $isSearching = false;
- $searchResults = [];
-
- console.error(error);
- // toast
- const toast = new bootstrap.Toast(document.getElementById('toastErrorSearching'));
- toast.show();
- });
- }
-
function triggerFileInput() {
document.getElementById('fileInput').click();
}
<DatepickerLogic />
<svelte:window onkeydown={on_key_down} onkeyup={on_key_up} />
-<!-- offcanvas-md d-md-none -->
<!-- shown on small Screen, when triggered -->
<div class="offcanvas offcanvas-start p-3" id="sidenav" tabindex="-1">
<div class="offcanvas-header">
aria-label="Close"
></button>
</div>
- <Sidenav {searchForString} {searchForTag} />
+ <Sidenav />
</div>
<div class="d-flex flex-row justify-content-between h-100 main-row">
<!-- shown on large Screen -->
{#if $alwaysShowSidenav}
- <!-- d-md-block d-none-->
<div class="sidenav p-3">
- <Sidenav {searchForString} {searchForTag} />
+ <Sidenav />
</div>
{/if}
</div>
</div>
- <div
- id="toastErrorSearching"
- 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 Suchen!</div>
- </div>
- </div>
-
<div
id="toastErrorSavingFile"
class="toast align-items-center text-bg-danger"
#editor {
height: 400px;
- overflow-y: scroll;
word-break: break-word;
}
}
.sidenav {
- /* max-width: 430px; */
width: 380px;
min-width: 380px;
}