add link functionality to create public links (closes #213)
This commit is contained in:
@@ -113,6 +113,37 @@ export const SUPPORTS_ABOUT = [
|
||||
'local',
|
||||
] as const
|
||||
|
||||
export const SUPPORTS_LINK = [
|
||||
'b2',
|
||||
'box',
|
||||
'drive',
|
||||
'dropbox',
|
||||
'fichier',
|
||||
'filescom',
|
||||
'gofile',
|
||||
'imagekit',
|
||||
'internetarchive',
|
||||
'jottacloud',
|
||||
'koofr',
|
||||
'mailru',
|
||||
'mega',
|
||||
'onedrive',
|
||||
'pikpak',
|
||||
'pixeldrain',
|
||||
'premiumizeme',
|
||||
'pcloud',
|
||||
's3',
|
||||
'seafile',
|
||||
'storj',
|
||||
'sugarsync',
|
||||
'yandex',
|
||||
] as const
|
||||
|
||||
export function supportsPublicLink(backendType?: string | null): boolean {
|
||||
if (!backendType) return false
|
||||
return SUPPORTS_LINK.includes(backendType.toLowerCase() as any)
|
||||
}
|
||||
|
||||
export const CANNOT_PERSIST_EMPTY_FOLDERS = [
|
||||
's3',
|
||||
'gcs',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button, Checkbox, Listbox, ListboxItem, cn } from '@heroui/react'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import { DownloadIcon, EyeIcon, PencilIcon, StarIcon, Trash2Icon } from 'lucide-react'
|
||||
import { DownloadIcon, EyeIcon, PencilIcon, Share2Icon, StarIcon, Trash2Icon } from 'lucide-react'
|
||||
import { useCallback } from 'react'
|
||||
import { formatBytes } from '../../../lib/format.ts'
|
||||
import FileIcon from './FileIcon'
|
||||
@@ -24,6 +24,7 @@ export default function FileList({
|
||||
favoritedKeys,
|
||||
onToggleFavorite,
|
||||
onDownload,
|
||||
onShare,
|
||||
onRename,
|
||||
onDelete,
|
||||
listHeight,
|
||||
@@ -44,6 +45,7 @@ export default function FileList({
|
||||
favoritedKeys?: Record<string, boolean>
|
||||
onToggleFavorite?: (entry: Entry, isFavorited: boolean) => void
|
||||
onDownload?: (entry: Entry) => void
|
||||
onShare?: (entry: Entry) => void
|
||||
onRename?: (entry: Entry) => void
|
||||
onDelete?: (entry: Entry) => void
|
||||
listHeight: number
|
||||
@@ -162,7 +164,7 @@ export default function FileList({
|
||||
}
|
||||
|
||||
const gridCols = showPreviewColumn
|
||||
? 'grid-cols-[2.5rem_1fr_6rem_9rem_9rem]'
|
||||
? 'grid-cols-[2.5rem_1fr_6rem_9rem_11rem]'
|
||||
: 'grid-cols-[2.5rem_1fr_6rem_9rem_2.5rem]'
|
||||
|
||||
return (
|
||||
@@ -298,6 +300,17 @@ export default function FileList({
|
||||
<DownloadIcon className="size-5" />
|
||||
</Button>
|
||||
)}
|
||||
{onShare && (
|
||||
<Button
|
||||
isIconOnly={true}
|
||||
size="sm"
|
||||
variant="light"
|
||||
className="transition-opacity duration-300 opacity-0 group-hover:opacity-100"
|
||||
onPress={() => onShare(entry)}
|
||||
>
|
||||
<Share2Icon className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
{onRename && (
|
||||
<Button
|
||||
isIconOnly={true}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Button, Divider, Tooltip } from '@heroui/react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { FolderPlusIcon } from 'lucide-react'
|
||||
import {
|
||||
forwardRef,
|
||||
@@ -10,6 +11,8 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { supportsPublicLink } from '../../../lib/rclone/constants'
|
||||
import rclone from '../../../lib/rclone/client'
|
||||
import { useHostStore } from '../../../store/host.ts'
|
||||
import FileList from './FileList'
|
||||
import PanelToolbar, { type ToolbarButtons } from './PanelToolbar'
|
||||
@@ -39,6 +42,7 @@ const FilePanel = forwardRef<
|
||||
showPreviewColumn?: boolean
|
||||
onPreviewRequest?: (item: Entry) => void
|
||||
onDownload?: (item: Entry) => void
|
||||
onShare?: (item: Entry) => void
|
||||
onRename?: (item: Entry) => void
|
||||
onDelete?: (item: Entry) => void
|
||||
contextMenuItems?: ContextMenuItem[]
|
||||
@@ -62,6 +66,7 @@ const FilePanel = forwardRef<
|
||||
showPreviewColumn = true,
|
||||
onPreviewRequest,
|
||||
onDownload,
|
||||
onShare,
|
||||
onRename,
|
||||
onDelete,
|
||||
contextMenuItems,
|
||||
@@ -84,6 +89,18 @@ const FilePanel = forwardRef<
|
||||
isActive,
|
||||
})
|
||||
|
||||
const remoteConfigQuery = useQuery({
|
||||
queryKey: ['remote', nav.selectedRemote, 'config'],
|
||||
queryFn: async () => {
|
||||
return await rclone('/config/get', {
|
||||
params: { query: { name: nav.selectedRemote! } },
|
||||
})
|
||||
},
|
||||
enabled: nav.isRemote,
|
||||
})
|
||||
|
||||
const canShare = supportsPublicLink(remoteConfigQuery.data?.type)
|
||||
|
||||
const { canCreateFolder, createFolder } = useCreateFolder(
|
||||
nav.selectedRemote,
|
||||
nav.cwd,
|
||||
@@ -338,7 +355,7 @@ const FilePanel = forwardRef<
|
||||
|
||||
<div className="relative flex flex-col w-full h-full overflow-hidden">
|
||||
<div
|
||||
className={`sticky top-0 z-10 grid ${showPreviewColumn ? 'grid-cols-[2.5rem_1fr_6rem_9rem_9rem]' : 'grid-cols-[2.5rem_1fr_6rem_9rem_2.5rem]'} items-center py-2 bg-default-100`}
|
||||
className={`sticky top-0 z-10 grid ${showPreviewColumn ? 'grid-cols-[2.5rem_1fr_6rem_9rem_11rem]' : 'grid-cols-[2.5rem_1fr_6rem_9rem_2.5rem]'} items-center py-2 bg-default-100`}
|
||||
>
|
||||
<div />
|
||||
<div className="pl-2 font-semibold text-small">Name</div>
|
||||
@@ -360,6 +377,7 @@ const FilePanel = forwardRef<
|
||||
showPreviewColumn={showPreviewColumn}
|
||||
onPreviewClick={handlePreviewClick}
|
||||
onDownload={onDownload}
|
||||
onShare={canShare ? onShare : undefined}
|
||||
onRename={onRename}
|
||||
onDelete={onDelete}
|
||||
draggable={selectionMode === 'drag' || selectionMode === 'both'}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from '@heroui/react'
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { writeText } from '@tauri-apps/plugin-clipboard-manager'
|
||||
import { ask, message, save } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
@@ -37,6 +38,7 @@ import { Group, Panel, Separator } from 'react-resizable-panels'
|
||||
import { getFsInfo } from '../../lib/format'
|
||||
// import { Document, Page, pdfjs } from 'react-pdf'
|
||||
import { formatBytes } from '../../lib/format.ts'
|
||||
import notify from '../../lib/notify'
|
||||
import { startCopy, startMove } from '../../lib/rclone/api'
|
||||
import rclone from '../../lib/rclone/client'
|
||||
import { openWindow } from '../../lib/window'
|
||||
@@ -214,6 +216,36 @@ export default function Browser() {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleShare = useCallback(async (entry: Entry) => {
|
||||
try {
|
||||
const { root, filePath } = getFsInfo(entry.fullPath)
|
||||
const result = await rclone('/operations/publiclink', {
|
||||
params: {
|
||||
query: {
|
||||
fs: root,
|
||||
remote: filePath,
|
||||
},
|
||||
},
|
||||
})
|
||||
if (result?.url) {
|
||||
await writeText(result.url)
|
||||
await notify({
|
||||
title: 'Link Copied',
|
||||
body: `Public link for "${entry.name}" copied to clipboard`,
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
await message(
|
||||
error instanceof Error ? error.message : 'Failed to generate public link',
|
||||
{
|
||||
title: 'Share Error',
|
||||
kind: 'error',
|
||||
okLabel: 'OK',
|
||||
}
|
||||
)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'r' && (e.metaKey || e.ctrlKey)) {
|
||||
@@ -249,6 +281,7 @@ export default function Browser() {
|
||||
showPreviewColumn={true}
|
||||
onDrop={(items, dest) => handleDrop(items, dest, 'left')}
|
||||
onDownload={handleDownload}
|
||||
onShare={handleShare}
|
||||
onRename={handleRename}
|
||||
onDelete={handleDelete}
|
||||
allowedKeys={['REMOTES', 'LOCAL_FS', 'FAVORITES']}
|
||||
@@ -269,6 +302,7 @@ export default function Browser() {
|
||||
showPreviewColumn={true}
|
||||
onDrop={(items, dest) => handleDrop(items, dest, 'right')}
|
||||
onDownload={handleDownload}
|
||||
onShare={handleShare}
|
||||
onRename={handleRename}
|
||||
onDelete={handleDelete}
|
||||
allowedKeys={['REMOTES', 'LOCAL_FS', 'FAVORITES']}
|
||||
|
||||
Reference in New Issue
Block a user