diff --git a/apps/client/src/widgets/collections/NoteList.tsx b/apps/client/src/widgets/collections/NoteList.tsx index c4402e8c7..d57c66871 100644 --- a/apps/client/src/widgets/collections/NoteList.tsx +++ b/apps/client/src/widgets/collections/NoteList.tsx @@ -1,4 +1,4 @@ -import { allViewTypes, ViewTypeOptions } from "./interface"; +import { allViewTypes, ViewModeProps, ViewTypeOptions } from "./interface"; import { useNoteContext, useNoteLabel, useTriliumEvent } from "../react/hooks"; import FNote from "../../entities/fnote"; import "./NoteList.css"; @@ -13,27 +13,27 @@ export default function NoteList({ }: NoteListProps) { const { note } = useNoteContext(); const viewType = useNoteViewType(note); const noteIds = useNoteIds(note, viewType); - const isEnabled = (!!viewType); + const isEnabled = (note && !!viewType); // Refresh note Ids - console.log("Got note ids", noteIds); return (
{isEnabled && (
- {getComponentByViewType(viewType)} + {getComponentByViewType(note, noteIds, viewType)}
)}
); } -function getComponentByViewType(viewType: ViewTypeOptions) { - console.log("Got ", viewType); +function getComponentByViewType(note: FNote, noteIds: string[], viewType: ViewTypeOptions) { + const props: ViewModeProps = { note, noteIds }; + switch (viewType) { case "list": - return ; + return ; } } @@ -54,12 +54,13 @@ function useNoteIds(note: FNote | null | undefined, viewType: ViewTypeOptions | const [ noteIds, setNoteIds ] = useState([]); async function refreshNoteIds() { - console.log("Refreshed note IDs"); if (!note) { setNoteIds([]); } else if (viewType === "list" || viewType === "grid") { + console.log("Refreshed note IDs"); setNoteIds(note.getChildNoteIds()); } else { + console.log("Refreshed note IDs"); setNoteIds(await note.getSubtreeNoteIds()); } } diff --git a/apps/client/src/widgets/collections/interface.ts b/apps/client/src/widgets/collections/interface.ts index 6d64c2b45..4c3c71e76 100644 --- a/apps/client/src/widgets/collections/interface.ts +++ b/apps/client/src/widgets/collections/interface.ts @@ -1,5 +1,14 @@ +import FNote from "../../entities/fnote"; import type { ViewModeArgs } from "../view_widgets/view_mode"; export const allViewTypes = ["list", "grid", "calendar", "table", "geoMap", "board"] as const; export type ArgsWithoutNoteId = Omit; export type ViewTypeOptions = typeof allViewTypes[number]; + +export interface ViewModeProps { + note: FNote; + /** + * We're using noteIds so that it's not necessary to load all notes at once when paging. + */ + noteIds: string[]; +} diff --git a/apps/client/src/widgets/collections/legacy/ListView.tsx b/apps/client/src/widgets/collections/legacy/ListView.tsx index 25817b806..550edff60 100644 --- a/apps/client/src/widgets/collections/legacy/ListView.tsx +++ b/apps/client/src/widgets/collections/legacy/ListView.tsx @@ -1,14 +1,80 @@ -export default function ListView() { +import { useEffect, useMemo, useState } from "preact/hooks"; +import FNote from "../../../entities/fnote"; +import Icon from "../../react/Icon"; +import { ViewModeProps } from "../interface"; +import { useNoteLabel, useNoteLabelBoolean } from "../../react/hooks"; +import froca from "../../../services/froca"; +import NoteLink from "../../react/NoteLink"; + +export default function ListView({ note, noteIds }: ViewModeProps) { + const [ isExpanded ] = useNoteLabelBoolean(note, "expanded"); + const filteredNoteIds = useMemo(() => { + // Filters the note IDs for the legacy view to filter out subnotes that are already included in the note content such as images, included notes. + const includedLinks = note ? note.getRelations().filter((rel) => rel.name === "imageLink" || rel.name === "includeNoteLink") : []; + const includedNoteIds = new Set(includedLinks.map((rel) => rel.value)); + return noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden"); + }, noteIds); + const { pageNotes } = usePagination(note, filteredNoteIds); + return (
- List view goes here.
- +
); +} + +function NoteCard({ note, expand }: { note: FNote, expand?: boolean }) { + const isSearch = note.type === "search"; + const notePath = isSearch + ? note.noteId // for search note parent, we want to display a non-search path + : `${note.noteId}/${note.noteId}`; + + return ( +
+
+ + +
+
+ ) +} + +function usePagination(note: FNote, noteIds: string[]) { + const [ page, setPage ] = useState(1); + const [ pageNotes, setPageNotes ] = useState(); + + // Parse page size. + const [ pageSize ] = useNoteLabel(note, "pageSize"); + const pageSizeNum = parseInt(pageSize ?? "", 10); + const normalizedPageSize = (pageSizeNum && pageSizeNum > 0 ? pageSizeNum : 20); + + // Calculate start/end index. + const startIdx = (page - 1) * normalizedPageSize; + const endIdx = startIdx + normalizedPageSize; + + // Obtain notes within the range. + const pageNoteIds = noteIds.slice(startIdx, Math.min(endIdx, noteIds.length)); + + useEffect(() => { + froca.getNotes(pageNoteIds).then(setPageNotes); + }, [ note, noteIds, page, pageSize ]); + + return { + page, + setPage, + pageNotes + } } \ No newline at end of file diff --git a/apps/client/src/widgets/react/Icon.tsx b/apps/client/src/widgets/react/Icon.tsx index cc7afe812..e047a1762 100644 --- a/apps/client/src/widgets/react/Icon.tsx +++ b/apps/client/src/widgets/react/Icon.tsx @@ -1,7 +1,8 @@ interface IconProps { icon?: string; + className?: string; } -export default function Icon({ icon }: IconProps) { - return +export default function Icon({ icon, className }: IconProps) { + return } \ No newline at end of file diff --git a/apps/client/src/widgets/view_widgets/list_or_grid_view.ts b/apps/client/src/widgets/view_widgets/list_or_grid_view.ts index f68521d5c..e533b3562 100644 --- a/apps/client/src/widgets/view_widgets/list_or_grid_view.ts +++ b/apps/client/src/widgets/view_widgets/list_or_grid_view.ts @@ -17,40 +17,10 @@ class ListOrGridView extends ViewMode<{}> { private showNotePath?: boolean; private highlightRegex?: RegExp | null; - /* - * We're using noteIds so that it's not necessary to load all notes at once when paging - */ constructor(viewType: ViewTypeOptions, args: ViewModeArgs) { super(args, viewType); this.$noteList = $(TPL); - - - args.$parent.append(this.$noteList); - - this.page = 1; - this.pageSize = parseInt(args.parentNote.getLabelValue("pageSize") || ""); - - if (!this.pageSize || this.pageSize < 1) { - this.pageSize = 20; - } - this.$noteList.addClass(`${this.viewType}-view`); - - this.showNotePath = args.showNotePath; - } - - /** @returns {Set} list of noteIds included (images, included notes) in the parent note and which - * don't have to be shown in the note list. */ - getIncludedNoteIds() { - const includedLinks = this.parentNote ? this.parentNote.getRelations().filter((rel) => rel.name === "imageLink" || rel.name === "includeNoteLink") : []; - - return new Set(includedLinks.map((rel) => rel.value)); - } - - async beforeRender() { - super.beforeRender(); - const includedNoteIds = this.getIncludedNoteIds(); - this.filteredNoteIds = this.noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden"); } async renderList() { @@ -70,20 +40,6 @@ class ListOrGridView extends ViewMode<{}> { this.$noteList.show(); - const $container = this.$noteList.find(".note-list-container").empty(); - - const startIdx = (this.page - 1) * this.pageSize; - const endIdx = startIdx + this.pageSize; - - const pageNoteIds = this.filteredNoteIds.slice(startIdx, Math.min(endIdx, this.filteredNoteIds.length)); - const pageNotes = await froca.getNotes(pageNoteIds); - - for (const note of pageNotes) { - const $card = await this.renderNote(note, this.parentNote.isLabelTruthy("expanded")); - - $container.append($card); - } - this.renderPager(); return this.$noteList; @@ -132,25 +88,15 @@ class ListOrGridView extends ViewMode<{}> { } async renderNote(note: FNote, expand: boolean = false) { - const $expander = $(''); - const { $renderedAttributes } = await attributeRenderer.renderNormalAttributes(note); - const notePath = - this.parentNote.type === "search" - ? note.noteId // for search note parent, we want to display a non-search path - : `${this.parentNote.noteId}/${note.noteId}`; const $card = $('
') - .attr("data-note-id", note.noteId) - .addClass("no-tooltip-preview") .append( $('
') - .append($expander) - .append($('').addClass(note.getIcon())) .append( this.viewType === "grid" ? $('').text(await treeService.getNoteTitle(note.noteId, this.parentNote.noteId)) - : (await linkService.createLink(notePath, { showTooltip: false, showNotePath: this.showNotePath })).addClass("note-book-title") + : (await linkService.createLink(notePath, { showNotePath: this.showNotePath })).addClass("note-book-title") ) .append($renderedAttributes) );