This commit is contained in:
Brent Bilis 2026-01-27 11:22:21 -05:00 committed by GitHub
commit 665ab485b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 84 additions and 7 deletions

View File

@ -1,5 +1,5 @@
import { useQueryClient } from "@tanstack/react-query";
import { ArrowUpIcon } from "lucide-react";
import { ArrowUpIcon, ChevronDownIcon, ChevronRightIcon } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { matchPath } from "react-router-dom";
import { Button } from "@/components/ui/button";
@ -28,6 +28,7 @@ interface Props {
pageSize?: number;
showCreator?: boolean;
enabled?: boolean;
collapsiblePinned?: boolean;
}
function useAutoFetchWhenNotScrollable({
@ -104,6 +105,29 @@ const PagedMemoList = (props: Props) => {
// Apply custom sorting if provided, otherwise use memos directly
const sortedMemoList = useMemo(() => (props.listSort ? props.listSort(memos) : memos), [memos, props.listSort]);
const enablePinnedSection = props.collapsiblePinned === true;
const pinnedStorageKey = "memos.ui.pinsCollapsed";
const [isPinnedCollapsed, setIsPinnedCollapsed] = useState(() => {
if (!enablePinnedSection) return false;
if (typeof window === "undefined") return false;
return window.localStorage.getItem(pinnedStorageKey) === "true";
});
const pinnedMemos = useMemo(() => {
if (!enablePinnedSection) return [];
return sortedMemoList.filter((memo) => memo.pinned);
}, [enablePinnedSection, sortedMemoList]);
const unpinnedMemos = useMemo(() => {
if (!enablePinnedSection) return sortedMemoList;
return sortedMemoList.filter((memo) => !memo.pinned);
}, [enablePinnedSection, sortedMemoList]);
useEffect(() => {
if (!enablePinnedSection || typeof window === "undefined") return;
window.localStorage.setItem(pinnedStorageKey, String(isPinnedCollapsed));
}, [enablePinnedSection, isPinnedCollapsed]);
// Prefetch creators when new data arrives to improve performance
useEffect(() => {
@ -155,19 +179,71 @@ const PagedMemoList = (props: Props) => {
<Skeleton showCreator={props.showCreator} count={4} />
) : (
<>
<MasonryView
memoList={sortedMemoList}
renderer={props.renderer}
prefixElement={
{(() => {
const hasPinned = pinnedMemos.length > 0;
const pinnedToggle = enablePinnedSection && hasPinned && (
<div className="w-full mt-1 mb-2 flex items-center gap-3 text-sm text-muted-foreground">
<button
type="button"
onClick={() => setIsPinnedCollapsed((prev) => !prev)}
className="flex items-center gap-2 hover:text-foreground transition-colors"
aria-expanded={!isPinnedCollapsed}
>
{isPinnedCollapsed ? <ChevronRightIcon className="w-4 h-4" /> : <ChevronDownIcon className="w-4 h-4" />}
<span className="font-medium">
{t("common.pinned")} ({pinnedMemos.length})
</span>
<span className="text-xs opacity-70">{isPinnedCollapsed ? t("common.expand") : t("common.collapse")}</span>
</button>
<div className="h-px flex-1 bg-border/60" aria-hidden="true" />
</div>
);
const prefixElement = (
<>
{showMemoEditor ? (
<MemoEditor className="mb-2" cacheKey="home-memo-editor" placeholder={t("editor.any-thoughts")} />
) : undefined}
<MemoFilters />
{pinnedToggle}
</>
);
if (!enablePinnedSection) {
return <MasonryView memoList={sortedMemoList} renderer={props.renderer} prefixElement={prefixElement} listMode={layout === "LIST"} />;
}
listMode={layout === "LIST"}
/>
if (layout === "LIST") {
const listMemoList = isPinnedCollapsed ? unpinnedMemos : sortedMemoList;
const lastPinnedName = !isPinnedCollapsed && hasPinned ? pinnedMemos[pinnedMemos.length - 1]?.name : undefined;
const listRenderer = lastPinnedName
? (memo: Memo, context?: MemoRenderContext) => (
<>
{props.renderer(memo, context)}
{memo.name === lastPinnedName && (
<div className="w-full max-w-2xl mx-auto my-2 h-px bg-border/60" aria-hidden="true" />
)}
</>
)
: props.renderer;
return <MasonryView memoList={listMemoList} renderer={listRenderer} prefixElement={prefixElement} listMode />;
}
return (
<>
<MasonryView
memoList={isPinnedCollapsed ? [] : pinnedMemos}
renderer={props.renderer}
prefixElement={prefixElement}
listMode={false}
/>
{hasPinned && !isPinnedCollapsed && <div className="w-full max-w-2xl mx-auto my-2 h-px bg-border/60" aria-hidden="true" />}
<div className={hasPinned ? "mt-2" : ""}>
<MasonryView memoList={unpinnedMemos} renderer={props.renderer} listMode={false} />
</div>
</>
);
})()}
{/* Loading indicator for pagination */}
{isFetchingNextPage && <Skeleton showCreator={props.showCreator} count={2} />}

View File

@ -28,6 +28,7 @@ const Home = () => {
listSort={listSort}
orderBy={orderBy}
filter={memoFilter}
collapsiblePinned
enabled={isInitialized && !!user} // Wait for contexts to stabilize before fetching
/>
</div>