📃 WIP bottom sheet
This commit is contained in:
32
package-lock.json
generated
32
package-lock.json
generated
@@ -58,7 +58,6 @@
|
||||
"react-i18next": "^16.5.4",
|
||||
"react-loader-spinner": "^8.0.2",
|
||||
"react-modal": "^3.16.3",
|
||||
"react-modal-sheet": "^5.6.0",
|
||||
"react-router": "^7.13.1",
|
||||
"react-router-dom": "^7.13.1",
|
||||
"react-select": "^5.10.2",
|
||||
@@ -15967,22 +15966,6 @@
|
||||
"react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/react-modal-sheet": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-modal-sheet/-/react-modal-sheet-5.6.0.tgz",
|
||||
"integrity": "sha512-+WE2nVPdB/Jx0QbndIBqGvy6k0IXriW2lFaPeZSW1xOVri6rWhAwrSnArtsR1rxOxW8HBdAYeIPUcbjMvNeeyw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-use-measure": "2.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"motion": ">=11",
|
||||
"react": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-refresh": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
|
||||
@@ -16138,21 +16121,6 @@
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-use-measure": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz",
|
||||
"integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13",
|
||||
"react-dom": ">=16.13"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build",
|
||||
"codegen": "wait-on http-get://localhost:3000/graphql/health && graphql-codegen",
|
||||
"codegen:watch": "graphql-codegen --config codegen.yml --watch"
|
||||
"codegen:watch": "graphql-codegen --config codegen.yml --watch",
|
||||
"knip": "knip"
|
||||
},
|
||||
"author": "Rishi Ghan",
|
||||
"license": "MIT",
|
||||
@@ -67,7 +68,6 @@
|
||||
"react-i18next": "^16.5.4",
|
||||
"react-loader-spinner": "^8.0.2",
|
||||
"react-modal": "^3.16.3",
|
||||
"react-modal-sheet": "^5.6.0",
|
||||
"react-router": "^7.13.1",
|
||||
"react-router-dom": "^7.13.1",
|
||||
"react-select": "^5.10.2",
|
||||
@@ -79,6 +79,7 @@
|
||||
"socket.io-client": "^4.8.3",
|
||||
"styled-components": "^6.3.11",
|
||||
"threetwo-ui-typings": "^1.0.14",
|
||||
"vaul": "^1.1.2",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"websocket": "^1.0.35",
|
||||
@@ -111,7 +112,7 @@
|
||||
"@types/ellipsize": "^0.1.3",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^25.3.0",
|
||||
"@types/node": "^25.5.2",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/react-redux": "^7.1.34",
|
||||
@@ -140,7 +141,7 @@
|
||||
"tailwindcss": "^4.2.1",
|
||||
"ts-jest": "^29.4.6",
|
||||
"tui-jsdoc-template": "^1.2.2",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript": "^6.0.2",
|
||||
"wait-on": "^9.0.4"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, ReactElement, useCallback } from "react";
|
||||
import React, { useState, ReactElement, useCallback, useMemo } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import Card from "../shared/Carda";
|
||||
import { RawFileDetails } from "./RawFileDetails";
|
||||
@@ -141,11 +141,9 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
||||
});
|
||||
|
||||
// Query for airdc++
|
||||
const airDCPPQuery = {
|
||||
issue: {
|
||||
name: issueName,
|
||||
},
|
||||
};
|
||||
const airDCPPQuery = useMemo(() => ({
|
||||
issue: { name: issueName },
|
||||
}), [issueName]);
|
||||
|
||||
// Create tab configuration
|
||||
const openReconcilePanel = useCallback(() => {
|
||||
@@ -153,7 +151,7 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
||||
setVisible(true);
|
||||
}, []);
|
||||
|
||||
const tabGroup = createTabConfig({
|
||||
const tabGroup = useMemo(() => createTabConfig({
|
||||
data: data.data,
|
||||
hasAnyMetadata,
|
||||
areRawFileDetailsAvailable,
|
||||
@@ -163,9 +161,9 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
||||
issueName,
|
||||
acquisition,
|
||||
onReconcileMetadata: openReconcilePanel,
|
||||
});
|
||||
}), [data.data, hasAnyMetadata, areRawFileDetailsAvailable, airDCPPQuery, _id, userSettings, issueName, acquisition, openReconcilePanel]);
|
||||
|
||||
const filteredTabs = tabGroup.filter((tab) => tab.shouldShow);
|
||||
const filteredTabs = useMemo(() => tabGroup.filter((tab) => tab.shouldShow), [tabGroup]);
|
||||
|
||||
// Sliding panel content mapping
|
||||
const renderSlidingPanelContent = () => {
|
||||
|
||||
@@ -47,10 +47,12 @@ export const TabControls = (props): ReactElement => {
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<Suspense>
|
||||
{filteredTabs.map(({ id, content }) => {
|
||||
return currentActive === id ? content : null;
|
||||
})}
|
||||
<Suspense fallback={null}>
|
||||
{filteredTabs.map(({ id, content }) => (
|
||||
<React.Fragment key={id}>
|
||||
{currentActive === id ? content : null}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -131,10 +131,12 @@ export const ArchiveOperations = (props: { data: any }): ReactElement => {
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
if (isSuccess && shouldRefetchComicBookData) {
|
||||
queryClient.invalidateQueries({ queryKey: ["comicBookMetadata"] });
|
||||
setShouldRefetchComicBookData(false);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (isSuccess && shouldRefetchComicBookData) {
|
||||
queryClient.invalidateQueries({ queryKey: ["comicBookMetadata"] });
|
||||
setShouldRefetchComicBookData(false);
|
||||
}
|
||||
}, [isSuccess, shouldRefetchComicBookData, queryClient]);
|
||||
|
||||
// sliding panel init
|
||||
const contentForSlidingPanel: Record<string, { content: () => JSX.Element }> = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { ReactElement, useMemo, useState } from "react";
|
||||
import { isEmpty, isNil } from "lodash";
|
||||
import { Sheet } from "react-modal-sheet";
|
||||
import { Drawer } from "vaul";
|
||||
import ComicVineDetails from "../ComicVineDetails";
|
||||
|
||||
interface ComicVineMetadata {
|
||||
@@ -60,10 +60,8 @@ const SOURCE_ICONS: Record<string, string> = {
|
||||
|
||||
const MetadataSourceChips = ({
|
||||
sources,
|
||||
onReconcile,
|
||||
}: {
|
||||
sources: string[];
|
||||
onReconcile: () => void;
|
||||
}): ReactElement => {
|
||||
const [isSheetOpen, setSheetOpen] = useState(false);
|
||||
|
||||
@@ -98,17 +96,18 @@ const MetadataSourceChips = ({
|
||||
Reconcile sources
|
||||
</button>
|
||||
|
||||
<Sheet isOpen={isSheetOpen} onClose={() => setSheetOpen(false)}>
|
||||
<Sheet.Container>
|
||||
<Sheet.Header />
|
||||
<Sheet.Content>
|
||||
<Drawer.Root open={isSheetOpen} onOpenChange={setSheetOpen}>
|
||||
<Drawer.Portal>
|
||||
<Drawer.Overlay className="fixed inset-0 bg-black/40" />
|
||||
<Drawer.Content aria-describedby={undefined} className="fixed bottom-0 left-0 right-0 rounded-t-2xl bg-white dark:bg-slate-800 p-4 outline-none">
|
||||
<Drawer.Title className="sr-only">Reconcile metadata sources</Drawer.Title>
|
||||
<div className="mx-auto mb-4 h-1.5 w-12 rounded-full bg-slate-300 dark:bg-slate-600" />
|
||||
<div className="p-4">
|
||||
{/* Reconciliation UI goes here */}
|
||||
</div>
|
||||
</Sheet.Content>
|
||||
</Sheet.Container>
|
||||
<Sheet.Backdrop />
|
||||
</Sheet>
|
||||
</Drawer.Content>
|
||||
</Drawer.Portal>
|
||||
</Drawer.Root>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -127,7 +126,7 @@ const MetadataSourceChips = ({
|
||||
export const VolumeInformation = (
|
||||
props: VolumeInformationProps,
|
||||
): ReactElement => {
|
||||
const { data, onReconcile } = props;
|
||||
const { data } = props;
|
||||
|
||||
const presentSources = useMemo(() => {
|
||||
const sources = SOURCED_METADATA_KEYS.filter((key) => {
|
||||
@@ -152,10 +151,7 @@ export const VolumeInformation = (
|
||||
return (
|
||||
<div key={1}>
|
||||
{presentSources.length > 1 && (
|
||||
<MetadataSourceChips
|
||||
sources={presentSources}
|
||||
onReconcile={onReconcile ?? (() => {})}
|
||||
/>
|
||||
<MetadataSourceChips sources={presentSources} />
|
||||
)}
|
||||
{presentSources.length === 1 &&
|
||||
data.sourcedMetadata?.comicvine?.volumeInformation && (
|
||||
|
||||
@@ -46,7 +46,7 @@ export const createTabConfig = ({
|
||||
<i className="h-5 w-5 icon-[solar--book-2-bold] text-slate-500 dark:text-slate-300"></i>
|
||||
),
|
||||
content: hasAnyMetadata ? (
|
||||
<VolumeInformation data={data} onReconcile={onReconcileMetadata} key={1} />
|
||||
<VolumeInformation data={data} onReconcile={onReconcileMetadata} />
|
||||
) : null,
|
||||
shouldShow: hasAnyMetadata,
|
||||
},
|
||||
@@ -56,7 +56,7 @@ export const createTabConfig = ({
|
||||
<i className="h-5 w-5 icon-[solar--winrar-bold-duotone] text-slate-500 dark:text-slate-300" />
|
||||
),
|
||||
name: "Archive Operations",
|
||||
content: <ArchiveOperations data={data} key={3} />,
|
||||
content: <ArchiveOperations data={data} />,
|
||||
shouldShow: areRawFileDetailsAvailable,
|
||||
},
|
||||
{
|
||||
@@ -71,7 +71,6 @@ export const createTabConfig = ({
|
||||
comicObjectId={comicObjectId}
|
||||
comicObject={data}
|
||||
settings={userSettings}
|
||||
key={4}
|
||||
/>
|
||||
),
|
||||
shouldShow: true,
|
||||
@@ -98,7 +97,7 @@ export const createTabConfig = ({
|
||||
),
|
||||
content:
|
||||
!isNil(data) && !isEmpty(data) ? (
|
||||
<DownloadsPanel key={5} />
|
||||
<DownloadsPanel />
|
||||
) : (
|
||||
<div className="column is-three-fifths">
|
||||
<article className="message is-info">
|
||||
|
||||
@@ -35,6 +35,9 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
dedupe: ["react", "react-dom"],
|
||||
},
|
||||
esbuild: {
|
||||
supported: {
|
||||
"top-level-await": true, //browsers can handle top-level-await features
|
||||
|
||||
Reference in New Issue
Block a user