"use client" import { useState, useEffect, useRef } from "react" import { Group, Panel, Separator, useDefaultLayout, useGroupRef } from "react-resizable-panels" import { useUserInterfaceStore } from "@/lib/stores/user-interface-store" import { useMarkAllAsRead } from "@/lib/queries/use-mark-all-as-read" import { useSubscriptions } from "@/lib/queries/use-subscriptions" import { useUnreadCounts } from "@/lib/queries/use-unread-counts" import { useUpdateSubscriptionTitle } from "@/lib/queries/use-subscription-mutations" import { useRenameFolder } from "@/lib/queries/use-folder-mutations" import { useUpdateCustomFeed } from "@/lib/queries/use-custom-feed-mutations" import { useIsMobile } from "@/lib/hooks/use-is-mobile" import { classNames } from "@/lib/utilities" import { EntryList } from "./entry-list" import { EntryDetailPanel } from "./entry-detail-panel" import { ErrorBoundary } from "./error-boundary" import { useRealtimeEntries } from "@/lib/hooks/use-realtime-entries" import { useCustomFeeds } from "@/lib/queries/use-custom-feeds" interface ReaderShellProperties { feedFilter: "all" | "saved" folderIdentifier?: string | null feedIdentifier?: string | null customFeedIdentifier?: string | null } export function ReaderShell({ feedFilter, folderIdentifier, feedIdentifier, customFeedIdentifier, }: ReaderShellProperties) { const selectedEntryIdentifier = useUserInterfaceStore( (state) => state.selectedEntryIdentifier ) const setSelectedEntryIdentifier = useUserInterfaceStore( (state) => state.setSelectedEntryIdentifier ) const entryListViewMode = useUserInterfaceStore( (state) => state.entryListViewMode ) const setEntryListViewMode = useUserInterfaceStore( (state) => state.setEntryListViewMode ) const setSearchOpen = useUserInterfaceStore((state) => state.setSearchOpen) const markAllAsRead = useMarkAllAsRead() const { data: subscriptionsData } = useSubscriptions() const { data: unreadCounts } = useUnreadCounts() const { data: customFeedsData } = useCustomFeeds() const isMobile = useIsMobile() const focusedPanel = useUserInterfaceStore((state) => state.focusedPanel) const toggleShortcutsDialog = useUserInterfaceStore( (state) => state.toggleShortcutsDialog ) const navigableEntryIdentifiers = useUserInterfaceStore( (state) => state.navigableEntryIdentifiers ) const toolbarPosition = useUserInterfaceStore( (state) => state.toolbarPosition ) const detailLayout = useDefaultLayout({ id: "asa-detail-layout", panelIds: ["entry-list", "detail-panel"], storage: typeof window !== "undefined" ? localStorage : { getItem: () => null, setItem: () => {} }, }) const detailGroupRef = useGroupRef() const detailOnLayoutChangedRef = useRef(detailLayout.onLayoutChanged) useEffect(() => { detailOnLayoutChangedRef.current = detailLayout.onLayoutChanged }, [detailLayout.onLayoutChanged]) useEffect(() => { useUserInterfaceStore.getState().setResetDetailLayout(() => { const group = detailGroupRef.current if (!group) return const currentLayout = group.getLayout() const panelIdentifiers = Object.keys(currentLayout) if (panelIdentifiers.length === 0) return const equalSharePercentage = 100 / panelIdentifiers.length const equalLayout: Record = {} for (const panelIdentifier of panelIdentifiers) { equalLayout[panelIdentifier] = equalSharePercentage } const appliedLayout = group.setLayout(equalLayout) detailOnLayoutChangedRef.current?.(appliedLayout) }) return () => useUserInterfaceStore.getState().setResetDetailLayout(null) }, [detailGroupRef]) useEffect(() => { useUserInterfaceStore.getState().setCurrentFeedIdentifier(feedIdentifier ?? null) useUserInterfaceStore.getState().setCurrentFolderIdentifier(folderIdentifier ?? null) }, [feedIdentifier, folderIdentifier]) useRealtimeEntries() const updateSubscriptionTitle = useUpdateSubscriptionTitle() const renameFolder = useRenameFolder() const updateCustomFeed = useUpdateCustomFeed() const [isRenamingTitle, setIsRenamingTitle] = useState(false) const [renameValue, setRenameValue] = useState("") let pageTitle = feedFilter === "saved" ? "saved" : "all entries" let isRenameable = false const matchingSubscription = feedFilter === "all" && feedIdentifier && subscriptionsData ? subscriptionsData.subscriptions.find( (subscription) => subscription.feedIdentifier === feedIdentifier ) : undefined const matchingFolder = feedFilter === "all" && folderIdentifier && subscriptionsData ? subscriptionsData.folders.find( (folder) => folder.folderIdentifier === folderIdentifier ) : undefined const matchingCustomFeed = feedFilter === "all" && customFeedIdentifier && customFeedsData ? customFeedsData.find( (customFeed) => customFeed.identifier === customFeedIdentifier ) : undefined if (matchingCustomFeed) { pageTitle = matchingCustomFeed.name isRenameable = true } if (matchingSubscription) { pageTitle = matchingSubscription.customTitle || matchingSubscription.feedTitle || "feed" isRenameable = true } if (matchingFolder) { pageTitle = matchingFolder.name isRenameable = true } function handleStartRename() { setRenameValue(pageTitle) setIsRenamingTitle(true) } function handleSaveRename() { const trimmedValue = renameValue.trim() if (!trimmedValue || trimmedValue === pageTitle) { setIsRenamingTitle(false) return } if (matchingSubscription) { updateSubscriptionTitle.mutate({ subscriptionIdentifier: matchingSubscription.subscriptionIdentifier, customTitle: trimmedValue, }) } else if (matchingFolder) { renameFolder.mutate({ folderIdentifier: matchingFolder.folderIdentifier, name: trimmedValue, }) } else if (matchingCustomFeed) { updateCustomFeed.mutate({ customFeedIdentifier: matchingCustomFeed.identifier, name: trimmedValue, query: matchingCustomFeed.query, matchMode: matchingCustomFeed.matchMode, sourceFolderIdentifier: matchingCustomFeed.sourceFolderIdentifier, }) } setIsRenamingTitle(false) } const totalUnreadCount = Object.values(unreadCounts ?? {}).reduce( (sum, count) => sum + count, 0 ) const allAreRead = totalUnreadCount === 0 const toolbar = (
{isMobile && selectedEntryIdentifier ? (() => { const currentIndex = navigableEntryIdentifiers.indexOf(selectedEntryIdentifier) const previousEntryIdentifier = currentIndex > 0 ? navigableEntryIdentifiers[currentIndex - 1] : null const nextEntryIdentifier = currentIndex < navigableEntryIdentifiers.length - 1 ? navigableEntryIdentifiers[currentIndex + 1] : null return (
) })() : isRenamingTitle ? (
setRenameValue(event.target.value)} onKeyDown={(event) => { if (event.key === "Enter") handleSaveRename() if (event.key === "Escape") setIsRenamingTitle(false) }} className="min-w-0 border border-border bg-background-primary px-2 py-1 text-text-primary outline-none focus:border-text-dim" autoFocus />
) : (

{pageTitle}

{isRenameable && ( )}
)}
{!(isMobile && selectedEntryIdentifier) && ( <> {feedFilter === "all" && ( )} )}
) return (
{toolbarPosition === "top" && toolbar}
{isMobile ? ( <>
{selectedEntryIdentifier && (
)} ) : (
{selectedEntryIdentifier && ( <>
)}
)}
{toolbarPosition === "bottom" && toolbar}
) }