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) => {
);
})}
-
+
+ {
+ dialog.open({ directory: true }).then((result) => {
+ createLocation({ path: result });
+ });
+ }}
+ className="w-full px-2 py-1.5 mt-1 text-xs font-bold text-center text-gray-400 dark:text-gray-500 border border-dashed rounded border-transparent cursor-normal border-gray-350 dark:border-gray-550 hover:dark:border-gray-500 transition"
+ >
Add Location
-
+
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
{/*
*/}
-
- generateThumbsForLocation({
- id: 1
- })
- }
- >
- Generate Thumbnails
-
{}}>
Open data folder
@@ -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: