mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-08 08:22:53 +00:00
[ENG-325] Local paths for the context menu (#507)
* use `path_id` instead of `object_id` for finding local file paths * fix bug * make automounting better, UX-wise * find file_paths correctly * use `obj_id` for fetching metadata/preview media * stop using `expect()` during jobs
This commit is contained in:
parent
de3a979127
commit
746ac76803
|
@ -221,6 +221,8 @@ pub(crate) fn mount() -> RouterBuilder {
|
|||
Protected::new(args.secret_key),
|
||||
)?;
|
||||
|
||||
invalidate_query!(library, "keys.hasMasterPassword");
|
||||
|
||||
let automount = library
|
||||
.db
|
||||
.key()
|
||||
|
@ -237,9 +239,9 @@ pub(crate) fn mount() -> RouterBuilder {
|
|||
"Error deserializing UUID from string".into(),
|
||||
)
|
||||
})?)?;
|
||||
}
|
||||
|
||||
invalidate_query!(library, "keys.hasMasterPassword");
|
||||
invalidate_query!(library, "keys.listMounted");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct FileDecryptorJobState {}
|
|||
#[derive(Serialize, Deserialize, Debug, Type, Hash)]
|
||||
pub struct FileDecryptorJobInit {
|
||||
pub location_id: i32,
|
||||
pub object_id: i32,
|
||||
pub path_id: i32,
|
||||
pub output_path: Option<PathBuf>,
|
||||
pub password: Option<String>, // if this is set, we can assume the user chose password decryption
|
||||
pub save_to_library: Option<bool>,
|
||||
|
@ -50,24 +50,33 @@ impl StatefulJob for FileDecryptorJob {
|
|||
.find_unique(location::id::equals(state.init.location_id))
|
||||
.exec()
|
||||
.await?
|
||||
.expect("critical error: can't find location");
|
||||
|
||||
let root_path = location
|
||||
.local_path
|
||||
.as_ref()
|
||||
.map(PathBuf::from)
|
||||
.expect("critical error: issue getting local path as pathbuf");
|
||||
|
||||
.ok_or(JobError::EarlyFinish {
|
||||
name: self.name().to_string(),
|
||||
reason: "can't find location".to_string(),
|
||||
})?;
|
||||
let root_path =
|
||||
location
|
||||
.local_path
|
||||
.as_ref()
|
||||
.map(PathBuf::from)
|
||||
.ok_or(JobError::EarlyFinish {
|
||||
name: self.name().to_string(),
|
||||
reason: "can't get path as pathbuf".to_string(),
|
||||
})?;
|
||||
let item = ctx
|
||||
.library_ctx
|
||||
.db
|
||||
.file_path()
|
||||
.find_first(vec![file_path::object_id::equals(Some(
|
||||
state.init.object_id,
|
||||
))])
|
||||
.find_unique(file_path::location_id_id(
|
||||
state.init.location_id,
|
||||
state.init.path_id,
|
||||
))
|
||||
.exec()
|
||||
.await?
|
||||
.expect("critical error: can't find object");
|
||||
.ok_or(JobError::EarlyFinish {
|
||||
name: self.name().to_string(),
|
||||
reason: "can't find file_path with location id and path id".to_string(),
|
||||
})?;
|
||||
|
||||
let obj_name = item.materialized_path;
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ pub struct FileEncryptorJobState {}
|
|||
#[derive(Serialize, Deserialize, Type, Hash)]
|
||||
pub struct FileEncryptorJobInit {
|
||||
pub location_id: i32,
|
||||
pub object_id: i32,
|
||||
pub path_id: i32,
|
||||
pub key_uuid: uuid::Uuid,
|
||||
pub algorithm: Algorithm,
|
||||
pub metadata: bool,
|
||||
|
@ -42,11 +42,12 @@ pub struct FileEncryptorJobStep {
|
|||
obj_name: String,
|
||||
obj_path: PathBuf,
|
||||
obj_type: ObjectType,
|
||||
obj_id: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Metadata {
|
||||
pub object_id: i32,
|
||||
pub path_id: i32,
|
||||
pub name: String,
|
||||
pub hidden: bool,
|
||||
pub favourite: bool,
|
||||
|
@ -78,24 +79,35 @@ impl StatefulJob for FileEncryptorJob {
|
|||
.find_unique(location::id::equals(state.init.location_id))
|
||||
.exec()
|
||||
.await?
|
||||
.expect("critical error: can't find location");
|
||||
.ok_or(JobError::EarlyFinish {
|
||||
name: self.name().to_string(),
|
||||
reason: "can't find location".to_string(),
|
||||
})?;
|
||||
|
||||
let root_path = location
|
||||
.local_path
|
||||
.as_ref()
|
||||
.map(PathBuf::from)
|
||||
.expect("critical error: issue getting local path as pathbuf");
|
||||
let root_path =
|
||||
location
|
||||
.local_path
|
||||
.as_ref()
|
||||
.map(PathBuf::from)
|
||||
.ok_or(JobError::EarlyFinish {
|
||||
name: self.name().to_string(),
|
||||
reason: "can't get path as pathbuf".to_string(),
|
||||
})?;
|
||||
|
||||
let item = ctx
|
||||
.library_ctx
|
||||
.db
|
||||
.file_path()
|
||||
.find_first(vec![file_path::object_id::equals(Some(
|
||||
state.init.object_id,
|
||||
))])
|
||||
.find_unique(file_path::location_id_id(
|
||||
state.init.location_id,
|
||||
state.init.path_id,
|
||||
))
|
||||
.exec()
|
||||
.await?
|
||||
.expect("critical error: can't find object");
|
||||
.ok_or(JobError::EarlyFinish {
|
||||
name: self.name().to_string(),
|
||||
reason: "can't find file_path with location id and path id".to_string(),
|
||||
})?;
|
||||
|
||||
let obj_name = item.materialized_path;
|
||||
|
||||
|
@ -114,6 +126,7 @@ impl StatefulJob for FileEncryptorJob {
|
|||
obj_name,
|
||||
obj_path,
|
||||
obj_type,
|
||||
obj_id: item.object_id,
|
||||
});
|
||||
|
||||
ctx.progress(vec![JobReportUpdate::TaskCount(state.steps.len())]);
|
||||
|
@ -149,7 +162,10 @@ impl StatefulJob for FileEncryptorJob {
|
|||
let mut path = step.obj_path.clone();
|
||||
let extension = if let Some(ext) = path.extension() {
|
||||
ext.to_str()
|
||||
.expect("critical error: path is not valid utf-8")
|
||||
.ok_or(JobError::EarlyFinish {
|
||||
name: self.name().to_string(),
|
||||
reason: "path isn't valid UTF-8".to_string(),
|
||||
})?
|
||||
.to_string() + ".sdenc"
|
||||
} else {
|
||||
"sdenc".to_string()
|
||||
|
@ -179,41 +195,49 @@ impl StatefulJob for FileEncryptorJob {
|
|||
|
||||
if state.init.metadata || state.init.preview_media {
|
||||
// if any are requested, we can make the query as it'll be used at least once
|
||||
let object = ctx
|
||||
.library_ctx
|
||||
.db
|
||||
.object()
|
||||
.find_unique(object::id::equals(state.init.object_id))
|
||||
.exec()
|
||||
.await?
|
||||
.expect("critical error: can't get object info");
|
||||
if let Some(obj_id) = step.obj_id {
|
||||
let object = ctx
|
||||
.library_ctx
|
||||
.db
|
||||
.object()
|
||||
.find_unique(object::id::equals(obj_id))
|
||||
.exec()
|
||||
.await?
|
||||
.ok_or(JobError::JobDataNotFound(String::from(
|
||||
"can't find information about the object",
|
||||
)))?;
|
||||
|
||||
if state.init.metadata {
|
||||
let metadata = Metadata {
|
||||
object_id: state.init.object_id,
|
||||
name: step.obj_name.clone(),
|
||||
hidden: object.hidden,
|
||||
favourite: object.favorite,
|
||||
important: object.important,
|
||||
note: object.note,
|
||||
date_created: object.date_created,
|
||||
date_modified: object.date_modified,
|
||||
};
|
||||
if state.init.metadata {
|
||||
let metadata = Metadata {
|
||||
path_id: state.init.path_id,
|
||||
name: step.obj_name.clone(),
|
||||
hidden: object.hidden,
|
||||
favourite: object.favorite,
|
||||
important: object.important,
|
||||
note: object.note,
|
||||
date_created: object.date_created,
|
||||
date_modified: object.date_modified,
|
||||
};
|
||||
|
||||
header.add_metadata(
|
||||
LATEST_METADATA,
|
||||
state.init.algorithm,
|
||||
master_key.clone(),
|
||||
&metadata,
|
||||
)?;
|
||||
}
|
||||
header.add_metadata(
|
||||
LATEST_METADATA,
|
||||
state.init.algorithm,
|
||||
master_key.clone(),
|
||||
&metadata,
|
||||
)?;
|
||||
}
|
||||
|
||||
if state.init.preview_media
|
||||
&& (object.has_thumbnail
|
||||
|| object.has_video_preview || object.has_thumbstrip)
|
||||
{
|
||||
// need to find the preview media, read it and return it as Some()
|
||||
// not currently able to do this as thumnails don't generate
|
||||
if state.init.preview_media
|
||||
&& (object.has_thumbnail
|
||||
|| object.has_video_preview || object.has_thumbstrip)
|
||||
{
|
||||
// need to find the preview media, read it and return it as Some()
|
||||
// not currently able to do this as thumnails don't generate
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"skipping metadata/preview media inclusion, no associated object found"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,9 +95,9 @@ export interface ExplorerData { context: ExplorerContext, items: Array<ExplorerI
|
|||
|
||||
export type ExplorerItem = { type: "Path" } & FilePathWithObject | { type: "Object" } & ObjectWithFilePaths
|
||||
|
||||
export interface FileDecryptorJobInit { location_id: number, object_id: number, output_path: string | null, password: string | null, save_to_library: boolean | null }
|
||||
export interface FileDecryptorJobInit { location_id: number, path_id: number, output_path: string | null, password: string | null, save_to_library: boolean | null }
|
||||
|
||||
export interface FileEncryptorJobInit { location_id: number, object_id: number, key_uuid: string, algorithm: Algorithm, metadata: boolean, preview_media: boolean, output_path: string | null }
|
||||
export interface FileEncryptorJobInit { location_id: number, path_id: number, key_uuid: string, algorithm: Algorithm, metadata: boolean, preview_media: boolean, output_path: string | null }
|
||||
|
||||
export interface FilePath { id: number, is_dir: boolean, location_id: number, materialized_path: string, name: string, extension: string | null, object_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string }
|
||||
|
||||
|
|
|
@ -12,13 +12,13 @@ interface DecryptDialogProps {
|
|||
open: boolean;
|
||||
setOpen: (isShowing: boolean) => void;
|
||||
location_id: number | null;
|
||||
object_id: number | null;
|
||||
path_id: number | undefined;
|
||||
setAlertDialogData: (data: GenericAlertDialogProps) => void;
|
||||
}
|
||||
|
||||
export const DecryptFileDialog = (props: DecryptDialogProps) => {
|
||||
const platform = usePlatform();
|
||||
const { location_id, object_id } = props;
|
||||
const { location_id, path_id } = props;
|
||||
const decryptFile = useLibraryMutation('files.decryptFiles');
|
||||
const [outputPath, setOutputpath] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
@ -59,11 +59,11 @@ export const DecryptFileDialog = (props: DecryptDialogProps) => {
|
|||
props.setOpen(false);
|
||||
|
||||
location_id &&
|
||||
object_id &&
|
||||
path_id &&
|
||||
decryptFile.mutate(
|
||||
{
|
||||
location_id,
|
||||
object_id,
|
||||
path_id,
|
||||
output_path: output,
|
||||
password: pw,
|
||||
save_to_library: save
|
||||
|
|
|
@ -15,13 +15,13 @@ interface EncryptDialogProps {
|
|||
open: boolean;
|
||||
setOpen: (isShowing: boolean) => void;
|
||||
location_id: number | null;
|
||||
object_id: number | null;
|
||||
path_id: number | undefined;
|
||||
setAlertDialogData: (data: GenericAlertDialogProps) => void;
|
||||
}
|
||||
|
||||
export const EncryptFileDialog = (props: EncryptDialogProps) => {
|
||||
const platform = usePlatform();
|
||||
const { location_id, object_id } = props;
|
||||
const { location_id, path_id } = props;
|
||||
const keys = useLibraryQuery(['keys.list']);
|
||||
const mountedUuids = useLibraryQuery(['keys.listMounted'], {
|
||||
onSuccess: (data) => {
|
||||
|
@ -64,13 +64,13 @@ export const EncryptFileDialog = (props: EncryptDialogProps) => {
|
|||
props.setOpen(false);
|
||||
|
||||
location_id &&
|
||||
object_id &&
|
||||
path_id &&
|
||||
encryptFile.mutate(
|
||||
{
|
||||
algorithm,
|
||||
key_uuid: key,
|
||||
location_id,
|
||||
object_id,
|
||||
path_id,
|
||||
metadata,
|
||||
preview_media: previewMedia,
|
||||
output_path: output
|
||||
|
|
|
@ -98,20 +98,24 @@ export default function Explorer(props: Props) {
|
|||
value={alertDialogData.value}
|
||||
inputBox={alertDialogData.inputBox}
|
||||
/>
|
||||
<EncryptFileDialog
|
||||
location_id={expStore.locationId}
|
||||
object_id={expStore.contextMenuObjectId}
|
||||
open={showEncryptDialog}
|
||||
setOpen={setShowEncryptDialog}
|
||||
setAlertDialogData={setAlertDialogData}
|
||||
/>
|
||||
<DecryptFileDialog
|
||||
location_id={expStore.locationId}
|
||||
object_id={expStore.contextMenuObjectId}
|
||||
open={showDecryptDialog}
|
||||
setOpen={setShowDecryptDialog}
|
||||
setAlertDialogData={setAlertDialogData}
|
||||
/>
|
||||
{props.data && props.data.items[expStore.selectedRowIndex] && (
|
||||
<EncryptFileDialog
|
||||
location_id={expStore.locationId}
|
||||
path_id={props.data?.items[expStore.selectedRowIndex].id}
|
||||
open={showEncryptDialog}
|
||||
setOpen={setShowEncryptDialog}
|
||||
setAlertDialogData={setAlertDialogData}
|
||||
/>
|
||||
)}
|
||||
{props.data && props.data.items[expStore.selectedRowIndex] && (
|
||||
<DecryptFileDialog
|
||||
location_id={expStore.locationId}
|
||||
path_id={props.data?.items[expStore.selectedRowIndex].id}
|
||||
open={showDecryptDialog}
|
||||
setOpen={setShowDecryptDialog}
|
||||
setAlertDialogData={setAlertDialogData}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue