Skip to content

Commit

Permalink
Feat: Add the EditSeriesModal to the collection view
Browse files Browse the repository at this point in the history
  • Loading branch information
fearnlj01 committed May 25, 2024
1 parent d421d6b commit 6424e0f
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 58 deletions.
10 changes: 9 additions & 1 deletion src/components/Collection/CollectionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Props = {
items: CollectionGroupType[] | SeriesType[];
mode: string;
total: number;
setEditSeriesModalId: (seriesId: number) => void;
};

const CollectionView = (props: Props) => {
Expand All @@ -37,6 +38,7 @@ const CollectionView = (props: Props) => {
isSidebarOpen,
items,
mode,
setEditSeriesModalId,
total,
} = props;

Expand Down Expand Up @@ -146,7 +148,12 @@ const CollectionView = (props: Props) => {
);
} else if (mode === 'poster') {
children.push(
<PosterViewItem item={item} key={`group-${item.IDs.ID}`} isSeries={isSeries} />,
<PosterViewItem
key={`group-${item.IDs.ID}`}
item={item}
isSeries={isSeries}
setEditSeriesModalId={setEditSeriesModalId}
/>,
);
} else {
children.push(
Expand All @@ -158,6 +165,7 @@ const CollectionView = (props: Props) => {
key={`group-${item.IDs.ID}`}
isSeries={isSeries}
isSidebarOpen={isSidebarOpen}
setEditSeriesModalId={setEditSeriesModalId}
/>,
);
}
Expand Down
29 changes: 21 additions & 8 deletions src/components/Collection/ListViewItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { listItemSize } from '@/components/Collection/constants';
import { useSeriesTagsQuery } from '@/core/react-query/series/queries';
import { useSettingsQuery } from '@/core/react-query/settings/queries';
import { dayjs, formatThousand } from '@/core/util';
import useEventCallback from '@/hooks/useEventCallback';
import useMainPoster from '@/hooks/useMainPoster';

import AnidbDescription from './AnidbDescription';
Expand Down Expand Up @@ -58,9 +59,10 @@ type Props = {
isSeries?: boolean;
groupExtras?: WebuiGroupExtra;
isSidebarOpen: boolean;
setEditSeriesModalId: (seriesId: number) => void;
};

const ListViewItem = ({ groupExtras, isSeries, isSidebarOpen, item }: Props) => {
const ListViewItem = ({ groupExtras, isSeries, isSidebarOpen, item, setEditSeriesModalId }: Props) => {
const settings = useSettingsQuery().data;
const { showCustomTags, showGroupIndicator, showItemType, showTopTags } = settings.WebUI_Settings.collection.list;

Expand Down Expand Up @@ -120,6 +122,12 @@ const ListViewItem = ({ groupExtras, isSeries, isSidebarOpen, item }: Props) =>
[isSeries, groupExtras?.Tags, tagsQuery.data, showCustomTags, showTopTags],
);

const editSeriesModalCallback = useEventCallback((event: React.MouseEvent) => {
event.stopPropagation();
event.preventDefault();
setEditSeriesModalId(('MainSeries' in item.IDs) ? item.IDs.MainSeries : item.IDs.ID);
});

return (
<div
className="flex h-full shrink-0 grow flex-col content-center gap-y-3 rounded-lg border border-panel-border bg-panel-background p-6"
Expand All @@ -136,13 +144,18 @@ const ListViewItem = ({ groupExtras, isSeries, isSidebarOpen, item }: Props) =>
zoomOnHover
>
<div className="pointer-events-none z-10 flex h-full bg-panel-background-poster-overlay p-3 opacity-0 transition-opacity group-hover:pointer-events-auto group-hover:opacity-100">
<Link to="#" className="h-fit">
<Icon
path={mdiPencilCircleOutline}
size="2rem"
className="text-panel-icon"
/>
</Link>
{(isSeries || item.Size === 1) && (
<div
className="pointer-events-auto h-fit"
onClick={editSeriesModalCallback}
>
<Icon
path={mdiPencilCircleOutline}
size="2rem"
className="text-panel-icon"
/>
</div>
)}
</div>
{showGroupIndicator && groupCount > 1 && (
<div className="absolute bottom-0 left-0 flex w-full justify-center rounded-bl-md bg-panel-background-overlay py-1.5 text-sm font-semibold opacity-100 transition-opacity group-hover:opacity-0">
Expand Down
32 changes: 22 additions & 10 deletions src/components/Collection/PosterViewItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { reduce } from 'lodash';

import BackgroundImagePlaceholderDiv from '@/components/BackgroundImagePlaceholderDiv';
import { useSettingsQuery } from '@/core/react-query/settings/queries';
import useEventCallback from '@/hooks/useEventCallback';
import useMainPoster from '@/hooks/useMainPoster';

import type { CollectionGroupType } from '@/core/types/api/collection';
Expand All @@ -14,9 +15,10 @@ import type { SeriesType } from '@/core/types/api/series';
type Props = {
item: CollectionGroupType | SeriesType;
isSeries?: boolean;
setEditSeriesModalId: (seriesId: number) => void;
};

const PosterViewItem = ({ isSeries = false, item }: Props) => {
const PosterViewItem = ({ isSeries = false, item, setEditSeriesModalId }: Props) => {
const settings = useSettingsQuery().data;
const { showEpisodeCount, showGroupIndicator, showUnwatchedCount } = settings.WebUI_Settings.collection.poster;

Expand All @@ -43,6 +45,12 @@ const PosterViewItem = ({ isSeries = false, item }: Props) => {
return link;
};

const editSeriesModalCallback = useEventCallback((event: React.MouseEvent) => {
event.stopPropagation();
event.preventDefault();
setEditSeriesModalId(('MainSeries' in item.IDs) ? item.IDs.MainSeries : item.IDs.ID);
});

return (
<Link to={viewRouteLink()}>
<div
Expand All @@ -62,15 +70,19 @@ const PosterViewItem = ({ isSeries = false, item }: Props) => {
)}
</div>
)}
<div className="pointer-events-none z-50 flex h-full bg-panel-background-poster-overlay p-3 opacity-0 transition-opacity group-hover:pointer-events-auto group-hover:opacity-100">
{/* FIXME: This can't be a <Link> otherwise Warning: validateDOMNesting(...): <a> cannot appear as a descendant of <a> happens, BackgroundImagePlaceholderDiv wraps everything in a <Link> internally */}
<span className="h-fit">
<Icon
path={mdiPencilCircleOutline}
size="2rem"
className="text-panel-icon"
/>
</span>
<div className="pointer-events-none z-10 flex h-full bg-panel-background-poster-overlay p-3 opacity-0 transition-opacity group-hover:pointer-events-auto group-hover:opacity-100">
{(isSeries || item.Size === 1) && (
<div
className="pointer-events-auto h-fit"
onClick={editSeriesModalCallback}
>
<Icon
path={mdiPencilCircleOutline}
size="2rem"
className="text-panel-icon"
/>
</div>
)}
</div>
{showGroupIndicator && !isSeries && groupCount > 1 && (
<div className="absolute bottom-4 left-3 flex w-[90%] justify-center rounded-lg bg-panel-background-overlay py-2 text-sm font-semibold text-panel-text opacity-100 transition-opacity group-hover:opacity-0">
Expand Down
54 changes: 28 additions & 26 deletions src/components/Collection/Series/EditSeriesModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import ModalPanel from '@/components/Panels/ModalPanel';
type Props = {
show: boolean;
onClose: () => void;
seriesId: number;
seriesId?: number;
};

const tabs = {
Expand Down Expand Up @@ -50,33 +50,35 @@ const EditSeriesModal = (props: Props) => {

const [activeTab, setActiveTab] = useState('update_actions');

return (
<ModalPanel show={show} onRequestClose={onClose} header="Edit Series" size="md" noPadding noGap>
<div className="flex h-[22rem] flex-row gap-x-6 p-6">
<div className="flex shrink-0 gap-y-6 font-semibold">
<div className="flex flex-col gap-y-1">
{map(tabs, (value, key) => (
<div
className={cx(
activeTab === key
? 'w-[12rem] text-center bg-panel-menu-item-background p-3 rounded-lg text-panel-menu-item-text cursor-pointer'
: 'w-[12rem] text-center p-3 rounded-lg hover:bg-panel-menu-item-background-hover cursor-pointer',
)}
key={key}
onClick={() => setActiveTab(key)}
>
{value}
</div>
))}
return (!seriesId)
? null
: (
<ModalPanel show={show} onRequestClose={onClose} header="Edit Series" size="md" noPadding noGap>
<div className="flex h-[22rem] flex-row gap-x-6 p-6">
<div className="flex shrink-0 gap-y-6 font-semibold">
<div className="flex flex-col gap-y-1">
{map(tabs, (value, key) => (
<div
className={cx(
activeTab === key
? 'w-[12rem] text-center bg-panel-menu-item-background p-3 rounded-lg text-panel-menu-item-text cursor-pointer'
: 'w-[12rem] text-center p-3 rounded-lg hover:bg-panel-menu-item-background-hover cursor-pointer',
)}
key={key}
onClick={() => setActiveTab(key)}
>
{value}
</div>
))}
</div>
</div>
<div className="border-r border-panel-border" />
<div className="grow">
{renderTab(activeTab, seriesId)}
</div>
</div>
<div className="border-r border-panel-border" />
<div className="grow">
{renderTab(activeTab, seriesId)}
</div>
</div>
</ModalPanel>
);
</ModalPanel>
);
};

export default EditSeriesModal;
34 changes: 21 additions & 13 deletions src/components/Collection/Series/EditSeriesTabs/NameTab.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import React, { useEffect, useMemo, useState } from 'react';
import { mdiCheckUnderlineCircleOutline, mdiCloseCircleOutline, mdiPencilCircleOutline } from '@mdi/js';
import cx from 'classnames';
import { map } from 'lodash';
import { useToggle } from 'usehooks-ts';

import Input from '@/components/Input/Input';
import toast from '@/components/Toast';
import { useOverrideSeriesTitleMutation } from '@/core/react-query/series/mutations';
import { useSeriesQuery } from '@/core/react-query/series/queries';

import type { SeriesType } from '@/core/types/api/series';

type Props = {
seriesId: number;
};
Expand All @@ -19,38 +16,39 @@ const NameTab = ({ seriesId }: Props) => {
const [name, setName] = useState('');
const [nameEditable, toggleNameEditable] = useToggle(false);

const seriesQuery = useSeriesQuery(seriesId, { includeDataFrom: ['AniDB'] });
const series = useMemo(() => seriesQuery?.data ?? {} as SeriesType, [seriesQuery.data]);
const { data: seriesData, isError, isFetching, isSuccess } = useSeriesQuery(seriesId, { includeDataFrom: ['AniDB'] });

const { mutate: overrideTitle } = useOverrideSeriesTitleMutation();

useEffect(() => {
setName(series.Name ?? '');
}, [series.Name]);
setName(seriesData?.Name ?? '');
}, [seriesData?.Name]);

const nameInputIcons = useMemo(() => {
if (!nameEditable || seriesQuery.isFetching) {
if (!nameEditable || isFetching) {
return [{
icon: mdiPencilCircleOutline,
className: 'text-panel-text-primary',
onClick: toggleNameEditable,
}];
}

if (!isSuccess) return [];

return [
{
icon: mdiCloseCircleOutline,
className: 'text-panel-text-danger',
onClick: () => {
setName(series.Name);
setName(seriesData.Name ?? '');
toggleNameEditable();
},
},
{
icon: mdiCheckUnderlineCircleOutline,
className: 'text-panel-text-primary',
onClick: () =>
overrideTitle({ seriesId: series.IDs.ID, Title: name }, {
overrideTitle({ seriesId: seriesData.IDs.ID, Title: name }, {
onSuccess: () => {
toast.success('Name updated successfully!');
toggleNameEditable();
Expand All @@ -59,11 +57,20 @@ const NameTab = ({ seriesId }: Props) => {
}),
},
];
}, [name, nameEditable, overrideTitle, series.IDs.ID, series.Name, seriesQuery.isFetching, toggleNameEditable]);
}, [
isFetching,
isSuccess,
name,
nameEditable,
overrideTitle,
seriesData?.IDs.ID,
seriesData?.Name,
toggleNameEditable,
]);

return (
<div className="flex h-full flex-col">
{seriesQuery.isError && (
{isError && (
<div className="m-auto text-lg font-semibold text-panel-text-danger">
Series data could not be loaded!
</div>
Expand All @@ -74,6 +81,7 @@ const NameTab = ({ seriesId }: Props) => {
type="text"
onChange={e => setName(e.target.value)}
value={name}
placeholder="Loading..."
label="Name"
className="mb-4"
inputClassName={cx(nameInputIcons.length > 1 ? 'pr-[4.5rem]' : 'pr-10', 'truncate')}
Expand All @@ -83,7 +91,7 @@ const NameTab = ({ seriesId }: Props) => {
{nameEditable && (
<div className="flex cursor-pointer overflow-y-auto rounded-lg border border-panel-border bg-panel-input p-6">
<div className="shoko-scrollbar flex grow flex-col gap-y-2 overflow-y-auto bg-panel-input pr-4">
{map(series.AniDB?.Titles, title => (
{seriesData?.AniDB?.Titles.map(title => (
<div
className="flex justify-between last:border-none hover:text-panel-text-primary"
key={title.Name + title.Language}
Expand Down
16 changes: 16 additions & 0 deletions src/pages/collection/Collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useDebounceValue, useToggle } from 'usehooks-ts';
import CollectionTitle from '@/components/Collection/CollectionTitle';
import CollectionView from '@/components/Collection/CollectionView';
import FilterSidebar from '@/components/Collection/Filter/FilterSidebar';
import EditSeriesModal from '@/components/Collection/Series/EditSeriesModal';
import TimelineSidebar from '@/components/Collection/TimelineSidebar';
import TitleOptions from '@/components/Collection/TitleOptions';
import buildFilter from '@/core/buildFilter';
Expand Down Expand Up @@ -101,6 +102,15 @@ function Collection() {
const [seriesSearch, setSeriesSearch] = useState('');
const [debouncedSeriesSearch] = useDebounceValue(seriesSearch, 200);

const [showEditSeriesModal, toggleEditSeriesModal] = useToggle(false);
const [editSeriesModalId, setEditSeriesModalId] = useState<number>();
const openEditModalWithSeriesId = useEventCallback((seriesId: number) => {
setEditSeriesModalId(() => {
toggleEditSeriesModal();
return seriesId;
});
});

const { mutate: patchSettings } = usePatchSettingsMutation();

useEffect(() => {
Expand Down Expand Up @@ -230,6 +240,7 @@ function Collection() {
items={items}
mode={mode}
total={total}
setEditSeriesModalId={openEditModalWithSeriesId}
/>
<div
className={cx(
Expand All @@ -243,6 +254,11 @@ function Collection() {
</div>
{isSeries && <TimelineSidebar series={timelineSeries} isFetching={seriesQuery.isPending} />}
</div>
<EditSeriesModal
show={showEditSeriesModal}
onClose={toggleEditSeriesModal}
seriesId={editSeriesModalId}
/>
</div>
);
}
Expand Down

0 comments on commit 6424e0f

Please sign in to comment.