Skip to content

Commit 650680b

Browse files
pokyunnElementalCrisisharshithmohan
authored
QoL: Add "Rename/Move" action to "Edit Group" and "Edit Series" modal (#1179)
* QoL: Add "Rename/Move" action to "Edit Group" and "Edit Series" modal This allow to call default renamer, on related files from Collection tab, making easier to rename files after organized in shoko groups * refactor to use the dedicated endpoint * Display message when targeting unsupported server version * Apply suggestions from code review Co-authored-by: Harshith Mohan <[email protected]> * Apply suggestions from code review --------- Co-authored-by: ElementalCrisis <[email protected]> Co-authored-by: Harshith Mohan <[email protected]>
1 parent b8bdeac commit 650680b

File tree

7 files changed

+96
-1
lines changed

7 files changed

+96
-1
lines changed

src/components/Collection/Group/EditGroupModal.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useDispatch, useSelector } from 'react-redux';
33
import cx from 'classnames';
44
import { map } from 'lodash';
55

6+
import FileActionsTab from '@/components/Collection/Group/EditGroupTabs/FileActionsTab';
67
import NameTab from '@/components/Collection/Group/EditGroupTabs/NameTab';
78
import SeriesTab from '@/components/Collection/Group/EditGroupTabs/SeriesTab';
89
import ModalPanel from '@/components/Panels/ModalPanel';
@@ -14,6 +15,7 @@ import type { RootState } from '@/core/store';
1415
const tabs = {
1516
name: 'Name',
1617
series: 'Series',
18+
file_actions: 'File Actions',
1719
};
1820

1921
const renderTab = (activeTab: string, groupId: number) => {
@@ -24,6 +26,8 @@ const renderTab = (activeTab: string, groupId: number) => {
2426
switch (activeTab) {
2527
case 'series':
2628
return <SeriesTab groupId={groupId} />;
29+
case 'file_actions':
30+
return <FileActionsTab groupId={groupId} />;
2731
case 'name':
2832
default:
2933
return <NameTab groupId={groupId} />;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
import cx from 'classnames';
3+
4+
const Action = (
5+
{ description, name, onClick, scroll }: { name: string, description: string, scroll?: boolean, onClick: () => void },
6+
) => (
7+
<div
8+
className={cx(
9+
'flex flex-row justify-between gap-y-2 cursor-pointer hover:text-panel-text-primary transition-colors',
10+
scroll ? 'mr-4' : '',
11+
)}
12+
onClick={onClick}
13+
>
14+
<div className="flex w-full flex-col gap-y-1">
15+
<div className="flex justify-between">
16+
<div>{name}</div>
17+
</div>
18+
<div className="text-sm text-panel-text opacity-65">{description}</div>
19+
</div>
20+
</div>
21+
);
22+
23+
export default React.memo(Action);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from 'react';
2+
3+
import Action from '@/components/Collection/Group/EditGroupTabs/Action';
4+
import toast from '@/components/Toast';
5+
import { useRelocateGroupFilesMutation } from '@/core/react-query/group/mutations';
6+
import useIsFeatureSupported, { FeatureType } from '@/hooks/useIsFeatureSupported';
7+
8+
type Props = {
9+
groupId: number;
10+
};
11+
12+
const FileActionsTab = ({ groupId }: Props) => {
13+
const { mutate: relocateGroupFiles } = useRelocateGroupFilesMutation(groupId);
14+
const showUnsupportedToast = () => {
15+
toast.error(`This feature require server version >= ${FeatureType.RelocateSeriesFiles}`);
16+
};
17+
18+
return (
19+
<div className="flex h-[22rem] grow flex-col gap-y-4 overflow-y-auto">
20+
<Action
21+
name="Rename/Move Files"
22+
description="Rename/Move every file associated with the group."
23+
onClick={useIsFeatureSupported(FeatureType.RelocateSeriesFiles) ? relocateGroupFiles : showUnsupportedToast}
24+
/>
25+
</div>
26+
);
27+
};
28+
29+
export default FileActionsTab;

src/components/Collection/Series/EditSeriesTabs/FileActionsTab.tsx

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import React from 'react';
22

33
import Action from '@/components/Collection/Series/EditSeriesTabs/Action';
4-
import { useRehashSeriesFilesMutation, useRescanSeriesFilesMutation } from '@/core/react-query/series/mutations';
4+
import toast from '@/components/Toast';
5+
import {
6+
useRehashSeriesFilesMutation,
7+
useRelocateSeriesFilesMutation,
8+
useRescanSeriesFilesMutation,
9+
} from '@/core/react-query/series/mutations';
10+
import useIsFeatureSupported, { FeatureType } from '@/hooks/useIsFeatureSupported';
511

612
type Props = {
713
seriesId: number;
@@ -10,6 +16,10 @@ type Props = {
1016
const FileActionsTab = ({ seriesId }: Props) => {
1117
const { mutate: rehashSeriesFiles } = useRehashSeriesFilesMutation(seriesId);
1218
const { mutate: rescanSeriesFiles } = useRescanSeriesFilesMutation(seriesId);
19+
const { mutate: relocateSeriesFiles } = useRelocateSeriesFilesMutation(seriesId);
20+
const showUnsupportedToast = () => {
21+
toast.error(`This feature require server version >= ${FeatureType.RelocateSeriesFiles}`);
22+
};
1323

1424
return (
1525
<div className="flex h-[22rem] grow flex-col gap-y-4 overflow-y-auto">
@@ -23,6 +33,11 @@ const FileActionsTab = ({ seriesId }: Props) => {
2333
description="Rehashes every file associated with the series."
2434
onClick={rehashSeriesFiles}
2535
/>
36+
<Action
37+
name="Rename/Move Files"
38+
description="Rename/Move every file associated with the group."
39+
onClick={useIsFeatureSupported(FeatureType.RelocateSeriesFiles) ? relocateSeriesFiles : showUnsupportedToast}
40+
/>
2641
</div>
2742
);
2843
};

src/core/react-query/group/mutations.ts

+17
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { axios } from '@/core/axios';
55
import { invalidateQueries } from '@/core/react-query/queryClient';
66

77
import type { MoveSeriesGroupRequestType, PatchGroupRequestType } from '@/core/react-query/group/types';
8+
import type { SeriesType } from '@/core/types/api/series';
89

910
// TODO: FIX INVALIDATIONS
1011

@@ -57,3 +58,19 @@ export const useMoveGroupMutation = () =>
5758
toast.success('Moved series into new group!');
5859
},
5960
});
61+
62+
export const useRelocateGroupFilesMutation = (groupId: number) =>
63+
useMutation({
64+
mutationFn: async () => {
65+
const targetSeries = await axios.get<unknown, SeriesType[]>(`Group/${groupId}/Series`, {
66+
params: { recursive: true },
67+
});
68+
69+
return Promise.all(
70+
targetSeries.map(
71+
series => axios.post(`Series/${series.IDs.ID}/File/Relocate`),
72+
),
73+
);
74+
},
75+
onSuccess: () => toast.success('Group files renamed/moved!'),
76+
});

src/core/react-query/series/mutations.ts

+6
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,9 @@ export const useSyncSeriesTraktMutation = (seriesId: number) =>
148148
mutationFn: () => axios.post(`Series/${seriesId}/Trakt/Sync`),
149149
onSuccess: () => toast.success('Trakt sync queued!'),
150150
});
151+
152+
export const useRelocateSeriesFilesMutation = (seriesId: number) =>
153+
useMutation({
154+
mutationFn: () => axios.post(`Series/${seriesId}/File/Relocate`),
155+
onSuccess: () => toast.success('Series files renamed/moved!'),
156+
});

src/hooks/useIsFeatureSupported.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { VersionType } from '@/core/types/api/init';
88

99
export enum FeatureType {
1010
Placeholder = '5.0.0.0', // This is as a placeholder so the string conversion for `parseServerVersion` works and also serves as an example
11+
RelocateSeriesFiles = '5.1.0.35',
1112
}
1213

1314
const useIsFeatureSupported = (feature: FeatureType) => {

0 commit comments

Comments
 (0)