Browse Source

fix single/double pane modes

imwald
Silberengel 3 months ago
parent
commit
b27efafd4a
  1. 1380
      src/PageManager.tsx
  2. 22
      src/components/NoteDrawer/index.tsx
  3. 14
      src/components/NoteOptions/useMenuActions.tsx
  4. 37
      src/components/PaneModeToggle/index.tsx
  5. 3
      src/components/ScrollToTopButton/index.tsx
  6. 39
      src/components/Sidebar/PaneModeToggle.tsx
  7. 6
      src/components/Sidebar/index.tsx
  8. 8
      src/components/ui/command.tsx
  9. 2
      src/components/ui/sheet.tsx
  10. 1
      src/constants.ts
  11. 2
      src/i18n/locales/ar.ts
  12. 2
      src/i18n/locales/de.ts
  13. 2
      src/i18n/locales/en.ts
  14. 2
      src/i18n/locales/es.ts
  15. 2
      src/i18n/locales/fa.ts
  16. 2
      src/i18n/locales/fr.ts
  17. 2
      src/i18n/locales/hi.ts
  18. 2
      src/i18n/locales/it.ts
  19. 2
      src/i18n/locales/ja.ts
  20. 2
      src/i18n/locales/ko.ts
  21. 2
      src/i18n/locales/pl.ts
  22. 2
      src/i18n/locales/pt-BR.ts
  23. 2
      src/i18n/locales/pt-PT.ts
  24. 2
      src/i18n/locales/ru.ts
  25. 2
      src/i18n/locales/th.ts
  26. 2
      src/i18n/locales/zh.ts
  27. 7
      src/pages/primary/NoteListPage/index.tsx
  28. 41
      src/providers/DeepBrowsingProvider.tsx
  29. 8
      src/routes.tsx
  30. 13
      src/services/local-storage.service.ts

1380
src/PageManager.tsx

File diff suppressed because it is too large Load Diff

22
src/components/NoteDrawer/index.tsx

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
import { Sheet, SheetContent } from '@/components/ui/sheet'
import NotePage from '@/pages/secondary/NotePage'
interface NoteDrawerProps {
open: boolean
onOpenChange: (open: boolean) => void
noteId: string | null
}
export default function NoteDrawer({ open, onOpenChange, noteId }: NoteDrawerProps) {
if (!noteId) return null
return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent side="right" className="w-full sm:max-w-[1042px] overflow-y-auto p-0">
<div className="h-full">
<NotePage id={noteId} index={0} hideTitlebar={false} />
</div>
</SheetContent>
</Sheet>
)
}

14
src/components/NoteOptions/useMenuActions.tsx

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import { ExtendedKind } from '@/constants'
import { getNoteBech32Id, isProtectedEvent, getRootEventHexId } from '@/lib/event'
import { getLongFormArticleMetadataFromEvent } from '@/lib/event-metadata'
import { toNjump, toAlexandria } from '@/lib/link'
import { toAlexandria } from '@/lib/link'
import logger from '@/lib/logger'
import { pubkeyToNpub } from '@/lib/pubkey'
import { normalizeUrl, simplifyUrl } from '@/lib/url'
@ -18,6 +18,7 @@ import { useMemo, useState, useEffect } from 'react' @@ -18,6 +18,7 @@ import { useMemo, useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import RelayIcon from '../RelayIcon'
import { usePrimaryPage } from '@/PageManager'
export interface SubMenuAction {
label: React.ReactNode
@ -55,6 +56,7 @@ export function useMenuActions({ @@ -55,6 +56,7 @@ export function useMenuActions({
openHighlightEditor
}: UseMenuActionsProps) {
const { t } = useTranslation()
const { current: currentPrimaryPage } = usePrimaryPage()
const { pubkey, attemptDelete, publish } = useNostr()
const { relayUrls: currentBrowsingRelayUrls } = useCurrentRelays()
const { relaySets, favoriteRelays } = useFavoriteRelays()
@ -455,9 +457,15 @@ export function useMenuActions({ @@ -455,9 +457,15 @@ export function useMenuActions({
},
{
icon: Link,
label: t('Share with Njump'),
label: t('Share with Jumble'),
onClick: () => {
navigator.clipboard.writeText(toNjump(getNoteBech32Id(event)))
const noteId = getNoteBech32Id(event)
// Only include context for discussions page, use plain /notes/{id} for others
const path = currentPrimaryPage === 'discussions'
? `/discussions/notes/${noteId}`
: `/notes/${noteId}`
const jumbleUrl = `https://jumble.imwald.eu${path}`
navigator.clipboard.writeText(jumbleUrl)
closeDrawer()
}
},

37
src/components/PaneModeToggle/index.tsx

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
import { Button } from '@/components/ui/button'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import storage from '@/services/local-storage.service'
import { PanelLeft, PanelsLeftRight } from 'lucide-react'
import { useState } from 'react'
export default function PaneModeToggle() {
const { isSmallScreen } = useScreenSize()
const [panelMode, setPanelMode] = useState<'single' | 'double'>(() => storage.getPanelMode())
// Hide on mobile
if (isSmallScreen) return null
const toggleMode = () => {
const newMode = panelMode === 'single' ? 'double' : 'single'
setPanelMode(newMode)
storage.setPanelMode(newMode)
}
return (
<Button
variant="ghost"
className="flex shadow-none items-center transition-colors duration-500 bg-transparent w-12 h-12 xl:w-full xl:h-auto p-3 m-0 xl:py-2 xl:px-3 rounded-lg xl:justify-start gap-4 text-lg font-semibold [&_svg]:size-full xl:[&_svg]:size-4"
title={panelMode === 'single' ? 'Switch to double-pane mode' : 'Switch to single-pane mode'}
onClick={toggleMode}
>
{panelMode === 'single' ? (
<PanelLeft strokeWidth={3} />
) : (
<PanelsLeftRight strokeWidth={3} />
)}
<div className="max-xl:hidden">
{panelMode === 'single' ? 'Single-pane' : 'Double-pane'}
</div>
</Button>
)
}

3
src/components/ScrollToTopButton/index.tsx

@ -32,7 +32,8 @@ export default function ScrollToTopButton({ @@ -32,7 +32,8 @@ export default function ScrollToTopButton({
style={{
bottom: isSmallScreen
? 'calc(env(safe-area-inset-bottom) + 3.75rem)'
: 'calc(env(safe-area-inset-bottom) + 0.75rem)'
: 'calc(env(safe-area-inset-bottom) + 0.75rem)',
willChange: 'opacity' // Hint to browser for better scroll performance
}}
>
<Button

39
src/components/Sidebar/PaneModeToggle.tsx

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
import { Button } from '@/components/ui/button'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import storage from '@/services/local-storage.service'
import { PanelLeft, PanelsLeftRight } from 'lucide-react'
import { useState } from 'react'
export default function PaneModeToggle() {
const { isSmallScreen } = useScreenSize()
const [panelMode, setPanelMode] = useState<'single' | 'double'>(() => storage.getPanelMode())
// Hide on mobile
if (isSmallScreen) return null
const toggleMode = () => {
const newMode = panelMode === 'single' ? 'double' : 'single'
setPanelMode(newMode)
storage.setPanelMode(newMode)
// Dispatch event to notify PageManager of the change
window.dispatchEvent(new CustomEvent('panelModeChanged', { detail: { mode: newMode } }))
}
return (
<Button
variant="ghost"
className="flex shadow-none items-center transition-colors duration-500 bg-transparent w-12 h-12 xl:w-full xl:h-auto p-3 m-0 xl:py-2 xl:px-3 rounded-lg xl:justify-start gap-4 text-lg font-semibold [&_svg]:size-full xl:[&_svg]:size-4"
title={panelMode === 'single' ? 'Switch to double-pane mode' : 'Switch to single-pane mode'}
onClick={toggleMode}
>
{panelMode === 'single' ? (
<PanelLeft strokeWidth={3} />
) : (
<PanelsLeftRight strokeWidth={3} />
)}
<div className="max-xl:hidden">
{panelMode === 'single' ? 'Single-pane' : 'Double-pane'}
</div>
</Button>
)
}

6
src/components/Sidebar/index.tsx

@ -11,6 +11,7 @@ import ProfileButton from './ProfileButton' @@ -11,6 +11,7 @@ import ProfileButton from './ProfileButton'
import RssButton from './RssButton'
import SearchButton from './SearchButton'
import SettingsButton from './SettingsButton'
import PaneModeToggle from './PaneModeToggle'
import storage from '@/services/local-storage.service'
export default function PrimaryPageSidebar() {
@ -40,7 +41,10 @@ export default function PrimaryPageSidebar() { @@ -40,7 +41,10 @@ export default function PrimaryPageSidebar() {
<SettingsButton />
<PostButton />
</div>
<AccountButton />
<div className="space-y-2">
<AccountButton />
<PaneModeToggle />
</div>
</div>
)
}

8
src/components/ui/command.tsx

@ -35,16 +35,16 @@ const CommandDialog = ({ @@ -35,16 +35,16 @@ const CommandDialog = ({
}: DialogProps & { classNames?: { content?: string } }) => {
return (
<Dialog {...props}>
<DialogHeader className="sr-only">
<DialogTitle>Command Menu</DialogTitle>
<DialogDescription>Search and select a command</DialogDescription>
</DialogHeader>
<DialogContent
className={cn(
'overflow-hidden p-0 shadow-lg top-4 translate-y-0 data-[state=closed]:slide-out-to-top-4 data-[state=open]:slide-in-from-top-4',
classNames?.content
)}
>
<DialogHeader className="sr-only">
<DialogTitle>Command Menu</DialogTitle>
<DialogDescription>Search and select a command</DialogDescription>
</DialogHeader>
<Command
shouldFilter={false}
className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"

2
src/components/ui/sheet.tsx

@ -98,7 +98,7 @@ const SheetContent = React.forwardRef< @@ -98,7 +98,7 @@ const SheetContent = React.forwardRef<
<SheetOverlay />
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
{!hideClose && (
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<SheetPrimitive.Close className="absolute right-4 top-4 z-[60] rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>

1
src/constants.ts

@ -54,6 +54,7 @@ export const StorageKey = { @@ -54,6 +54,7 @@ export const StorageKey = {
RESPECT_QUIET_TAGS: 'respectQuietTags',
GLOBAL_QUIET_MODE: 'globalQuietMode',
SHOW_RSS_FEED: 'showRssFeed',
PANE_MODE: 'paneMode',
MEDIA_UPLOAD_SERVICE: 'mediaUploadService', // deprecated
HIDE_UNTRUSTED_EVENTS: 'hideUntrustedEvents', // deprecated
ACCOUNT_RELAY_LIST_EVENT_MAP: 'accountRelayListEventMap', // deprecated

2
src/i18n/locales/ar.ts

@ -63,7 +63,7 @@ export default { @@ -63,7 +63,7 @@ export default {
Add: 'إضافة',
'n relays': '{{n}} ريلايات',
Rename: 'إعادة تسمية',
'Share with Njump': 'مشاركة مع Njump',
'Share with Jumble': 'مشاركة مع Jumble',
'Share with Alexandria': 'مشاركة مع Alexandria',
Delete: 'حذف',
'Relay already exists': 'الريلاي موجود بالفعل',

2
src/i18n/locales/de.ts

@ -63,7 +63,7 @@ export default { @@ -63,7 +63,7 @@ export default {
Add: 'Hinzufügen',
'n relays': '{{n}} Relays',
Rename: 'Umbenennen',
'Share with Njump': 'Mit Njump teilen',
'Share with Jumble': 'Mit Jumble teilen',
'Share with Alexandria': 'Mit Alexandria teilen',
Delete: 'Löschen',
'Relay already exists': 'Relay existiert bereits',

2
src/i18n/locales/en.ts

@ -66,7 +66,7 @@ export default { @@ -66,7 +66,7 @@ export default {
'n relays': '{{n}} relays',
Rename: 'Rename',
'Copy share link': 'Copy share link',
'Share with Njump': 'Share with Njump',
'Share with Jumble': 'Share with Jumble',
'Share with Alexandria': 'Share with Alexandria',
Delete: 'Delete',
'Relay already exists': 'Relay already exists',

2
src/i18n/locales/es.ts

@ -63,7 +63,7 @@ export default { @@ -63,7 +63,7 @@ export default {
Add: 'Agregar',
'n relays': '{{n}} relés',
Rename: 'Renombrar',
'Share with Njump': 'Compartir con Njump',
'Share with Jumble': 'Compartir con Jumble',
'Share with Alexandria': 'Compartir con Alexandria',
Delete: 'Eliminar',
'Relay already exists': 'El relé ya existe',

2
src/i18n/locales/fa.ts

@ -62,7 +62,7 @@ export default { @@ -62,7 +62,7 @@ export default {
Add: 'افزودن',
'n relays': '{{n}} رله',
Rename: 'تغییر نام',
'Share with Njump': 'اشتراکگذاری با Njump',
'Share with Jumble': 'اشتراکگذاری با Jumble',
'Share with Alexandria': 'اشتراکگذاری با Alexandria',
Delete: 'حذف',
'Relay already exists': 'رله از قبل موجود است',

2
src/i18n/locales/fr.ts

@ -63,7 +63,7 @@ export default { @@ -63,7 +63,7 @@ export default {
Add: 'Ajouter',
'n relays': '{{n}} relais',
Rename: 'Renommer',
'Share with Njump': 'Partager avec Njump',
'Share with Jumble': 'Partager avec Jumble',
'Share with Alexandria': 'Partager avec Alexandria',
Delete: 'Supprimer',
'Relay already exists': 'Le relais existe déjà',

2
src/i18n/locales/hi.ts

@ -62,7 +62,7 @@ export default { @@ -62,7 +62,7 @@ export default {
Add: 'ज',
'n relays': '{{n}} रि',
Rename: 'नम बदल',
'Share with Njump': 'Njumpथ शयर कर',
'Share with Jumble': 'Jumbleथ शयर कर',
'Share with Alexandria': 'Alexandria कथ शयर कर',
Delete: 'हट',
'Relay already exists': 'रि पहलद ह',

2
src/i18n/locales/it.ts

@ -62,7 +62,7 @@ export default { @@ -62,7 +62,7 @@ export default {
Add: 'Aggiungi',
'n relays': '{{n}} relays',
Rename: 'Rinomina',
'Share with Njump': 'Condividi con Njump',
'Share with Jumble': 'Condividi con Jumble',
'Share with Alexandria': 'Condividi con Alexandria',
Delete: 'Cancella',
'Relay already exists': 'Relay già esistente',

2
src/i18n/locales/ja.ts

@ -63,7 +63,7 @@ export default { @@ -63,7 +63,7 @@ export default {
Add: '追加',
'n relays': '{{n}} 個のリレイ',
Rename: '名前変更',
'Share with Njump': 'Njumpで共有',
'Share with Jumble': 'Jumbleで共有',
'Share with Alexandria': 'Alexandriaで共有',
Delete: '削除',
'Relay already exists': 'リレイは既に存在します',

2
src/i18n/locales/ko.ts

@ -62,7 +62,7 @@ export default { @@ -62,7 +62,7 @@ export default {
Add: '추가',
'n relays': '{{n}}개의 릴레이',
Rename: '이름 변경',
'Share with Njump': 'Njump로 공유',
'Share with Jumble': 'Jumble로 공유',
'Share with Alexandria': 'Alexandria로 공유',
Delete: '삭제',
'Relay already exists': '릴레이가 이미 존재합니다',

2
src/i18n/locales/pl.ts

@ -62,7 +62,7 @@ export default { @@ -62,7 +62,7 @@ export default {
Add: 'Dodaj',
'n relays': '{{n}} szt.',
Rename: 'Zmień nazwę',
'Share with Njump': 'Udostępnij przez Njump',
'Share with Jumble': 'Udostępnij przez Jumble',
'Share with Alexandria': 'Udostępnij przez Alexandria',
Delete: 'Usuń',
'Relay already exists': 'Transmiter już istnieje',

2
src/i18n/locales/pt-BR.ts

@ -62,7 +62,7 @@ export default { @@ -62,7 +62,7 @@ export default {
Add: 'Adicionar',
'n relays': '{{n}} relays',
Rename: 'Renomear',
'Share with Njump': 'Compartilhar com Njump',
'Share with Jumble': 'Compartilhar com Jumble',
'Share with Alexandria': 'Compartilhar com Alexandria',
Delete: 'Excluir',
'Relay already exists': 'Relay já existe',

2
src/i18n/locales/pt-PT.ts

@ -63,7 +63,7 @@ export default { @@ -63,7 +63,7 @@ export default {
Add: 'Adicionar',
'n relays': '{{n}} relés',
Rename: 'Renomear',
'Share with Njump': 'Compartilhar com Njump',
'Share with Jumble': 'Compartilhar com Jumble',
'Share with Alexandria': 'Compartilhar com Alexandria',
Delete: 'Excluir',
'Relay already exists': 'Relé já existe',

2
src/i18n/locales/ru.ts

@ -63,7 +63,7 @@ export default { @@ -63,7 +63,7 @@ export default {
Add: 'Добавить',
'n relays': '{{n}} ретрансляторов',
Rename: 'Переименовать',
'Share with Njump': 'Поделиться через Njump',
'Share with Jumble': 'Поделиться через Jumble',
'Share with Alexandria': 'Поделиться через Alexandria',
Delete: 'Удалить',
'Relay already exists': 'Ретранслятор уже существует',

2
src/i18n/locales/th.ts

@ -62,7 +62,7 @@ export default { @@ -62,7 +62,7 @@ export default {
Add: 'เพม',
'n relays': '{{n}} รเลย',
Rename: 'เปลยนชอ',
'Share with Njump': 'แชราน Njump',
'Share with Jumble': 'แชราน Jumble',
'Share with Alexandria': 'แชราน Alexandria',
Delete: 'ลบ',
'Relay already exists': 'รเลยอยแลว',

2
src/i18n/locales/zh.ts

@ -62,7 +62,7 @@ export default { @@ -62,7 +62,7 @@ export default {
Add: '添加',
'n relays': '{{n}} 个服务器',
Rename: '重命名',
'Share with Njump': '通过Njump分享',
'Share with Jumble': '通过Jumble分享',
'Share with Alexandria': '通过Alexandria分享',
Delete: '删除',
'Relay already exists': '服务器已存在',

7
src/pages/primary/NoteListPage/index.tsx

@ -38,11 +38,8 @@ const NoteListPage = forwardRef((_, ref) => { @@ -38,11 +38,8 @@ const NoteListPage = forwardRef((_, ref) => {
const [showRelayDetails, setShowRelayDetails] = useState(false)
useImperativeHandle(ref, () => layoutRef.current)
useEffect(() => {
if (layoutRef.current) {
layoutRef.current.scrollToTop('instant')
}
}, [JSON.stringify(relayUrls), feedInfo])
// REMOVED: Scroll-to-top logic - feed should NEVER scroll to top when drawer opens/closes
// The feed stays mounted and maintains scroll position at all times
useEffect(() => {
if (relayUrls.length) {

41
src/providers/DeepBrowsingProvider.tsx

@ -33,30 +33,41 @@ export function DeepBrowsingProvider({ @@ -33,30 +33,41 @@ export function DeepBrowsingProvider({
useEffect(() => {
if (!active) return
let rafId: number | null = null
const handleScroll = () => {
const scrollTop = (!scrollAreaRef ? window.scrollY : scrollAreaRef.current?.scrollTop) || 0
const diff = scrollTop - lastScrollTopRef.current
lastScrollTopRef.current = scrollTop
setLastScrollTop(scrollTop)
if (scrollTop <= 800) {
setDeepBrowsing(false)
return
}
// Use requestAnimationFrame to throttle scroll updates and prevent scroll-linked positioning warnings
if (rafId !== null) return
rafId = requestAnimationFrame(() => {
const scrollTop = (!scrollAreaRef ? window.scrollY : scrollAreaRef.current?.scrollTop) || 0
const diff = scrollTop - lastScrollTopRef.current
lastScrollTopRef.current = scrollTop
setLastScrollTop(scrollTop)
if (scrollTop <= 800) {
setDeepBrowsing(false)
rafId = null
return
}
if (diff > 20) {
setDeepBrowsing(true)
} else if (diff < -20) {
setDeepBrowsing(false)
}
if (diff > 20) {
setDeepBrowsing(true)
} else if (diff < -20) {
setDeepBrowsing(false)
}
rafId = null
})
}
const target = scrollAreaRef ? scrollAreaRef.current : window
target?.addEventListener('scroll', handleScroll)
target?.addEventListener('scroll', handleScroll, { passive: true })
return () => {
target?.removeEventListener('scroll', handleScroll)
if (rafId !== null) {
cancelAnimationFrame(rafId)
}
}
}, [active])
}, [active, scrollAreaRef])
return (
<DeepBrowsingContext.Provider value={{ deepBrowsing, lastScrollTop }}>

8
src/routes.tsx

@ -23,12 +23,20 @@ import FollowPacksPage from './pages/secondary/FollowPacksPage' @@ -23,12 +23,20 @@ import FollowPacksPage from './pages/secondary/FollowPacksPage'
const ROUTES = [
{ path: '/notes', element: <NoteListPage /> },
{ path: '/notes/:id', element: <NotePage /> },
// Contextual note routes (e.g., /discussions/notes/:id, /search/notes/:id)
{ path: '/discussions/notes/:id', element: <NotePage /> },
{ path: '/search/notes/:id', element: <NotePage /> },
{ path: '/profile/notes/:id', element: <NotePage /> },
{ path: '/explore/notes/:id', element: <NotePage /> },
{ path: '/notifications/notes/:id', element: <NotePage /> },
{ path: '/users', element: <ProfileListPage /> },
{ path: '/users/:id', element: <ProfilePage /> },
{ path: '/users/:id/following', element: <FollowingListPage /> },
{ path: '/users/:id/relays', element: <OthersRelaySettingsPage /> },
{ path: '/relays/:url', element: <RelayPage /> },
{ path: '/relays/:url/reviews', element: <RelayReviewsPage /> },
// Contextual relay route (only for explore page where you discover relays)
{ path: '/explore/relays/:url', element: <RelayPage /> },
{ path: '/search', element: <SearchPage /> },
{ path: '/settings', element: <SettingsPage /> },
{ path: '/settings/relays', element: <RelaySettingsPage /> },

13
src/services/local-storage.service.ts

@ -60,6 +60,7 @@ class LocalStorageService { @@ -60,6 +60,7 @@ class LocalStorageService {
private respectQuietTags: boolean = true
private globalQuietMode: boolean = false
private showRssFeed: boolean = true
private panelMode: 'single' | 'double' = 'single'
constructor() {
if (!LocalStorageService.instance) {
@ -296,6 +297,9 @@ class LocalStorageService { @@ -296,6 +297,9 @@ class LocalStorageService {
const showRssFeedStr = window.localStorage.getItem(StorageKey.SHOW_RSS_FEED)
this.showRssFeed = showRssFeedStr === null ? true : showRssFeedStr === 'true' // Default to true
const panelModeStr = window.localStorage.getItem(StorageKey.PANE_MODE)
this.panelMode = panelModeStr === 'double' ? 'double' : 'single' // Default to 'single'
// Clean up deprecated data
window.localStorage.removeItem(StorageKey.ACCOUNT_PROFILE_EVENT_MAP)
window.localStorage.removeItem(StorageKey.ACCOUNT_FOLLOW_LIST_EVENT_MAP)
@ -675,6 +679,15 @@ class LocalStorageService { @@ -675,6 +679,15 @@ class LocalStorageService {
this.showRssFeed = show
window.localStorage.setItem(StorageKey.SHOW_RSS_FEED, show.toString())
}
getPanelMode(): 'single' | 'double' {
return this.panelMode
}
setPanelMode(mode: 'single' | 'double') {
this.panelMode = mode
window.localStorage.setItem(StorageKey.PANE_MODE, mode)
}
}
const instance = new LocalStorageService()

Loading…
Cancel
Save