@@ -2,8 +2,7 @@ import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator,
22import { Input } from "@/components/ui/input" ;
33import useArticleStore , { DirTree } from "@/stores/article" ;
44import { BaseDirectory , exists , readTextFile , remove , rename , writeTextFile } from "@tauri-apps/plugin-fs" ;
5- import { appDataDir } from '@tauri-apps/api/path' ;
6- import { Cloud , CloudDownload , File } from "lucide-react"
5+ import { Cloud , CloudDownload , File , ImageIcon } from "lucide-react"
76import { useEffect , useRef , useState } from "react" ;
87import { ask } from '@tauri-apps/plugin-dialog' ;
98import { Store } from '@tauri-apps/plugin-store' ;
@@ -14,6 +13,9 @@ import { computedParentPath, getCurrentFolder } from "@/lib/path";
1413import { toast } from "@/hooks/use-toast" ;
1514import { useTranslations } from "next-intl" ;
1615import useClipboardStore from "@/stores/clipboard" ;
16+ import { PhotoProvider , PhotoView } from "react-photo-view" ;
17+ import { convertImageByWorkspace } from "@/lib/utils" ;
18+ import { appDataDir } from '@tauri-apps/api/path' ;
1719
1820export function FileItem ( { item } : { item : DirTree } ) {
1921 const [ isEditing , setIsEditing ] = useState ( item . isEditing )
@@ -22,16 +24,28 @@ export function FileItem({ item }: { item: DirTree }) {
2224 const { activeFilePath, setActiveFilePath, readArticle, setCurrentArticle, fileTree, setFileTree, loadFileTree } = useArticleStore ( )
2325 const { setClipboardItem, clipboardItem, clipboardOperation } = useClipboardStore ( )
2426 const t = useTranslations ( 'article.file' )
27+ const [ imageUrl , setImageUrl ] = useState ( '' )
2528
2629 const path = computedParentPath ( item )
2730 const isRoot = path . split ( '/' ) . length === 1
2831 const folderPath = path . includes ( '/' ) ? path . split ( '/' ) . slice ( 0 , - 1 ) . join ( '/' ) : ''
2932 const cacheTree = cloneDeep ( fileTree )
3033 const currentFolder = getCurrentFolder ( folderPath , cacheTree )
3134
32- function handleSelectFile ( ) {
33- setActiveFilePath ( computedParentPath ( item ) )
34- readArticle ( computedParentPath ( item ) , item . sha , item . isLocale )
35+ async function handleSelectFile ( ) {
36+ if ( item . name . match ( / \. ( j p g | j p e g | p n g | g i f | b m p | w e b p | s v g ) $ / i) ) {
37+ let path = ''
38+ if ( item . isLocale ) {
39+ path = computedParentPath ( item )
40+ } else {
41+ path = activeFilePath
42+ }
43+ const url = await convertImageByWorkspace ( path )
44+ setImageUrl ( url )
45+ } else {
46+ setActiveFilePath ( computedParentPath ( item ) )
47+ readArticle ( computedParentPath ( item ) , item . sha , item . isLocale )
48+ }
3549 }
3650
3751 async function handleDeleteFile ( ) {
@@ -381,72 +395,96 @@ export function FileItem({ item }: { item: DirTree }) {
381395 } , [ item ] )
382396
383397 return (
384- < ContextMenu >
385- < ContextMenuTrigger >
386- < div
387- className = { `${ path === activeFilePath ? 'file-manange-item active' : 'file-manange-item' } ${ ! isRoot && 'translate-x-5 !w-[calc(100%-22px)]' } ` }
388- onClick = { handleSelectFile }
389- onContextMenu = { handleSelectFile }
390- >
391- {
392- isEditing ?
393- < div className = "flex gap-1 items-center w-full select-none" >
394- < span className = { item . parent ? 'size-0' : 'size-4 ml-1' } />
395- < File className = "size-4" />
396- < Input
397- ref = { inputRef }
398- className = "h-5 rounded-sm text-xs px-1 font-normal flex-1 mr-1"
399- value = { name }
400- onBlur = { handleRename }
401- onChange = { ( e ) => { setName ( e . target . value ) } }
402- onKeyDown = { ( e ) => {
403- if ( e . code === 'Enter' && ! e . nativeEvent . isComposing ) {
404- handleRename ( )
405- } else if ( e . code === 'Escape' ) {
406- handleEditEnd ( )
407- }
408- } }
409- />
410- </ div > :
411- < span draggable onDragStart = { handleDragStart }
412- className = { `${ item . isLocale ? '' : 'opacity-50' } flex justify-between flex-1 select-none items-center gap-1 dark:hover:text-white` } >
413- < div className = "flex flex-1 gap-1 select-none relative" >
414- < span className = { item . parent ? 'size-0' : 'size-4 ml-1' } > </ span >
415- < div className = "relative" >
416- { item . isLocale ? < File className = "size-4" /> : < CloudDownload className = "size-4" /> }
417- { item . sha && item . isLocale && < Cloud className = "size-2.5 absolute left-0 bottom-0 z-10 bg-primary-foreground" /> }
418- </ div >
419- < span className = "text-xs flex-1 line-clamp-1" > { item . name } </ span >
420- </ div >
421- </ span >
422- }
423- </ div >
424- </ ContextMenuTrigger >
425- < ContextMenuContent >
426- < ContextMenuItem inset onClick = { handleShowFileManager } >
427- { t ( 'context.viewDirectory' ) }
428- </ ContextMenuItem >
429- < ContextMenuSeparator />
430- < ContextMenuItem inset disabled = { ! item . isLocale } onClick = { handleCutFile } >
431- { t ( 'context.cut' ) }
432- </ ContextMenuItem >
433- < ContextMenuItem inset onClick = { handleCopyFile } >
434- { t ( 'context.copy' ) }
435- </ ContextMenuItem >
436- < ContextMenuItem inset disabled = { ! clipboardItem } onClick = { handlePasteFile } >
437- { t ( 'context.paste' ) }
438- </ ContextMenuItem >
439- < ContextMenuSeparator />
440- < ContextMenuItem disabled = { ! item . isLocale } inset onClick = { handleStartRename } >
441- { t ( 'context.rename' ) }
442- </ ContextMenuItem >
443- < ContextMenuItem disabled = { ! item . sha } inset className = "text-red-900" onClick = { handleDeleteSyncFile } >
444- { t ( 'context.deleteSyncFile' ) }
445- </ ContextMenuItem >
446- < ContextMenuItem disabled = { ! item . isLocale } inset className = "text-red-900" onClick = { handleDeleteFile } >
447- { t ( 'context.deleteLocalFile' ) }
448- </ ContextMenuItem >
449- </ ContextMenuContent >
450- </ ContextMenu >
398+ < >
399+ < ContextMenu >
400+ < ContextMenuTrigger >
401+ < div
402+ className = { `${ path === activeFilePath ? 'file-manange-item active' : 'file-manange-item' } ${ ! isRoot && 'translate-x-5 !w-[calc(100%-22px)]' } ` }
403+ onClick = { handleSelectFile }
404+ onContextMenu = { handleSelectFile }
405+ >
406+ {
407+ isEditing ?
408+ < div className = "flex gap-1 items-center w-full select-none" >
409+ < span className = { item . parent ? 'size-0' : 'size-4 ml-1' } />
410+ < File className = "size-4" />
411+ < Input
412+ ref = { inputRef }
413+ className = "h-5 rounded-sm text-xs px-1 font-normal flex-1 mr-1"
414+ value = { name }
415+ onBlur = { handleRename }
416+ onChange = { ( e ) => { setName ( e . target . value ) } }
417+ onKeyDown = { ( e ) => {
418+ if ( e . code === 'Enter' && ! e . nativeEvent . isComposing ) {
419+ handleRename ( )
420+ } else if ( e . code === 'Escape' ) {
421+ handleEditEnd ( )
422+ }
423+ } }
424+ />
425+ </ div > :
426+ item . name . match ( / \. ( j p g | j p e g | p n g | g i f | b m p | w e b p | s v g ) $ / i) ?
427+ < PhotoProvider >
428+ < PhotoView src = { imageUrl } >
429+ < span
430+ draggable
431+ onDragStart = { handleDragStart }
432+ title = { item . name }
433+ className = { `${ item . isLocale ? '' : 'opacity-50' } flex justify-between flex-1 select-none items-center gap-1 dark:hover:text-white` } >
434+ < div className = "flex flex-1 gap-1 select-none relative" >
435+ < span className = { item . parent ? 'size-0' : 'size-4 ml-1' } > </ span >
436+ < div className = "relative" >
437+ < ImageIcon className = "size-4" />
438+ { item . sha && item . isLocale && < Cloud className = "size-2.5 absolute left-0 bottom-0 z-10 bg-primary-foreground" /> }
439+ </ div >
440+ < span className = "text-xs flex-1 line-clamp-1" > { item . name } </ span >
441+ </ div >
442+ </ span >
443+ </ PhotoView >
444+ </ PhotoProvider > :
445+ < span
446+ draggable
447+ onDragStart = { handleDragStart }
448+ title = { item . name }
449+ className = { `${ item . isLocale ? '' : 'opacity-50' } flex justify-between flex-1 select-none items-center gap-1 dark:hover:text-white` } >
450+ < div className = "flex flex-1 gap-1 select-none relative" >
451+ < span className = { item . parent ? 'size-0' : 'size-4 ml-1' } > </ span >
452+ < div className = "relative" >
453+ { item . isLocale ? < File className = "size-4" /> : < CloudDownload className = "size-4" /> }
454+ { item . sha && item . isLocale && < Cloud className = "size-2.5 absolute left-0 bottom-0 z-10 bg-primary-foreground" /> }
455+ </ div >
456+ < span className = "text-xs flex-1 line-clamp-1" > { item . name } </ span >
457+ </ div >
458+ </ span >
459+ }
460+ </ div >
461+ </ ContextMenuTrigger >
462+ < ContextMenuContent >
463+ < ContextMenuItem inset onClick = { handleShowFileManager } >
464+ { t ( 'context.viewDirectory' ) }
465+ </ ContextMenuItem >
466+ < ContextMenuSeparator />
467+ < ContextMenuItem inset disabled = { ! item . isLocale } onClick = { handleCutFile } >
468+ { t ( 'context.cut' ) }
469+ </ ContextMenuItem >
470+ < ContextMenuItem inset onClick = { handleCopyFile } >
471+ { t ( 'context.copy' ) }
472+ </ ContextMenuItem >
473+ < ContextMenuItem inset disabled = { ! clipboardItem } onClick = { handlePasteFile } >
474+ { t ( 'context.paste' ) }
475+ </ ContextMenuItem >
476+ < ContextMenuSeparator />
477+ < ContextMenuItem disabled = { ! item . isLocale } inset onClick = { handleStartRename } >
478+ { t ( 'context.rename' ) }
479+ </ ContextMenuItem >
480+ < ContextMenuItem disabled = { ! item . sha } inset className = "text-red-900" onClick = { handleDeleteSyncFile } >
481+ { t ( 'context.deleteSyncFile' ) }
482+ </ ContextMenuItem >
483+ < ContextMenuItem disabled = { ! item . isLocale } inset className = "text-red-900" onClick = { handleDeleteFile } >
484+ { t ( 'context.deleteLocalFile' ) }
485+ </ ContextMenuItem >
486+ </ ContextMenuContent >
487+ </ ContextMenu >
488+ </ >
451489 )
452490}
0 commit comments