diff --git a/shaarli-pro/css/custom_views.css b/shaarli-pro/css/custom_views.css index 534852f..4068ef6 100644 --- a/shaarli-pro/css/custom_views.css +++ b/shaarli-pro/css/custom_views.css @@ -2111,4 +2111,72 @@ body.view-notes .content-container { grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 10px; +} + +/* --- ARCHIVE VIEW --- */ +body.view-archive .content-container { + padding: 2rem; + background-color: var(--bg-body); + min-height: 100vh; +} + +[data-theme="dark"] body.view-archive .content-container { + background-color: var(--bg-body); +} + +/* Archive Title */ +.archive-title-container { + text-align: center; + margin-bottom: 1rem; +} + +.archive-title { + font-size: 1.75rem; + font-weight: 600; + color: var(--text-color, #202124); + margin: 0; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.archive-title i { + font-size: 1.5rem; + color: var(--primary-color, #2563eb); +} + +[data-theme="dark"] .archive-title { + color: #e8eaed; +} + +.archive-subtitle { + font-size: 0.875rem; + color: var(--text-light, #5f6368); + margin: 0.25rem 0 0 0; +} + +[data-theme="dark"] .archive-subtitle { + color: #9aa0a6; +} + +/* Archive wrapper */ +.archive-wrapper { + max-width: 1200px; + margin: 0 auto; +} + +/* Archive top bar adjustments */ +.archive-top-bar { + flex-direction: column; + align-items: center; + padding-right: 0; + gap: 1rem; +} + +.archive-top-bar .notes-tools { + position: relative; + right: auto; + top: auto; + transform: none; } \ No newline at end of file diff --git a/shaarli-pro/js/custom_views.js b/shaarli-pro/js/custom_views.js index 8b02e47..4326ba6 100644 --- a/shaarli-pro/js/custom_views.js +++ b/shaarli-pro/js/custom_views.js @@ -33,6 +33,13 @@ document.addEventListener("DOMContentLoaded", function () { if (typeof initTagDisplayAndRemoval === "function") { initTagDisplayAndRemoval(); } + } else if (searchTags === "shaarli-archive") { + // Vue Archive - similaire à Notes mais pour les notes archivées + initArchiveView(linkList, container); + // Puis supprimer les tags techniques de l'affichage + if (typeof initTagDisplayAndRemoval === "function") { + initTagDisplayAndRemoval(); + } } else { // Vue standard : supprimer les tags techniques if (typeof initTagDisplayAndRemoval === "function") { @@ -1964,11 +1971,11 @@ function initNoteView(linkList, container) { const editUrl = modalCard.dataset.editUrl; if (!noteId || !editUrl) return; - addTagToNote(editUrl, "shaarli-archiver") + addTagToNote(editUrl, "shaarli-archive") .then(() => { if (modalOverlay.currentNote) { - if (!modalOverlay.currentNote.tags.includes("shaarli-archiver")) { - modalOverlay.currentNote.tags.push("shaarli-archiver"); + if (!modalOverlay.currentNote.tags.includes("shaarli-archive")) { + modalOverlay.currentNote.tags.push("shaarli-archive"); } } @@ -1993,7 +2000,231 @@ function initNoteView(linkList, container) { }); document.addEventListener("click", (e) => { - if (e.target.closest(".note-hover-actions .palette-popup") || e.target.closest('.note-hover-actions [id^="palette-"]')) { + if (!e.target.closest(".note-hover-actions .palette-popup") || e.target.closest('.note-hover-actions [id^="palette-"]')) { + return; + } + + document.querySelectorAll(".note-hover-actions .palette-popup.open").forEach((p) => { + p.classList.remove("open"); + }); + }); +} + +/** + * Initialize the Archive view (similar to Notes but for archived notes) + */ +function initArchiveView(linkList, container) { + document.body.classList.add("view-notes", "view-archive"); + + // Hide standard toolbar + const toolbar = document.querySelector(".content-toolbar"); + if (toolbar) toolbar.style.display = "none"; + + // 1. Create Layout Wrapper + const wrapper = document.createElement("div"); + wrapper.className = "notes-wrapper archive-wrapper"; + + // 2. Create Title Area (Top) + const topBar = document.createElement("div"); + topBar.className = "notes-top-bar archive-top-bar"; + + // Title for archive view + const titleContainer = document.createElement("div"); + titleContainer.className = "archive-title-container"; + titleContainer.innerHTML = ` +
Notes archivées
+ `; + topBar.appendChild(titleContainer); + + // View Toggle and other tools + const tools = document.createElement("div"); + tools.className = "notes-tools"; + tools.innerHTML = ` + + + `; + topBar.appendChild(tools); + + wrapper.appendChild(topBar); + + // 3. Content Area + const contentArea = document.createElement("div"); + contentArea.className = "notes-content-area"; + + const links = Array.from(linkList.querySelectorAll(".link-outer")); + const notes = links.map((link) => parseNoteFromLink(link)); + + // Filter only archived notes + const archivedNotes = notes.filter((note) => (note.tags || []).includes("shaarli-archive")); + + // Initial Render (Grid) + renderNotes(contentArea, archivedNotes, "grid", true); // true = archive mode + + wrapper.appendChild(contentArea); + + // Replace original list + linkList.style.display = "none"; + if (linkList.parentNode) { + linkList.parentNode.insertBefore(wrapper, linkList); + } else { + container.appendChild(wrapper); + } + + // Modal Container (reuse the same modal as notes) + const modalOverlay = document.createElement("div"); + modalOverlay.className = "note-modal-overlay"; + modalOverlay.innerHTML = ` + + `; + document.body.appendChild(modalOverlay); + + // Event Listeners for Toggles + const btnGrid = wrapper.querySelector("#btn-view-grid"); + const btnList = wrapper.querySelector("#btn-view-list"); + + btnGrid.addEventListener("click", () => { + btnGrid.classList.add("active"); + btnList.classList.remove("active"); + renderNotes(contentArea, archivedNotes, "grid", true); + }); + + btnList.addEventListener("click", () => { + btnList.classList.add("active"); + btnGrid.classList.remove("active"); + renderNotes(contentArea, archivedNotes, "list", true); + }); + + // Close Modal + modalOverlay.querySelector("#note-modal-close").addEventListener("click", () => { + modalOverlay.classList.remove("open"); + }); + modalOverlay.addEventListener("click", (e) => { + if (e.target === modalOverlay) modalOverlay.classList.remove("open"); + }); + + const modalPinBtn = modalOverlay.querySelector("#note-modal-pin"); + const modalColorBtn = modalOverlay.querySelector("#note-modal-color-btn"); + const modalColorPopup = modalOverlay.querySelector("#note-modal-color-popup"); + + modalColorBtn.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + const modalCard = modalOverlay.querySelector(".note-modal"); + openBackgroundStudioPanel({ + anchorEl: modalColorBtn, + mode: "modal", + entityId: modalCard ? modalCard.dataset.noteId || "" : "", + editUrl: modalCard ? modalCard.dataset.editUrl || "" : "", + currentColor: modalCard ? getElementVisualColor(modalCard) : "default", + currentFilter: modalCard ? getElementVisualFilter(modalCard) : "none", + currentBackground: modalCard ? getElementVisualBackground(modalCard) : "none", + currentFontColor: modalCard ? getElementVisualFontColor(modalCard) : "auto", + title: "Mes images & couleurs", + }); + }); + + modalPinBtn.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + + const modalCard = modalOverlay.querySelector(".note-modal"); + const noteId = modalCard.dataset.noteId; + const editUrl = modalCard.dataset.editUrl; + + if (!noteId || !editUrl) return; + + togglePinTag(noteId, editUrl, modalPinBtn); + + const isPinned = modalPinBtn.classList.contains("active"); + let tags = (modalCard.dataset.tags || "") + .split("||") + .filter((t) => t); + + if (isPinned) { + if (!tags.includes("shaarli-pin")) tags.push("shaarli-pin"); + } else { + tags = tags.filter((t) => t !== "shaarli-pin"); + } + + modalCard.dataset.tags = tags.join("||"); + renderModalTags(modalOverlay.querySelector("#note-modal-tags"), tags); + + if (modalOverlay.currentNote) { + modalOverlay.currentNote.isPinned = isPinned; + modalOverlay.currentNote.tags = tags; + } + }); + + modalOverlay.querySelector("#note-modal-delete").addEventListener("click", () => { + const modalCard = modalOverlay.querySelector(".note-modal"); + const deleteUrl = modalCard.dataset.deleteUrl; + if (deleteUrl && deleteUrl !== "#") { + window.location.href = deleteUrl; + } + }); + + // Unarchive button in modal + modalOverlay.querySelector("#note-modal-unarchive").addEventListener("click", (e) => { + e.preventDefault(); + + const modalCard = modalOverlay.querySelector(".note-modal"); + const noteId = modalCard.dataset.noteId; + const editUrl = modalCard.dataset.editUrl; + if (!noteId || !editUrl) return; + + removeTagFromEntity(editUrl, "shaarli-archive") + .then(() => { + if (modalOverlay.currentNote) { + modalOverlay.currentNote.tags = (modalOverlay.currentNote.tags || []).filter((t) => t !== "shaarli-archive"); + } + + const noteCard = document.querySelector(`.note-card[data-id="${noteId}"]`); + if (noteCard) noteCard.remove(); + + const index = archivedNotes.findIndex((n) => String(n.id) === String(noteId)); + if (index > -1) archivedNotes.splice(index, 1); + + modalOverlay.classList.remove("open"); + }) + .catch((err) => { + console.error("Error unarchiving note:", err); + alert("Erreur lors du désarchivage de la note."); + }); + }); + + modalOverlay.addEventListener("click", (e) => { + if (!e.target.closest(".note-modal-color-picker")) { + modalColorPopup.classList.remove("open"); + } + }); + + document.addEventListener("click", (e) => { + if (!e.target.closest(".note-hover-actions .palette-popup") || e.target.closest('.note-hover-actions [id^="palette-"]')) { return; } @@ -2064,11 +2295,19 @@ function parseNoteFromLink(linkEl) { return { id, title, descHtml, descText, coverImage, url, tags, color, filter, background, fontColor, editUrl, deleteUrl, pinUrl, isPinned }; } -function renderNotes(container, notes, viewMode) { +function renderNotes(container, notes, viewMode, isArchiveMode = false) { container.innerHTML = ""; container.className = viewMode === "grid" ? "notes-masonry" : "notes-list-view"; - const visibleNotes = notes.filter((note) => !(note.tags || []).includes("shaarli-archiver")); + // Filter notes based on archive mode + let visibleNotes; + if (isArchiveMode) { + // In archive mode: show only notes with shaarli-archive tag + visibleNotes = notes.filter((note) => (note.tags || []).includes("shaarli-archive")); + } else { + // In normal notes mode: hide archived notes + visibleNotes = notes.filter((note) => !(note.tags || []).includes("shaarli-archive")); + } // Sort: Pinned items first visibleNotes.sort((a, b) => { @@ -2143,6 +2382,7 @@ function renderNotes(container, notes, viewMode) { // Palette Button Logic const paletteBtnId = `palette-${note.id}`; + const archiveBtnId = `archive-${note.id}`; actions.innerHTML = ` @@ -2151,7 +2391,7 @@ function renderNotes(container, notes, viewMode) { - + @@ -2176,6 +2416,26 @@ function renderNotes(container, notes, viewMode) { }); }); + // Archive button handler + const archiveBtn = actions.querySelector(`#${archiveBtnId}`); + archiveBtn.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + + if (!note.editUrl || note.editUrl === "#") return; + + addTagToNote(note.editUrl, "shaarli-archive") + .then(() => { + // Remove the card from the view + card.remove(); + console.log(`Note ${note.id} archived`); + }) + .catch((err) => { + console.error("Error archiving note:", err); + alert("Erreur lors de l'archivage de la note."); + }); + }); + inner.appendChild(actions); card.appendChild(inner); diff --git a/shaarli-pro/page.header.html b/shaarli-pro/page.header.html index d5bba2c..8b09129 100644 --- a/shaarli-pro/page.header.html +++ b/shaarli-pro/page.header.html @@ -57,6 +57,10 @@ Bookmarklet detection logic Notes + + + Archive + Read It Later @@ -151,6 +155,10 @@ Bookmarklet detection logic NOTES + + + ARCHIVE + READ IT LATER