From 31f859238094d912a2b46f4bdd661172c64efa39 Mon Sep 17 00:00:00 2001 From: Syntax <2079305+TheUltDev@users.noreply.github.com> Date: Sat, 15 Feb 2025 19:29:56 -0600 Subject: [PATCH] refactor --- client/src/app/routes/menu/menu-group.tsx | 3 +- client/src/app/routes/menu/menu-item.tsx | 3 +- client/src/env.d.ts | 5 ++ client/src/global.ts | 3 ++ client/src/index.native.tsx | 1 + client/src/index.tsx | 1 + client/src/media/dir/hooks/use-dir-hfs.ts | 2 +- client/src/media/dir/hooks/use-dir-zip.ts | 9 ++-- client/src/media/dir/hooks/use-entry-hfs.ts | 18 +++---- .../src/media/dir/hooks/use-entry-torrent.ts | 6 +-- client/src/media/dir/hooks/use-entry-zip.ts | 6 +-- client/src/media/dir/stacks/dir-hfs.tsx | 17 ++++-- client/src/media/dir/stacks/dir-torrent.tsx | 13 +++-- client/src/media/dir/stacks/dir-zip.tsx | 14 +++-- client/src/media/dir/stacks/entry-hfs.tsx | 5 +- client/src/media/dir/stacks/entry-torrent.tsx | 3 +- client/src/media/dir/stacks/entry-zip.tsx | 3 +- client/src/media/dir/types/hfs.ts | 7 ++- client/src/media/dir/types/torrent.ts | 7 +++ client/src/media/dir/types/zip.ts | 7 +++ client/src/media/stacks/controls.tsx | 13 ++--- client/src/media/stacks/list/bar.tsx | 12 ++--- client/src/media/stacks/list/empty.tsx | 7 +-- client/src/media/stacks/list/index.tsx | 54 +++++++++---------- client/src/media/stacks/list/row.tsx | 51 ++++++------------ client/src/media/stacks/select/item.tsx | 14 +++-- client/src/media/stacks/thumb.tsx | 10 ++-- client/src/social/stacks/stream-list-item.tsx | 3 +- translations/ar.po | 8 +-- translations/en.po | 8 +-- translations/es.po | 8 +-- translations/ja.po | 8 +-- translations/ko.po | 8 +-- translations/ru.po | 8 +-- 34 files changed, 183 insertions(+), 162 deletions(-) create mode 100644 client/src/global.ts diff --git a/client/src/app/routes/menu/menu-group.tsx b/client/src/app/routes/menu/menu-group.tsx index 1b979871..b7834ed5 100644 --- a/client/src/app/routes/menu/menu-group.tsx +++ b/client/src/app/routes/menu/menu-group.tsx @@ -2,7 +2,6 @@ import {useState, useRef} from 'react'; import {useStyles, createStyleSheet} from 'react-native-unistyles'; import {Pressable, View, Text} from 'react-native'; import {Icon} from 'react-exo/icon'; -import {isTouch} from 'app/utils/platform'; import {useTheme} from 'app/hooks/use-theme'; interface MenuGroupProps extends React.PropsWithChildren { @@ -101,7 +100,7 @@ const stylesheet = createStyleSheet(theme => ({ color: theme.colors.mutedForeground, lineHeight: theme.font.labelHeight, fontSize: 11, - ...isTouch() && { + ...__TOUCH__ && { lineHeight: 36, fontSize: 12, }, diff --git a/client/src/app/routes/menu/menu-item.tsx b/client/src/app/routes/menu/menu-item.tsx index 2fe44589..6618db79 100644 --- a/client/src/app/routes/menu/menu-item.tsx +++ b/client/src/app/routes/menu/menu-item.tsx @@ -3,7 +3,6 @@ import {View, Text} from 'react-native'; import {useFocusable} from '@noriginmedia/norigin-spatial-navigation'; import {useStyles, createStyleSheet} from 'react-native-unistyles'; import {useLocation, useNavigate, Link} from 'react-exo/navigation'; -import {isTouch} from 'app/utils/platform'; interface MenuItemProps extends React.PropsWithChildren { path: string, @@ -81,7 +80,7 @@ const stylesheet = createStyleSheet(theme => ({ color: theme.colors.secondaryForeground, lineHeight: 24, fontSize: 11, - ...isTouch() && { + ...__TOUCH__ && { marginLeft: theme.display.space2, lineHeight: 40, fontSize: 13, diff --git a/client/src/env.d.ts b/client/src/env.d.ts index 954c6e91..0fb97eb5 100644 --- a/client/src/env.d.ts +++ b/client/src/env.d.ts @@ -3,6 +3,11 @@ import type {AppThemes, AppBreakpoints} from 'design/styles'; import type {SvgProps} from 'react-native-svg'; +// Global variables +declare global { + var __TOUCH__: boolean; +} + // Set Unistyles theme types declare module 'react-native-unistyles' { export interface UnistylesThemes extends AppThemes {} diff --git a/client/src/global.ts b/client/src/global.ts new file mode 100644 index 00000000..201dc1e2 --- /dev/null +++ b/client/src/global.ts @@ -0,0 +1,3 @@ +import {isTouch} from 'app/utils/platform'; + +globalThis.__TOUCH__ = isTouch(); diff --git a/client/src/index.native.tsx b/client/src/index.native.tsx index 8d776816..950177ee 100644 --- a/client/src/index.native.tsx +++ b/client/src/index.native.tsx @@ -1,3 +1,4 @@ +import 'global'; import 'design/styles'; import 'react-exo/gesture'; import 'react-native-url-polyfill/auto'; diff --git a/client/src/index.tsx b/client/src/index.tsx index cefd3748..c1ab3378 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -1,3 +1,4 @@ +import 'global'; import 'design/styles'; import 'maplibre-gl/dist/maplibre-gl.css'; import 'react-exo/checkbox.css'; diff --git a/client/src/media/dir/hooks/use-dir-hfs.ts b/client/src/media/dir/hooks/use-dir-hfs.ts index 13bf49e2..332273da 100644 --- a/client/src/media/dir/hooks/use-dir-hfs.ts +++ b/client/src/media/dir/hooks/use-dir-hfs.ts @@ -10,8 +10,8 @@ import {isInitDirectory, INIT_DIRECTORIES} from '../utils/hfs/path'; import {getThumbnail} from '../utils/hfs/meta'; import {saveAs} from '../utils/hfs/fs'; -import type {GestureResponderEvent} from 'react-native'; import type {HfsCtx, HfsFileEntry} from 'media/dir/types/hfs'; +import type {GestureResponderEvent} from 'react-native'; export function useDirHfs(path: string, tmp?: boolean): Omit { const [list, setList] = useState([]); diff --git a/client/src/media/dir/hooks/use-dir-zip.ts b/client/src/media/dir/hooks/use-dir-zip.ts index 9d4855b2..0eafa874 100644 --- a/client/src/media/dir/hooks/use-dir-zip.ts +++ b/client/src/media/dir/hooks/use-dir-zip.ts @@ -1,16 +1,16 @@ import {fs} from '@zip.js/zip.js'; import {web} from 'react-exo/fs'; -import {useCallback, useEffect, useState, useRef} from 'react'; +import {useEffect, useCallback, useState, useRef} from 'react'; import {usePath} from 'app/hooks/use-path'; import {usePut} from 'app/data/store'; import {useFile} from 'media/file/hooks/use-file'; import {toPath} from 'app/utils/formatting'; import media from 'media/store'; -import type {FS} from '@zip.js/zip.js'; import type {Zip, ZipCtx, ZipFileEntry} from 'media/dir/types/zip'; import type {GestureResponderEvent} from 'react-native'; import type {HfsFileEntry} from 'media/dir/types/hfs'; +import type {FS} from '@zip.js/zip.js'; export function useDirZip(path: string): ZipCtx { const [zip, setZip] = useState(null); @@ -99,5 +99,8 @@ export function useDirZip(path: string): ZipCtx { })(); }, [buffer]); - return {zip, cmd: {extract}}; + return { + zip, + cmd: {extract} + }; } diff --git a/client/src/media/dir/hooks/use-entry-hfs.ts b/client/src/media/dir/hooks/use-entry-hfs.ts index e3f2c375..c2ab145c 100644 --- a/client/src/media/dir/hooks/use-entry-hfs.ts +++ b/client/src/media/dir/hooks/use-entry-hfs.ts @@ -9,36 +9,36 @@ import media from 'media/store'; import {is as isZip} from './use-entry-zip'; import {is as isTorrent} from './use-entry-torrent'; -import type {View, GestureResponderEvent} from 'react-native'; import type {HfsCmd, HfsFileEntry} from 'media/dir/types/hfs'; import type {EntryHfsProps} from 'media/dir/stacks/entry-hfs'; import type {CleanupFn} from 'app/utils/dragdrop'; +import type * as RN from 'react-native'; export const {is, get, type} = $.tag('hfs'); -export function useEntryHfs({item, cmd, opt, tmp}: EntryHfsProps) { +export function useEntryHfs({item, cmd, opt}: EntryHfsProps) { const [dropping, setDropping] = useState(false); const put = usePut(); // Spatial navigation const {focused, ref: refFoc, focusSelf: foc} = useFocusable({ - onFocus: (_lay, _props, e) => tmp + onFocus: (_lay, _props, e) => opt.preview ? undefined - : cmd.select(item, e.event as unknown as GestureResponderEvent), - onEnterPress: () => tmp + : cmd.select(item, e.event as unknown as RN.GestureResponderEvent), + onEnterPress: () => opt.preview ? cmd.select(item) : item.isDirectory ? cmd.open(item) : cmd.select(item), onArrowPress: (dir) => { - if (dir !== 'left' || tmp) return true; + if (dir !== 'left' || opt.preview) return true; if (cmd.goUp()) return false; return true; }, }); // Drag and drop - const refDnd = useRef(null); + const refDnd = useRef(null); useEffect(() => { if (!refDnd.current) return; const element = refDnd.current as unknown as HTMLElement; @@ -46,7 +46,7 @@ export function useEntryHfs({item, cmd, opt, tmp}: EntryHfsProps) { _.draggable({ element, getInitialData: () => get(item, cmd), - onGenerateDragPreview: _.dragPreview(opt.selected.count), + onGenerateDragPreview: _.dragPreview(opt.selected?.count ?? 1), onDragStart: () => put(media.actions.drag(item.name)), onDrop: () => put(media.actions.drag(null)), }), @@ -86,7 +86,7 @@ export function useEntryHfs({item, cmd, opt, tmp}: EntryHfsProps) { }, }), ].filter(Boolean) as CleanupFn[]); - }, [item, cmd, opt.selected.count, put]); + }, [item, cmd, opt.selected?.count, put]); return { ext: toPath(item.name, item.isDirectory)?.ext, diff --git a/client/src/media/dir/hooks/use-entry-torrent.ts b/client/src/media/dir/hooks/use-entry-torrent.ts index ac84eb64..ddfeefc3 100644 --- a/client/src/media/dir/hooks/use-entry-torrent.ts +++ b/client/src/media/dir/hooks/use-entry-torrent.ts @@ -4,10 +4,10 @@ import {toPath} from 'app/utils/formatting'; import * as dnd from 'app/utils/dragdrop'; import * as $ from 'media/utils/entry'; -import type {View} from 'react-native'; -import type {CleanupFn} from 'app/utils/dragdrop'; import type {TorrentFileEntry, TorrentCmd} from 'media/dir/types/torrent'; import type {EntryTorrentProps} from 'media/dir/stacks/entry-torrent'; +import type {CleanupFn} from 'app/utils/dragdrop'; +import type * as RN from 'react-native'; export const {is, get, type} = $.tag('torrent'); @@ -20,7 +20,7 @@ export function useEntryTorrent({item, cmd, opt}: EntryTorrentProps) { }); // Drag and drop - const refDnd = useRef(null); + const refDnd = useRef(null); useEffect(() => { if (!refDnd.current) return; const element = refDnd.current as unknown as HTMLElement; diff --git a/client/src/media/dir/hooks/use-entry-zip.ts b/client/src/media/dir/hooks/use-entry-zip.ts index bbb5d7ed..b8f0f34a 100644 --- a/client/src/media/dir/hooks/use-entry-zip.ts +++ b/client/src/media/dir/hooks/use-entry-zip.ts @@ -4,10 +4,10 @@ import {toPath} from 'app/utils/formatting'; import * as dnd from 'app/utils/dragdrop'; import * as $ from 'media/utils/entry'; -import type {View} from 'react-native'; -import type {CleanupFn} from 'app/utils/dragdrop'; import type {ZipFileEntry, ZipCmd} from 'media/dir/types/zip'; import type {EntryZipProps} from 'media/dir/stacks/entry-zip'; +import type {CleanupFn} from 'app/utils/dragdrop'; +import type * as RN from 'react-native'; export const {is, get, type} = $.tag('zip'); @@ -20,7 +20,7 @@ export function useEntryZip({item, cmd, opt}: EntryZipProps) { }); // Drag and drop - const refDnd = useRef(null); + const refDnd = useRef(null); useEffect(() => { if (!refDnd.current) return; const element = refDnd.current as unknown as HTMLElement; diff --git a/client/src/media/dir/stacks/dir-hfs.tsx b/client/src/media/dir/stacks/dir-hfs.tsx index 4260c524..79d408df 100644 --- a/client/src/media/dir/stacks/dir-hfs.tsx +++ b/client/src/media/dir/stacks/dir-hfs.tsx @@ -1,18 +1,27 @@ import {List} from 'media/stacks/list'; import {EntryHfs} from 'media/dir/stacks/entry-hfs'; -import type {HfsCtx} from 'media/dir/types/hfs'; +import type {HfsCtx, HfsOpt} from 'media/dir/types/hfs'; export function DirHfs({hfs, cmd, ext, bar}: HfsCtx) { const {list, path} = hfs; const {dnd, sel, tmp} = ext; + const layout = tmp ? 'grid' : 'list'; // This is default, make user configurable return ( { const self = path ? `${path}/${item.name}` : item.name; const prev = list[index - 1]; const next = list[index + 1]; - const opt = { + const opt: Partial = { + layout, + preview: tmp, dragging: dnd?.includes(self), selected: { self: sel?.includes(self), @@ -22,7 +31,7 @@ export function DirHfs({hfs, cmd, ext, bar}: HfsCtx) { }, }; return ( - + ); }} /> diff --git a/client/src/media/dir/stacks/dir-torrent.tsx b/client/src/media/dir/stacks/dir-torrent.tsx index b42306e0..e3869ed8 100644 --- a/client/src/media/dir/stacks/dir-torrent.tsx +++ b/client/src/media/dir/stacks/dir-torrent.tsx @@ -1,12 +1,19 @@ import {List} from 'media/stacks/list'; import {EntryTorrent} from 'media/dir/stacks/entry-torrent'; -import type {TorrentCtx} from 'media/dir/types/torrent'; +import type {TorrentCtx, TorrentOpt} from 'media/dir/types/torrent'; export function DirTorrent({torrent, cmd}: TorrentCtx) { return ( } + items={torrent?.list} + opts={{layout: 'grid', preview: true}} + render={({item}) => { + const opt: Partial = { + layout: 'grid', + preview: true, + }; + return ; + }} /> ); } diff --git a/client/src/media/dir/stacks/dir-zip.tsx b/client/src/media/dir/stacks/dir-zip.tsx index 970c6c29..c5bfa8bf 100644 --- a/client/src/media/dir/stacks/dir-zip.tsx +++ b/client/src/media/dir/stacks/dir-zip.tsx @@ -1,13 +1,19 @@ import {List} from 'media/stacks/list'; import {EntryZip} from 'media/dir/stacks/entry-zip'; -import type {ZipCtx} from 'media/dir/types/zip'; +import type {ZipCtx, ZipOpt} from 'media/dir/types/zip'; export function DirZip({zip, cmd}: ZipCtx) { return ( } + items={zip?.list} + opts={{layout: 'grid', preview: true}} + render={({item}) => { + const opt: Partial = { + layout: 'grid', + preview: true, + }; + return ; + }} /> ); } diff --git a/client/src/media/dir/stacks/entry-hfs.tsx b/client/src/media/dir/stacks/entry-hfs.tsx index 5304a633..4fc04e75 100644 --- a/client/src/media/dir/stacks/entry-hfs.tsx +++ b/client/src/media/dir/stacks/entry-hfs.tsx @@ -9,11 +9,10 @@ export interface EntryHfsProps { item: HfsFileEntry, cmd: HfsCmd, opt: HfsOpt, - tmp?: boolean, } export function EntryHfs(props: EntryHfsProps) { - const {item, tmp} = props; + const {item} = props; const {name, size, isFile} = item; const {ext, cmd, opt, ref, foc} = useEntryHfs(props); const dir = !isFile; @@ -24,7 +23,7 @@ export function EntryHfs(props: EntryHfsProps) { onPress={cmd.select} onDoublePress={dir ? () => cmd.open(true) : undefined}> foc()}> - + ); diff --git a/client/src/media/dir/stacks/entry-torrent.tsx b/client/src/media/dir/stacks/entry-torrent.tsx index f8d87212..e78dbff3 100644 --- a/client/src/media/dir/stacks/entry-torrent.tsx +++ b/client/src/media/dir/stacks/entry-torrent.tsx @@ -21,8 +21,7 @@ export function EntryTorrent(props: EntryTorrentProps) { return ( + onPress={cmd.download}> foc()}> diff --git a/client/src/media/dir/stacks/entry-zip.tsx b/client/src/media/dir/stacks/entry-zip.tsx index 1a503477..5fea9bb4 100644 --- a/client/src/media/dir/stacks/entry-zip.tsx +++ b/client/src/media/dir/stacks/entry-zip.tsx @@ -19,8 +19,7 @@ export function EntryZip(props: EntryZipProps) { return ( + onPress={cmd.extract}> foc()}> diff --git a/client/src/media/dir/types/hfs.ts b/client/src/media/dir/types/hfs.ts index a3bc9fc7..46682d8f 100644 --- a/client/src/media/dir/types/hfs.ts +++ b/client/src/media/dir/types/hfs.ts @@ -19,7 +19,12 @@ export type HfsCtx = { } export type HfsOpt = { - selected: { + layout?: 'list' | 'grid', + preview?: boolean, + focused?: boolean, + dragging?: boolean, + dropping?: boolean, + selected?: { self: boolean, prev: boolean, next: boolean, diff --git a/client/src/media/dir/types/torrent.ts b/client/src/media/dir/types/torrent.ts index e8655dc2..9c986f42 100644 --- a/client/src/media/dir/types/torrent.ts +++ b/client/src/media/dir/types/torrent.ts @@ -15,6 +15,13 @@ export type TorrentCtx = { cmd: TorrentCmd, } +export type TorrentOpt = { + layout?: 'list' | 'grid', + preview?: boolean, + focused?: boolean, + dragging?: boolean, +}; + export type TorrentCmd = { download: ( file: TorrentFileEntry, diff --git a/client/src/media/dir/types/zip.ts b/client/src/media/dir/types/zip.ts index 19bd59ea..066164c7 100644 --- a/client/src/media/dir/types/zip.ts +++ b/client/src/media/dir/types/zip.ts @@ -20,6 +20,13 @@ export interface Zip { }>, } +export type ZipOpt = { + layout?: 'list' | 'grid', + preview?: boolean, + focused?: boolean, + dragging?: boolean, +}; + export type ZipCtx = { zip: Zip | null, cmd: ZipCmd, diff --git a/client/src/media/stacks/controls.tsx b/client/src/media/stacks/controls.tsx index c70aec7c..67b5e949 100644 --- a/client/src/media/stacks/controls.tsx +++ b/client/src/media/stacks/controls.tsx @@ -7,14 +7,11 @@ import {useStyles, createStyleSheet} from 'react-native-unistyles'; import {useMediaControls} from 'media/hooks/use-media-controls'; import {FileType} from 'media/file/types'; import {Thumb} from 'media/stacks/thumb'; -import {isTouch} from 'app/utils/platform'; import type {FileProps} from 'media/file'; import type {FileRef, FileRenderInfo} from 'media/file/types'; import type {PressableStateCallbackType} from 'react-native'; -const TOUCH = isTouch(); - export interface MediaControlsProps { file: React.RefObject, renderer?: FileRenderInfo, @@ -46,7 +43,7 @@ export function MediaControls(props: MediaControlsProps) { const isDir = fileType === FileType.Directory; const vstyles = useMemo(() => ({ icon: (state: PressableStateCallbackType) => - state.hovered || TOUCH + state.hovered || __TOUCH__ ? theme.colors.foreground : theme.colors.mutedForeground, track: [ @@ -90,7 +87,7 @@ export function MediaControls(props: MediaControlsProps) { ? Promise.resolve(props.metadata.cover ?? null)} @@ -99,7 +96,7 @@ export function MediaControls(props: MediaControlsProps) { - + {props.metadata.title} @@ -113,7 +110,7 @@ export function MediaControls(props: MediaControlsProps) { icon && )} @@ -147,7 +144,7 @@ const stylesheet = createStyleSheet((theme, rt) => ({ zIndex: 100, margin: theme.display.space1, paddingHorizontal: theme.display.space3, - paddingVertical: TOUCH + paddingVertical: __TOUCH__ ? theme.display.space4 : theme.display.space3, }, diff --git a/client/src/media/stacks/list/bar.tsx b/client/src/media/stacks/list/bar.tsx index cc42589d..a494d7ad 100644 --- a/client/src/media/stacks/list/bar.tsx +++ b/client/src/media/stacks/list/bar.tsx @@ -1,6 +1,5 @@ import {View, ScrollView} from 'react-native'; import {Icon} from 'react-exo/icon'; -import {isTouch} from 'react-exo/utils'; import {useLingui} from '@lingui/react/macro'; import {useNavigate} from 'react-exo/navigation'; import {useCallback, useEffect, useRef} from 'react'; @@ -10,10 +9,9 @@ import {useMediaName} from 'media/hooks/use-media-name'; import {ButtonText} from 'app/stacks/button/text'; import {ButtonIcon} from 'app/stacks/button/icon'; -const TOUCH = isTouch(); -const ITEM_SIZE = TOUCH ? 46 : 32; -const ICON_SIZE = TOUCH ? 18 : 16; -const TEXT_SIZE = TOUCH ? 14 : 11; +const ITEM_SIZE = __TOUCH__ ? 46 : 32; +const ICON_SIZE = __TOUCH__ ? 18 : 16; +const TEXT_SIZE = __TOUCH__ ? 14 : 11; export interface ListBarProps { path?: string, @@ -146,7 +144,7 @@ const stylesheet = createStyleSheet((theme) => ({ root: { height: ITEM_SIZE, paddingVertical: theme.display.space2, - paddingHorizontal: TOUCH ? theme.display.space3 : 6, + paddingHorizontal: __TOUCH__ ? theme.display.space3 : 6, flexDirection: 'row', alignItems: 'center', alignSelf: 'stretch', @@ -173,7 +171,7 @@ const stylesheet = createStyleSheet((theme) => ({ paddingBottom: 1, paddingRight: 1, alignItems: 'center', - gap: TOUCH ? theme.display.space3 : theme.display.space2, + gap: __TOUCH__ ? theme.display.space3 : theme.display.space2, }, separator: { width: 10, diff --git a/client/src/media/stacks/list/empty.tsx b/client/src/media/stacks/list/empty.tsx index a448f10c..3d1a4599 100644 --- a/client/src/media/stacks/list/empty.tsx +++ b/client/src/media/stacks/list/empty.tsx @@ -3,9 +3,6 @@ import {useLingui} from '@lingui/react/macro'; import {useState, useEffect} from 'react'; import {useImportHfs} from 'media/dir/hooks/use-import-hfs'; import {Watermark} from 'app/stacks/watermark'; -import {isTouch} from 'react-exo/utils'; - -const TOUCH = isTouch(); export function ListEmpty({offset}: {offset: number}) { const [visible, setVisible] = useState(false); @@ -23,12 +20,12 @@ export function ListEmpty({offset}: {offset: number}) { { await importFolder(); }} diff --git a/client/src/media/stacks/list/index.tsx b/client/src/media/stacks/list/index.tsx index d684688c..eaa0c4dc 100644 --- a/client/src/media/stacks/list/index.tsx +++ b/client/src/media/stacks/list/index.tsx @@ -4,60 +4,56 @@ import {useFocusable, FocusContext} from '@noriginmedia/norigin-spatial-navigati import {useStyles, createStyleSheet} from 'react-native-unistyles'; import {useWindowDimensions} from 'react-native'; import {useRef} from 'react'; -// import {useGet} from 'app/data/store'; import {ListBar} from 'media/stacks/list/bar'; import {ListEmpty} from 'media/stacks/list/empty'; import {HEIGHT_ROW, HEIGHT_CELL} from 'media/stacks/list/row'; -// import media from 'media/store'; import type {LegendListRef} from '@legendapp/list'; export interface ListProps { - list?: T[]; - path?: string; - ext?: unknown; - bar?: { - actions?: Array<{ - id: string, - icon: string, - onPress: () => void, - }>; - }, + items?: T[]; + data?: unknown; + opts?: { + preview?: boolean, + layout?: 'list' | 'grid', + header?: { + path: string, + actions?: Array<{id: string, icon: string, onPress: () => void}>, + }, + }; render: (props: { item: T, index: number, }) => React.ReactNode; } -export function List({path, list, ext, bar, render}: ListProps) { - //const layout = useGet(media.selectors.getLayout); +export function List({items, data, opts, render}: ListProps) { + const {ref, focusKey} = useFocusable({saveLastFocusedChild: !opts?.preview}); + const {styles} = useStyles(stylesheet); const {width} = useWindowDimensions(); - const layout = !ext?.tmp ? 'list' : 'grid'; - const height = layout === 'list' ? HEIGHT_ROW : HEIGHT_CELL; - const columns = layout === 'grid' ? Math.floor(width / (HEIGHT_CELL * 1.15)) : 1; const listRef = useRef(null); - const {styles} = useStyles(stylesheet); - const {ref, focusKey} = useFocusable({ - saveLastFocusedChild: !ext?.tmp, // TODO: type ext - }); + const layout = opts?.layout ?? 'list'; + const isGrid = layout === 'grid'; + const height = isGrid ? HEIGHT_CELL : HEIGHT_ROW; + const columns = isGrid ? Math.floor(width / (HEIGHT_CELL * 1.15)) : 1; return ( - {bar && } - {!list?.length - ? + {opts?.header && } + {!items?.length + ? : ( : null} + contentContainerStyle={[styles.list, isGrid && styles.grid]} + ListHeaderComponent={opts?.header ? : null} keyExtractor={(_,i) => i.toString()} renderItem={render} recycleItems @@ -84,4 +80,4 @@ const stylesheet = createStyleSheet(theme => ({ header: { height: theme.display.space1, }, -})); \ No newline at end of file +})); diff --git a/client/src/media/stacks/list/row.tsx b/client/src/media/stacks/list/row.tsx index dabae4c6..31cc72f9 100644 --- a/client/src/media/stacks/list/row.tsx +++ b/client/src/media/stacks/list/row.tsx @@ -1,12 +1,12 @@ import {useStyles, createStyleSheet} from 'react-native-unistyles'; import {View, Text} from 'react-native'; import {Thumb} from 'media/stacks/thumb'; -import {isTouch} from 'app/utils/platform'; import {bytesize} from 'app/utils/formatting'; import {useMediaName} from 'media/hooks/use-media-name'; -const TOUCH = isTouch(); -export const HEIGHT_ROW = TOUCH ? 40 : 26; +import type {HfsOpt} from 'media/dir/types/hfs'; + +export const HEIGHT_ROW = __TOUCH__ ? 40 : 26; export const HEIGHT_CELL = 130; interface ListRow { @@ -14,34 +14,17 @@ interface ListRow { size?: number, ext?: string, dir?: boolean, - tmp?: boolean, - img?: () => Promise, - opt?: { - dragging?: boolean, - dropping?: boolean, - focused?: boolean, - selected?: { - self?: boolean, - prev?: boolean, - next?: boolean, - count?: number, - }, - } + img?: (() => Promise) | string, + opt?: Partial, } export function ListRow(props: ListRow) { const title = useMediaName(props.name); const {styles} = useStyles(stylesheet); - const {name, size, ext, dir, tmp, opt, img} = props; - const { - focused, - selected, - dragging, - dropping, - } = opt ?? {}; - - const cell = tmp; - const thumbSize = cell ? 4 : TOUCH ? 1 : 0; + const {name, size, ext, dir, opt, img} = props; + const {focused, selected, dragging, dropping} = opt ?? {}; + const isGrid = opt?.layout === 'grid'; + const thumbSize = isGrid ? 4 : __TOUCH__ ? 1 : 0; const vstyles = { root: [ styles.root, @@ -50,24 +33,24 @@ export function ListRow(props: ListRow) { selected?.next && styles.selectedNext, (dropping || focused) && !dragging && styles.outline, dragging && styles.disabled, - cell && styles.cell, + isGrid && styles.cell, ], }; return ( - - + + - + {title} {dir ? '‎' : bytesize(size ?? 0)} @@ -127,7 +110,7 @@ const stylesheet = createStyleSheet((theme) => ({ lineHeight: theme.font.height, letterSpacing: theme.font.spacing, color: theme.colors.foreground, - ...TOUCH && { + ...__TOUCH__ && { fontSize: theme.font.contentSize, lineHeight: theme.font.contentHeight, letterSpacing: theme.font.contentSpacing, diff --git a/client/src/media/stacks/select/item.tsx b/client/src/media/stacks/select/item.tsx index 71b8ffc1..53fea630 100644 --- a/client/src/media/stacks/select/item.tsx +++ b/client/src/media/stacks/select/item.tsx @@ -7,15 +7,13 @@ import {useStyles, createStyleSheet} from 'react-native-unistyles'; import {useFocusable} from '@noriginmedia/norigin-spatial-navigation'; import {useMediaName} from 'media/hooks/use-media-name'; import {usePut} from 'app/data/store'; -import {isTouch} from 'app/utils/platform'; import media from 'media/store'; import type {HfsImpl} from 'react-exo/fs'; -const TOUCH = isTouch(); -export const HEIGHT = TOUCH ? 46 : 32; -export const ICON_SIZE = TOUCH ? 1 : 0; -export const TEXT_LINES = TOUCH ? 2 : 1; +export const HEIGHT = __TOUCH__ ? 46 : 32; +export const ICON_SIZE = __TOUCH__ ? 1 : 0; +export const TEXT_LINES = __TOUCH__ ? 2 : 1; interface SelectItemProps { focused: boolean, @@ -93,7 +91,7 @@ export function SelectItem(props: SelectItemProps) { close(index)}> @@ -105,13 +103,13 @@ export function SelectItem(props: SelectItemProps) { const stylesheet = createStyleSheet((theme) => ({ root: { height: HEIGHT, - gap: TOUCH ? theme.display.space3 : theme.display.space2, + gap: __TOUCH__ ? theme.display.space3 : theme.display.space2, flexDirection: 'row', alignItems: 'center', alignContent: 'center', justifyContent: 'center', paddingVertical: theme.display.space1, - paddingHorizontal: TOUCH ? theme.display.space3 : theme.display.space2, + paddingHorizontal: __TOUCH__ ? theme.display.space3 : theme.display.space2, borderRadius: theme.display.radius1, borderColor: theme.colors.border, borderWidth: 1, diff --git a/client/src/media/stacks/thumb.tsx b/client/src/media/stacks/thumb.tsx index f2083b2a..3894e38d 100644 --- a/client/src/media/stacks/thumb.tsx +++ b/client/src/media/stacks/thumb.tsx @@ -10,7 +10,7 @@ interface ThumbProps { size?: ThumbSize, name?: string, dir?: boolean, - img?: () => Promise, + img?: (() => Promise) | string, ext?: string, } @@ -61,10 +61,14 @@ export function Thumb({ getIcon(name, ext, '', scheme).then(setIcon); }, [name, ext, scheme]); - // Set image when img function changes + // Set image when source changes useEffect(() => { if (!img) return; - img().then(setImage); + if (typeof img === 'string') { + setImage(img); + } else { + img().then(setImage); + } }, [img]); // Revoke image when component unmounts diff --git a/client/src/social/stacks/stream-list-item.tsx b/client/src/social/stacks/stream-list-item.tsx index 73a7e334..eda88452 100644 --- a/client/src/social/stacks/stream-list-item.tsx +++ b/client/src/social/stacks/stream-list-item.tsx @@ -2,7 +2,6 @@ import {useRef, useEffect, useState} from 'react'; import {useStyles, createStyleSheet} from 'react-native-unistyles'; import {View, Text} from 'react-native'; import {Link} from 'react-exo/navigation'; -import {isTouch} from 'app/utils/platform'; import type {StreamMediaProps} from 'social/stacks/stream-media'; @@ -63,7 +62,7 @@ const stylesheet = createStyleSheet((theme) => ({ lineHeight: theme.font.height, letterSpacing: theme.font.spacing, color: theme.colors.foreground, - ...isTouch() && { + ...__TOUCH__ && { fontSize: theme.font.contentSize, letterSpacing: theme.font.contentSpacing, lineHeight: 32, diff --git a/translations/ar.po b/translations/ar.po index 12ff653c..7e512ce5 100644 --- a/translations/ar.po +++ b/translations/ar.po @@ -142,11 +142,11 @@ msgstr "وضع المطور" msgid "Device Space" msgstr "مساحة الجهاز" -#: ../../client/src/media/stacks/list/empty.tsx:28 +#: ../../client/src/media/stacks/list/empty.tsx:25 msgid "Directory empty. Drop items or select folder to import." msgstr "المجلد فارغ. اسحب العناصر أو حدد مجلداً للاستيراد." -#: ../../client/src/media/stacks/list/empty.tsx:27 +#: ../../client/src/media/stacks/list/empty.tsx:24 msgid "Directory empty. Select folder to import." msgstr "المجلد فارغ. حدد مجلداً للاستيراد." @@ -206,7 +206,7 @@ msgstr "فهرنهايت" msgid "Feature in development..." msgstr "الميزة قيد التطوير..." -#: ../../client/src/media/hooks/use-media-name.ts:25 +#: ../../client/src/media/stacks/list/bar.tsx:52 msgid "Files" msgstr "الملفات" @@ -258,7 +258,7 @@ msgstr "إنسان" msgid "Import Files" msgstr "استيراد الملفات" -#: ../../client/src/media/stacks/list/empty.tsx:29 +#: ../../client/src/media/stacks/list/empty.tsx:26 msgid "Import Folder" msgstr "استيراد مجلد" diff --git a/translations/en.po b/translations/en.po index 38915c44..04e31aa1 100644 --- a/translations/en.po +++ b/translations/en.po @@ -142,11 +142,11 @@ msgstr "Dev Mode" msgid "Device Space" msgstr "Device Space" -#: ../../client/src/media/stacks/list/empty.tsx:28 +#: ../../client/src/media/stacks/list/empty.tsx:25 msgid "Directory empty. Drop items or select folder to import." msgstr "Directory empty. Drop items or select folder to import." -#: ../../client/src/media/stacks/list/empty.tsx:27 +#: ../../client/src/media/stacks/list/empty.tsx:24 msgid "Directory empty. Select folder to import." msgstr "Directory empty. Select folder to import." @@ -206,7 +206,7 @@ msgstr "Fahrenheit" msgid "Feature in development..." msgstr "Feature in development..." -#: ../../client/src/media/hooks/use-media-name.ts:25 +#: ../../client/src/media/stacks/list/bar.tsx:52 msgid "Files" msgstr "Files" @@ -258,7 +258,7 @@ msgstr "Human" msgid "Import Files" msgstr "Import Files" -#: ../../client/src/media/stacks/list/empty.tsx:29 +#: ../../client/src/media/stacks/list/empty.tsx:26 msgid "Import Folder" msgstr "Import Folder" diff --git a/translations/es.po b/translations/es.po index c9ff356b..7dc53346 100644 --- a/translations/es.po +++ b/translations/es.po @@ -142,11 +142,11 @@ msgstr "Modo Desarrollador" msgid "Device Space" msgstr "Espacio del Dispositivo" -#: ../../client/src/media/stacks/list/empty.tsx:28 +#: ../../client/src/media/stacks/list/empty.tsx:25 msgid "Directory empty. Drop items or select folder to import." msgstr "Directorio vacío. Suelta elementos o selecciona carpeta para importar." -#: ../../client/src/media/stacks/list/empty.tsx:27 +#: ../../client/src/media/stacks/list/empty.tsx:24 msgid "Directory empty. Select folder to import." msgstr "Directorio vacío. Selecciona carpeta para importar." @@ -206,7 +206,7 @@ msgstr "Fahrenheit" msgid "Feature in development..." msgstr "Función en desarrollo..." -#: ../../client/src/media/hooks/use-media-name.ts:25 +#: ../../client/src/media/stacks/list/bar.tsx:52 msgid "Files" msgstr "Archivos" @@ -258,7 +258,7 @@ msgstr "Humano" msgid "Import Files" msgstr "Importar Archivos" -#: ../../client/src/media/stacks/list/empty.tsx:29 +#: ../../client/src/media/stacks/list/empty.tsx:26 msgid "Import Folder" msgstr "Importar Carpeta" diff --git a/translations/ja.po b/translations/ja.po index fa511f9f..d72abd77 100644 --- a/translations/ja.po +++ b/translations/ja.po @@ -142,11 +142,11 @@ msgstr "開発モード" msgid "Device Space" msgstr "デバイス容量" -#: ../../client/src/media/stacks/list/empty.tsx:28 +#: ../../client/src/media/stacks/list/empty.tsx:25 msgid "Directory empty. Drop items or select folder to import." msgstr "ディレクトリが空です。アイテムをドロップするかフォルダを選択してインポートしてください。" -#: ../../client/src/media/stacks/list/empty.tsx:27 +#: ../../client/src/media/stacks/list/empty.tsx:24 msgid "Directory empty. Select folder to import." msgstr "ディレクトリが空です。インポートするフォルダを選択してください。" @@ -206,7 +206,7 @@ msgstr "華氏" msgid "Feature in development..." msgstr "開発中の機能..." -#: ../../client/src/media/hooks/use-media-name.ts:25 +#: ../../client/src/media/stacks/list/bar.tsx:52 msgid "Files" msgstr "ファイル" @@ -258,7 +258,7 @@ msgstr "ユーザー" msgid "Import Files" msgstr "ファイルをインポート" -#: ../../client/src/media/stacks/list/empty.tsx:29 +#: ../../client/src/media/stacks/list/empty.tsx:26 msgid "Import Folder" msgstr "フォルダをインポート" diff --git a/translations/ko.po b/translations/ko.po index e13fd685..143f445e 100644 --- a/translations/ko.po +++ b/translations/ko.po @@ -142,11 +142,11 @@ msgstr "개발자 모드" msgid "Device Space" msgstr "기기 저장공간" -#: ../../client/src/media/stacks/list/empty.tsx:28 +#: ../../client/src/media/stacks/list/empty.tsx:25 msgid "Directory empty. Drop items or select folder to import." msgstr "빈 디렉토리. 항목을 끌어다 놓거나 가져올 폴더를 선택하세요." -#: ../../client/src/media/stacks/list/empty.tsx:27 +#: ../../client/src/media/stacks/list/empty.tsx:24 msgid "Directory empty. Select folder to import." msgstr "빈 디렉토리. 가져올 폴더를 선택하세요." @@ -206,7 +206,7 @@ msgstr "화씨" msgid "Feature in development..." msgstr "개발 중인 기능..." -#: ../../client/src/media/hooks/use-media-name.ts:25 +#: ../../client/src/media/stacks/list/bar.tsx:52 msgid "Files" msgstr "파일" @@ -258,7 +258,7 @@ msgstr "사용자" msgid "Import Files" msgstr "파일 가져오기" -#: ../../client/src/media/stacks/list/empty.tsx:29 +#: ../../client/src/media/stacks/list/empty.tsx:26 msgid "Import Folder" msgstr "폴더 가져오기" diff --git a/translations/ru.po b/translations/ru.po index 5aa0ce29..0749e5a7 100644 --- a/translations/ru.po +++ b/translations/ru.po @@ -142,11 +142,11 @@ msgstr "Режим dev" msgid "Device Space" msgstr "Место на устройстве" -#: ../../client/src/media/stacks/list/empty.tsx:28 +#: ../../client/src/media/stacks/list/empty.tsx:25 msgid "Directory empty. Drop items or select folder to import." msgstr "Каталог пуст. Перетащите элементы или выберите папку для импорта." -#: ../../client/src/media/stacks/list/empty.tsx:27 +#: ../../client/src/media/stacks/list/empty.tsx:24 msgid "Directory empty. Select folder to import." msgstr "Каталог пуст. Выберите папку для импорта." @@ -206,7 +206,7 @@ msgstr "Фаренгейт" msgid "Feature in development..." msgstr "Функция в разработке..." -#: ../../client/src/media/hooks/use-media-name.ts:25 +#: ../../client/src/media/stacks/list/bar.tsx:52 msgid "Files" msgstr "Файлы" @@ -258,7 +258,7 @@ msgstr "Человек" msgid "Import Files" msgstr "Импорт файлов" -#: ../../client/src/media/stacks/list/empty.tsx:29 +#: ../../client/src/media/stacks/list/empty.tsx:26 msgid "Import Folder" msgstr "Импорт папки"