mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-02 10:03:28 +00:00
Open Trash from the application (#2338)
* Open Trash from the application * Working Trash Sidebar Button * Small UI fixes * Update common.json * Move openTrash to Tauri Command instead of RSPC * format and remove type assertion --------- Co-authored-by: Utku Bakir <74243531+utkubakir@users.noreply.github.com>
This commit is contained in:
parent
745399ecab
commit
b4037d6537
|
@ -7,6 +7,7 @@ use std::{
|
|||
collections::HashMap,
|
||||
fs,
|
||||
path::PathBuf,
|
||||
process::Command,
|
||||
sync::{Arc, Mutex, PoisonError},
|
||||
time::Duration,
|
||||
};
|
||||
|
@ -149,6 +150,47 @@ async fn open_logs_dir(node: tauri::State<'_, Arc<Node>>) -> Result<(), ()> {
|
|||
})
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[specta::specta]
|
||||
async fn open_trash_in_os_explorer() -> Result<(), ()> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let full_path = format!("{}/.Trash/", std::env::var("HOME").unwrap());
|
||||
|
||||
Command::new("open")
|
||||
.arg(full_path)
|
||||
.spawn()
|
||||
.map_err(|err| error!("Error opening trash: {err:#?}"))?
|
||||
.wait()
|
||||
.map_err(|err| error!("Error opening trash: {err:#?}"))?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
Command::new("explorer")
|
||||
.arg("shell:RecycleBinFolder")
|
||||
.spawn()
|
||||
.map_err(|err| error!("Error opening trash: {err:#?}"))?
|
||||
.wait()
|
||||
.map_err(|err| error!("Error opening trash: {err:#?}"))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
Command::new("xdg-open")
|
||||
.arg("~/.local/share/Trash/")
|
||||
.spawn()
|
||||
.map_err(|err| error!("Error opening trash: {err:#?}"))?
|
||||
.wait()
|
||||
.map_err(|err| error!("Error opening trash: {err:#?}"))?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, specta::Type, tauri_specta::Event)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum DragAndDropEvent {
|
||||
|
@ -218,6 +260,7 @@ async fn main() -> tauri::Result<()> {
|
|||
reload_webview,
|
||||
set_menu_bar_item_state,
|
||||
request_fda_macos,
|
||||
open_trash_in_os_explorer,
|
||||
file::open_file_paths,
|
||||
file::open_ephemeral_files,
|
||||
file::get_file_path_open_with_apps,
|
||||
|
|
|
@ -41,6 +41,17 @@ export const commands = {
|
|||
async requestFdaMacos(): Promise<null> {
|
||||
return await TAURI_INVOKE('plugin:tauri-specta|request_fda_macos');
|
||||
},
|
||||
async openTrashInOsExplorer(): Promise<__Result__<null, null>> {
|
||||
try {
|
||||
return {
|
||||
status: 'ok',
|
||||
data: await TAURI_INVOKE('plugin:tauri-specta|open_trash_in_os_explorer')
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof Error) throw e;
|
||||
else return { status: 'error', error: e as any };
|
||||
}
|
||||
},
|
||||
async openFilePaths(
|
||||
library: string,
|
||||
ids: number[]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, path::PathBuf};
|
||||
use std::{collections::HashMap, path::PathBuf, process::Command};
|
||||
|
||||
use crate::{
|
||||
api::{locations::ExplorerItem, utils::library},
|
||||
|
@ -28,7 +28,7 @@ use futures::StreamExt;
|
|||
use rspc::{alpha::AlphaRouter, ErrorCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use tracing::{error, warn};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
pub mod file_path;
|
||||
pub mod media_data;
|
||||
|
|
|
@ -17,8 +17,8 @@ This means the queries will always render the newest version of the model.
|
|||
|
||||
## Terminology
|
||||
|
||||
- `CacheNode`: A node in the cache - this contains the data and can be identified by the model's name and unique ID within the data (eg. database primary key).
|
||||
- `Reference<T>`: A reference to a node in the cache - This contains the model's name and unique ID.
|
||||
- `CacheNode`: A node in the cache - this contains the data and can be identified by the model's name and unique ID within the data (eg. database primary key).
|
||||
- `Reference<T>`: A reference to a node in the cache - This contains the model's name and unique ID.
|
||||
|
||||
## High level overview
|
||||
|
||||
|
@ -26,8 +26,7 @@ We turn the data on the backend into a list of `CacheNode`'s and a list of `Refe
|
|||
|
||||
We insert the `CacheNode`'s into a global cache on the frontend and then use the `Reference<T>`'s to reconstruct the data by looking up the `CacheNode`'s.
|
||||
|
||||
When the cache changes (from another query, invalidation, etc), we can reconstruct *all* queries using their `Reference<T>`'s to reflect the updated data.
|
||||
|
||||
When the cache changes (from another query, invalidation, etc), we can reconstruct _all_ queries using their `Reference<T>`'s to reflect the updated data.
|
||||
|
||||
## Rust usage
|
||||
|
||||
|
@ -129,7 +128,6 @@ const filePaths = useCache(query.data?.file_paths);
|
|||
|
||||
This is only possible because `useNodes` and `useCache` take in a specific key, instead of the whole `data` object, so you can tell it where to look.
|
||||
|
||||
|
||||
## Known issues
|
||||
|
||||
### Specta support
|
||||
|
|
|
@ -7,16 +7,17 @@ We use a fork based on [rspc 0.1.4](https://docs.rs/rspc) which contains heavy m
|
|||
|
||||
## What's different?
|
||||
|
||||
- A super pre-release version of rspc v1's procedure syntax.
|
||||
- Upgrade to Specta v2 prelease
|
||||
- Add `Router::sd_patch_types_dangerously`
|
||||
- Expose internal type maps for the invalidation system.
|
||||
- All procedures must return a result
|
||||
- `Procedure::with2` which is a hack to properly support the middleware mapper API
|
||||
- Legacy executor system - Will require major changes to the React Native link.
|
||||
- A super pre-release version of rspc v1's procedure syntax.
|
||||
- Upgrade to Specta v2 prelease
|
||||
- Add `Router::sd_patch_types_dangerously`
|
||||
- Expose internal type maps for the invalidation system.
|
||||
- All procedures must return a result
|
||||
- `Procedure::with2` which is a hack to properly support the middleware mapper API
|
||||
- Legacy executor system - Will require major changes to the React Native link.
|
||||
|
||||
Removed features relied on by Spacedrive:
|
||||
- Argument middleware mapper API has been removed upstream
|
||||
|
||||
- Argument middleware mapper API has been removed upstream
|
||||
|
||||
## Basic usage
|
||||
|
||||
|
@ -83,9 +84,8 @@ Minus batching HTTP requests are run in parallel.
|
|||
|
||||
### Websocket reconnect
|
||||
|
||||
If the websocket connection is dropped (due to network disruption) all subscriptions *will not* restart upon reconnecting.
|
||||
If the websocket connection is dropped (due to network disruption) all subscriptions _will not_ restart upon reconnecting.
|
||||
|
||||
This will cause the invalidation system to break and potentially other parts of the app that rely on subscriptions.
|
||||
|
||||
Queries and mutations done during the network disruption will hang indefinitely.
|
||||
|
||||
|
|
|
@ -44,9 +44,10 @@ docker run -d --name spacedrive -p 8080:8080 -e SD_AUTH=admin:spacedrive -v /var
|
|||
When using the Spacedrive server you can use the `SD_AUTH` environment variable to configure authentication.
|
||||
|
||||
Valid values:
|
||||
- `SD_AUTH=disabled` - Disables authentication.
|
||||
- `SD_AUTH=username:password` - Enables authentication for a single user.
|
||||
- `SD_AUTH=username:password,username1:password1` - Enables authentication with multiple users (you can add as many users as you want).
|
||||
|
||||
- `SD_AUTH=disabled` - Disables authentication.
|
||||
- `SD_AUTH=username:password` - Enables authentication for a single user.
|
||||
- `SD_AUTH=username:password,username1:password1` - Enables authentication with multiple users (you can add as many users as you want).
|
||||
|
||||
### Mobile (Preview)
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { EjectSimple } from '@phosphor-icons/react';
|
||||
import { ArrowRight, EjectSimple } from '@phosphor-icons/react';
|
||||
import clsx from 'clsx';
|
||||
import { PropsWithChildren, useMemo } from 'react';
|
||||
import { useBridgeQuery, useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { Button, toast, tw } from '@sd/ui';
|
||||
import { Icon, IconName } from '~/components';
|
||||
import { useLocale } from '~/hooks';
|
||||
import { useLocale, useOperatingSystem } from '~/hooks';
|
||||
import { useHomeDir } from '~/hooks/useHomeDir';
|
||||
import { usePlatform } from '~/util/Platform';
|
||||
|
||||
import { useExplorerDroppable } from '../../../../Explorer/useExplorerDroppable';
|
||||
import { useExplorerSearchParams } from '../../../../Explorer/util';
|
||||
|
@ -26,11 +27,18 @@ const EjectButton = ({ className }: { className?: string }) => (
|
|||
</Button>
|
||||
);
|
||||
|
||||
const OpenToButton = ({ className }: { className?: string; what_is_opening?: string }) => (
|
||||
<Button className={clsx('absolute right-[2px] !p-[5px]', className)} variant="subtle">
|
||||
<ArrowRight size={18} className="size-3 opacity-70" />
|
||||
</Button>
|
||||
);
|
||||
|
||||
const SidebarIcon = ({ name }: { name: IconName }) => {
|
||||
return <Icon name={name} size={20} className="mr-1" />;
|
||||
};
|
||||
|
||||
export default function LocalSection() {
|
||||
const platform = usePlatform();
|
||||
const locationsQuery = useLibraryQuery(['locations.list']);
|
||||
useNodes(locationsQuery.data?.nodes);
|
||||
const locations = useCache(locationsQuery.data?.items);
|
||||
|
@ -76,6 +84,7 @@ export default function LocalSection() {
|
|||
)
|
||||
);
|
||||
|
||||
const os = useOperatingSystem();
|
||||
return (
|
||||
<Section name={t('local')}>
|
||||
<SeeMore>
|
||||
|
@ -137,6 +146,17 @@ export default function LocalSection() {
|
|||
</EphemeralLocation>
|
||||
);
|
||||
})}
|
||||
{platform.openTrashInOsExplorer && (
|
||||
// eslint-disable-next-line tailwindcss/migration-from-tailwind-2
|
||||
<div
|
||||
className={`max-w relative flex grow flex-row items-center gap-0.5 truncate rounded border border-transparent ${os === 'macOS' ? 'bg-opacity-90' : ''} px-2 py-1 text-sm font-medium text-sidebar-inkDull outline-none ring-0 ring-inset ring-transparent ring-offset-0 focus:ring-1 focus:ring-accent focus:ring-offset-0`}
|
||||
onClick={() => platform.openTrashInOsExplorer?.()}
|
||||
>
|
||||
<SidebarIcon name="Trash" />
|
||||
<Name>{t('trash')}</Name>
|
||||
<OpenToButton />
|
||||
</div>
|
||||
)}
|
||||
</SeeMore>
|
||||
</Section>
|
||||
);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useCallback, useEffect } from 'react';
|
||||
import { useExplorerContext } from '~/app/$libraryId/Explorer/Context';
|
||||
import { getSizes } from '~/app/$libraryId/Explorer/OptionsPanel/ListView/util';
|
||||
import { LIST_VIEW_ICON_SIZES } from '~/app/$libraryId/Explorer/View/ListView/useTable';
|
||||
|
||||
import { useOperatingSystem } from './useOperatingSystem';
|
||||
import { getSizes } from '~/app/$libraryId/Explorer/OptionsPanel/ListView/util';
|
||||
|
||||
/**
|
||||
* Hook that allows resizing of items in the Explorer views for GRID and LIST only - using the mouse wheel.
|
||||
|
@ -19,11 +19,11 @@ export const useMouseItemResize = () => {
|
|||
const isList = layoutMode === 'list';
|
||||
const deltaYModifier = isList ? Math.sign(e.deltaY) : e.deltaY / 10; // Sensitivity adjustment
|
||||
const newSize =
|
||||
Number(
|
||||
isList
|
||||
? explorer.settingsStore.listViewIconSize
|
||||
: explorer.settingsStore.gridItemSize
|
||||
) + deltaYModifier;
|
||||
Number(
|
||||
isList
|
||||
? explorer.settingsStore.listViewIconSize
|
||||
: explorer.settingsStore.gridItemSize
|
||||
) + deltaYModifier;
|
||||
|
||||
const minSize = isList ? 0 : 60;
|
||||
const maxSize = isList ? 2 : 200;
|
||||
|
@ -31,7 +31,7 @@ export const useMouseItemResize = () => {
|
|||
|
||||
if (isList) {
|
||||
const listSizes = getSizes(LIST_VIEW_ICON_SIZES);
|
||||
explorer.settingsStore.listViewIconSize = listSizes.sizeMap.get(clampedSize) ?? "0"
|
||||
explorer.settingsStore.listViewIconSize = listSizes.sizeMap.get(clampedSize) ?? '0';
|
||||
} else if (layoutMode === 'grid') {
|
||||
explorer.settingsStore.gridItemSize = Number(clampedSize.toFixed(0));
|
||||
}
|
||||
|
|
|
@ -471,6 +471,7 @@
|
|||
"toggle_metadata": "Toggle metadata",
|
||||
"toggle_path_bar": "Toggle path bar",
|
||||
"toggle_quick_preview": "Toggle quick preview",
|
||||
"trash": "Trash",
|
||||
"type": "Type",
|
||||
"ui_animations": "UI Animations",
|
||||
"ui_animations_description": "Dialogs and other UI elements will animate when opening and closing.",
|
||||
|
|
|
@ -36,6 +36,7 @@ export type Platform = {
|
|||
showDevtools?(): void;
|
||||
openPath?(path: string): void;
|
||||
openLogsDir?(): void;
|
||||
openTrashInOsExplorer?(): void;
|
||||
userHomeDir?(): Promise<string>;
|
||||
// Opens a file path with a given ID
|
||||
openFilePaths?(library: string, ids: number[]): any;
|
||||
|
|
Loading…
Reference in a new issue