From dbee10361baa1a50c8275d430a07f95268a58957 Mon Sep 17 00:00:00 2001 From: codytseng Date: Sun, 19 Oct 2025 14:50:49 +0800 Subject: [PATCH] feat: add single column layout toggle option --- src/App.tsx | 72 ++++++------ src/PageManager.tsx | 77 ++++++++++++- src/components/Sidebar/index.tsx | 48 ++++---- src/constants.ts | 1 + src/i18n/locales/ar.ts | 5 +- src/i18n/locales/de.ts | 5 +- src/i18n/locales/en.ts | 5 +- src/i18n/locales/es.ts | 5 +- src/i18n/locales/fa.ts | 5 +- src/i18n/locales/fr.ts | 5 +- src/i18n/locales/hi.ts | 5 +- src/i18n/locales/it.ts | 5 +- src/i18n/locales/ja.ts | 5 +- src/i18n/locales/ko.ts | 5 +- src/i18n/locales/pl.ts | 5 +- src/i18n/locales/pt-BR.ts | 5 +- src/i18n/locales/pt-PT.ts | 5 +- src/i18n/locales/ru.ts | 5 +- src/i18n/locales/th.ts | 5 +- src/i18n/locales/zh.ts | 5 +- .../AppearanceSettingsPage/index.tsx | 104 ++++++++++++------ src/providers/UserPreferencesProvider.tsx | 15 ++- src/services/local-storage.service.ts | 13 +++ 23 files changed, 298 insertions(+), 112 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0150625..503b51e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -24,43 +24,43 @@ import { PageManager } from './PageManager' export default function App(): JSX.Element { return ( - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + ) } diff --git a/src/PageManager.tsx b/src/PageManager.tsx index 541df6f..5bd5bfa 100644 --- a/src/PageManager.tsx +++ b/src/PageManager.tsx @@ -29,6 +29,7 @@ import SearchPage from './pages/primary/SearchPage' import { NotificationProvider } from './providers/NotificationProvider' import { useScreenSize } from './providers/ScreenSizeProvider' import { useTheme } from './providers/ThemeProvider' +import { useUserPreferences } from './providers/UserPreferencesProvider' import { routes } from './routes' import modalManager from './services/modal-manager.service' @@ -106,6 +107,7 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { const [secondaryStack, setSecondaryStack] = useState([]) const { isSmallScreen } = useScreenSize() const { themeSetting } = useTheme() + const { enableSingleColumnLayout } = useUserPreferences() const ignorePopStateRef = useRef(false) useEffect(() => { @@ -243,7 +245,7 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { if (needScrollToTop) { PRIMARY_PAGE_REF_MAP[page].current?.scrollToTop('smooth') } - if (isSmallScreen) { + if (isSmallScreen || enableSingleColumnLayout) { clearSecondaryPages() } } @@ -333,6 +335,79 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { ) } + if (enableSingleColumnLayout) { + return ( + + + + +
+
+
+
+ +
+
+ {!!secondaryStack.length && + secondaryStack.map((item, index) => ( +
+ {item.component} +
+ ))} + {primaryPages.map(({ name, element, props }) => ( +
+ {props ? cloneElement(element as React.ReactElement, props) : element} +
+ ))} +
+
+
+
+
+ + + + + + + + ) + } + return (
- {sidebarCollapse ? ( + {sidebarCollapse && !enableSingleColumnLayout ? (
@@ -38,27 +38,29 @@ export default function PrimaryPageSidebar() {
)} - - - - - - - + + + + + + +
- - + + {!enableSingleColumnLayout && ( + + )} ) } diff --git a/src/constants.ts b/src/constants.ts index 4888cff..4fa9ab0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -50,6 +50,7 @@ export const StorageKey = { SHOWN_CREATE_WALLET_GUIDE_TOAST_PUBKEYS: 'shownCreateWalletGuideToastPubkeys', SIDEBAR_COLLAPSE: 'sidebarCollapse', PRIMARY_COLOR: 'primaryColor', + ENABLE_SINGLE_COLUMN_LAYOUT: 'enableSingleColumnLayout', MEDIA_UPLOAD_SERVICE: 'mediaUploadService', // deprecated HIDE_UNTRUSTED_EVENTS: 'hideUntrustedEvents', // deprecated ACCOUNT_RELAY_LIST_EVENT_MAP: 'accountRelayListEventMap', // deprecated diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts index 16ba402..66861fe 100644 --- a/src/i18n/locales/ar.ts +++ b/src/i18n/locales/ar.ts @@ -476,6 +476,9 @@ export default { Fuchsia: 'فوشيا', Pink: 'وردي', Rose: 'وردة', - 'Primary color': 'اللون الأساسي' + 'Primary color': 'اللون الأساسي', + Layout: 'التخطيط', + 'Double column': 'عمودين', + 'Single column': 'عمود واحد' } } diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index 56ae1c2..45e32f5 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -490,6 +490,9 @@ export default { Fuchsia: 'Fuchsia', Pink: 'Rosa', Rose: 'Rose', - 'Primary color': 'Primärfarbe' + 'Primary color': 'Primärfarbe', + Layout: 'Layout', + 'Double column': 'Zweispaltig', + 'Single column': 'Einspaltig' } } diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 6524412..c339470 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -475,6 +475,9 @@ export default { Fuchsia: 'Fuchsia', Pink: 'Pink', Rose: 'Rose', - 'Primary color': 'Primary color' + 'Primary color': 'Primary color', + Layout: 'Layout', + 'Double column': 'Double column', + 'Single column': 'Single column' } } diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index 9bfd986..1109f6a 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -484,6 +484,9 @@ export default { Fuchsia: 'Fucsia', Pink: 'Rosa', Rose: 'Rosa', - 'Primary color': 'Color primario' + 'Primary color': 'Color primario', + Layout: 'Diseño', + 'Double column': 'Doble columna', + 'Single column': 'Columna única' } } diff --git a/src/i18n/locales/fa.ts b/src/i18n/locales/fa.ts index d0869dd..d760950 100644 --- a/src/i18n/locales/fa.ts +++ b/src/i18n/locales/fa.ts @@ -479,6 +479,9 @@ export default { Fuchsia: 'فوشیا', Pink: 'صورتی', Rose: 'گلی', - 'Primary color': 'رنگ اصلی' + 'Primary color': 'رنگ اصلی', + Layout: 'چیدمان', + 'Double column': 'دو ستونی', + 'Single column': 'تک ستونی' } } diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index b123a64..3b61e6b 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -489,6 +489,9 @@ export default { Fuchsia: 'Fuchsia', Pink: 'Rose', Rose: 'Rose', - 'Primary color': 'Couleur principale' + 'Primary color': 'Couleur principale', + Layout: 'Disposition', + 'Double column': 'Double colonne', + 'Single column': 'Colonne unique' } } diff --git a/src/i18n/locales/hi.ts b/src/i18n/locales/hi.ts index 87da19a..81c1d2f 100644 --- a/src/i18n/locales/hi.ts +++ b/src/i18n/locales/hi.ts @@ -481,6 +481,9 @@ export default { Fuchsia: 'फुशिया', Pink: 'गुलाबी', Rose: 'गुलाब', - 'Primary color': 'प्राथमिक रंग' + 'Primary color': 'प्राथमिक रंग', + Layout: 'लेआउट', + 'Double column': 'दोहरा स्तंभ', + 'Single column': 'एकल स्तंभ' } } diff --git a/src/i18n/locales/it.ts b/src/i18n/locales/it.ts index 7d89a8e..ad0d006 100644 --- a/src/i18n/locales/it.ts +++ b/src/i18n/locales/it.ts @@ -484,6 +484,9 @@ export default { Fuchsia: 'Fucsia', Pink: 'Rosa', Rose: 'Rosa', - 'Primary color': 'Colore primario' + 'Primary color': 'Colore primario', + Layout: 'Layout', + 'Double column': 'Doppia colonna', + 'Single column': 'Colonna singola' } } diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts index 683b768..405194e 100644 --- a/src/i18n/locales/ja.ts +++ b/src/i18n/locales/ja.ts @@ -480,6 +480,9 @@ export default { Fuchsia: 'フクシア', Pink: 'ピンク', Rose: 'ローズ', - 'Primary color': '主要な色' + 'Primary color': '主要な色', + Layout: 'レイアウト', + 'Double column': '2列', + 'Single column': '1列' } } diff --git a/src/i18n/locales/ko.ts b/src/i18n/locales/ko.ts index 864875d..0e1c5b9 100644 --- a/src/i18n/locales/ko.ts +++ b/src/i18n/locales/ko.ts @@ -480,6 +480,9 @@ export default { Fuchsia: '자홍', Pink: '분홍', Rose: '장미', - 'Primary color': '기본 색상' + 'Primary color': '기본 색상', + Layout: '레이아웃', + 'Double column': '두 열', + 'Single column': '한 열' } } diff --git a/src/i18n/locales/pl.ts b/src/i18n/locales/pl.ts index 4b3f16e..8b29e41 100644 --- a/src/i18n/locales/pl.ts +++ b/src/i18n/locales/pl.ts @@ -484,6 +484,9 @@ export default { Fuchsia: 'Fuksja', Pink: 'Różowy', Rose: 'Różany', - 'Primary color': 'Kolor podstawowy' + 'Primary color': 'Kolor podstawowy', + Layout: 'Układ', + 'Double column': 'Dwie kolumny', + 'Single column': 'Jedna kolumna' } } diff --git a/src/i18n/locales/pt-BR.ts b/src/i18n/locales/pt-BR.ts index fbe13e2..8cd458c 100644 --- a/src/i18n/locales/pt-BR.ts +++ b/src/i18n/locales/pt-BR.ts @@ -481,6 +481,9 @@ export default { Fuchsia: 'Fúcsia', Pink: 'Rosa', Rose: 'Rosa', - 'Primary color': 'Cor primária' + 'Primary color': 'Cor primária', + Layout: 'Layout', + 'Double column': 'Coluna dupla', + 'Single column': 'Coluna única' } } diff --git a/src/i18n/locales/pt-PT.ts b/src/i18n/locales/pt-PT.ts index 223de30..9332192 100644 --- a/src/i18n/locales/pt-PT.ts +++ b/src/i18n/locales/pt-PT.ts @@ -484,6 +484,9 @@ export default { Fuchsia: 'Fúcsia', Pink: 'Rosa', Rose: 'Rosa', - 'Primary color': 'Cor primária' + 'Primary color': 'Cor primária', + Layout: 'Layout', + 'Double column': 'Coluna dupla', + 'Single column': 'Coluna única' } } diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index 33b0d11..ab6aa8b 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -486,6 +486,9 @@ export default { Fuchsia: 'Фуксия', Pink: 'Розовый', Rose: 'Роза', - 'Primary color': 'Основной цвет' + 'Primary color': 'Основной цвет', + Layout: 'Макет', + 'Double column': 'Две колонки', + 'Single column': 'Одна колонка' } } diff --git a/src/i18n/locales/th.ts b/src/i18n/locales/th.ts index 9f43900..2725684 100644 --- a/src/i18n/locales/th.ts +++ b/src/i18n/locales/th.ts @@ -474,6 +474,9 @@ export default { Fuchsia: 'บานเย็น', Pink: 'ชมพู', Rose: 'กุหลาบ', - 'Primary color': 'สีหลัก' + 'Primary color': 'สีหลัก', + Layout: 'เค้าโครง', + 'Double column': 'สองคอลัมน์', + 'Single column': 'คอลัมน์เดียว' } } diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts index bdae525..6471f7f 100644 --- a/src/i18n/locales/zh.ts +++ b/src/i18n/locales/zh.ts @@ -472,6 +472,9 @@ export default { Fuchsia: '紫红色', Pink: '粉色', Rose: '玫瑰色', - 'Primary color': '主色调' + 'Primary color': '主色调', + Layout: '布局', + 'Double column': '双栏', + 'Single column': '单栏' } } diff --git a/src/pages/secondary/AppearanceSettingsPage/index.tsx b/src/pages/secondary/AppearanceSettingsPage/index.tsx index a2cfc5e..d2fadd2 100644 --- a/src/pages/secondary/AppearanceSettingsPage/index.tsx +++ b/src/pages/secondary/AppearanceSettingsPage/index.tsx @@ -3,43 +3,56 @@ import { PRIMARY_COLORS, TPrimaryColor } from '@/constants' import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' import { cn } from '@/lib/utils' import { useTheme } from '@/providers/ThemeProvider' -import { Monitor, Moon, Sun } from 'lucide-react' +import { useUserPreferences } from '@/providers/UserPreferencesProvider' +import { Monitor, Moon, Sun, Columns2, PanelLeft } from 'lucide-react' import { forwardRef } from 'react' import { useTranslation } from 'react-i18next' const THEMES = [ - { key: 'system', label: 'System', icon: }, - { key: 'light', label: 'Light', icon: }, - { key: 'dark', label: 'Dark', icon: }, - { key: 'pure-black', label: 'Pure Black', icon: } + { key: 'system', label: 'System', icon: }, + { key: 'light', label: 'Light', icon: }, + { key: 'dark', label: 'Dark', icon: }, + { key: 'pure-black', label: 'Pure Black', icon: } +] as const + +const LAYOUTS = [ + { key: false, label: 'Double column', icon: }, + { key: true, label: 'Single column', icon: } ] as const const AppearanceSettingsPage = forwardRef(({ index }: { index?: number }, ref) => { const { t } = useTranslation() const { themeSetting, setThemeSetting, primaryColor, setPrimaryColor } = useTheme() + const { enableSingleColumnLayout, updateEnableSingleColumnLayout } = useUserPreferences() return (
+
+ +
+ {LAYOUTS.map(({ key, label, icon }) => ( + updateEnableSingleColumnLayout(key)} + /> + ))} +
+
{THEMES.map(({ key, label, icon }) => ( - + isSelected={themeSetting === key} + icon={icon} + label={t(label)} + onClick={() => setThemeSetting(key)} + /> ))}
@@ -47,24 +60,20 @@ const AppearanceSettingsPage = forwardRef(({ index }: { index?: number }, ref) =
{Object.entries(PRIMARY_COLORS).map(([key, config]) => ( - + /> ))}
@@ -74,3 +83,28 @@ const AppearanceSettingsPage = forwardRef(({ index }: { index?: number }, ref) = }) AppearanceSettingsPage.displayName = 'AppearanceSettingsPage' export default AppearanceSettingsPage + +const OptionButton = ({ + isSelected, + onClick, + icon, + label +}: { + isSelected: boolean + onClick: () => void + icon: React.ReactNode + label: string +}) => { + return ( + + ) +} diff --git a/src/providers/UserPreferencesProvider.tsx b/src/providers/UserPreferencesProvider.tsx index cc2292e..1928029 100644 --- a/src/providers/UserPreferencesProvider.tsx +++ b/src/providers/UserPreferencesProvider.tsx @@ -11,6 +11,9 @@ type TUserPreferencesContext = { sidebarCollapse: boolean updateSidebarCollapse: (collapse: boolean) => void + + enableSingleColumnLayout: boolean + updateEnableSingleColumnLayout: (enable: boolean) => void } const UserPreferencesContext = createContext(undefined) @@ -29,6 +32,9 @@ export function UserPreferencesProvider({ children }: { children: React.ReactNod ) const [muteMedia, setMuteMedia] = useState(true) const [sidebarCollapse, setSidebarCollapse] = useState(storage.getSidebarCollapse()) + const [enableSingleColumnLayout, setEnableSingleColumnLayout] = useState( + storage.getEnableSingleColumnLayout() + ) const updateNotificationListStyle = (style: TNotificationStyle) => { setNotificationListStyle(style) @@ -40,6 +46,11 @@ export function UserPreferencesProvider({ children }: { children: React.ReactNod storage.setSidebarCollapse(collapse) } + const updateEnableSingleColumnLayout = (enable: boolean) => { + setEnableSingleColumnLayout(enable) + storage.setEnableSingleColumnLayout(enable) + } + return ( {children} diff --git a/src/services/local-storage.service.ts b/src/services/local-storage.service.ts index 58195fc..935e904 100644 --- a/src/services/local-storage.service.ts +++ b/src/services/local-storage.service.ts @@ -51,6 +51,7 @@ class LocalStorageService { private shownCreateWalletGuideToastPubkeys: Set = new Set() private sidebarCollapse: boolean = false private primaryColor: TPrimaryColor = 'DEFAULT' + private enableSingleColumnLayout: boolean = false constructor() { if (!LocalStorageService.instance) { @@ -201,6 +202,9 @@ class LocalStorageService { this.primaryColor = (window.localStorage.getItem(StorageKey.PRIMARY_COLOR) as TPrimaryColor) ?? 'DEFAULT' + this.enableSingleColumnLayout = + window.localStorage.getItem(StorageKey.ENABLE_SINGLE_COLUMN_LAYOUT) === 'true' + // Clean up deprecated data window.localStorage.removeItem(StorageKey.ACCOUNT_PROFILE_EVENT_MAP) window.localStorage.removeItem(StorageKey.ACCOUNT_FOLLOW_LIST_EVENT_MAP) @@ -502,6 +506,15 @@ class LocalStorageService { this.primaryColor = color window.localStorage.setItem(StorageKey.PRIMARY_COLOR, color) } + + getEnableSingleColumnLayout() { + return this.enableSingleColumnLayout + } + + setEnableSingleColumnLayout(enable: boolean) { + this.enableSingleColumnLayout = enable + window.localStorage.setItem(StorageKey.ENABLE_SINGLE_COLUMN_LAYOUT, enable.toString()) + } } const instance = new LocalStorageService()