added bookmark-feature
authorPhiTux <redacted>
Fri, 4 Jul 2025 14:20:54 +0000 (16:20 +0200)
committerPhiTux <redacted>
Fri, 4 Jul 2025 14:20:54 +0000 (16:20 +0200)
backend/server/routers/logs.py
backend/server/utils/fileHandling.py
frontend/src/lib/Datepicker.svelte
frontend/src/lib/DatepickerLogic.svelte
frontend/src/lib/Sidenav.svelte
frontend/src/lib/TagModal.svelte
frontend/src/lib/calendarStore.js
frontend/src/routes/+layout.svelte

index 8b7c0bc4aaf43a06f8c427f400326712d11f557d..2defafa7dba11cbce6a3b168fc422eb0e9c821ad 100644 (file)
@@ -257,18 +257,21 @@ async def searchTag(tag_id: int, cookie = Depends(users.isLoggedIn)):
 async def getMarkedDays(month: str, year: str, cookie = Depends(users.isLoggedIn)):
     days_with_logs = []
     days_with_files = []
+    days_bookmarked = []
 
     content:dict = fileHandling.getMonth(cookie["user_id"], year, int(month))
     if "days" not in content.keys():
-        return {"days_with_logs": [], "days_with_files": []}
+        return {"days_with_logs": [], "days_with_files": [], "days_bookmarked": []}
 
     for dayLog in content["days"]:
         if "text" in dayLog.keys():
             days_with_logs.append(dayLog["day"])
         if "files" in dayLog.keys() and len(dayLog["files"]) > 0:
             days_with_files.append(dayLog["day"])
+        if "bookmarked" in dayLog.keys() and dayLog["bookmarked"]:
+            days_bookmarked.append(dayLog["day"])
     
-    return {"days_with_logs": days_with_logs, "days_with_files": days_with_files}
+    return {"days_with_logs": days_with_logs, "days_with_files": days_with_files, "days_bookmarked": days_bookmarked}
 
 
 @router.get("/loadMonthForReading")
@@ -605,4 +608,31 @@ async def getHistory(day: int, month: int, year: int, cookie = Depends(users.isL
                 })
             return history
     
-    return []
\ No newline at end of file
+    return []
+
+@router.get("/bookmarkDay")
+async def bookmarkDay(day: int, month: int, year: int, cookie = Depends(users.isLoggedIn)):
+    content:dict = fileHandling.getMonth(cookie["user_id"], year, month)
+    
+    if "days" not in content.keys():
+        content["days"] = []
+    
+    day_found = False
+    bookmarked = True
+    for dayLog in content["days"]:
+        if dayLog["day"] == day:
+            day_found = True
+            if "bookmarked" in dayLog and dayLog["bookmarked"]:
+                dayLog["bookmarked"] = False
+                bookmarked = False
+            else:
+                dayLog["bookmarked"] = True
+            break
+    
+    if not day_found:
+        content["days"].append({"day": day, "bookmarked": True})
+    
+    if not fileHandling.writeMonth(cookie["user_id"], year, month, content):
+        raise HTTPException(status_code=500, detail="Failed to bookmark day - error writing log")
+    
+    return {"success": True, "bookmarked": bookmarked}
\ No newline at end of file
index 954e0b8bc9d8e84ab413d230bcc9d8377fdfafdb..608d68884de096b79c4300e81954bf3816d9b9ac 100644 (file)
@@ -27,7 +27,7 @@ def getMonth(user_id, year, month):
     try:
         f = open(os.path.join(settings.data_path, f"{user_id}/{year}/{month:02d}.json"), "r")
     except FileNotFoundError:
-        logger.info(f"{user_id}/{year}/{month:02d}.json - File not found")
+        logger.debug(f"{user_id}/{year}/{month:02d}.json - File not found")
         return {}
     except Exception as e:
         logger.exception(e)
index 952ce191c0e4c684a05bfb131d01e96a0dcf9613..2d510d06e8643c959d7959c519038441a5dc22b3 100644 (file)
@@ -5,6 +5,8 @@
        import * as bootstrap from 'bootstrap';
        import { offcanvasIsOpen, sameDate } from '$lib/helpers.js';
 
+       let { bookmarkDay } = $props();
+
        let days = $state([]);
 
        let animationDirection = $state(1); // swipe the dates left or right
                                                        class="day
                                                                {$cal.daysWithLogs.includes(day.day) ? 'mark-background' : ''} 
                                                                {$cal.daysWithFiles.includes(day.day) ? 'mark-dot' : ''} 
+                                                               {$cal.daysBookmarked.includes(day.day) ? 'mark-circle' : ''}
                                                                {(!$readingDate && sameDate($selectedDate, day)) || sameDate($readingDate, day) ? 'selected' : ''}"
                                                        onclick={() => onDateClick(day)}
                                                >
                        >
                </div>
                <div class="col-4 d-flex justify-content-end">
-                       <button class="btn btn-secondary me-2"> Mark </button>
+                       <!-- svelte-ignore a11y_consider_explicit_label -->
+                       <button class="btn btn-secondary me-2" onclick={bookmarkDay}>
+                               <svg
+                                       id="bookmark-icon"
+                                       data-name="Layer 1"
+                                       xmlns="http://www.w3.org/2000/svg"
+                                       viewBox="0 0 91.5 122.88"
+                                       width="18"
+                                       ><defs
+                                               ><style>
+                                                       .cls-1 {
+                                                               fill-rule: evenodd;
+                                                       }
+                                               </style></defs
+                                       ><title>add-bookmark</title>
+                                       {#if !$cal.daysBookmarked.includes($selectedDate.day)}
+                                               <path
+                                                       class="cls-1"
+                                                       d="M62.42,0A29.08,29.08,0,1,1,33.34,29.08,29.08,29.08,0,0,1,62.42,0ZM3.18,19.65H24.73a38,38,0,0,0-1,6.36H6.35v86.75L37.11,86.12a3.19,3.19,0,0,1,4.18,0l31,26.69V66.68a39.26,39.26,0,0,0,6.35-2.27V119.7a3.17,3.17,0,0,1-5.42,2.24l-34-29.26-34,29.42a3.17,3.17,0,0,1-4.47-.33A3.11,3.11,0,0,1,0,119.7H0V22.83a3.18,3.18,0,0,1,3.18-3.18Zm55-2.79a4.1,4.1,0,0,1,.32-1.64l0-.06a4.33,4.33,0,0,1,3.9-2.59h0a4.23,4.23,0,0,1,1.63.32,4.3,4.3,0,0,1,1.39.93,4.15,4.15,0,0,1,.93,1.38l0,.07a4.23,4.23,0,0,1,.3,1.55v8.6h8.57a4.3,4.3,0,0,1,3,1.26,4.23,4.23,0,0,1,.92,1.38l0,.07a4.4,4.4,0,0,1,.31,1.49v.18a4.37,4.37,0,0,1-.32,1.55,4.45,4.45,0,0,1-.93,1.4,4.39,4.39,0,0,1-1.38.92l-.08,0a4.14,4.14,0,0,1-1.54.3H66.71v8.57a4.35,4.35,0,0,1-1.25,3l-.09.08a4.52,4.52,0,0,1-1.29.85l-.08,0a4.36,4.36,0,0,1-1.54.31h0a4.48,4.48,0,0,1-1.64-.32,4.3,4.3,0,0,1-1.39-.93,4.12,4.12,0,0,1-.92-1.38,4.3,4.3,0,0,1-.34-1.62V34H49.56a4.28,4.28,0,0,1-1.64-.32l-.07,0a4.32,4.32,0,0,1-2.25-2.28l0-.08a4.58,4.58,0,0,1-.3-1.54v0a4.39,4.39,0,0,1,.33-1.63,4.3,4.3,0,0,1,3.93-2.66h8.61V16.86Z"
+                                               />
+                                       {:else}
+                                               <path
+                                                       class="cls-1"
+                                                       d="M62.42,0A29.08,29.08,0,1,1,33.34,29.08,29.08,29.08,0,0,1,62.42,0ZM3.18,19.65H24.73a38,38,0,0,0-1,6.36H6.35v86.75L37.11,86.12a3.19,3.19,0,0,1,4.18,0l31,26.69V66.68a39.26,39.26,0,0,0,6.35-2.27V119.7a3.17,3.17,0,0,1-5.42,2.24l-34-29.26-34,29.42a3.17,3.17,0,0,1-4.47-.33A3.11,3.11,0,0,1,0,119.7H0V22.83a3.18,3.18,0,0,1,3.18-3.18Zm72.1,5.77a4.3,4.3,0,0,1,3,1.26,4.23,4.23,0,0,1,.92,1.38l0,.07a4.4,4.4,0,0,1,.31,1.49v.18a4.37,4.37,0,0,1-.32,1.55,4.45,4.45,0,0,1-.93,1.4,4.39,4.39,0,0,1-1.38.92l-.08,0a4.14,4.14,0,0,1-1.54.3H49.56a4.28,4.28,0,0,1-1.64-.32l-.07,0a4.32,4.32,0,0,1-2.25-2.28l0-.08a4.58,4.58,0,0,1-.3-1.54v0a4.39,4.39,0,0,1,.33-1.63,4.3,4.3,0,0,1,3.93-2.66Z"
+                                               />
+                                       {/if}
+                               </svg>
+                       </button>
                </div>
        </div>
 </div>
 
 <style>
+       button:has(#bookmark-icon) {
+               background: #f57c00;
+               border: none;
+       }
+       button:has(#bookmark-icon):hover {
+               background: rgb(223, 111, 0);
+       }
+
        .btnLeftRight {
                color: white;
        }
                color: white;
                aspect-ratio: 1;
        }
+
+       .day.mark-circle {
+               /* background-color: transparent;*/
+               border: 3px solid #f57c00;
+               /* color: #ff9224; */
+       }
+
        .day.mark-dot::after {
                content: '';
                width: 6px;
index 6f2ba340a27fa5df4faa7dbabb7b6d818f08f5ca..650aa20a17f5d2048453ba292f9cd1b86cc0c337 100644 (file)
@@ -32,6 +32,7 @@
                        .then((response) => {
                                $cal.daysWithLogs = [...response.data.days_with_logs];
                                $cal.daysWithFiles = [...response.data.days_with_files];
+                               $cal.daysBookmarked = [...response.data.days_bookmarked];
                        })
                        .catch((error) => {
                                console.error(error);
index 18de68587b4c0e35506a12a60b4109448e5f920b..8607244de6a8764aa5599df50265dd7bb8f29ae6 100644 (file)
@@ -11,6 +11,7 @@
        import { offcanvasIsOpen, sameDate } from '$lib/helpers.js';
        import { API_URL } from '$lib/APIurl.js';
        import axios from 'axios';
+       import { cal } from '$lib/calendarStore.js';
 
        let oc;
 
                        }
                }
        }
+
+       function bookmarkDay() {
+               axios
+                       .get(API_URL + '/logs/bookmarkDay', {
+                               params: {
+                                       year: $selectedDate.year,
+                                       month: $selectedDate.month,
+                                       day: $selectedDate.day
+                               }
+                       })
+                       .then((response) => {
+                               if (response.data.success) {
+                                       if (response.data.bookmarked) {
+                                               $cal.daysBookmarked = [...$cal.daysBookmarked, $selectedDate.day];
+                                       } else {
+                                               $cal.daysBookmarked = $cal.daysBookmarked.filter((day) => day !== $selectedDate.day);
+                                       }
+                               } else {
+                                       console.log('Error highlighting day:', response.data);
+                                       // toast
+                                       const toast = new bootstrap.Toast(document.getElementById('toastErrorHighlighting'));
+                                       toast.show();
+                               }
+                       })
+                       .catch((error) => {
+                               console.error(error);
+                               // toast
+                               const toast = new bootstrap.Toast(document.getElementById('toastErrorHighlighting'));
+                               toast.show();
+                       });
+       }
 </script>
 
 <svelte:window onkeydown={on_key_down} onkeyup={on_key_up} />
 
 <div class="d-flex flex-column h-100">
-       <Datepicker />
+       <Datepicker {bookmarkDay} />
        <br />
 
        <div class="search d-flex flex-column">
                        <div class="toast-body">Fehler beim Suchen!</div>
                </div>
        </div>
+
+       <div
+               id="toastErrorBookmarking"
+               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 Markieren des Tages!</div>
+               </div>
+       </div>
 </div>
 
 <style>
index 6e8e641877b8ada3785a1e462a86c26827341f77..8d3a8cdc129459f6b881aa9a5a64bac21e39e8f2 100644 (file)
                editTag.icon = ev.detail.unicode;
                togglePicker();
        }
+
+       window.addEventListener('freeze', () => {
+               const picker = document.querySelector('emoji-picker');
+               if (picker?.database?.close) {
+                       picker.database.close();
+               }
+       });
 </script>
 
 <div bind:this={modalElement} class="modal fade" id="modalTag" tabindex="-1">
                                        <div class="col-8">
                                                {#if editTag.icon}
                                                        <span>{editTag.icon}</span>
-                                                       <button class="removeBtn" type="button" onclick={(editTag.icon = '')}
-                                                               ><Fa icon={faTrash} fw /></button
+                                                       <button
+                                                               class="removeBtn"
+                                                               type="button"
+                                                               onclick={() => {
+                                                                       editTag.icon = '';
+                                                               }}><Fa icon={faTrash} fw /></button
                                                        >
                                                {:else}
                                                        <span><em>Kein Emoji ausgewählt...</em></span>
index 1a4a94a8a46d4c803ecfe0dac495309a4871ff45..c408c8f3affe538e54e962717fa1fcf719c991a1 100644 (file)
@@ -11,6 +11,7 @@ export let selectedDate = writable({
 export let cal = writable({
   daysWithLogs: [],
   daysWithFiles: [],
+  daysBookmarked: [],
   currentMonth: date.getMonth(),
   currentYear: date.getFullYear(),
 });
index 7812a3a545972da355876af14f00b335d397c040..f42dfd2671953941721a57880fb1f54429f61a2e 100644 (file)
                                                                id="settings-content"
                                                        >
                                                                <div id="appearance">
-                                                                       <h3 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 class="text-primary">Funktionen</h3>
+                                                                       <h3 class="text-primary">🛠️ Funktionen</h3>
 
                                                                        <div id="autoLoadImages">
                                                                                {#if $tempSettings.setAutoloadImagesPerDevice !== $settings.setAutoloadImagesPerDevice || $tempSettings.autoloadImagesByDefault !== $settings.autoloadImagesByDefault}
                                                                </div>
 
                                                                <div id="tags">
-                                                                       <h3 class="text-primary">Tags</h3>
+                                                                       <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">
                                                                </div>
 
                                                                <div id="templates">
-                                                                       <h3 class="text-primary">Vorlagen</h3>
+                                                                       <h3 class="text-primary">📝 Vorlagen</h3>
                                                                        <div>
                                                                                {#if oldTemplateName !== templateName || oldTemplateText !== templateText}
                                                                                        <div class="unsaved-changes" transition:slide></div>
                                                                </div>
 
                                                                <div id="data">
-                                                                       <h4>Daten</h4>
+                                                                       <h3 class="text-primary">📁 Daten</h3>
                                                                        <div id="export"><h5>Export</h5></div>
                                                                        <div id="import"><h5>Import</h5></div>
                                                                </div>
 
                                                                <div id="security">
-                                                                       <h4>Sicherheit</h4>
+                                                                       <h3 class="text-primary">🔒 Sicherheit</h3>
                                                                        <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>
 
                                                                <div id="about">
-                                                                       <h4>About</h4>
+                                                                       <h3 class="text-primary">💡 About</h3>
                                                                        Version:<br />
                                                                        Changelog: <br />
                                                                        Link zu github
git clone https://git.99rst.org/PROJECT