From 7b882c72cbaa8aeedcbaba9b9c9a2a5c6512493f Mon Sep 17 00:00:00 2001 From: codytseng Date: Thu, 8 May 2025 23:24:57 +0800 Subject: [PATCH] feat: add autoplay switch --- src/App.tsx | 47 +++++----- src/components/VideoPlayer/index.tsx | 6 +- src/constants.ts | 1 + src/i18n/locales/ar.ts | 5 +- src/i18n/locales/de.ts | 6 +- src/i18n/locales/en.ts | 5 +- src/i18n/locales/es.ts | 6 +- src/i18n/locales/fr.ts | 6 +- src/i18n/locales/it.ts | 6 +- src/i18n/locales/ja.ts | 5 +- src/i18n/locales/pl.ts | 5 +- src/i18n/locales/pt-BR.ts | 6 +- src/i18n/locales/pt-PT.ts | 6 +- src/i18n/locales/ru.ts | 5 +- src/i18n/locales/zh.ts | 5 +- src/lib/link.ts | 1 + .../secondary/GeneralSettingsPage/index.tsx | 89 +++++++++++++++++++ src/pages/secondary/SettingsPage/index.tsx | 53 ++--------- src/providers/AutoplayProvider.tsx | 32 +++++++ src/routes.tsx | 2 + src/services/local-storage.service.ts | 12 +++ 21 files changed, 228 insertions(+), 81 deletions(-) create mode 100644 src/pages/secondary/GeneralSettingsPage/index.tsx create mode 100644 src/providers/AutoplayProvider.tsx diff --git a/src/App.tsx b/src/App.tsx index bdbe009b..793e06c5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import './index.css' import { Toaster } from '@/components/ui/toaster' import { ThemeProvider } from '@/providers/ThemeProvider' import { PageManager } from './PageManager' +import { AutoplayProvider } from './providers/AutoplayProvider' import { BookmarksProvider } from './providers/BookmarksProvider' import { FavoriteRelaysProvider } from './providers/FavoriteRelaysProvider' import { FeedProvider } from './providers/FeedProvider' @@ -18,28 +19,30 @@ import { ZapProvider } from './providers/ZapProvider' export default function App(): JSX.Element { return ( - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + ) } diff --git a/src/components/VideoPlayer/index.tsx b/src/components/VideoPlayer/index.tsx index ae549434..a86eb1c4 100644 --- a/src/components/VideoPlayer/index.tsx +++ b/src/components/VideoPlayer/index.tsx @@ -1,4 +1,5 @@ import { cn, isInViewport } from '@/lib/utils' +import { useAutoplay } from '@/providers/AutoplayProvider' import videoManager from '@/services/video-manager.service' import { useEffect, useRef } from 'react' import NsfwOverlay from '../NsfwOverlay' @@ -12,10 +13,13 @@ export default function VideoPlayer({ className?: string isNsfw?: boolean }) { + const { autoplay } = useAutoplay() const videoRef = useRef(null) const containerRef = useRef(null) useEffect(() => { + if (!autoplay) return + const video = videoRef.current const container = containerRef.current @@ -41,7 +45,7 @@ export default function VideoPlayer({ return () => { observer.unobserve(container) } - }, []) + }, [autoplay]) return (
diff --git a/src/constants.ts b/src/constants.ts index caa7597d..b0d27e6f 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -13,6 +13,7 @@ export const StorageKey = { LAST_READ_NOTIFICATION_TIME_MAP: 'lastReadNotificationTimeMap', ACCOUNT_FEED_INFO_MAP: 'accountFeedInfoMap', MEDIA_UPLOAD_SERVICE: 'mediaUploadService', + AUTOPLAY: 'autoplay', ACCOUNT_RELAY_LIST_EVENT_MAP: 'accountRelayListEventMap', // deprecated ACCOUNT_FOLLOW_LIST_EVENT_MAP: 'accountFollowListEventMap', // deprecated ACCOUNT_MUTE_LIST_EVENT_MAP: 'accountMuteListEventMap', // deprecated diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts index c1e47026..cfb3beb4 100644 --- a/src/i18n/locales/ar.ts +++ b/src/i18n/locales/ar.ts @@ -224,6 +224,9 @@ export default { 'no bookmarks found': 'لم يتم العثور على إشارات', 'no more bookmarks': 'لا مزيد من الإشارات', Bookmarks: 'الإشارات المرجعية', - 'Show more': 'عرض المزيد' + 'Show more': 'عرض المزيد', + General: 'عام', + Autoplay: 'التشغيل التلقائي', + 'Enable video autoplay on this device': 'تمكين التشغيل التلقائي للفيديو على هذا الجهاز' } } diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index c1b4374e..f7b2bff9 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -228,6 +228,10 @@ export default { 'no bookmarks found': 'Keine Lesezeichen gefunden', 'no more bookmarks': 'Keine weiteren Lesezeichen', Bookmarks: 'Lesezeichen', - 'Show more': 'Mehr anzeigen' + 'Show more': 'Mehr anzeigen', + General: 'Allgemein', + Autoplay: 'Automatische Wiedergabe', + 'Enable video autoplay on this device': + 'Aktiviere die automatische Video-Wiedergabe auf diesem Gerät' } } diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 503c8f27..7304b1b7 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -224,6 +224,9 @@ export default { 'no bookmarks found': 'no bookmarks found', 'no more bookmarks': 'no more bookmarks', Bookmarks: 'Bookmarks', - 'Show more': 'Show more' + 'Show more': 'Show more', + General: 'General', + Autoplay: 'Autoplay', + 'Enable video autoplay on this device': 'Enable video autoplay on this device' } } diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index b041d9ed..bb319ba9 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -228,6 +228,10 @@ export default { 'no bookmarks found': 'No se encontraron marcadores', 'no more bookmarks': 'No hay más marcadores', Bookmarks: 'Marcadores', - 'Show more': 'Mostrar más' + 'Show more': 'Mostrar más', + General: 'General', + Autoplay: 'Reproducción automática', + 'Enable video autoplay on this device': + 'Habilitar reproducción automática de video en este dispositivo' } } diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index d61c0dcd..cd5616b2 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -227,6 +227,10 @@ export default { 'no bookmarks found': 'Aucun favori trouvé', 'no more bookmarks': 'Plus de favoris', Bookmarks: 'Favoris', - 'Show more': 'Afficher plus' + 'Show more': 'Afficher plus', + General: 'Général', + Autoplay: 'Lecture automatique', + 'Enable video autoplay on this device': + 'Activer la lecture automatique des vidéos sur cet appareil' } } diff --git a/src/i18n/locales/it.ts b/src/i18n/locales/it.ts index 0430d493..12957364 100644 --- a/src/i18n/locales/it.ts +++ b/src/i18n/locales/it.ts @@ -227,6 +227,10 @@ export default { 'no bookmarks found': 'Nessun segnalibro trovato', 'no more bookmarks': 'Nessun altro segnalibro', Bookmarks: 'Segnalibri', - 'Show more': 'Mostra di più' + 'Show more': 'Mostra di più', + General: 'Generale', + Autoplay: 'Riproduzione automatica', + 'Enable video autoplay on this device': + 'Abilita riproduzione automatica video su questo dispositivo' } } diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts index 3ac0c4c6..72f5939a 100644 --- a/src/i18n/locales/ja.ts +++ b/src/i18n/locales/ja.ts @@ -225,6 +225,9 @@ export default { 'no bookmarks found': 'ブックマークが見つかりません', 'no more bookmarks': 'これ以上ブックマークはありません', Bookmarks: 'ブックマーク一覧', - 'Show more': 'もっと見る' + 'Show more': 'もっと見る', + General: '一般', + Autoplay: '自動再生', + 'Enable video autoplay on this device': 'このデバイスでのビデオ自動再生を有効にする' } } diff --git a/src/i18n/locales/pl.ts b/src/i18n/locales/pl.ts index 4d9e4c75..21341e68 100644 --- a/src/i18n/locales/pl.ts +++ b/src/i18n/locales/pl.ts @@ -226,6 +226,9 @@ export default { 'no bookmarks found': 'Nie znaleziono zakładek', 'no more bookmarks': 'Koniec zakładek', Bookmarks: 'Zakładki', - 'Show more': 'Pokaż więcej' + 'Show more': 'Pokaż więcej', + General: 'Ogólne', + Autoplay: 'Autoodtwarzanie', + 'Enable video autoplay on this device': 'Włącz automatyczne odtwarzanie wideo na tym urządzeniu' } } diff --git a/src/i18n/locales/pt-BR.ts b/src/i18n/locales/pt-BR.ts index 0364c5ed..90c313a0 100644 --- a/src/i18n/locales/pt-BR.ts +++ b/src/i18n/locales/pt-BR.ts @@ -226,6 +226,10 @@ export default { 'no bookmarks found': 'Nenhum favorito encontrado', 'no more bookmarks': 'Sem mais favoritos', Bookmarks: 'Favoritos', - 'Show more': 'Mostrar mais' + 'Show more': 'Mostrar mais', + General: 'Geral', + Autoplay: 'Reprodução automática', + 'Enable video autoplay on this device': + 'Habilitar reprodução automática de vídeo neste dispositivo' } } diff --git a/src/i18n/locales/pt-PT.ts b/src/i18n/locales/pt-PT.ts index f8d43da7..e2dba258 100644 --- a/src/i18n/locales/pt-PT.ts +++ b/src/i18n/locales/pt-PT.ts @@ -227,6 +227,10 @@ export default { 'no bookmarks found': 'Nenhum favorito encontrado', 'no more bookmarks': 'Sem mais favoritos', Bookmarks: 'Favoritos', - 'Show more': 'Mostrar mais' + 'Show more': 'Mostrar mais', + General: 'Geral', + Autoplay: 'Reprodução Automática', + 'Enable video autoplay on this device': + 'Habilitar reprodução automática de vídeo neste dispositivo' } } diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index f8b0c8d7..b28f42b3 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -228,6 +228,9 @@ export default { 'no bookmarks found': 'Закладки не найдены', 'no more bookmarks': 'Больше нет закладок', Bookmarks: 'Закладки', - 'Show more': 'Показать больше' + 'Show more': 'Показать больше', + General: 'Общие', + Autoplay: 'Автовоспроизведение', + 'Enable video autoplay on this device': 'Включить автовоспроизведение видео на этом устройстве' } } diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts index 4ddf2208..dd766c00 100644 --- a/src/i18n/locales/zh.ts +++ b/src/i18n/locales/zh.ts @@ -225,6 +225,9 @@ export default { 'no bookmarks found': '暂无收藏', 'no more bookmarks': '到底了', Bookmarks: '收藏', - 'Show more': '显示更多' + 'Show more': '显示更多', + General: '常规', + Autoplay: '自动播放', + 'Enable video autoplay on this device': '在此设备上启用视频自动播放' } } diff --git a/src/lib/link.ts b/src/lib/link.ts index 0e90d2d3..a8a18b91 100644 --- a/src/lib/link.ts +++ b/src/lib/link.ts @@ -41,6 +41,7 @@ export const toRelaySettings = (tag?: 'mailbox' | 'favorite-relays') => { } export const toWallet = () => '/settings/wallet' export const toPostSettings = () => '/settings/posts' +export const toGeneralSettings = () => '/settings/general' export const toProfileEditor = () => '/profile-editor' export const toRelay = (url: string) => `/relays/${encodeURIComponent(url)}` export const toMuteList = () => '/mutes' diff --git a/src/pages/secondary/GeneralSettingsPage/index.tsx b/src/pages/secondary/GeneralSettingsPage/index.tsx new file mode 100644 index 00000000..215aae7b --- /dev/null +++ b/src/pages/secondary/GeneralSettingsPage/index.tsx @@ -0,0 +1,89 @@ +import { Label } from '@/components/ui/label' +import { Select, SelectContent, SelectItem, SelectTrigger } from '@/components/ui/select' +import { Switch } from '@/components/ui/switch' +import { LocalizedLanguageNames, TLanguage } from '@/i18n' +import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' +import { cn } from '@/lib/utils' +import { useAutoplay } from '@/providers/AutoplayProvider' +import { useTheme } from '@/providers/ThemeProvider' +import { SelectValue } from '@radix-ui/react-select' +import { forwardRef, HTMLProps, useState } from 'react' +import { useTranslation } from 'react-i18next' + +const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => { + const { t, i18n } = useTranslation() + const [language, setLanguage] = useState(i18n.language as TLanguage) + const { themeSetting, setThemeSetting } = useTheme() + const { autoplay, setAutoplay } = useAutoplay() + + const handleLanguageChange = (value: TLanguage) => { + i18n.changeLanguage(value) + setLanguage(value) + } + + return ( + +
+ + + + + + + + + + + + +
+
+ ) +}) +GeneralSettingsPage.displayName = 'GeneralSettingsPage' +export default GeneralSettingsPage + +const SettingItem = forwardRef>( + ({ children, className, ...props }, ref) => { + return ( +
+ {children} +
+ ) + } +) +SettingItem.displayName = 'SettingItem' diff --git a/src/pages/secondary/SettingsPage/index.tsx b/src/pages/secondary/SettingsPage/index.tsx index 44370cc9..2f6f2eee 100644 --- a/src/pages/secondary/SettingsPage/index.tsx +++ b/src/pages/secondary/SettingsPage/index.tsx @@ -1,78 +1,39 @@ import AboutInfoDialog from '@/components/AboutInfoDialog' import Donation from '@/components/Donation' -import { Select, SelectContent, SelectItem, SelectTrigger } from '@/components/ui/select' -import { LocalizedLanguageNames, TLanguage } from '@/i18n' import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' -import { toPostSettings, toRelaySettings, toWallet } from '@/lib/link' +import { toGeneralSettings, toPostSettings, toRelaySettings, toWallet } from '@/lib/link' import { cn } from '@/lib/utils' import { useSecondaryPage } from '@/PageManager' import { useNostr } from '@/providers/NostrProvider' -import { useTheme } from '@/providers/ThemeProvider' -import { SelectValue } from '@radix-ui/react-select' import { Check, ChevronRight, Copy, Info, KeyRound, - Languages, PencilLine, Server, - SunMoon, + Settings2, Wallet } from 'lucide-react' import { forwardRef, HTMLProps, useState } from 'react' import { useTranslation } from 'react-i18next' const SettingsPage = forwardRef(({ index }: { index?: number }, ref) => { - const { t, i18n } = useTranslation() + const { t } = useTranslation() const { nsec, ncryptsec } = useNostr() const { push } = useSecondaryPage() - const [language, setLanguage] = useState(i18n.language as TLanguage) - const { themeSetting, setThemeSetting } = useTheme() const [copiedNsec, setCopiedNsec] = useState(false) const [copiedNcryptsec, setCopiedNcryptsec] = useState(false) - const handleLanguageChange = (value: TLanguage) => { - i18n.changeLanguage(value) - setLanguage(value) - } - return ( - + push(toGeneralSettings())}>
- -
{t('Languages')}
+ +
{t('General')}
- -
- -
- -
{t('Theme')}
-
- +
push(toRelaySettings())}>
diff --git a/src/providers/AutoplayProvider.tsx b/src/providers/AutoplayProvider.tsx new file mode 100644 index 00000000..3ea42060 --- /dev/null +++ b/src/providers/AutoplayProvider.tsx @@ -0,0 +1,32 @@ +import { createContext, useContext, useState } from 'react' +import storage from '@/services/local-storage.service' + +type TAutoplayContext = { + autoplay: boolean + setAutoplay: (autoplay: boolean) => void +} + +const AutoplayContext = createContext(undefined) + +export const useAutoplay = () => { + const context = useContext(AutoplayContext) + if (!context) { + throw new Error('useAutoplay must be used within an AutoplayProvider') + } + return context +} + +export function AutoplayProvider({ children }: { children: React.ReactNode }) { + const [autoplay, setAutoplay] = useState(storage.getAutoplay()) + + const updateAutoplay = (autoplay: boolean) => { + storage.setAutoplay(autoplay) + setAutoplay(autoplay) + } + + return ( + + {children} + + ) +} diff --git a/src/routes.tsx b/src/routes.tsx index aa28e2b8..ef617ed6 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,6 +1,7 @@ import { match } from 'path-to-regexp' import { isValidElement } from 'react' import FollowingListPage from './pages/secondary/FollowingListPage' +import GeneralSettingsPage from './pages/secondary/GeneralSettingsPage' import MuteListPage from './pages/secondary/MuteListPage' import NoteListPage from './pages/secondary/NoteListPage' import NotePage from './pages/secondary/NotePage' @@ -26,6 +27,7 @@ const ROUTES = [ { path: '/settings/relays', element: }, { path: '/settings/wallet', element: }, { path: '/settings/posts', element: }, + { path: '/settings/general', element: }, { path: '/profile-editor', element: }, { path: '/mutes', element: } ] diff --git a/src/services/local-storage.service.ts b/src/services/local-storage.service.ts index 78e6e56a..31f0641f 100644 --- a/src/services/local-storage.service.ts +++ b/src/services/local-storage.service.ts @@ -24,6 +24,7 @@ class LocalStorageService { private quickZap: boolean = false private accountFeedInfoMap: Record = {} private mediaUploadService: string = DEFAULT_NIP_96_SERVICE + private autoplay: boolean = true constructor() { if (!LocalStorageService.instance) { @@ -89,6 +90,8 @@ class LocalStorageService { this.mediaUploadService = window.localStorage.getItem(StorageKey.MEDIA_UPLOAD_SERVICE) ?? DEFAULT_NIP_96_SERVICE + this.autoplay = window.localStorage.getItem(StorageKey.AUTOPLAY) !== 'false' + // Clean up deprecated data window.localStorage.removeItem(StorageKey.ACCOUNT_PROFILE_EVENT_MAP) window.localStorage.removeItem(StorageKey.ACCOUNT_FOLLOW_LIST_EVENT_MAP) @@ -235,6 +238,15 @@ class LocalStorageService { this.mediaUploadService = service window.localStorage.setItem(StorageKey.MEDIA_UPLOAD_SERVICE, service) } + + getAutoplay() { + return this.autoplay + } + + setAutoplay(autoplay: boolean) { + this.autoplay = autoplay + window.localStorage.setItem(StorageKey.AUTOPLAY, autoplay.toString()) + } } const instance = new LocalStorageService()