🔎 Glorious universal library search first draft
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
|||||||
LS_IMPORT_CALL_IN_PROGRESS,
|
LS_IMPORT_CALL_IN_PROGRESS,
|
||||||
LS_TOGGLE_IMPORT_QUEUE,
|
LS_TOGGLE_IMPORT_QUEUE,
|
||||||
SS_SEARCH_FAILED,
|
SS_SEARCH_FAILED,
|
||||||
|
SS_SEARCH_RESULTS_FETCHED_SPECIAL,
|
||||||
} from "../constants/action-types";
|
} from "../constants/action-types";
|
||||||
import { success } from "react-notification-system-redux";
|
import { success } from "react-notification-system-redux";
|
||||||
import { removeLeadingPeriod } from "../shared/utils/formatting.utils";
|
import { removeLeadingPeriod } from "../shared/utils/formatting.utils";
|
||||||
@@ -298,6 +299,10 @@ export const searchIssue = (query, options) => async (dispatch) => {
|
|||||||
type: SS_SEARCH_RESULTS_FETCHED,
|
type: SS_SEARCH_RESULTS_FETCHED,
|
||||||
data: response.data.body,
|
data: response.data.body,
|
||||||
});
|
});
|
||||||
|
dispatch({
|
||||||
|
type: SS_SEARCH_RESULTS_FETCHED_SPECIAL,
|
||||||
|
data: response.data.body,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
export const analyzeImage =
|
export const analyzeImage =
|
||||||
(imageFilePath: string | Buffer) => async (dispatch) => {
|
(imageFilePath: string | Buffer) => async (dispatch) => {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ interface ICardProps {
|
|||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||||
cardContainerStyle?: PropTypes.object;
|
cardContainerStyle?: PropTypes.object;
|
||||||
|
imageStyle?: PropTypes.object;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderCard = (props): ReactElement => {
|
const renderCard = (props): ReactElement => {
|
||||||
@@ -23,6 +24,7 @@ const renderCard = (props): ReactElement => {
|
|||||||
<div className="is-horizontal">
|
<div className="is-horizontal">
|
||||||
<div className="card-image">
|
<div className="card-image">
|
||||||
<img
|
<img
|
||||||
|
style={props.imageStyle}
|
||||||
src={props.imageUrl}
|
src={props.imageUrl}
|
||||||
alt="Placeholder image"
|
alt="Placeholder image"
|
||||||
className="cropped-image"
|
className="cropped-image"
|
||||||
@@ -54,7 +56,11 @@ const renderCard = (props): ReactElement => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<figure>
|
<figure>
|
||||||
<img src={props.imageUrl} alt="Placeholder image" />
|
<img
|
||||||
|
src={props.imageUrl}
|
||||||
|
style={props.imageStyle}
|
||||||
|
alt="Placeholder image"
|
||||||
|
/>
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
{props.hasDetails && (
|
{props.hasDetails && (
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { withAsyncPaginate } from "react-select-async-paginate";
|
|||||||
const CreatableAsyncPaginate = withAsyncPaginate(Creatable);
|
const CreatableAsyncPaginate = withAsyncPaginate(Creatable);
|
||||||
|
|
||||||
export const AsyncSelectPaginate = (props): ReactElement => {
|
export const AsyncSelectPaginate = (props): ReactElement => {
|
||||||
// console.log(props);
|
|
||||||
const [value, setValue] = useState(null);
|
const [value, setValue] = useState(null);
|
||||||
const [isAddingInProgress, setIsAddingInProgress] = useState(false);
|
const [isAddingInProgress, setIsAddingInProgress] = useState(false);
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ export const EditMetadataPanel = (props): ReactElement => {
|
|||||||
(state: RootState) => state.comicInfo.comicBookDetail.rawFileDetails.name,
|
(state: RootState) => state.comicInfo.comicBookDetail.rawFileDetails.name,
|
||||||
);
|
);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
console.log(rawFileDetails);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
71
src/client/components/GlobalSearchBar/SearchBar.tsx
Normal file
71
src/client/components/GlobalSearchBar/SearchBar.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { debounce, isEmpty, isUndefined, map } from "lodash";
|
||||||
|
import React, { ReactElement, useCallback, useState } from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import Card from "../Carda";
|
||||||
|
|
||||||
|
import { searchIssue } from "../../actions/fileops.actions";
|
||||||
|
import MetadataPanel from "../shared/MetadataPanel";
|
||||||
|
|
||||||
|
interface ISearchBarProps {
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SearchBar = (data: ISearchBarProps): ReactElement => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const searchResults = useSelector(
|
||||||
|
(state: RootState) => state.fileOps.librarySearchResultsFormatted,
|
||||||
|
);
|
||||||
|
|
||||||
|
const performSearch = useCallback(
|
||||||
|
(e) => {
|
||||||
|
dispatch(
|
||||||
|
searchIssue(
|
||||||
|
{
|
||||||
|
query: {
|
||||||
|
volumeName: e.target.value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagination: {
|
||||||
|
size: 25,
|
||||||
|
from: 0,
|
||||||
|
},
|
||||||
|
type: "volumeName",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[data],
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="control has-icons-right">
|
||||||
|
<input
|
||||||
|
className="input mt-2"
|
||||||
|
placeholder="Search Library"
|
||||||
|
onChange={(e) => performSearch(e)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span className="icon is-right mt-2">
|
||||||
|
<i className="fa-solid fa-magnifying-glass"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!isEmpty(searchResults) ? (
|
||||||
|
<div
|
||||||
|
className="columns box is-multiline"
|
||||||
|
style={{
|
||||||
|
padding: 4,
|
||||||
|
position: "absolute",
|
||||||
|
width: 360,
|
||||||
|
margin: "60px 0 0 350px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{map(searchResults, (result) => (
|
||||||
|
<MetadataPanel data={result} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,49 +1,11 @@
|
|||||||
import React, { ReactElement } from "react";
|
import { debounce } from "lodash";
|
||||||
|
import React, { ReactElement, useCallback } from "react";
|
||||||
|
import { SearchBar } from "./GlobalSearchBar/SearchBar";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import Select, {
|
|
||||||
components,
|
|
||||||
DropdownIndicatorProps,
|
|
||||||
IndicatorSeparatorProps,
|
|
||||||
} from "react-select";
|
|
||||||
|
|
||||||
const Navbar: React.FunctionComponent = (props) => {
|
const Navbar: React.FunctionComponent = (props) => {
|
||||||
const DropdownIndicator = (
|
|
||||||
props: DropdownIndicatorProps<ColourOption, true>,
|
|
||||||
) => {
|
|
||||||
return (
|
|
||||||
<components.DropdownIndicator {...props}>
|
|
||||||
<></>
|
|
||||||
</components.DropdownIndicator>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const IndicatorSeparator = ({
|
|
||||||
innerProps,
|
|
||||||
}: IndicatorSeparatorProps<ColourOption, true>) => {
|
|
||||||
return <></>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const searchPlaceholder = (): ReactElement => (
|
|
||||||
<>
|
|
||||||
Search Library...{" "}
|
|
||||||
<i className="fa-solid fa-magnifying-glass is-pulled-right mt-1"></i>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
const performSearch = (searchQuery) => {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const customStyles = {
|
|
||||||
control: () => ({
|
|
||||||
width: 250,
|
|
||||||
marginTop: 0,
|
|
||||||
border: "1px solid #CCC",
|
|
||||||
height: 40,
|
|
||||||
borderRadius: 8,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="navbar is-fixed-top">
|
<nav className="navbar is-fixed-top">
|
||||||
<div className="navbar-brand">
|
<div className="navbar-brand">
|
||||||
@@ -93,22 +55,7 @@ const Navbar: React.FunctionComponent = (props) => {
|
|||||||
Downloads
|
Downloads
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Select
|
<SearchBar/>
|
||||||
className="basic-single mt-2"
|
|
||||||
classNamePrefix="select"
|
|
||||||
styles={customStyles}
|
|
||||||
components={{ DropdownIndicator, IndicatorSeparator }}
|
|
||||||
placeholder={searchPlaceholder()}
|
|
||||||
// defaultValue={colourOptions[0]}
|
|
||||||
// isDisabled={isDisabled}
|
|
||||||
// isLoading={isLoading}
|
|
||||||
isClearable={true}
|
|
||||||
// isRtl={isRtl}
|
|
||||||
// isSearchable={isSearchable}
|
|
||||||
name="color"
|
|
||||||
// options={colourOptions}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Link to="/search" className="navbar-item">
|
<Link to="/search" className="navbar-item">
|
||||||
Search ComicVine
|
Search ComicVine
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export const IMS_COMIC_BOOK_GROUPS_CALL_FAILED =
|
|||||||
|
|
||||||
// search results from the Search service
|
// search results from the Search service
|
||||||
export const SS_SEARCH_RESULTS_FETCHED = "SS_SEARCH_RESULTS_FETCHED";
|
export const SS_SEARCH_RESULTS_FETCHED = "SS_SEARCH_RESULTS_FETCHED";
|
||||||
|
export const SS_SEARCH_RESULTS_FETCHED_SPECIAL = "SS_SEARCH_RESULTS_FETCHED_SPECIAL";
|
||||||
export const SS_SEARCH_IN_PROGRESS = "SS_SEARCH_IN_PROGRESS";
|
export const SS_SEARCH_IN_PROGRESS = "SS_SEARCH_IN_PROGRESS";
|
||||||
export const SS_SEARCH_FAILED = "SS_SEARCH_FAILED";
|
export const SS_SEARCH_FAILED = "SS_SEARCH_FAILED";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import { isUndefined, map } from "lodash";
|
||||||
import { LOCATION_CHANGE } from "redux-first-history";
|
import { LOCATION_CHANGE } from "redux-first-history";
|
||||||
|
import { determineCoverFile } from "../shared/utils/metadata.utils";
|
||||||
import {
|
import {
|
||||||
IMS_COMICBOOK_METADATA_FETCHED,
|
IMS_COMICBOOK_METADATA_FETCHED,
|
||||||
IMS_RAW_IMPORT_SUCCESSFUL,
|
IMS_RAW_IMPORT_SUCCESSFUL,
|
||||||
@@ -24,6 +26,7 @@ import {
|
|||||||
FILEOPS_STATE_RESET,
|
FILEOPS_STATE_RESET,
|
||||||
LS_IMPORT_CALL_IN_PROGRESS,
|
LS_IMPORT_CALL_IN_PROGRESS,
|
||||||
SS_SEARCH_FAILED,
|
SS_SEARCH_FAILED,
|
||||||
|
SS_SEARCH_RESULTS_FETCHED_SPECIAL,
|
||||||
} from "../constants/action-types";
|
} from "../constants/action-types";
|
||||||
const initialState = {
|
const initialState = {
|
||||||
IMSCallInProgress: false,
|
IMSCallInProgress: false,
|
||||||
@@ -41,6 +44,7 @@ const initialState = {
|
|||||||
recentComics: [],
|
recentComics: [],
|
||||||
wantedComics: [],
|
wantedComics: [],
|
||||||
librarySearchResults: [],
|
librarySearchResults: [],
|
||||||
|
librarySearchResultsFormatted: [],
|
||||||
librarySearchResultCount: 0,
|
librarySearchResultCount: 0,
|
||||||
libraryQueueResults: [],
|
libraryQueueResults: [],
|
||||||
librarySearchError: {},
|
librarySearchError: {},
|
||||||
@@ -169,7 +173,6 @@ function fileOpsReducer(state = initialState, action) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
case IMG_ANALYSIS_DATA_FETCH_SUCCESS: {
|
case IMG_ANALYSIS_DATA_FETCH_SUCCESS: {
|
||||||
console.log(action)
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
imageAnalysisResults: action.result,
|
imageAnalysisResults: action.result,
|
||||||
@@ -184,15 +187,26 @@ function fileOpsReducer(state = initialState, action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case SS_SEARCH_RESULTS_FETCHED: {
|
case SS_SEARCH_RESULTS_FETCHED: {
|
||||||
console.log(action.data);
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
librarySearchResults: action.data,
|
librarySearchResults: action.data,
|
||||||
SSCallInProgress: false,
|
SSCallInProgress: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case SS_SEARCH_RESULTS_FETCHED_SPECIAL: {
|
||||||
|
const foo = [];
|
||||||
|
if (!isUndefined(action.data.hits)) {
|
||||||
|
map(action.data.hits.hits, ({ _source }) => {
|
||||||
|
foo.push(_source);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
librarySearchResultsFormatted: foo,
|
||||||
|
SSCallInProgress: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
case SS_SEARCH_FAILED: {
|
case SS_SEARCH_FAILED: {
|
||||||
console.log(action.data);
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
librarySearchError: action.data,
|
librarySearchError: action.data,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export const determineCoverFile = (data) => {
|
|||||||
priority: 1,
|
priority: 1,
|
||||||
url: "",
|
url: "",
|
||||||
issueName: "",
|
issueName: "",
|
||||||
|
publisher: "",
|
||||||
},
|
},
|
||||||
comicvine: {
|
comicvine: {
|
||||||
objectReference: "comicvine",
|
objectReference: "comicvine",
|
||||||
|
|||||||
Reference in New Issue
Block a user