Skip to content

Commit

Permalink
feat(joplin-batch): 支持响应式
Browse files Browse the repository at this point in the history
  • Loading branch information
rxliuli committed Aug 8, 2024
1 parent 78f237e commit cbc6d7f
Show file tree
Hide file tree
Showing 26 changed files with 689 additions and 273 deletions.
Binary file added packages/joplin-batch/images/icon-128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 18 additions & 2 deletions packages/joplin-batch/jpl.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,25 @@ export default defineConfig({
author: 'rxliuli',
homepage_url: 'https://joplin-utils.rxliuli.com/en-US/joplin-batch-web/',
repository_url: 'https://github.com/rxliuli/joplin-utils/tree/master/packages/joplin-batch',
keywords: ['Batch'],
keywords: ['Batch', 'Find And Replace', 'Clear Unuse Resources'],
categories: ['productivity'],
icons: {},
screenshots: [
{
label: 'Clean Unused Resources',
src: 'https://github.com/rxliuli/joplin-utils/tree/master/packages/joplin-batch/images/screenshots/clean.png',
},
{
label: 'Find And Replace',
src: 'https://github.com/rxliuli/joplin-utils/tree/master/packages/joplin-batch/images/screenshots/replace.png',
},
{
label: 'Diff Viewer',
src: 'https://github.com/rxliuli/joplin-utils/tree/master/diff.png',
},
],
icons: {
'128': 'packages/joplin-publisher/images/icon-128.png',
},
vite: {
plugins: [react() as any],
resolve: {
Expand Down
2 changes: 2 additions & 0 deletions packages/joplin-batch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"scripts": {
"setup": "pnpm build",
"dev": "jpl",
"dev:webview": "jpl dev",
"build": "jpl build"
},
"license": "MIT",
Expand Down Expand Up @@ -42,6 +43,7 @@
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@types/react-dom": "^18.0.6",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
Expand Down
3 changes: 2 additions & 1 deletion packages/joplin-batch/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import joplin, { MenuItemLocation, ViewHandle } from 'joplin-plugin-api'
import { MenuItemLocation, ViewHandle, joplin } from 'jpl-vite/api'
import { IProtocol } from './message'
import { defineExtensionMessaging } from 'jpl-vite/messaging'
import { joplinDataApi } from 'joplin-api'
Expand Down Expand Up @@ -27,6 +27,7 @@ async function registerCommands() {
return
}
handler = await panels.create('Batch Utils')
await panels.setHtml(handler, `<meta name="platform" content="${(await joplin.versionInfo()).platform}"`)
await panels.addScript(handler, '/webview/index.js')
await panels.addScript(handler, '/webview/style.css')
const { onMessage } = defineExtensionMessaging<IProtocol>(handler)
Expand Down
1 change: 1 addition & 0 deletions packages/joplin-batch/src/message.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineExtensionMessaging } from 'jpl-vite/messaging'
import { VersionInfo } from 'jpl-vite/api'

export interface StorageValues {
selectedFontId: string
Expand Down
64 changes: 24 additions & 40 deletions packages/joplin-batch/src/webview/App.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { Button } from './components/ui/button'
import { Link, RouteConfig, RouterView, useLocation, useMatch } from '@liuli-util/react-router'
import { DemoView } from './views/DemoView'
import { CheckUnusedResourceView } from './views/UnusedResourceView'
import { Link, RouteConfig, RouterView, useLocation } from '@liuli-util/react-router'
import { CleanUnusedResourcesView } from './views/CleanUnusedResourcesView'
import { SettingsView } from './views/SettingsView'
import { Menu } from 'lucide-react'
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from './components/ui/sheet'
import { useDeepSignal } from 'deepsignal/react'
import { useEffect } from 'react'
import { ReplaceAllView } from './views/ReplaceAllView'
import { Separator } from './components/ui/separator'

function HomeView() {
return <div>home</div>
}
import { useEffect, useMemo } from 'react'
import { FindAndReplaceView } from './views/FindAndReplaceView'

type MenuConfig = RouteConfig & {
meta?: {
Expand All @@ -22,16 +16,16 @@ type MenuConfig = RouteConfig & {
export const routeList: MenuConfig[] = [
{
path: '/',
component: CheckUnusedResourceView,
component: CleanUnusedResourcesView,
meta: {
title: 'Unused Resources',
title: 'Clean Unused Resources',
},
},
{
path: '/replace-all',
component: ReplaceAllView,
path: '/find-and-replace',
component: FindAndReplaceView,
meta: {
title: 'Replace All',
title: 'Find and Replace',
},
},
{
Expand All @@ -41,26 +35,8 @@ export const routeList: MenuConfig[] = [
title: 'Settings',
},
},
// {
// path: '/demo',
// component: DemoView,
// meta: {
// title: 'Demo',
// },
// },
]

function MenuItem(route: Pick<MenuConfig, 'path' | 'meta'>) {
const loc = useLocation()
return (
<Link to={route.path} key={route.path}>
<Button variant={loc.pathname === route.path ? 'secondary' : 'ghost'} className="w-full justify-start">
{route.meta?.title}
</Button>
</Link>
)
}

function Sidebar() {
const loc = useLocation()
return (
Expand All @@ -85,16 +61,24 @@ export default function App() {
useEffect(() => {
isOpen.value = false
}, [useLocation()])
const loc = useLocation()
const title = useMemo(
() => routeList.find((it) => it.path === loc.pathname)?.meta?.title ?? 'Joplin Batch',
[loc.pathname],
)
return (
<div className="min-h-screen flex flex-col">
<div className="h-screen flex flex-col">
<header className="border-b flex justify-between items-center">
<div className="flex items-center">
<Sheet open={isOpen.value} onOpenChange={(value) => (isOpen.value = value)}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" onClick={() => (isOpen.value = !isOpen.value)}>
<Menu className={'h-6 w-6'} />
</Button>
</SheetTrigger>
<div className={'flex items-center'}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" onClick={() => (isOpen.value = !isOpen.value)}>
<Menu className={'h-6 w-6'} />
</Button>
</SheetTrigger>
<h2 className="ml-4">{title}</h2>
</div>
<SheetContent side={'left'}>
<SheetHeader>
<SheetTitle>
Expand All @@ -109,7 +93,7 @@ export default function App() {
</Sheet>
</div>
</header>
<div className="flex-grow">
<div className="flex-1 overflow-hidden flex-grow">
<div className="h-full px-4 py-6 lg:px-8">
<RouterView />
</div>
Expand Down
28 changes: 28 additions & 0 deletions packages/joplin-batch/src/webview/components/ui/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from 'react'
import * as TooltipPrimitive from '@radix-ui/react-tooltip'

import { cn } from '@/lib/utils'

const TooltipProvider = TooltipPrimitive.Provider

const Tooltip = TooltipPrimitive.Root

const TooltipTrigger = TooltipPrimitive.Trigger

const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className,
)}
{...props}
/>
))
TooltipContent.displayName = TooltipPrimitive.Content.displayName

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
1 change: 1 addition & 0 deletions packages/joplin-batch/src/webview/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="./assets/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="platform" content="desktop" />
<title>Joplin Batch</title>
</head>
<body>
Expand Down
7 changes: 7 additions & 0 deletions packages/joplin-batch/src/webview/lib/getPlatform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { VersionInfo } from 'jpl-vite/api'

export function getPlatform(): VersionInfo['platform'] {
return (
(document.querySelector('meta[name="platform"]')?.getAttribute('content') as VersionInfo['platform']) ?? 'desktop'
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { dataApi, getSettings } from '../lib/dataApi'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Link } from '@liuli-util/react-router'
import ZoomableImage from '@/components/3rd/ZoomableImage'
import { cn } from '@/lib/utils'
import { getPlatform } from '@/lib/getPlatform'

async function isUse(id: string): Promise<boolean> {
const res = await dataApi.search.search({
Expand All @@ -22,7 +24,7 @@ function buildResourceUrl(id: string): string {
return `${settings?.baseUrl}/resources/${id}/file?token=${settings?.token}`
}

export function CheckUnusedResourceView() {
export function CleanUnusedResourcesView() {
const list = useDeepSignal({
value: [] as Pick<ResourceProperties, 'id' | 'title' | 'mime'>[],
})
Expand Down Expand Up @@ -58,24 +60,28 @@ export function CheckUnusedResourceView() {
token?: string
}>('joplin-batch-settings')
return (
<div>
<div className={'h-full flex flex-col overflow-hidden'}>
<header className={'flex justify-end items-center gap-2 mb-2'}>
<Button onClick={doFetch} disabled={state.loading}>
{state.loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Check
</Button>
<Button disabled={list.value.length === 0 || deleteState.loading} variant={'destructive'} onClick={onDeleteAll}>
<Button
disabled={list.value.length === 0 || state.loading || deleteState.loading}
variant={'destructive'}
onClick={onDeleteAll}
>
{deleteState.loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Delete all
</Button>
</header>
<div>
{!(settings?.baseUrl && settings.token) && (
<div className={'flex-1 overflow-y-auto'}>
{!(settings?.baseUrl && settings.token) && getPlatform() === 'desktop' && (
<Alert className={'mb-2 text-yellow-600 border-yellow-600 [&>svg]:text-yellow-600'}>
<AlertCircle className="h-4 w-4" />
<AlertTitle>Warn</AlertTitle>
<AlertDescription>
<div>Please set the Joplin server address and token in the settings to view the image.</div>
<div>Please set the Joplin Web Clipper Service address and token in the settings to view the image.</div>
<Link to={'/settings'} className={'text-blue-600 underline'}>
Go to Settings
</Link>
Expand All @@ -87,31 +93,51 @@ export function CheckUnusedResourceView() {
<ScrollArea className="rounded-md">
<ul className="space-y-4">
{list.value.map((it) => (
<li key={it.id} className="bg-secondary rounded-lg p-4">
<div className="flex justify-between items-center mb-2">
<span className="font-medium overflow-hidden text-ellipsis whitespace-nowrap max-w-[calc(100%-120px)]">
{it.title}
</span>
<div className="space-x-2">
<Button variant="destructive" size="sm" onClick={() => onDelete(it.id)}>
<Trash2 className="mr-2 h-4 w-4" />
Delete
</Button>
</div>
</div>
{it.mime.startsWith('image/') && settings?.baseUrl && settings.token && (
<ZoomableImage
src={buildResourceUrl(it.id)}
alt={it.title}
className="mx-auto h-40 object-contain rounded-md mt-2"
/>
)}
</li>
<RenderItem key={it.id} resource={it} onDelete={onDelete} settings={settings} />
))}
</ul>
</ScrollArea>
)}
{list.value.length === 0 && !state.loading && (
<div className="flex items-center justify-center h-full">
<div className="text-gray-500">No unused resources found</div>
</div>
)}
</div>
</div>
)
}

function RenderItem(props: {
resource: Pick<ResourceProperties, 'id' | 'title' | 'mime'>
onDelete: (id: string) => void
settings?: { baseUrl?: string; token?: string }
}) {
const { resource, onDelete, settings } = props
return (
<li key={resource.id} className="bg-secondary rounded-lg p-4">
<div
className={cn('flex flex-wrap justify-between items-center', {
'mb-2': resource.mime.startsWith('image/') && settings?.baseUrl && settings.token,
})}
>
<span className="font-medium overflow-hidden text-ellipsis whitespace-nowrap max-w-[calc(100%-120px)]">
{resource.title}
</span>
<div className="space-x-2">
<Button variant="destructive" size="sm" onClick={() => onDelete(resource.id)}>
<Trash2 className="mr-2 h-4 w-4" />
Delete
</Button>
</div>
</div>
{resource.mime.startsWith('image/') && settings?.baseUrl && settings.token && (
<ZoomableImage
src={buildResourceUrl(resource.id)}
alt={resource.title}
className="mx-auto h-40 object-contain rounded-md mt-2"
/>
)}
</li>
)
}
Loading

0 comments on commit cbc6d7f

Please sign in to comment.