From 20eae57e7530472186804b760f7da8cd2fd666c4 Mon Sep 17 00:00:00 2001 From: Utku <74243531+utkubakir@users.noreply.github.com> Date: Mon, 17 Jul 2023 14:47:57 +0300 Subject: [PATCH] [MOB-34] Update thumb logic (#1100) update thumb logic --- .../src/components/explorer/FileThumb.tsx | 191 +++++++++++------- .../settings/client/GeneralSettings.tsx | 16 +- apps/mobile/src/stores/explorerStore.ts | 2 +- packages/client/src/hooks/useThemeStore.ts | 4 + 4 files changed, 137 insertions(+), 76 deletions(-) diff --git a/apps/mobile/src/components/explorer/FileThumb.tsx b/apps/mobile/src/components/explorer/FileThumb.tsx index 0a5845e79..3a6b92a17 100644 --- a/apps/mobile/src/components/explorer/FileThumb.tsx +++ b/apps/mobile/src/components/explorer/FileThumb.tsx @@ -1,10 +1,53 @@ -import * as icons from '@sd/assets/icons'; -import { PropsWithChildren } from 'react'; +import { getIcon } from '@sd/assets/util'; +import { PropsWithChildren, useEffect, useLayoutEffect, useMemo, useState } from 'react'; import { Image, View } from 'react-native'; import { DocumentDirectoryPath } from 'react-native-fs'; -import { ExplorerItem, ObjectKind, getItemFilePath, getItemObject, isPath } from '@sd/client'; +import { + ExplorerItem, + getExplorerItemData, + getItemFilePath, + getItemLocation, + isDarkTheme +} from '@sd/client'; +import { flattenThumbnailKey, useExplorerStore } from '~/stores/explorerStore'; import { tw } from '../../lib/tailwind'; -import FolderIcon from '../icons/FolderIcon'; + +export const getThumbnailUrlByThumbKey = (thumbKey: string[]) => + `${DocumentDirectoryPath}/thumbnails/${thumbKey + .map((i) => encodeURIComponent(i)) + .join('/')}.webp`; + +const FileThumbWrapper = ({ children, size = 1 }: PropsWithChildren<{ size: number }>) => ( + + {children} + +); + +function useExplorerItemData(explorerItem: ExplorerItem) { + const explorerStore = useExplorerStore(); + + const newThumbnail = !!( + explorerItem.thumbnail_key && + explorerStore.newThumbnails.has(flattenThumbnailKey(explorerItem.thumbnail_key)) + ); + + return useMemo(() => { + const itemData = getExplorerItemData(explorerItem); + + if (!itemData.hasLocalThumbnail) { + itemData.hasLocalThumbnail = newThumbnail; + } + + return itemData; + }, [explorerItem, newThumbnail]); +} + +enum ThumbType { + Icon, + // Original, + Thumbnail, + Location +} type FileThumbProps = { data: ExplorerItem; @@ -13,87 +56,87 @@ type FileThumbProps = { * default: `1` */ size?: number; + // loadOriginal?: boolean; }; -export const getThumbnailUrlById = (keyParts: string[]) => - `${DocumentDirectoryPath}/thumbnails/${keyParts - .map((i) => encodeURIComponent(i)) - .join('/')}.webp`; +export default function FileThumb({ size = 1, ...props }: FileThumbProps) { + const itemData = useExplorerItemData(props.data); + const locationData = getItemLocation(props.data); + const filePath = getItemFilePath(props.data); -type KindType = keyof typeof icons | 'Unknown'; + const [src, setSrc] = useState(null); + const [thumbType, setThumbType] = useState(ThumbType.Icon); + // const [loaded, setLoaded] = useState(false); -function getExplorerItemData(data: ExplorerItem) { - const objectData = getItemObject(data); - const filePath = getItemFilePath(data); + useLayoutEffect(() => { + // Reset src when item changes, to allow detection of yet not updated src + setSrc(null); + // setLoaded(false); - return { - casId: filePath?.cas_id || null, - isDir: isPath(data) && data.item.is_dir, - kind: ObjectKind[objectData?.kind || 0] as KindType, - hasLocalThumbnail: data.has_local_thumbnail, // this will be overwritten if new thumbnail is generated - thumbnailKey: data.thumbnail_key, - extension: filePath?.extension - }; -} + if (locationData) { + setThumbType(ThumbType.Location); + // } else if (props.loadOriginal) { + // setThumbType(ThumbType.Original); + } else if (itemData.hasLocalThumbnail) { + setThumbType(ThumbType.Thumbnail); + } else { + setThumbType(ThumbType.Icon); + } + }, [locationData, itemData]); -const FileThumbWrapper = ({ children, size = 1 }: PropsWithChildren<{ size: number }>) => ( - - {children} - -); + // This sets the src to the thumbnail url + useEffect(() => { + const { casId, kind, isDir, extension, locationId, thumbnailKey } = itemData; -export default function FileThumb({ data, size = 1 }: FileThumbProps) { - const { casId, isDir, kind, hasLocalThumbnail, extension, thumbnailKey } = - getExplorerItemData(data); + // ??? + // const locationId = + // itemLocationId ?? (parent?.type === 'Location' ? parent.location.id : null); - if (isPath(data) && data.item.is_dir) { - return ( - - - - ); - } - - if (hasLocalThumbnail && thumbnailKey) { - // TODO: Handle Image checkers bg? - return ( - - - - ); - } - - // Default icon - let icon = icons['Document']; - - if (isDir) { - icon = icons['Folder']; - } else if ( - kind && - extension && - icons[`${kind}_${extension.toLowerCase()}` as keyof typeof icons] - ) { - // e.g. Document_pdf - icon = icons[`${kind}_${extension.toLowerCase()}` as keyof typeof icons]; - } else if (kind !== 'Unknown' && kind && icons[kind]) { - icon = icons[kind]; - } - - // TODO: Handle video thumbnails (do we have ffmpeg on mobile?) - - // // 10 percent of the size - // const videoBarsHeight = Math.floor(size / 10); - - // // calculate 16:9 ratio for height from size - // const videoHeight = Math.floor((size * 9) / 16) + videoBarsHeight * 2; + switch (thumbType) { + // case ThumbType.Original: + // if (locationId) { + // setSrc( + // platform.getFileUrl( + // library.uuid, + // locationId, + // filePath?.id || props.data.item.id, + // // Workaround Linux webview not supporting playing video and audio through custom protocol urls + // kind == 'Video' || kind == 'Audio' + // ) + // ); + // } else { + // setThumbType(ThumbType.Thumbnail); + // } + // break; + case ThumbType.Thumbnail: + if (casId && thumbnailKey) { + setSrc(getThumbnailUrlByThumbKey(thumbnailKey)); + } else { + setThumbType(ThumbType.Icon); + } + break; + case ThumbType.Location: + setSrc(getIcon('Folder', isDarkTheme(), extension, true)); + break; + default: + if (isDir !== null) setSrc(getIcon(kind, isDarkTheme(), extension, isDir)); + break; + } + }, [filePath?.id, itemData, props.data.item.id, thumbType]); return ( - + {(() => { + if (src == null) return null; + let source = null; + // getIcon returns number for some magic reason + if (typeof src === 'number') { + source = src; + } else { + source = { uri: src }; + } + return ; + })()} ); } diff --git a/apps/mobile/src/screens/settings/client/GeneralSettings.tsx b/apps/mobile/src/screens/settings/client/GeneralSettings.tsx index 759d319d2..fe1cc95d8 100644 --- a/apps/mobile/src/screens/settings/client/GeneralSettings.tsx +++ b/apps/mobile/src/screens/settings/client/GeneralSettings.tsx @@ -1,5 +1,5 @@ import { Text, View } from 'react-native'; -import { useBridgeQuery } from '@sd/client'; +import { useBridgeQuery, useDebugState } from '@sd/client'; import { Input } from '~/components/form/Input'; import Card from '~/components/layout/Card'; import { Divider } from '~/components/primitive/Divider'; @@ -10,6 +10,8 @@ import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator'; const GeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'GeneralSettings'>) => { const { data: node } = useBridgeQuery(['nodeState']); + const debugState = useDebugState(); + if (!node) return null; return ( @@ -37,6 +39,18 @@ const GeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'General Node Port + {/* TODO: Move this to Debug screen */} + {debugState.enabled && ( + + {/* Card Header */} + Debug + {/* Divider */} + + Data Folder + {/* Useful for simulator, not so for real devices. */} + + + )} ); }; diff --git a/apps/mobile/src/stores/explorerStore.ts b/apps/mobile/src/stores/explorerStore.ts index 6e26ddfc1..e2ac60f63 100644 --- a/apps/mobile/src/stores/explorerStore.ts +++ b/apps/mobile/src/stores/explorerStore.ts @@ -17,7 +17,7 @@ const state = { newThumbnails: proxySet() as Set }; -function flattenThumbnailKey(thumbKey: string[]) { +export function flattenThumbnailKey(thumbKey: string[]) { return thumbKey.join('/'); } diff --git a/packages/client/src/hooks/useThemeStore.ts b/packages/client/src/hooks/useThemeStore.ts index 07ff71311..dc8fbabd4 100644 --- a/packages/client/src/hooks/useThemeStore.ts +++ b/packages/client/src/hooks/useThemeStore.ts @@ -16,3 +16,7 @@ export function useThemeStore() { export function getThemeStore() { return themeStore; } + +export function isDarkTheme() { + return themeStore.theme === 'dark'; +}