From 5a50b50e674627a4c5faaed79ee3c2bd77906413 Mon Sep 17 00:00:00 2001 From: Kobe Attias Date: Wed, 7 Oct 2026 10:56:36 -0400 Subject: [PATCH 1/2] Recently visited shows each document's picture and when you were there The homepage rail's Recently visited rows now carry a 48px picture on the right and the time of the visit above a two-line title, with no document type. A row's picture is the cover image (or a paper's first PDF page), else a drawn title page for papers, else the first author's photo, else the file icon from My Funding. The list stops at six rows so it fits under the funding power card on a 900px-tall window; the single Clear stays. Visits now record the picture, an author photo and the visit time. Entries saved before this show the file icon and no time until revisited. A paper and a post with the same id no longer evict each other. Everything lives in components/RecentlyVisited; FundSidebar, which only ever showed this list, is now RecentlyVisited. Co-Authored-By: Claude Opus 5.5 --- .../components/RightSidebarContainer.tsx | 4 +- .../RecentlyVisited.tsx} | 3 +- .../RecentlyVisitedCard.tsx | 76 +++++++++++-------- .../RecentlyVisitedThumbnail.tsx | 48 ++++++++++++ components/work/SearchHistoryTracker.tsx | 13 +++- types/search.ts | 4 + 6 files changed, 114 insertions(+), 34 deletions(-) rename components/{Funding/FundSidebar.tsx => RecentlyVisited/RecentlyVisited.tsx} (76%) rename components/{Funding => RecentlyVisited}/RecentlyVisitedCard.tsx (64%) create mode 100644 components/RecentlyVisited/RecentlyVisitedThumbnail.tsx diff --git a/app/layouts/components/RightSidebarContainer.tsx b/app/layouts/components/RightSidebarContainer.tsx index 21212e604..31f1a82d6 100644 --- a/app/layouts/components/RightSidebarContainer.tsx +++ b/app/layouts/components/RightSidebarContainer.tsx @@ -4,12 +4,12 @@ import { ReactNode, Suspense } from 'react'; import { cn } from '@/lib/utils'; import { useWorkTab } from '@/components/work/WorkHeader/WorkTabContext'; import { SwipeableDrawer } from '@/components/ui/SwipeableDrawer'; -import { FundSidebar } from '@/components/Funding/FundSidebar'; +import { RecentlyVisited } from '@/components/RecentlyVisited/RecentlyVisited'; import { FundingPowerCard } from '@/components/Funding/FundingPowerCard'; function RightSidebarContent({ rightSidebar }: { rightSidebar: boolean | ReactNode }) { if (typeof rightSidebar === 'boolean') { - return ; + return ; } return <>{rightSidebar}; diff --git a/components/Funding/FundSidebar.tsx b/components/RecentlyVisited/RecentlyVisited.tsx similarity index 76% rename from components/Funding/FundSidebar.tsx rename to components/RecentlyVisited/RecentlyVisited.tsx index 29f672dcc..0d03f6783 100644 --- a/components/Funding/FundSidebar.tsx +++ b/components/RecentlyVisited/RecentlyVisited.tsx @@ -6,7 +6,8 @@ import { useRecentlyVisited, } from './RecentlyVisitedCard'; -export function FundSidebar() { +/** The default right sidebar's Recently visited section, once local history is readable. */ +export function RecentlyVisited() { const { pages, clear, isHydrated } = useRecentlyVisited(); if (!isHydrated) { diff --git a/components/Funding/RecentlyVisitedCard.tsx b/components/RecentlyVisited/RecentlyVisitedCard.tsx similarity index 64% rename from components/Funding/RecentlyVisitedCard.tsx rename to components/RecentlyVisited/RecentlyVisitedCard.tsx index 79445744a..c267cbc30 100644 --- a/components/Funding/RecentlyVisitedCard.tsx +++ b/components/RecentlyVisited/RecentlyVisitedCard.tsx @@ -2,21 +2,19 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import Link from 'next/link'; -import { getSearchHistory, clearSearchHistory, MAX_HISTORY_ITEMS } from '@/utils/searchHistory'; +import { getSearchHistory, clearSearchHistory } from '@/utils/searchHistory'; import { buildWorkUrl } from '@/utils/url'; +import { formatTimeAgo } from '@/utils/date'; import { SearchSuggestion } from '@/types/search'; import { ContentType } from '@/types/work'; import { cn } from '@/utils/styles'; -import { FieldLabel } from '@/components/ui/FieldLabel'; +import { RecentlyVisitedThumbnail } from './RecentlyVisitedThumbnail'; -const WORK_TYPE_LABELS: Record = { - paper: 'Paper', - post: 'Post', - preregistration: 'Proposal', - question: 'Question', - discussion: 'Discussion', - funding_request: 'Request for Proposal', -}; +/** + * Storage keeps more visits than the rail lists (the search modal reads them + * too); six rows fit under the funding power card on a 900px-tall window. + */ +const MAX_VISIBLE = 6; const SKELETON_ROWS = [ { title: ['w-[92%]', 'w-[68%]'], meta: 'w-16' }, @@ -24,12 +22,17 @@ const SKELETON_ROWS = [ { title: ['w-[90%]', 'w-[72%]'], meta: 'w-20' }, { title: ['w-[78%]', 'w-[48%]'], meta: 'w-14' }, { title: ['w-[88%]', 'w-[60%]'], meta: 'w-16' }, + { title: ['w-[82%]', 'w-[64%]'], meta: 'w-12' }, ] as const; interface RecentPage { href: string; title: string; - typeLabel?: string; + isPaper: boolean; + imageUrl?: string; + authorImage?: string; + /** ISO time of the visit; entries saved before it was recorded have none. */ + lastVisited?: string; } export interface RecentlyVisited { @@ -57,7 +60,10 @@ function toRecentPage(visit: VisitRecord): RecentPage | null { return { href, title, - typeLabel: WORK_TYPE_LABELS[contentType], + isPaper: contentType === 'paper', + imageUrl: visit.imageUrl, + authorImage: visit.authorImage, + lastVisited: visit.lastVisited, }; } @@ -65,7 +71,7 @@ function toRecentPage(visit: VisitRecord): RecentPage | null { return { href: visit.url || `/post/${visit.id}`, title, - typeLabel: WORK_TYPE_LABELS.post, + isPaper: false, }; } @@ -82,7 +88,7 @@ function visitsToPages(visits: VisitRecord[]): RecentPage[] { if (!page || seen.has(page.href)) continue; seen.add(page.href); collected.push(page); - if (collected.length === MAX_HISTORY_ITEMS) break; + if (collected.length === MAX_VISIBLE) break; } return collected; @@ -128,8 +134,8 @@ interface RecentlyVisitedCardProps extends Omit { } /** - * Lightweight browsing history for the Activity sidebar: a plain text list of - * documents from local visit history, no thumbnails or metrics. + * Browsing history for the Activity sidebar: documents from local visit + * history, each with its picture and when it was last opened. */ export function RecentlyVisitedCard({ pages, clear, className }: RecentlyVisitedCardProps) { if (pages.length === 0) return null; @@ -137,29 +143,38 @@ export function RecentlyVisitedCard({ pages, clear, className }: RecentlyVisited return (