mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-18 13:09:15 +00:00
hotfix for identifier orphan query
This commit is contained in:
parent
98f900b99e
commit
6b1e8c3088
|
@ -90,7 +90,7 @@ impl StatefulJob for FileIdentifierJob {
|
|||
let total_count = library
|
||||
.db
|
||||
.file_path()
|
||||
.count(orphan_path_filters(location_id))
|
||||
.count(orphan_path_filters(location_id, None))
|
||||
.exec()
|
||||
.await? as usize;
|
||||
|
||||
|
@ -106,7 +106,7 @@ impl StatefulJob for FileIdentifierJob {
|
|||
let first_path_id = library
|
||||
.db
|
||||
.file_path()
|
||||
.find_first(orphan_path_filters(location_id))
|
||||
.find_first(orphan_path_filters(location_id, None))
|
||||
.exec()
|
||||
.await?
|
||||
.map(|d| d.id)
|
||||
|
@ -310,12 +310,17 @@ impl StatefulJob for FileIdentifierJob {
|
|||
}
|
||||
}
|
||||
|
||||
fn orphan_path_filters(location_id: i32) -> Vec<file_path::WhereParam> {
|
||||
vec![
|
||||
fn orphan_path_filters(location_id: i32, file_path_id: Option<i32>) -> Vec<file_path::WhereParam> {
|
||||
let mut params = vec![
|
||||
file_path::file_id::equals(None),
|
||||
file_path::is_dir::equals(false),
|
||||
file_path::location_id::equals(location_id),
|
||||
]
|
||||
];
|
||||
// this is a workaround for the cursor not working properly
|
||||
if let Some(file_path_id) = file_path_id {
|
||||
params.push(file_path::id::gte(file_path_id))
|
||||
}
|
||||
params
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
|
@ -334,9 +339,9 @@ async fn get_orphan_file_paths(
|
|||
);
|
||||
ctx.db
|
||||
.file_path()
|
||||
.find_many(orphan_path_filters(location_id))
|
||||
.find_many(orphan_path_filters(location_id, Some(cursor.file_path_id)))
|
||||
.order_by(file_path::id::order(Direction::Asc))
|
||||
.cursor(cursor.into())
|
||||
// .cursor(cursor.into())
|
||||
.take(CHUNK_SIZE as i64)
|
||||
.skip(1)
|
||||
.exec()
|
||||
|
|
|
@ -1,122 +1,125 @@
|
|||
import { explorerStore, useLibraryMutation, useLibraryQuery } from '@sd/client';
|
||||
import { ContextMenu as CM } from '@sd/ui';
|
||||
import {
|
||||
ArrowBendUpRight,
|
||||
FilePlus,
|
||||
FileX,
|
||||
LockSimple,
|
||||
Package,
|
||||
Plus,
|
||||
Share,
|
||||
TagSimple,
|
||||
Trash,
|
||||
TrashSimple,
|
||||
ArrowBendUpRight,
|
||||
FilePlus,
|
||||
FileX,
|
||||
LockSimple,
|
||||
Package,
|
||||
Plus,
|
||||
Share,
|
||||
TagSimple,
|
||||
Trash,
|
||||
TrashSimple
|
||||
} from 'phosphor-react';
|
||||
import React from 'react';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import { NewContextMenu as CM } from "@sd/ui"
|
||||
|
||||
const AssignTagMenuItems = (props: { objectId: number }) => {
|
||||
const tags = useLibraryQuery(['tags.getAll'], { suspense: true });
|
||||
const tagsForFile = useLibraryQuery(['tags.getForFile', props.objectId], { suspense: true });
|
||||
const tags = useLibraryQuery(['tags.getAll'], { suspense: true });
|
||||
const tagsForFile = useLibraryQuery(['tags.getForFile', props.objectId], { suspense: true });
|
||||
|
||||
const { mutate: assignTag } = useLibraryMutation('tags.assign');
|
||||
const { mutate: assignTag } = useLibraryMutation('tags.assign');
|
||||
|
||||
return (
|
||||
<>
|
||||
{tags.data?.map(tag => {
|
||||
const active = !!tagsForFile.data?.find(t => t.id === tag.id)
|
||||
return (
|
||||
<>
|
||||
{tags.data?.map((tag) => {
|
||||
const active = !!tagsForFile.data?.find((t) => t.id === tag.id);
|
||||
|
||||
return <CM.Item
|
||||
key={tag.id}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (props.objectId === null) return;
|
||||
return (
|
||||
<CM.Item
|
||||
key={tag.id}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (props.objectId === null) return;
|
||||
|
||||
assignTag({
|
||||
tag_id: tag.id,
|
||||
file_id: props.objectId,
|
||||
unassign: active
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="block w-[15px] h-[15px] mr-0.5 border rounded-full"
|
||||
style={{
|
||||
backgroundColor: active
|
||||
? tag.color || '#efefef'
|
||||
: 'transparent' || '#efefef',
|
||||
borderColor: tag.color || '#efefef'
|
||||
}}
|
||||
/>
|
||||
<p>{tag.name}</p>
|
||||
</CM.Item>
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
assignTag({
|
||||
tag_id: tag.id,
|
||||
file_id: props.objectId,
|
||||
unassign: active
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="block w-[15px] h-[15px] mr-0.5 border rounded-full"
|
||||
style={{
|
||||
backgroundColor: active ? tag.color || '#efefef' : 'transparent' || '#efefef',
|
||||
borderColor: tag.color || '#efefef'
|
||||
}}
|
||||
/>
|
||||
<p>{tag.name}</p>
|
||||
</CM.Item>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function ExplorerContextMenu(props: Props) {
|
||||
const store = useSnapshot(explorerStore);
|
||||
const store = useSnapshot(explorerStore);
|
||||
|
||||
return (
|
||||
<div className="relative" >
|
||||
<CM.ContextMenu
|
||||
trigger={props.children}
|
||||
>
|
||||
<CM.Item label="Open" />
|
||||
<CM.Item label="Open with..." />
|
||||
return (
|
||||
<div className="relative">
|
||||
<CM.ContextMenu trigger={props.children}>
|
||||
<CM.Item label="Open" />
|
||||
<CM.Item label="Open with..." />
|
||||
|
||||
<CM.Separator />
|
||||
<CM.Separator />
|
||||
|
||||
<CM.Item label="Quick view" />
|
||||
<CM.Item label="Open in Finder" />
|
||||
<CM.Item label="Quick view" />
|
||||
<CM.Item label="Open in Finder" />
|
||||
|
||||
<CM.Separator />
|
||||
<CM.Separator />
|
||||
|
||||
<CM.Item label="Rename" />
|
||||
<CM.Item label="Duplicate" />
|
||||
<CM.Item label="Rename" />
|
||||
<CM.Item label="Duplicate" />
|
||||
|
||||
<CM.Separator />
|
||||
<CM.Separator />
|
||||
|
||||
<CM.Item label="Share" icon={Share} onClick={e => {
|
||||
e.preventDefault();
|
||||
<CM.Item
|
||||
label="Share"
|
||||
icon={Share}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
navigator.share?.({
|
||||
title: 'Spacedrive',
|
||||
text: 'Check out this cool app',
|
||||
url: 'https://spacedrive.com'
|
||||
});
|
||||
}} />
|
||||
navigator.share?.({
|
||||
title: 'Spacedrive',
|
||||
text: 'Check out this cool app',
|
||||
url: 'https://spacedrive.com'
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<CM.Separator />
|
||||
<CM.Separator />
|
||||
|
||||
{store.contextMenuObjectId && <CM.SubMenu label="Assign tag" icon={TagSimple}>
|
||||
<AssignTagMenuItems objectId={store.contextMenuObjectId} />
|
||||
</CM.SubMenu>}
|
||||
<CM.SubMenu label="More actions..." icon={Plus}>
|
||||
<CM.SubMenu label="Move to library" icon={FilePlus}>
|
||||
{/* {libraries.map(library => <CM.Item key={library.id} label={library.config.name} />)} */}
|
||||
<CM.Item label="Remove from library" icon={FileX} />
|
||||
</CM.SubMenu>
|
||||
<CM.Separator />
|
||||
<CM.Item label="Encrypt" icon={LockSimple} />
|
||||
<CM.Item label="Compress" icon={Package} />
|
||||
<CM.SubMenu label="Convert to" icon={ArrowBendUpRight}>
|
||||
<CM.Item label="PNG" />
|
||||
<CM.Item label="WebP" />
|
||||
</CM.SubMenu>
|
||||
<CM.Item label="Secure delete" icon={TrashSimple} />
|
||||
</CM.SubMenu>
|
||||
{store.contextMenuObjectId && (
|
||||
<CM.SubMenu label="Assign tag" icon={TagSimple}>
|
||||
<AssignTagMenuItems objectId={store.contextMenuObjectId} />
|
||||
</CM.SubMenu>
|
||||
)}
|
||||
<CM.SubMenu label="More actions..." icon={Plus}>
|
||||
<CM.SubMenu label="Move to library" icon={FilePlus}>
|
||||
{/* {libraries.map(library => <CM.Item key={library.id} label={library.config.name} />)} */}
|
||||
<CM.Item label="Remove from library" icon={FileX} />
|
||||
</CM.SubMenu>
|
||||
<CM.Separator />
|
||||
<CM.Item label="Encrypt" icon={LockSimple} />
|
||||
<CM.Item label="Compress" icon={Package} />
|
||||
<CM.SubMenu label="Convert to" icon={ArrowBendUpRight}>
|
||||
<CM.Item label="PNG" />
|
||||
<CM.Item label="WebP" />
|
||||
</CM.SubMenu>
|
||||
<CM.Item label="Secure delete" icon={TrashSimple} />
|
||||
</CM.SubMenu>
|
||||
|
||||
<CM.Separator />
|
||||
<CM.Separator />
|
||||
|
||||
<CM.Item icon={Trash} label="Delete" variant="danger" />
|
||||
</CM.ContextMenu>
|
||||
</div >
|
||||
);
|
||||
<CM.Item icon={Trash} label="Delete" variant="danger" />
|
||||
</CM.ContextMenu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
126
packages/ui/src/ContextMenu.tsx
Normal file
126
packages/ui/src/ContextMenu.tsx
Normal file
|
@ -0,0 +1,126 @@
|
|||
import * as RadixCM from '@radix-ui/react-context-menu';
|
||||
import { VariantProps, cva } from 'class-variance-authority';
|
||||
import clsx from 'clsx';
|
||||
import { CaretRight, Icon } from 'phosphor-react';
|
||||
import { HTMLAttributes, PropsWithChildren, Suspense } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
interface Props extends RadixCM.MenuContentProps {
|
||||
trigger: React.ReactNode;
|
||||
}
|
||||
|
||||
const MENU_CLASSES = `
|
||||
flex flex-col
|
||||
min-w-[11rem] p-2 space-y-1
|
||||
text-left text-sm dark:text-gray-100 text-gray-800
|
||||
bg-gray-50 border-gray-200 dark:bg-gray-950
|
||||
shadow-md shadow-gray-300 dark:shadow-gray-750
|
||||
select-none cursor-default rounded-lg
|
||||
`;
|
||||
|
||||
export const ContextMenu = ({
|
||||
trigger,
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: PropsWithChildren<Props>) => {
|
||||
return (
|
||||
<RadixCM.Root>
|
||||
<RadixCM.Trigger>{trigger}</RadixCM.Trigger>
|
||||
<RadixCM.Portal>
|
||||
<RadixCM.Content {...props} className={clsx(MENU_CLASSES, className)}>
|
||||
{children}
|
||||
</RadixCM.Content>
|
||||
</RadixCM.Portal>
|
||||
</RadixCM.Root>
|
||||
);
|
||||
};
|
||||
|
||||
export const Separator = () => (
|
||||
<RadixCM.Separator className="mx-2 border-0 border-b pointer-events-none border-b-gray-300 dark:border-b-gray-600" />
|
||||
);
|
||||
|
||||
export const SubMenu = ({
|
||||
label,
|
||||
icon,
|
||||
className,
|
||||
...props
|
||||
}: RadixCM.MenuSubContentProps & ItemProps) => {
|
||||
return (
|
||||
<RadixCM.Sub>
|
||||
<RadixCM.SubTrigger className="[&[data-state='open']_div]:bg-primary focus:outline-none">
|
||||
<DivItem rightArrow {...{ label, icon }} />
|
||||
</RadixCM.SubTrigger>
|
||||
<RadixCM.Portal>
|
||||
<Suspense fallback={null}>
|
||||
<RadixCM.SubContent {...props} className={clsx(MENU_CLASSES, '-mt-2', className)} />
|
||||
</Suspense>
|
||||
</RadixCM.Portal>
|
||||
</RadixCM.Sub>
|
||||
);
|
||||
};
|
||||
|
||||
const ITEM_CLASSES = `
|
||||
flex flex-row items-center justify-start flex-1
|
||||
px-2 py-1 space-x-2
|
||||
cursor-default rounded
|
||||
focus:outline-none
|
||||
`;
|
||||
|
||||
const itemStyles = cva([ITEM_CLASSES], {
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'hover:bg-primary focus:bg-primary',
|
||||
danger: `
|
||||
text-red-600 dark:text-red-400
|
||||
hover:text-white focus:text-white
|
||||
hover:bg-red-500 focus:bg-red-500
|
||||
`
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
interface ItemProps extends VariantProps<typeof itemStyles> {
|
||||
icon?: Icon;
|
||||
rightArrow?: boolean;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export const Item = ({
|
||||
icon,
|
||||
label,
|
||||
rightArrow,
|
||||
children,
|
||||
variant,
|
||||
...props
|
||||
}: ItemProps & RadixCM.MenuItemProps) => (
|
||||
<RadixCM.Item {...props} className={itemStyles({ variant })}>
|
||||
{children ? children : <ItemInternals {...{ icon, label, rightArrow }} />}
|
||||
</RadixCM.Item>
|
||||
);
|
||||
|
||||
const DivItem = ({ variant, ...props }: ItemProps) => (
|
||||
<div className={itemStyles({ variant })}>
|
||||
<ItemInternals {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const ItemInternals = ({ icon, label, rightArrow }: ItemProps) => {
|
||||
const ItemIcon = icon;
|
||||
return (
|
||||
<>
|
||||
{ItemIcon && <ItemIcon size={18} />}
|
||||
{label && <p>{label}</p>}
|
||||
|
||||
{rightArrow && (
|
||||
<>
|
||||
<div className="flex-1" />
|
||||
<CaretRight weight="fill" size={12} alt="" />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,107 +0,0 @@
|
|||
import * as RadixCM from "@radix-ui/react-context-menu"
|
||||
import clsx from "clsx";
|
||||
import { CaretRight, Icon } from "phosphor-react";
|
||||
import { HTMLAttributes, PropsWithChildren, Suspense, } from "react"
|
||||
import React from 'react'
|
||||
import { cva, VariantProps } from "class-variance-authority";
|
||||
|
||||
interface Props extends RadixCM.MenuContentProps {
|
||||
trigger: React.ReactNode,
|
||||
}
|
||||
|
||||
const MENU_CLASSES = `
|
||||
flex flex-col
|
||||
min-w-[11rem] p-2 space-y-1
|
||||
text-left text-sm dark:text-gray-100 text-gray-800
|
||||
bg-gray-50 border-gray-200 dark:bg-gray-950
|
||||
shadow-md shadow-gray-300 dark:shadow-gray-750
|
||||
select-none cursor-default rounded-lg
|
||||
`;
|
||||
|
||||
export const ContextMenu = ({ trigger, children, className, ...props }: PropsWithChildren<Props>) => {
|
||||
return (
|
||||
<RadixCM.Root>
|
||||
<RadixCM.Trigger>{trigger}</RadixCM.Trigger>
|
||||
<RadixCM.Portal>
|
||||
<RadixCM.Content {...props} className={clsx(MENU_CLASSES, className)}>
|
||||
{children}
|
||||
</RadixCM.Content>
|
||||
</RadixCM.Portal>
|
||||
</RadixCM.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export const Separator = () =>
|
||||
<RadixCM.Separator className="mx-2 border-0 border-b pointer-events-none border-b-gray-300 dark:border-b-gray-600" />
|
||||
|
||||
export const SubMenu = ({ label, icon, className, ...props }: RadixCM.MenuSubContentProps & ItemProps) => {
|
||||
return (
|
||||
<RadixCM.Sub>
|
||||
<RadixCM.SubTrigger className="[&[data-state='open']_div]:bg-primary focus:outline-none">
|
||||
<DivItem rightArrow {...{ label, icon }} />
|
||||
</RadixCM.SubTrigger>
|
||||
<RadixCM.Portal>
|
||||
<Suspense fallback={null}>
|
||||
<RadixCM.SubContent {...props} className={clsx(MENU_CLASSES, "-mt-2", className)} />
|
||||
</Suspense>
|
||||
</RadixCM.Portal>
|
||||
</RadixCM.Sub>
|
||||
)
|
||||
}
|
||||
|
||||
const ITEM_CLASSES = `
|
||||
flex flex-row items-center justify-start flex-1
|
||||
px-2 py-1 space-x-2
|
||||
cursor-default rounded
|
||||
focus:outline-none
|
||||
`;
|
||||
|
||||
const itemStyles = cva([ITEM_CLASSES], {
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'hover:bg-primary focus:bg-primary',
|
||||
danger: `
|
||||
text-red-600 dark:text-red-400
|
||||
hover:text-white focus:text-white
|
||||
hover:bg-red-500 focus:bg-red-500
|
||||
`
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
})
|
||||
|
||||
interface ItemProps extends VariantProps<typeof itemStyles> {
|
||||
icon?: Icon,
|
||||
rightArrow?: boolean,
|
||||
label?: string,
|
||||
}
|
||||
|
||||
export const Item = ({ icon, label, rightArrow, children, variant, ...props }: ItemProps & RadixCM.MenuItemProps) => (
|
||||
<RadixCM.Item {...props} className={itemStyles({ variant })}>
|
||||
{children ? children : <ItemInternals {...{icon, label, rightArrow}} />}
|
||||
</RadixCM.Item>
|
||||
)
|
||||
|
||||
const DivItem = ({ variant, ...props }: ItemProps) => (
|
||||
<div className={itemStyles({ variant })}>
|
||||
<ItemInternals {...props} />
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
const ItemInternals = ({ icon, label, rightArrow }: ItemProps) => {
|
||||
const ItemIcon = icon;
|
||||
return (
|
||||
<>
|
||||
{ItemIcon && <ItemIcon size={18} />}
|
||||
{label && <p>{label}</p>}
|
||||
|
||||
{rightArrow && <>
|
||||
<div className="flex-1"/>
|
||||
<CaretRight weight="fill" size={12} alt=""/>
|
||||
</>}
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
export * from './Button';
|
||||
export * from './Dropdown';
|
||||
export * from './ContextMenu';
|
||||
export * as NewContextMenu from "./ContextMenu/index"
|
||||
export * as ContextMenu from './ContextMenu';
|
||||
export * from './Input';
|
||||
|
|
Loading…
Reference in a new issue