diff --git a/package.json b/package.json index fab4791..d4c6eb6 100644 --- a/package.json +++ b/package.json @@ -60,13 +60,14 @@ "react-masonry-css": "^1.0.16", "react-notification-system": "^0.4.0", "react-notification-system-redux": "^2.0.1", + "react-responsive-carousel": "^3.2.21", "react-select": "^4.3.1", "react-sliding-pane": "^7.0.0", "react-table": "^7.7.0", "react-window-dynamic-list": "^2.3.5", "sharp": "^0.28.1", "socket.io-client": "^4.1.2", - "threetwo-ui-typings": "^1.0.9", + "threetwo-ui-typings": "^1.0.10", "voca": "^1.4.0", "websocket": "^1.0.34", "ws": "^7.5.3", diff --git a/src/client/actions/comicinfo.actions.tsx b/src/client/actions/comicinfo.actions.tsx index 342dd93..cb335a7 100644 --- a/src/client/actions/comicinfo.actions.tsx +++ b/src/client/actions/comicinfo.actions.tsx @@ -1,6 +1,7 @@ import axios from "axios"; import rateLimiter from "axios-rate-limit"; import qs from "qs"; +import { IExtractionOptions } from "threetwo-ui-typings"; import { CV_SEARCH_SUCCESS, CV_API_CALL_IN_PROGRESS, @@ -8,6 +9,7 @@ import { IMS_COMIC_BOOK_DB_OBJECT_CALL_IN_PROGRESS, IMS_COMIC_BOOK_DB_OBJECT_CALL_FAILED, IMS_COMIC_BOOK_DB_OBJECT_FETCHED, + IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS, } from "../constants/action-types"; import { COMICBOOKINFO_SERVICE_URI } from "../constants/endpoints"; @@ -109,3 +111,22 @@ export const applyComicVineMatch = IMS_inProgress: false, }); }; + +export const extractComicArchive = + (path: string, options: IExtractionOptions) => async (dispatch) => { + const extractedComicBookArchive = await axios({ + method: "POST", + url: "http://localhost:3000/api/import/unrarArchive", + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + data: { + options, + filePath: path, + }, + }); + dispatch({ + type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS, + extractedComicBookArchive: extractedComicBookArchive.data, + }); + }; diff --git a/src/client/assets/scss/App.scss b/src/client/assets/scss/App.scss index 2072972..51b7c7a 100644 --- a/src/client/assets/scss/App.scss +++ b/src/client/assets/scss/App.scss @@ -28,11 +28,11 @@ $border-color: red; // Dashboard .recent-comics-container { - display: -webkit-box; /* Not needed if autoprefixing */ - display: -ms-flexbox; /* Not needed if autoprefixing */ - display: flex; - margin-left: -30px; /* gutter size offset */ - width: auto; + display: -webkit-box; /* Not needed if autoprefixing */ + display: -ms-flexbox; /* Not needed if autoprefixing */ + display: flex; + margin-left: -30px; /* gutter size offset */ + width: auto; .recent-comics-column { padding-left: 22px; /* gutter size */ @@ -88,10 +88,9 @@ $border-color: red; /* change div to reference your elements you put in */ margin-bottom: 20px; } - } + } } - .min { overflow: visible; margin: auto; @@ -281,6 +280,40 @@ $border-color: red; } // Comic Detail + +// extracted comic book archive carousel +.carousel-root { + margin: 0 auto; + border-radius: 10px; + box-shadow: 1px 8px 23px 7px rgba(0, 0, 0, 0.12); + max-width: 300px; + ul.slider { + li.slide { + img { + border-radius: 10px; + } + } + } + .control-next { + border-top-right-radius: 10px; + border-bottom-right-radius: 10px; + &:hover { + border-top-right-radius: 10px; + border-bottom-right-radius: 10px; + } +} + .control-prev { + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + &:hover { + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + } +} + +} + +// airdcpp downloads tab .tabs { .download-icon-labels { .downloads-count { @@ -368,7 +401,7 @@ $border-color: red; margin-bottom: 20px; } -// +// // progress .progress-indicator-container { height: 100%; diff --git a/src/client/components/ComicDetail.tsx b/src/client/components/ComicDetail.tsx index f57078d..d842b57 100644 --- a/src/client/components/ComicDetail.tsx +++ b/src/client/components/ComicDetail.tsx @@ -7,15 +7,21 @@ import ComicVineSearchForm from "./ComicVineSearchForm"; import AcquisitionPanel from "./AcquisitionPanel"; import DownloadsPanel from "./DownloadsPanel"; import SlidingPane from "react-sliding-pane"; +import "react-responsive-carousel/lib/styles/carousel.min.css"; +import { Carousel } from "react-responsive-carousel"; + import Select, { components } from "react-select"; import "react-sliding-pane/dist/react-sliding-pane.css"; import "react-loader-spinner/dist/loader/css/react-spinner-loader.css"; import Loader from "react-loader-spinner"; -import { isEmpty, isUndefined, isNil } from "lodash"; +import { isEmpty, isUndefined, isNil, map } from "lodash"; import { RootState } from "threetwo-ui-typings"; import { fetchComicVineMatches } from "../actions/fileops.actions"; -import { getComicBookDetailById } from "../actions/comicinfo.actions"; +import { + extractComicArchive, + getComicBookDetailById, +} from "../actions/comicinfo.actions"; import { detectTradePaperbacks } from "../shared/utils/tradepaperback.utils"; import dayjs from "dayjs"; const prettyBytes = require("pretty-bytes"); @@ -54,6 +60,9 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => { const comicBookDetailData = useSelector( (state: RootState) => state.comicInfo.comicBookDetail, ); + const extractedComicBookArchive = useSelector( + (state: RootState) => state.fileOps.extractedComicBookArchive, + ); const { comicObjectId } = useParams<{ comicObjectId: string }>(); const dispatch = useDispatch(); @@ -126,7 +135,45 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => { ); }, }, - editComicArchive: { content: () => <>bastard fucks }, + editComicArchive: { + content: () => ( + <> +
+ + Pages + + {extractedComicBookArchive.length} + + +
+ + {map(extractedComicBookArchive, (page, idx) => { + return ( +
+ +
+ ); + })} +
+ + ), + }, }; const openDrawerWithCVMatches = useCallback(() => { @@ -136,9 +183,23 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => { }, [dispatch, comicBookDetailData]); const openDrawerForEditingComicArchive = useCallback(() => { + dispatch( + extractComicArchive( + comicBookDetailData.rawFileDetails.containedIn + + "/" + + comicBookDetailData.rawFileDetails.name + + comicBookDetailData.rawFileDetails.extension, + { + extractTarget: "book", + targetExtractionFolder: + "./userdata/expanded/" + comicBookDetailData.rawFileDetails.name, + extractionMode: "all", + }, + ), + ); setSlidingPanelContentId("editComicArchive"); setVisible(true); - }, [dispatch]); + }, [dispatch, comicBookDetailData, extractedComicBookArchive]); const [active, setActive] = useState(1); const createDescriptionMarkup = (html) => { diff --git a/src/client/constants/action-types.ts b/src/client/constants/action-types.ts index b6fef3e..07db074 100644 --- a/src/client/constants/action-types.ts +++ b/src/client/constants/action-types.ts @@ -38,6 +38,14 @@ export const IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS = export const IMS_COMIC_BOOK_GROUPS_CALL_FAILED = "IMS_COMIC_BOOK_GROUPS_CALL_FAILED"; +// extracted comic archive +export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS = + "IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS"; +export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS = + "IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS"; +export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_FAILED = + "IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_FAILED"; + // AirDC++ export const AIRDCPP_SEARCH_IN_PROGRESS = "AIRDCPP_SEARCH_IN_PROGRESS"; export const AIRDCPP_SEARCH_RESULTS_ADDED = "AIRDCPP_SEARCH_RESULTS_ADDED"; diff --git a/src/client/reducers/fileops.reducer.ts b/src/client/reducers/fileops.reducer.ts index 9f9a247..7e328a8 100644 --- a/src/client/reducers/fileops.reducer.ts +++ b/src/client/reducers/fileops.reducer.ts @@ -13,6 +13,8 @@ import { IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS, IMS_COMIC_BOOK_GROUPS_FETCHED, IMS_COMIC_BOOK_GROUPS_CALL_FAILED, + IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS, + IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS, } from "../constants/action-types"; const initialState = { IMSCallInProgress: false, @@ -22,6 +24,7 @@ const initialState = { isComicVineMetadataImportInProgress: false, comicVineMetadataImportError: {}, rawImportError: {}, + extractedComicBookArchive: [], }; function fileOpsReducer(state = initialState, action) { @@ -92,6 +95,19 @@ function fileOpsReducer(state = initialState, action) { error: action.error, }; } + case IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS: { + return { + ...state, + IMSCallInProgress: true, + }; + } + case IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS: { + return { + ...state, + extractedComicBookArchive: action.extractedComicBookArchive, + IMSCallInProgress: false, + }; + } default: return state; } diff --git a/yarn.lock b/yarn.lock index 6ec7ecc..a9ac6b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3749,6 +3749,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classnames@^2.2.5: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + clean-css@^4.1.11, clean-css@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" @@ -11085,6 +11090,13 @@ react-dom@^17.0.1: object-assign "^4.1.1" scheduler "^0.20.2" +react-easy-swipe@^0.0.21: + version "0.0.21" + resolved "https://registry.yarnpkg.com/react-easy-swipe/-/react-easy-swipe-0.0.21.tgz#ce9384d576f7a8529dc2ca377c1bf03920bac8eb" + integrity sha512-OeR2jAxdoqUMHIn/nS9fgreI5hSpgGoL5ezdal4+oO7YSSgJR8ga+PkYGJrSrJ9MKlPcQjMQXnketrD7WNmNsg== + dependencies: + prop-types "^15.5.8" + react-fast-compare@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" @@ -11212,6 +11224,15 @@ react-redux@^7.2.3: prop-types "^15.7.2" react-is "^16.13.1" +react-responsive-carousel@^3.2.21: + version "3.2.21" + resolved "https://registry.yarnpkg.com/react-responsive-carousel/-/react-responsive-carousel-3.2.21.tgz#a464a93c33f9fc29e3bbd1944ce885a0a343a996" + integrity sha512-y2ueGqv/yo6HQJi9IbrJVRJobp4EOrNe6BQOC61CoADg3wUjq0gqAcrtSsLQIRLtbt6nCo7cTPRX4wAkSwdiTA== + dependencies: + classnames "^2.2.5" + prop-types "^15.5.8" + react-easy-swipe "^0.0.21" + react-router-dom@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.0.tgz#da1bfb535a0e89a712a93b97dd76f47ad1f32363" @@ -13139,10 +13160,10 @@ text-table@^0.2.0, text-table@~0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -threetwo-ui-typings@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/threetwo-ui-typings/-/threetwo-ui-typings-1.0.9.tgz#c7d30ec50afb244917628727b91ee8fbc58e4518" - integrity sha512-zOxDtVfe7Z8vSo0XsOBjT2F4WrBRVfL6ooQ82P/oor1UENZfBnBKj6aKsJT8/zINfvS/GZM96F8q8V1f2YrpCw== +threetwo-ui-typings@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/threetwo-ui-typings/-/threetwo-ui-typings-1.0.10.tgz#bbe74b53a6ba3963068b99a1944a5b2723cd62da" + integrity sha512-4FKd/Oq6wVgQZmgFErZ4/zIjp+T3UbV9zkVbgTqSyCsIu9MPXKAgHvfZaiV7TU05MCjuiaPJ+9XuUjtBrP6ixQ== dependencies: typescript "^4.3.2"