From 65b9dcf71c306372fcfb763cab165bc94473b462 Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Tue, 12 Apr 2022 19:38:50 -0700 Subject: [PATCH] working location creation + thumbnail gen --- apps/desktop/package.json | 1 + apps/desktop/src/App.tsx | 6 +-- apps/desktop/src/components/file/FileList.tsx | 24 +++++++-- apps/desktop/src/components/file/Sidebar.tsx | 22 ++++++-- .../src/components/jobs/RunningJobsWidget.tsx | 30 +++++++++++ apps/desktop/src/components/layout/TopBar.tsx | 25 +++++++-- .../src/components/primitive/ProgressBar.tsx | 24 +++++++++ apps/desktop/src/hooks/useCoreEvents.tsx | 5 ++ .../src/screens/settings/GeneralSettings.tsx | 53 ++----------------- core/src/file/indexer.rs | 8 +-- core/src/file/thumb.rs | 11 +++- core/src/lib.rs | 3 +- core/src/sys/locations.rs | 7 ++- packages/ui/style/tailwind.config.js | 14 +++++ pnpm-lock.yaml | 13 +++++ 15 files changed, 176 insertions(+), 70 deletions(-) create mode 100644 apps/desktop/src/components/jobs/RunningJobsWidget.tsx create mode 100644 apps/desktop/src/components/primitive/ProgressBar.tsx diff --git a/apps/desktop/package.json b/apps/desktop/package.json index b84db6bc8..875aa74c1 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -34,6 +34,7 @@ "@radix-ui/react-dialog": "^0.1.7", "@radix-ui/react-dropdown-menu": "^0.1.6", "@radix-ui/react-icons": "^1.1.0", + "@radix-ui/react-progress": "^0.1.4", "@radix-ui/react-slider": "^0.1.4", "@sd/client": "workspace:*", "@sd/core": "workspace:*", diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index f3407d42e..cb9d04292 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -135,10 +135,10 @@ function NotFound() { role="alert" className="flex flex-col items-center justify-center w-full h-full p-4 rounded-lg dark:text-white" > -

Error: 404

-

Not found bozo.

+

Error: 404

+

You chose nothingness.

-
diff --git a/apps/desktop/src/components/file/FileList.tsx b/apps/desktop/src/components/file/FileList.tsx index dc54497e6..44ed3bf86 100644 --- a/apps/desktop/src/components/file/FileList.tsx +++ b/apps/desktop/src/components/file/FileList.tsx @@ -14,11 +14,20 @@ import { useSearchParams } from 'react-router-dom'; type ExplorerState = { selectedRowIndex: number; setSelectedRowIndex: (index: number) => void; + locationId: number; + setLocationId: (index: number) => void; + newThumbnails: Record; + addNewThumbnail: (cas_id: string) => void; }; export const useExplorerState = create((set) => ({ selectedRowIndex: 1, - setSelectedRowIndex: (index) => set((state) => ({ ...state, selectedRowIndex: index })) + setSelectedRowIndex: (index) => set((state) => ({ ...state, selectedRowIndex: index })), + locationId: -1, + setLocationId: (id: number) => set((state) => ({ ...state, locationId: id })), + newThumbnails: {}, + addNewThumbnail: (cas_id: string) => + set((state) => ({ ...state, newThumbnails: { ...state.newThumbnails, [cas_id]: true } })) })); interface IColumn { @@ -36,7 +45,7 @@ function ensureIsColumns(data: T) { const columns = ensureIsColumns([ { column: 'Name', key: 'name', width: 280 } as const, - { column: 'Size', key: 'size_in_bytes', width: 120 } as const, + // { column: 'Size', key: 'size_in_bytes', width: 120 } as const, { column: 'Type', key: 'extension', width: 100 } as const ]); @@ -51,7 +60,7 @@ export const FileList: React.FC<{ location_id: number; path: string; limit: numb const path = props.path; - const { selectedRowIndex, setSelectedRowIndex } = useExplorerState(); + const { selectedRowIndex, setSelectedRowIndex, setLocationId } = useExplorerState(); const [goingUp, setGoingUp] = useState(false); const { data: currentDir } = useBridgeQuery('LibGetExplorerDir', { @@ -71,6 +80,10 @@ export const FileList: React.FC<{ location_id: number; path: string; limit: numb } }, [selectedRowIndex]); + useEffect(() => { + setLocationId(props.location_id); + }, [props.location_id]); + useKey('ArrowUp', (e) => { e.preventDefault(); setGoingUp(true); @@ -197,6 +210,9 @@ const RenderCell: React.FC<{ colKey?: ColumnKey; dirId?: number; file?: FilePath refetchOnWindowFocus: false }); const location = useContext(LocationContext); + const { newThumbnails } = useExplorerState(); + + const hasThumbnail = !!row?.has_local_thumbnail || !!newThumbnails[row?.temp_cas_id ?? '']; switch (colKey) { case 'name': @@ -206,7 +222,7 @@ const RenderCell: React.FC<{ colKey?: ColumnKey; dirId?: number; file?: FilePath {row.is_dir ? ( ) : ( - row.has_local_thumbnail && + hasThumbnail && client?.data_path && ( = (props) => { const [isMacos, setIsMacos] = useState(false); const { data: locations } = useBridgeQuery('SysGetLocations'); + const { mutate: createLocation } = useBridgeCommand('LocCreate'); useEffect(() => { platform().then((platform) => { @@ -72,7 +75,7 @@ export const Sidebar: React.FC = (props) => { ]; return ( -
+
{isMacos ? ( <>
@@ -157,9 +160,17 @@ export const Sidebar: React.FC = (props) => {
); })} -
+ +
+
Tags @@ -176,6 +187,9 @@ export const Sidebar: React.FC = (props) => {
+ + {/*
+
*/}
{({ isActive }) => ( diff --git a/apps/desktop/src/components/jobs/RunningJobsWidget.tsx b/apps/desktop/src/components/jobs/RunningJobsWidget.tsx new file mode 100644 index 000000000..3e40b0952 --- /dev/null +++ b/apps/desktop/src/components/jobs/RunningJobsWidget.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { useBridgeQuery } from '@sd/client'; +import ProgressBar from '../primitive/ProgressBar'; +import { Transition } from '@headlessui/react'; + +export default function RunningJobsWidget() { + const { data: jobs } = useBridgeQuery('JobGetRunning'); + + return ( +
+ {jobs?.map((job) => ( + +
+ {/* {job.status} Job */} + {job.message} + +
+
+ ))} +
+ ); +} diff --git a/apps/desktop/src/components/layout/TopBar.tsx b/apps/desktop/src/components/layout/TopBar.tsx index d5c7e3e36..1ed765152 100644 --- a/apps/desktop/src/components/layout/TopBar.tsx +++ b/apps/desktop/src/components/layout/TopBar.tsx @@ -1,12 +1,22 @@ import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/outline'; import clsx from 'clsx'; -import { ArrowsLeftRight, Cloud, FolderPlus, Key, Tag, TerminalWindow } from 'phosphor-react'; +import { + ArrowsClockwise, + ArrowsLeftRight, + Cloud, + FolderPlus, + Key, + Tag, + TerminalWindow +} from 'phosphor-react'; import React from 'react'; import { ButtonProps } from '../primitive'; import { Shortcut } from '../primitive/Shortcut'; import { DefaultProps } from '../primitive/types'; import { invoke } from '@tauri-apps/api'; import { useLocation, useNavigate } from 'react-router-dom'; +import { useBridgeCommand } from '@sd/client'; +import { useExplorerState } from '../file/FileList'; export interface TopBarProps extends DefaultProps {} export interface TopBarButtonProps extends ButtonProps { @@ -38,7 +48,12 @@ const TopBarButton: React.FC = ({ icon: Icon, ...props }) => }; export const TopBar: React.FC = (props) => { - let location = useLocation(); + const { locationId } = useExplorerState(); + const { mutate: generateThumbsForLocation } = useBridgeCommand('GenerateThumbsForLocation', { + onMutate: (data) => { + console.log('GenerateThumbsForLocation', data); + } + }); let navigate = useNavigate(); return ( <> @@ -75,8 +90,10 @@ export const TopBar: React.FC = (props) => { invoke('start_watcher', { path: '/Users/jamie' })} + icon={ArrowsClockwise} + onClick={() => { + generateThumbsForLocation({ id: locationId, path: '' }); + }} />
diff --git a/apps/desktop/src/components/primitive/ProgressBar.tsx b/apps/desktop/src/components/primitive/ProgressBar.tsx new file mode 100644 index 000000000..26882b61e --- /dev/null +++ b/apps/desktop/src/components/primitive/ProgressBar.tsx @@ -0,0 +1,24 @@ +import * as ProgressPrimitive from '@radix-ui/react-progress'; +import React, { useEffect } from 'react'; + +interface Props { + value: number; + total: number; +} + +const ProgressBar = (props: Props) => { + const percentage = Math.round((props.value / props.total) * 100); + return ( + + + + ); +}; + +export default ProgressBar; diff --git a/apps/desktop/src/hooks/useCoreEvents.tsx b/apps/desktop/src/hooks/useCoreEvents.tsx index 5d03a5b9a..bf61b5b72 100644 --- a/apps/desktop/src/hooks/useCoreEvents.tsx +++ b/apps/desktop/src/hooks/useCoreEvents.tsx @@ -3,12 +3,17 @@ import { emit, listen, Event } from '@tauri-apps/api/event'; // import { useExplorerStore } from '../store/explorer'; import { CoreEvent } from '../../../../core'; import { useQuery, useQueryClient } from 'react-query'; +import { useExplorerState } from '../components/file/FileList'; export function useCoreEvents() { const client = useQueryClient(); + const { addNewThumbnail } = useExplorerState(); useEffect(() => { listen('core_event', (e: Event) => { switch (e.payload?.key) { + case 'NewThumbnail': + addNewThumbnail(e.payload.data.cas_id); + break; case 'InvalidateQuery': case 'InvalidateQueryDebounced': let query = [e.payload.data.key]; diff --git a/apps/desktop/src/screens/settings/GeneralSettings.tsx b/apps/desktop/src/screens/settings/GeneralSettings.tsx index 7a91088f2..8da06531d 100644 --- a/apps/desktop/src/screens/settings/GeneralSettings.tsx +++ b/apps/desktop/src/screens/settings/GeneralSettings.tsx @@ -11,7 +11,7 @@ import { useBridgeCommand, useBridgeQuery } from '@sd/client'; export default function GeneralSettings() { const { data: volumes } = useBridgeQuery('SysGetVolumes'); - const [tempWatchDir, setTempWatchDir] = useState('/users/jamie/Projects'); + const [tempWatchDir, setTempWatchDir] = useState('/users/jamie/Desktop/Pictures'); const [fakeSliderVal, setFakeSliderVal] = useState([30, 0]); @@ -33,24 +33,12 @@ export default function GeneralSettings() { return (
-
+

General Settings

Basic settings related to this client

{/*
*/}
- @@ -58,7 +46,7 @@ export default function GeneralSettings() { Purge database
- @@ -81,38 +69,8 @@ export default function GeneralSettings() { Scan Now
- + */} - -
diff --git a/core/src/file/indexer.rs b/core/src/file/indexer.rs index 0e3de510b..a4a1370c8 100644 --- a/core/src/file/indexer.rs +++ b/core/src/file/indexer.rs @@ -140,9 +140,9 @@ pub async fn scan_path( on_progress(vec![ ScanProgress::SavedChunks(i as usize), ScanProgress::Message(format!( - "Writing {} files to db at chunk {}", - chunk.len(), - i + "Writing {} of {} to db", + i * chunk.len(), + paths.len(), )), ]); @@ -220,7 +220,7 @@ fn prepare_values( location.id, materialized_path, name, - extension, + extension.to_lowercase(), parent_id .clone() .map(|id| format!("\"{}\"", &id)) diff --git a/core/src/file/thumb.rs b/core/src/file/thumb.rs index 0a1717fae..a1c447645 100644 --- a/core/src/file/thumb.rs +++ b/core/src/file/thumb.rs @@ -1,13 +1,14 @@ use crate::job::jobs::JobReportUpdate; use crate::prisma::FilePathData; use crate::state::client; -use crate::sys; use crate::{ job::{jobs::Job, worker::WorkerContext}, prisma::FilePath, CoreContext, }; +use crate::{sys, CoreEvent}; use anyhow::Result; +use futures::executor::block_on; use image::*; use prisma_client_rust::or; use std::fs; @@ -47,6 +48,8 @@ impl Job for ThumbnailJob { println!("Found {:?} files", image_files.len()); + let is_background = self.background.clone(); + tokio::task::spawn_blocking(move || { ctx.progress(vec![ JobReportUpdate::TaskCount(image_files.len()), @@ -76,6 +79,12 @@ impl Job for ThumbnailJob { } ctx.progress(vec![JobReportUpdate::CompletedTaskCount(i + 1)]); + + if !is_background { + block_on(ctx.core_ctx.emit(CoreEvent::NewThumbnail { + cas_id: checksum.to_string(), + })); + }; } }) .await?; diff --git a/core/src/lib.rs b/core/src/lib.rs index f1681efc2..0b7fcb772 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -231,7 +231,7 @@ impl Core { } async fn exec_command(&mut self, cmd: ClientCommand) -> Result { - info!("Core command: {:?}", cmd); + println!("Core command: {:?}", cmd); let ctx = self.get_context(); Ok(match cmd { // CRUD for locations @@ -359,6 +359,7 @@ pub enum CoreEvent { InvalidateQuery(ClientQuery), InvalidateQueryDebounced(ClientQuery), InvalidateResource(CoreResource), + NewThumbnail { cas_id: String }, Log { message: String }, DatabaseDisconnected { reason: Option }, } diff --git a/core/src/sys/locations.rs b/core/src/sys/locations.rs index a3ee8122a..f7859a5c7 100644 --- a/core/src/sys/locations.rs +++ b/core/src/sys/locations.rs @@ -3,7 +3,7 @@ use crate::{ prisma::{FilePath, Location}, state::client, sys::{volumes, volumes::Volume}, - CoreContext, + ClientQuery, CoreContext, CoreEvent, }; use anyhow::Result; use log::info; @@ -176,6 +176,11 @@ pub async fn create_location(ctx: &CoreContext, path: &str) -> Result (), Err(e) => Err(LocationError::DotfileWriteFailure(e, path.to_string()))?, } + + ctx + .emit(CoreEvent::InvalidateQuery(ClientQuery::SysGetLocations)) + .await; + location } }; diff --git a/packages/ui/style/tailwind.config.js b/packages/ui/style/tailwind.config.js index 58c9d2e68..fc88b3510 100644 --- a/packages/ui/style/tailwind.config.js +++ b/packages/ui/style/tailwind.config.js @@ -7,6 +7,20 @@ module.exports = { darkMode: 'media', mode: 'jit', theme: { + fontSize: { + 'tiny': '.65rem', + 'xs': '.75rem', + 'sm': '.84rem', + 'base': '1rem', + 'lg': '1.125rem', + 'xl': '1.25rem', + '2xl': '1.5rem', + '3xl': '1.875rem', + '4xl': '2.25rem', + '5xl': '3rem', + '6xl': '4rem', + '7xl': '5rem' + }, extend: { boxShadow: { box: '0px 4px 9px rgba(0, 0, 0, 0.05)', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea64803e2..14d97e8f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,6 +18,7 @@ importers: '@radix-ui/react-dialog': ^0.1.7 '@radix-ui/react-dropdown-menu': ^0.1.6 '@radix-ui/react-icons': ^1.1.0 + '@radix-ui/react-progress': ^0.1.4 '@radix-ui/react-slider': ^0.1.4 '@sd/client': workspace:* '@sd/core': workspace:* @@ -74,6 +75,7 @@ importers: '@radix-ui/react-dialog': 0.1.7_761282c47ea27a9f40e1344337910648 '@radix-ui/react-dropdown-menu': 0.1.6_761282c47ea27a9f40e1344337910648 '@radix-ui/react-icons': 1.1.0_react@18.0.0 + '@radix-ui/react-progress': 0.1.4_react@18.0.0 '@radix-ui/react-slider': 0.1.4_react@18.0.0 '@sd/client': link:../../packages/client '@sd/core': link:../../core @@ -826,6 +828,17 @@ packages: react: 18.0.0 dev: false + /@radix-ui/react-progress/0.1.4_react@18.0.0: + resolution: {integrity: sha512-Q+H7r/S0HMlwVriksgyUgBtol5TgkNllQPVtaXDDaeJBNFK2uiToHBp3j1Gi88/4Fw+EKT+SyWpeM/5nVtGujw==} + peerDependencies: + react: ^16.8 || ^17.0 + dependencies: + '@babel/runtime': 7.17.9 + '@radix-ui/react-context': 0.1.1_react@18.0.0 + '@radix-ui/react-primitive': 0.1.4_react@18.0.0 + react: 18.0.0 + dev: false + /@radix-ui/react-roving-focus/0.1.5_react@18.0.0: resolution: {integrity: sha512-ClwKPS5JZE+PaHCoW7eu1onvE61pDv4kO8W4t5Ra3qMFQiTJLZMdpBQUhksN//DaVygoLirz4Samdr5Y1x1FSA==} peerDependencies: