diff options
| author | Fuwn <[email protected]> | 2026-02-08 09:03:09 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-08 09:03:09 -0800 |
| commit | 06dfa122f395c64852ed33472b6fbc166293d627 (patch) | |
| tree | 3e8f844c6551cdfa510d8ed263b09feb56432272 /apps/web/app | |
| parent | feat: add toolbar position setting (top or bottom) (diff) | |
| download | asa.news-06dfa122f395c64852ed33472b6fbc166293d627.tar.xz asa.news-06dfa122f395c64852ed33472b6fbc166293d627.zip | |
fix: mobile scroll preservation, bottom toolbar for detail panel
Diffstat (limited to 'apps/web/app')
| -rw-r--r-- | apps/web/app/reader/_components/entry-detail-panel.tsx | 122 | ||||
| -rw-r--r-- | apps/web/app/reader/_components/reader-shell.tsx | 28 |
2 files changed, 84 insertions, 66 deletions
diff --git a/apps/web/app/reader/_components/entry-detail-panel.tsx b/apps/web/app/reader/_components/entry-detail-panel.tsx index b772ea1..fa74d6f 100644 --- a/apps/web/app/reader/_components/entry-detail-panel.tsx +++ b/apps/web/app/reader/_components/entry-detail-panel.tsx @@ -27,6 +27,7 @@ import { import { HighlightSelectionToolbar } from "./highlight-selection-toolbar" import { HighlightPopover } from "./highlight-popover" import { formatDistanceToNow, format } from "date-fns" +import { useIsMobile } from "@/lib/hooks/use-is-mobile" import { notify } from "@/lib/notify" import type { Highlight } from "@/lib/types/highlight" @@ -68,6 +69,10 @@ export function EntryDetailPanel({ const showReadingTime = useUserInterfaceStore( (state) => state.showReadingTime ) + const toolbarPosition = useUserInterfaceStore( + (state) => state.toolbarPosition + ) + const isMobile = useIsMobile() const proseContainerReference = useRef<HTMLDivElement>(null) const [selectionToolbarState, setSelectionToolbarState] = useState<{ selectionRect: DOMRect @@ -436,72 +441,78 @@ export function EntryDetailPanel({ const isRead = currentEntry?.isRead ?? false const isSaved = currentEntry?.isSaved ?? false - return ( - <div data-detail-panel className="flex h-full flex-col"> - <div className="flex items-center gap-2 overflow-x-auto border-b border-border px-4 py-2"> - <button - type="button" - onClick={() => - toggleReadState.mutate({ - entryIdentifier, - isRead: !isRead, - }) - } + const actionBarAtBottom = isMobile && toolbarPosition === "bottom" + + const actionBar = ( + <div className={`flex items-center gap-2 overflow-x-auto border-border px-4 py-2 ${actionBarAtBottom ? "border-t" : "border-b"}`}> + <button + type="button" + onClick={() => + toggleReadState.mutate({ + entryIdentifier, + isRead: !isRead, + }) + } + className="shrink-0 whitespace-nowrap border border-border px-2 py-1 text-text-secondary transition-colors hover:bg-background-tertiary hover:text-text-primary" + > + {isRead ? "mark unread" : "mark read"} + </button> + <button + type="button" + onClick={() => + toggleSavedState.mutate({ + entryIdentifier, + isSaved: !isSaved, + }) + } + className="shrink-0 whitespace-nowrap border border-border px-2 py-1 text-text-secondary transition-colors hover:bg-background-tertiary hover:text-text-primary" + > + {isSaved ? "unsave" : "save"} + </button> + {entryDetail.url && ( + <a + href={entryDetail.url} + target="_blank" + rel="noopener noreferrer" className="shrink-0 whitespace-nowrap border border-border px-2 py-1 text-text-secondary transition-colors hover:bg-background-tertiary hover:text-text-primary" > - {isRead ? "mark unread" : "mark read"} - </button> + open original + </a> + )} + {shareData?.isShared ? ( <button type="button" - onClick={() => - toggleSavedState.mutate({ - entryIdentifier, - isSaved: !isSaved, - }) - } + onClick={() => unshareMutation.mutate(shareData.shareToken!)} className="shrink-0 whitespace-nowrap border border-border px-2 py-1 text-text-secondary transition-colors hover:bg-background-tertiary hover:text-text-primary" > - {isSaved ? "unsave" : "save"} + unshare </button> - {entryDetail.url && ( - <a - href={entryDetail.url} - target="_blank" - rel="noopener noreferrer" - className="shrink-0 whitespace-nowrap border border-border px-2 py-1 text-text-secondary transition-colors hover:bg-background-tertiary hover:text-text-primary" - > - open original - </a> - )} - {shareData?.isShared ? ( - <button - type="button" - onClick={() => unshareMutation.mutate(shareData.shareToken!)} - className="shrink-0 whitespace-nowrap border border-border px-2 py-1 text-text-secondary transition-colors hover:bg-background-tertiary hover:text-text-primary" - > - unshare - </button> - ) : ( - <button - type="button" - onClick={() => { - setShareNoteText("") - setIsShareNoteDialogOpen(true) - }} - className="shrink-0 whitespace-nowrap border border-border px-2 py-1 text-text-secondary transition-colors hover:bg-background-tertiary hover:text-text-primary" - > - share - </button> - )} - <div className="flex-1" /> + ) : ( <button type="button" - onClick={() => setSelectedEntryIdentifier(null)} - className="hidden px-2 py-1 text-text-dim transition-colors hover:text-text-secondary md:block" + onClick={() => { + setShareNoteText("") + setIsShareNoteDialogOpen(true) + }} + className="shrink-0 whitespace-nowrap border border-border px-2 py-1 text-text-secondary transition-colors hover:bg-background-tertiary hover:text-text-primary" > - close + share </button> - </div> + )} + <div className="flex-1" /> + <button + type="button" + onClick={() => setSelectedEntryIdentifier(null)} + className="hidden px-2 py-1 text-text-dim transition-colors hover:text-text-secondary md:block" + > + close + </button> + </div> + ) + + return ( + <div data-detail-panel className="flex h-full flex-col"> + {!actionBarAtBottom && actionBar} <article data-detail-article className="min-h-0 flex-1 overflow-y-scroll px-6 py-4"> <h2 className="mb-1 text-base text-text-primary"> {entryDetail.title} @@ -637,6 +648,7 @@ export function EntryDetailPanel({ </div> </div> )} + {actionBarAtBottom && actionBar} </div> ) } diff --git a/apps/web/app/reader/_components/reader-shell.tsx b/apps/web/app/reader/_components/reader-shell.tsx index eb63f63..8a5a044 100644 --- a/apps/web/app/reader/_components/reader-shell.tsx +++ b/apps/web/app/reader/_components/reader-shell.tsx @@ -287,18 +287,14 @@ export function ReaderShell({ fontSize === "small" ? "text-sm" : fontSize === "large" ? "text-lg" : "text-base" )}> {toolbarPosition === "top" && toolbar} + <div className="relative min-h-0 flex-1"> <ErrorBoundary> {isMobile ? ( - selectedEntryIdentifier ? ( - <div className="flex-1 overflow-hidden"> - <ErrorBoundary> - <EntryDetailPanel - entryIdentifier={selectedEntryIdentifier} - /> - </ErrorBoundary> - </div> - ) : ( - <div className="flex-1 overflow-hidden"> + <> + <div className={classNames( + "absolute inset-0 overflow-x-hidden overflow-y-hidden", + selectedEntryIdentifier ? "invisible" : "visible" + )}> <ErrorBoundary> <EntryList feedFilter={feedFilter} @@ -308,7 +304,16 @@ export function ReaderShell({ /> </ErrorBoundary> </div> - ) + {selectedEntryIdentifier && ( + <div className="absolute inset-0 overflow-hidden bg-background-primary"> + <ErrorBoundary> + <EntryDetailPanel + entryIdentifier={selectedEntryIdentifier} + /> + </ErrorBoundary> + </div> + )} + </> ) : ( <Group orientation="horizontal" @@ -352,6 +357,7 @@ export function ReaderShell({ </Group> )} </ErrorBoundary> + </div> {toolbarPosition === "bottom" && toolbar} </div> ) |