added display of currently shared notes
This commit is contained in:
parent
4061633daa
commit
142a3a59cd
|
|
@ -19,6 +19,9 @@ const CONFIG = {
|
|||
TOAST_DURATION_SUCCESS_MS: 3500,
|
||||
};
|
||||
|
||||
/** Heroicons outline "share" (same d= as shared-note icon in the file tree) */
|
||||
const SHARE_ICON_PATH = 'M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z';
|
||||
|
||||
// localStorage settings configuration - centralized definition of all persisted settings
|
||||
const LOCAL_SETTINGS = {
|
||||
// Boolean settings
|
||||
|
|
@ -231,7 +234,7 @@ function noteApp() {
|
|||
sortMode: localStorage.getItem('sortMode') || 'a-z',
|
||||
|
||||
// Icon rail / panel state
|
||||
activePanel: 'files', // 'files', 'search', 'tags', 'settings'
|
||||
activePanel: 'files', // 'files', 'search', 'tags', 'outline', 'backlinks', 'shared', 'settings'
|
||||
|
||||
// Folder state
|
||||
folderTree: [],
|
||||
|
|
@ -329,6 +332,7 @@ function noteApp() {
|
|||
showShareQR: false,
|
||||
shareLinkCopied: false,
|
||||
_sharedNotePaths: new Set(), // O(1) lookup for shared note indicators
|
||||
_sharedNotePathsList: [], // sorted paths, mirrors Set for reactive sidebar panel
|
||||
|
||||
// Quick Switcher state (Ctrl+Alt+P)
|
||||
showQuickSwitcher: false,
|
||||
|
|
@ -2237,7 +2241,7 @@ function noteApp() {
|
|||
|
||||
// Share icon for shared notes
|
||||
const isShared = !isMediaFile && this.isNoteShared(note.path);
|
||||
const shareIcon = isShared ? '<svg aria-hidden="true" style="display: inline-block; width: 12px; height: 12px; vertical-align: middle; margin-right: 2px; opacity: 0.7;" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"></path></svg>' : '';
|
||||
const shareIcon = isShared ? `<svg aria-hidden="true" style="display: inline-block; width: 12px; height: 12px; vertical-align: middle; margin-right: 2px; opacity: 0.7;" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${SHARE_ICON_PATH}"></path></svg>` : '';
|
||||
const icon = this.getMediaIcon(note.type);
|
||||
const deleteTitle = !isMediaFile
|
||||
? this.t('toolbar.delete_note')
|
||||
|
|
@ -6111,19 +6115,38 @@ function noteApp() {
|
|||
// Share Functions
|
||||
// ============================================================================
|
||||
|
||||
// Load list of shared note paths (for visual indicators)
|
||||
// Load list of shared note paths (for visual indicators and Shared sidebar panel)
|
||||
async loadSharedNotePaths() {
|
||||
try {
|
||||
const response = await fetch('/api/shared-notes');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this._sharedNotePaths = new Set(data.paths || []);
|
||||
this._syncSharedNotePathsList();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load shared note paths:', error);
|
||||
this._sharedNotePaths = new Set();
|
||||
this._sharedNotePathsList = [];
|
||||
}
|
||||
},
|
||||
|
||||
_syncSharedNotePathsList() {
|
||||
this._sharedNotePathsList = [...this._sharedNotePaths].sort((a, b) =>
|
||||
a.localeCompare(b, undefined, { sensitivity: 'base' })
|
||||
);
|
||||
},
|
||||
|
||||
/** Display rows for Shared notes panel: name + folder line (search-style) */
|
||||
getSharedPanelItems() {
|
||||
return this._sharedNotePathsList.map((path) => {
|
||||
const noMd = path.replace(/\.md$/i, '');
|
||||
const last = Math.max(noMd.lastIndexOf('/'), noMd.lastIndexOf('\\'));
|
||||
const name = last < 0 ? noMd : noMd.slice(last + 1);
|
||||
const folder = last < 0 ? '' : noMd.slice(0, last).replace(/\\/g, '/');
|
||||
return { path, name, folder: folder || 'Root' };
|
||||
});
|
||||
},
|
||||
|
||||
// Check if a note is currently shared (O(1) lookup)
|
||||
isNoteShared(notePath) {
|
||||
|
|
@ -6284,6 +6307,7 @@ function noteApp() {
|
|||
this.shareInfo.shared = true;
|
||||
// Update the shared paths set
|
||||
this._sharedNotePaths.add(this.currentNote);
|
||||
this._syncSharedNotePathsList();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
this.toast(this.t('share.error_creating', { error: error.detail || 'Unknown error' }), { type: 'error' });
|
||||
|
|
@ -6342,6 +6366,7 @@ function noteApp() {
|
|||
this.shareInfo = { shared: false };
|
||||
// Update the shared paths set
|
||||
this._sharedNotePaths.delete(this.currentNote);
|
||||
this._syncSharedNotePathsList();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
this.toast(this.t('share.error_revoking', { error: error.detail || 'Unknown error' }), { type: 'error' });
|
||||
|
|
|
|||
|
|
@ -1521,6 +1521,26 @@
|
|||
x-text="backlinks.length > 9 ? '9+' : backlinks.length"
|
||||
></span>
|
||||
</button>
|
||||
|
||||
<!-- Shared notes (public links) -->
|
||||
<button
|
||||
class="icon-rail-btn relative"
|
||||
:class="{'active': activePanel === 'shared'}"
|
||||
@click="activePanel = 'shared'"
|
||||
:title="t('share.panel_title')"
|
||||
:aria-label="t('share.panel_title')"
|
||||
>
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"></path>
|
||||
</svg>
|
||||
<span
|
||||
x-show="_sharedNotePathsList.length > 0"
|
||||
x-cloak
|
||||
class="absolute -top-1 -right-1 w-4 h-4 text-[9px] font-bold rounded-full flex items-center justify-center"
|
||||
style="background-color: var(--accent-primary); color: white;"
|
||||
x-text="_sharedNotePathsList.length > 9 ? '9+' : _sharedNotePathsList.length"
|
||||
></span>
|
||||
</button>
|
||||
|
||||
<div class="icon-rail-spacer"></div>
|
||||
|
||||
|
|
@ -1992,6 +2012,48 @@
|
|||
</div>
|
||||
<!-- END BACKLINKS PANEL -->
|
||||
|
||||
<!-- ==================== SHARED NOTES PANEL ==================== -->
|
||||
<div x-show="activePanel === 'shared'" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" class="flex flex-col h-full">
|
||||
<div class="flex-shrink-0 px-3 py-2 border-b flex items-center justify-between" style="border-color: var(--border-primary);">
|
||||
<span class="text-xs font-semibold uppercase tracking-wide" style="color: var(--text-tertiary);" x-text="t('share.panel_title')"></span>
|
||||
<span class="text-xs px-1.5 py-0.5 rounded" style="background-color: var(--bg-tertiary); color: var(--text-tertiary);" x-text="_sharedNotePathsList.length"></span>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto custom-scrollbar">
|
||||
<div class="py-2">
|
||||
<template x-if="_sharedNotePathsList.length > 0">
|
||||
<div class="px-2">
|
||||
<template x-for="item in getSharedPanelItems()" :key="item.path">
|
||||
<div
|
||||
@click="openItem(item.path, 'note')"
|
||||
class="hover-accent px-2 py-1.5 text-sm cursor-pointer mb-1 rounded"
|
||||
:style="currentNote === item.path ? 'background-color: var(--accent-light); color: var(--accent-primary);' : 'color: var(--text-primary);'"
|
||||
>
|
||||
<div class="flex items-start gap-2 mb-0.5">
|
||||
<svg class="w-4 h-4 flex-shrink-0 mt-0.5" style="color: var(--accent-primary); opacity: 0.9;" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"></path>
|
||||
</svg>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="font-medium truncate" x-text="item.name" :title="item.name"></div>
|
||||
<div class="text-xs truncate" style="color: var(--text-tertiary);" x-text="item.folder" :title="item.folder"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="_sharedNotePathsList.length === 0">
|
||||
<div class="text-center py-6 px-3">
|
||||
<svg class="w-12 h-12 mx-auto mb-3 opacity-40" style="color: var(--text-tertiary);" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"></path>
|
||||
</svg>
|
||||
<p class="text-sm" style="color: var(--text-tertiary);" x-text="t('share.no_shared')"></p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END SHARED NOTES PANEL -->
|
||||
|
||||
<!-- ==================== SETTINGS PANEL ==================== -->
|
||||
<template x-if="activePanel === 'settings'">
|
||||
<div class="flex flex-col h-full">
|
||||
|
|
|
|||
|
|
@ -142,7 +142,9 @@
|
|||
"error_creating": "Fehler beim Erstellen des Freigabelinks: {{error}}",
|
||||
"error_revoking": "Fehler beim Widerrufen des Freigabelinks: {{error}}",
|
||||
"show_qr": "QR-Code anzeigen",
|
||||
"hide_qr": "QR-Code ausblenden"
|
||||
"hide_qr": "QR-Code ausblenden",
|
||||
"panel_title": "Geteilt",
|
||||
"no_shared": "Keine geteilten Notizen"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "Notizen suchen...",
|
||||
|
|
|
|||
|
|
@ -141,7 +141,9 @@
|
|||
"error_creating": "Failed to create share link: {{error}}",
|
||||
"error_revoking": "Failed to revoke share link: {{error}}",
|
||||
"show_qr": "Show QR Code",
|
||||
"hide_qr": "Hide QR Code"
|
||||
"hide_qr": "Hide QR Code",
|
||||
"panel_title": "Shared",
|
||||
"no_shared": "No shared notes"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "Type to search notes...",
|
||||
|
|
|
|||
|
|
@ -142,7 +142,9 @@
|
|||
"error_creating": "Failed to create share link: {{error}}",
|
||||
"error_revoking": "Failed to revoke share link: {{error}}",
|
||||
"show_qr": "Show QR Code",
|
||||
"hide_qr": "Hide QR Code"
|
||||
"hide_qr": "Hide QR Code",
|
||||
"panel_title": "Shared",
|
||||
"no_shared": "No shared notes"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "Type to search notes...",
|
||||
|
|
|
|||
|
|
@ -142,7 +142,9 @@
|
|||
"error_creating": "Error al crear el enlace: {{error}}",
|
||||
"error_revoking": "Error al revocar el enlace: {{error}}",
|
||||
"show_qr": "Mostrar código QR",
|
||||
"hide_qr": "Ocultar código QR"
|
||||
"hide_qr": "Ocultar código QR",
|
||||
"panel_title": "Compartidas",
|
||||
"no_shared": "No hay notas compartidas"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "Escribe para buscar notas...",
|
||||
|
|
|
|||
|
|
@ -142,7 +142,9 @@
|
|||
"error_creating": "Échec de la création du lien : {{error}}",
|
||||
"error_revoking": "Échec de la révocation du lien : {{error}}",
|
||||
"show_qr": "Afficher le code QR",
|
||||
"hide_qr": "Masquer le code QR"
|
||||
"hide_qr": "Masquer le code QR",
|
||||
"panel_title": "Partagées",
|
||||
"no_shared": "Aucune note partagée"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "Rechercher des notes...",
|
||||
|
|
|
|||
|
|
@ -142,7 +142,9 @@
|
|||
"error_creating": "Hiba történt a megosztási link létrehozásánál: {{error}}",
|
||||
"error_revoking": "Nemsikerült visszavonni a megosztási linket: {{error}}",
|
||||
"show_qr": "QR-Kód Megjelenítése",
|
||||
"hide_qr": "QR-Kód Elrejtése"
|
||||
"hide_qr": "QR-Kód Elrejtése",
|
||||
"panel_title": "Megosztott",
|
||||
"no_shared": "Nincs megosztott jegyzet"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "Írj a jegyzetek kereséséhez...",
|
||||
|
|
|
|||
|
|
@ -141,7 +141,9 @@
|
|||
"error_creating": "Impossibile creare il link: {{error}}",
|
||||
"error_revoking": "Impossibile revocare il link: {{error}}",
|
||||
"show_qr": "Mostra codice QR",
|
||||
"hide_qr": "Nascondi codice QR"
|
||||
"hide_qr": "Nascondi codice QR",
|
||||
"panel_title": "Condivise",
|
||||
"no_shared": "Nessuna nota condivisa"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "Cerca note...",
|
||||
|
|
|
|||
|
|
@ -141,7 +141,9 @@
|
|||
"error_creating": "共有リンクの作成に失敗しました: {{error}}",
|
||||
"error_revoking": "共有リンクの取り消しに失敗しました: {{error}}",
|
||||
"show_qr": "QRコードを表示",
|
||||
"hide_qr": "QRコードを非表示"
|
||||
"hide_qr": "QRコードを非表示",
|
||||
"panel_title": "共有",
|
||||
"no_shared": "共有中のノートはありません"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "ノートを検索...",
|
||||
|
|
|
|||
|
|
@ -141,7 +141,9 @@
|
|||
"error_creating": "Не удалось создать ссылку: {{error}}",
|
||||
"error_revoking": "Не удалось отозвать ссылку: {{error}}",
|
||||
"show_qr": "Показать QR-код",
|
||||
"hide_qr": "Скрыть QR-код"
|
||||
"hide_qr": "Скрыть QR-код",
|
||||
"panel_title": "Опубликовано",
|
||||
"no_shared": "Нет опубликованных заметок"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "Поиск заметок...",
|
||||
|
|
|
|||
|
|
@ -141,7 +141,9 @@
|
|||
"error_creating": "Napaka pri ustvarjanju povezave: {{error}}",
|
||||
"error_revoking": "Napaka pri preklicu povezave: {{error}}",
|
||||
"show_qr": "Prikaži QR kodo",
|
||||
"hide_qr": "Skrij QR kodo"
|
||||
"hide_qr": "Skrij QR kodo",
|
||||
"panel_title": "V skupni rabi",
|
||||
"no_shared": "Ni deljenih zapisov"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "Išči beležke...",
|
||||
|
|
|
|||
|
|
@ -141,7 +141,9 @@
|
|||
"error_creating": "创建分享链接失败:{{error}}",
|
||||
"error_revoking": "撤销分享链接失败:{{error}}",
|
||||
"show_qr": "显示二维码",
|
||||
"hide_qr": "隐藏二维码"
|
||||
"hide_qr": "隐藏二维码",
|
||||
"panel_title": "已分享",
|
||||
"no_shared": "暂无已分享的笔记"
|
||||
},
|
||||
"quick_switcher": {
|
||||
"placeholder": "搜索笔记...",
|
||||
|
|
|
|||
Loading…
Reference in New Issue