This commit is contained in:
Infinite
2020-01-12 20:37:44 +01:00
parent 129dcecdef
commit 0e717d6aae
16 changed files with 356 additions and 347 deletions

View File

@@ -60,6 +60,7 @@
"@types/query-string": "6.3.0", "@types/query-string": "6.3.0",
"@types/react": "16.9.15", "@types/react": "16.9.15",
"@types/react-dom": "16.9.4", "@types/react-dom": "16.9.4",
"@types/react-images": "^0.5.1",
"@types/react-router-bootstrap": "^0.24.5", "@types/react-router-bootstrap": "^0.24.5",
"@types/react-router-dom": "5.1.3", "@types/react-router-dom": "5.1.3",
"@types/react-select": "^3.0.8", "@types/react-select": "^3.0.8",

View File

@@ -3,17 +3,12 @@ import { Table } from 'react-bootstrap';
import { QueryHookResult } from "react-apollo-hooks"; import { QueryHookResult } from "react-apollo-hooks";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { FindGalleriesQuery, FindGalleriesVariables } from "src/core/generated-graphql"; import { FindGalleriesQuery, FindGalleriesVariables } from "src/core/generated-graphql";
import { ListHook } from "src/hooks"; import { useGalleriesList } from "src/hooks";
import { IBaseProps } from "src/models/base-props";
import { ListFilterModel } from "src/models/list-filter/filter"; import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode, FilterMode } from "src/models/list-filter/types"; import { DisplayMode } from "src/models/list-filter/types";
interface IProps extends IBaseProps {} export const GalleryList: React.FC = () => {
const listData = useGalleriesList({
export const GalleryList: React.FC<IProps> = (props: IProps) => {
const listData = ListHook.useList({
filterMode: FilterMode.Galleries,
props,
renderContent, renderContent,
}); });

View File

@@ -1,10 +1,10 @@
import { Nav, Navbar, Button } from "react-bootstrap";
import { LinkContainer } from 'react-router-bootstrap';
import React from "react"; import React from "react";
import { Nav, Navbar, Button } from "react-bootstrap";
import { IconName } from '@fortawesome/fontawesome-svg-core';
import { LinkContainer } from 'react-router-bootstrap';
import { Link, useLocation } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Icon } from 'src/components/Shared'
import { IconName } from '@fortawesome/fontawesome-svg-core';
interface IMenuItem { interface IMenuItem {
text: string; text: string;
@@ -74,7 +74,7 @@ export const MainNavbar: React.FC = () => {
key={i.href} key={i.href}
> >
<Button variant="secondary"> <Button variant="secondary">
<FontAwesomeIcon icon={i.icon} /> <Icon icon={i.icon} />
{i.text} {i.text}
</Button> </Button>
</LinkContainer> </LinkContainer>
@@ -86,7 +86,7 @@ export const MainNavbar: React.FC = () => {
exact={true} exact={true}
to="/settings"> to="/settings">
<Button variant="secondary"> <Button variant="secondary">
<FontAwesomeIcon icon="cog" /> <Icon icon="cog" />
</Button> </Button>
</LinkContainer> </LinkContainer>
</Nav> </Nav>

View File

@@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Button, ButtonGroup, InputGroup, Form } from 'react-bootstrap'; import { Button, ButtonGroup, InputGroup, Form } from 'react-bootstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Icon } from 'src/components/Shared'
import { TextUtils } from "src/utils"; import { TextUtils } from "src/utils";
interface IProps { interface IProps {
@@ -83,13 +83,13 @@ export const DurationInput: React.FC<IProps> = (props: IProps) => {
disabled={props.disabled} disabled={props.disabled}
onClick={() => increment()} onClick={() => increment()}
> >
<FontAwesomeIcon icon="chevron-up" /> <Icon icon="chevron-up" />
</Button> </Button>
<Button <Button
disabled={props.disabled} disabled={props.disabled}
onClick={() => decrement()} onClick={() => decrement()}
> >
<FontAwesomeIcon icon="chevron-down" /> <Icon icon="chevron-down" />
</Button> </Button>
</ButtonGroup> </ButtonGroup>
) )
@@ -107,7 +107,7 @@ export const DurationInput: React.FC<IProps> = (props: IProps) => {
<Button <Button
onClick={() => onReset()} onClick={() => onReset()}
> >
<FontAwesomeIcon icon="clock" /> <Icon icon="clock" />
</Button> </Button>
) )
} }

View File

@@ -1,18 +1,13 @@
import React, { FunctionComponent } from "react"; import React from "react";
import { QueryHookResult } from "react-apollo-hooks"; import { QueryHookResult } from "react-apollo-hooks";
import { FindStudiosQuery, FindStudiosVariables } from "src/core/generated-graphql"; import { FindStudiosQuery, FindStudiosVariables } from "src/core/generated-graphql";
import { ListHook } from "src/hooks"; import { useStudiosList } from "src/hooks";
import { IBaseProps } from "src/models/base-props";
import { ListFilterModel } from "src/models/list-filter/filter"; import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode, FilterMode } from "src/models/list-filter/types"; import { DisplayMode } from "src/models/list-filter/types";
import { StudioCard } from "./StudioCard"; import { StudioCard } from "./StudioCard";
interface IProps extends IBaseProps {} export const StudioList: React.FC = () => {
const listData = useStudiosList({
export const StudioList: FunctionComponent<IProps> = (props: IProps) => {
const listData = ListHook.useList({
filterMode: FilterMode.Studios,
props,
renderContent, renderContent,
}); });

View File

@@ -6,6 +6,7 @@ interface IPaginationProps {
currentPage: number; currentPage: number;
totalItems: number; totalItems: number;
onChangePage: (page: number) => void; onChangePage: (page: number) => void;
loading?: boolean;
} }
interface IPaginationState { interface IPaginationState {
@@ -27,6 +28,8 @@ export class Pagination extends React.Component<IPaginationProps, IPaginationSta
} }
public componentDidUpdate(prevProps: IPaginationProps) { public componentDidUpdate(prevProps: IPaginationProps) {
if (this.props.loading)
return;
if (this.props.totalItems !== prevProps.totalItems || this.props.itemsPerPage !== prevProps.itemsPerPage) { if (this.props.totalItems !== prevProps.totalItems || this.props.itemsPerPage !== prevProps.itemsPerPage) {
this.setPage(this.props.currentPage); this.setPage(this.props.currentPage);
} }
@@ -81,7 +84,7 @@ export class Pagination extends React.Component<IPaginationProps, IPaginationSta
} }
private getPagerState(totalItems: number, currentPage: number, pageSize: number) { private getPagerState(totalItems: number, currentPage: number, pageSize: number) {
const totalPages = Math.ceil(totalItems / pageSize); const totalPages = Math.max(Math.ceil(totalItems / pageSize), 1);
let startPage: number; let startPage: number;
let endPage: number; let endPage: number;

View File

@@ -1,18 +1,17 @@
import _ from "lodash"; import _ from "lodash";
import React from "react"; import React from "react";
import { QueryHookResult } from "react-apollo-hooks"; import { QueryHookResult } from "react-apollo-hooks";
import { useHistory } from 'react-router-dom';
import { FindPerformersQuery, FindPerformersVariables } from "src/core/generated-graphql"; import { FindPerformersQuery, FindPerformersVariables } from "src/core/generated-graphql";
import { StashService } from "src/core/StashService"; import { StashService } from "src/core/StashService";
import { ListHook } from "src/hooks"; import { usePerformersList } from "src/hooks";
import { IBaseProps } from "src/models/base-props";
import { ListFilterModel } from "src/models/list-filter/filter"; import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode, FilterMode } from "src/models/list-filter/types"; import { DisplayMode } from "src/models/list-filter/types";
import { PerformerCard } from "./PerformerCard"; import { PerformerCard } from "./PerformerCard";
import { PerformerListTable } from "./PerformerListTable"; import { PerformerListTable } from "./PerformerListTable";
interface IPerformerListProps extends IBaseProps {} export const PerformerList: React.FC = () => {
const history = useHistory();
export const PerformerList: React.FC<IPerformerListProps> = (props: IPerformerListProps) => {
const otherOperations = [ const otherOperations = [
{ {
text: "Open Random", text: "Open Random",
@@ -20,9 +19,7 @@ export const PerformerList: React.FC<IPerformerListProps> = (props: IPerformerLi
} }
]; ];
const listData = ListHook.useList({ const listData = usePerformersList({
filterMode: FilterMode.Performers,
props,
otherOperations: otherOperations, otherOperations: otherOperations,
renderContent, renderContent,
}); });
@@ -37,7 +34,7 @@ export const PerformerList: React.FC<IPerformerListProps> = (props: IPerformerLi
const singleResult = await StashService.queryFindPerformers(filterCopy); const singleResult = await StashService.queryFindPerformers(filterCopy);
if (singleResult && singleResult.data && singleResult.data.findPerformers && singleResult.data.findPerformers.performers.length === 1) { if (singleResult && singleResult.data && singleResult.data.findPerformers && singleResult.data.findPerformers.performers.length === 1) {
let id = singleResult!.data!.findPerformers!.performers[0]!.id; let id = singleResult!.data!.findPerformers!.performers[0]!.id;
props.history.push("/performers/" + id); history.push("/performers/" + id);
} }
} }
} }

View File

@@ -1,20 +1,19 @@
import React from "react"; import React from "react";
import _ from "lodash"; import _ from "lodash";
import { QueryHookResult } from "react-apollo-hooks"; import { QueryHookResult } from "react-apollo-hooks";
import { useHistory } from 'react-router-dom';
import { FindScenesQuery, FindScenesVariables, SlimSceneDataFragment } from "src/core/generated-graphql"; import { FindScenesQuery, FindScenesVariables, SlimSceneDataFragment } from "src/core/generated-graphql";
import { StashService } from "src/core/StashService"; import { StashService } from "src/core/StashService";
import { ListHook } from "src/hooks"; import { useScenesList } from "src/hooks";
import { IBaseProps } from "src/models/base-props";
import { ListFilterModel } from "src/models/list-filter/filter"; import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode, FilterMode } from "src/models/list-filter/types"; import { DisplayMode } from "src/models/list-filter/types";
import { WallPanel } from "../Wall/WallPanel"; import { WallPanel } from "../Wall/WallPanel";
import { SceneCard } from "./SceneCard"; import { SceneCard } from "./SceneCard";
import { SceneListTable } from "./SceneListTable"; import { SceneListTable } from "./SceneListTable";
import { SceneSelectedOptions } from "./SceneSelectedOptions"; import { SceneSelectedOptions } from "./SceneSelectedOptions";
interface ISceneListProps extends IBaseProps {} export const SceneList: React.FC = () => {
const history = useHistory();
export const SceneList: React.FC<ISceneListProps> = (props: ISceneListProps) => {
const otherOperations = [ const otherOperations = [
{ {
text: "Play Random", text: "Play Random",
@@ -22,9 +21,7 @@ export const SceneList: React.FC<ISceneListProps> = (props: ISceneListProps) =>
} }
]; ];
const listData = ListHook.useList({ const listData = useScenesList({
filterMode: FilterMode.Scenes,
props,
zoomable: true, zoomable: true,
otherOperations: otherOperations, otherOperations: otherOperations,
renderContent, renderContent,
@@ -44,7 +41,7 @@ export const SceneList: React.FC<ISceneListProps> = (props: ISceneListProps) =>
if (singleResult && singleResult.data && singleResult.data.findScenes && singleResult.data.findScenes.scenes.length === 1) { if (singleResult && singleResult.data && singleResult.data.findScenes && singleResult.data.findScenes.scenes.length === 1) {
let id = singleResult!.data!.findScenes!.scenes[0].id; let id = singleResult!.data!.findScenes!.scenes[0].id;
// navigate to the scene player page // navigate to the scene player page
props.history.push("/scenes/" + id + "?autoplay=true"); history.push("/scenes/" + id + "?autoplay=true");
} }
} }
} }

View File

@@ -1,27 +1,24 @@
import _ from "lodash"; import _ from "lodash";
import React from "react"; import React from "react";
import { QueryHookResult } from "react-apollo-hooks"; import { QueryHookResult } from "react-apollo-hooks";
import { useHistory } from 'react-router-dom';
import { FindSceneMarkersQuery, FindSceneMarkersVariables } from "src/core/generated-graphql"; import { FindSceneMarkersQuery, FindSceneMarkersVariables } from "src/core/generated-graphql";
import { StashService } from "src/core/StashService"; import { StashService } from "src/core/StashService";
import { NavUtils } from "src/utils"; import { NavUtils } from "src/utils";
import { ListHook } from "src/hooks"; import { useSceneMarkersList } from "src/hooks";
import { IBaseProps } from "src/models/base-props";
import { ListFilterModel } from "src/models/list-filter/filter"; import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode, FilterMode } from "src/models/list-filter/types"; import { DisplayMode } from "src/models/list-filter/types";
import { WallPanel } from "../Wall/WallPanel"; import { WallPanel } from "../Wall/WallPanel";
interface IProps extends IBaseProps {} export const SceneMarkerList: React.FC = () => {
const history = useHistory();
export const SceneMarkerList: React.FC<IProps> = (props: IProps) => {
const otherOperations = [{ const otherOperations = [{
text: "Play Random", text: "Play Random",
onClick: playRandom onClick: playRandom
}]; }];
const listData = ListHook.useList({ const listData = useSceneMarkersList({
filterMode: FilterMode.SceneMarkers,
otherOperations: otherOperations, otherOperations: otherOperations,
props,
renderContent, renderContent,
}); });
@@ -38,7 +35,7 @@ export const SceneMarkerList: React.FC<IProps> = (props: IProps) => {
if (singleResult && singleResult.data && singleResult.data.findSceneMarkers && singleResult.data.findSceneMarkers.scene_markers.length === 1) { if (singleResult && singleResult.data && singleResult.data.findSceneMarkers && singleResult.data.findSceneMarkers.scene_markers.length === 1) {
// navigate to the scene player page // navigate to the scene player page
let url = NavUtils.makeSceneMarkerUrl(singleResult.data.findSceneMarkers.scene_markers[0]) let url = NavUtils.makeSceneMarkerUrl(singleResult.data.findSceneMarkers.scene_markers[0])
props.history.push(url); history.push(url);
} }
} }
} }

View File

@@ -1,131 +1,147 @@
import _ from "lodash"; import _ from "lodash";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect, useState } from "react"; import React, { useState } from "react";
import { Spinner } from 'react-bootstrap'; import { Spinner } from 'react-bootstrap';
import { QueryHookResult } from "react-apollo-hooks"; import { QueryHookResult } from "react-apollo-hooks";
import { ApolloError } from 'apollo-client';
import { useHistory } from 'react-router-dom';
import {
FindScenesQuery,
FindScenesVariables,
SlimSceneDataFragment,
FindSceneMarkersQuery,
FindSceneMarkersVariables,
FindSceneMarkersSceneMarkers,
FindGalleriesQuery,
FindGalleriesVariables,
GalleryDataFragment,
FindStudiosQuery,
FindStudiosVariables,
StudioDataFragment,
FindPerformersQuery,
FindPerformersVariables,
PerformerDataFragment
} from 'src/core/generated-graphql';
import { ListFilter } from "../components/list/ListFilter"; import { ListFilter } from "../components/list/ListFilter";
import { Pagination } from "../components/list/Pagination"; import { Pagination } from "../components/list/Pagination";
import { StashService } from "../core/StashService"; import { StashService } from "../core/StashService";
import { IBaseProps } from "../models";
import { Criterion } from "../models/list-filter/criteria/criterion"; import { Criterion } from "../models/list-filter/criteria/criterion";
import { ListFilterModel } from "../models/list-filter/filter"; import { ListFilterModel } from "../models/list-filter/filter";
import { DisplayMode, FilterMode } from "../models/list-filter/types"; import { DisplayMode, FilterMode } from "../models/list-filter/types";
export interface IListHookData { interface IListHookData {
filter: ListFilterModel; filter: ListFilterModel;
template: JSX.Element; template: JSX.Element;
options: IListHookOptions;
onSelectChange: (id: string, selected : boolean, shiftKey: boolean) => void; onSelectChange: (id: string, selected : boolean, shiftKey: boolean) => void;
} }
interface IListHookOperation { interface IListHookOperation<T> {
text: string; text: string;
onClick: (result: QueryHookResult<any, any>, filter: ListFilterModel, selectedIds: Set<string>) => void; onClick: (result: T, filter: ListFilterModel, selectedIds: Set<string>) => void;
} }
export interface IListHookOptions { interface IListHookOptions<T> {
filterMode: FilterMode;
props: IBaseProps;
zoomable?: boolean; zoomable?: boolean;
otherOperations?: IListHookOperation[]; otherOperations?: IListHookOperation<T>[];
renderContent: (result: QueryHookResult<any, any>, filter: ListFilterModel, selectedIds: Set<string>, zoomIndex: number) => JSX.Element | undefined; renderContent: (result: T, filter: ListFilterModel, selectedIds: Set<string>, zoomIndex: number) => JSX.Element | undefined;
renderSelectedOptions?: (result: QueryHookResult<any, any>, selectedIds: Set<string>) => JSX.Element | undefined; renderSelectedOptions?: (result: T, selectedIds: Set<string>) => JSX.Element | undefined;
} }
export class ListHook { interface IDataItem {
public static useList(options: IListHookOptions): IListHookData { id: string;
const [filter, setFilter] = useState<ListFilterModel>(new ListFilterModel(options.filterMode)); }
interface IQueryResult {
error?: ApolloError;
loading: boolean;
}
interface IQuery<T extends IQueryResult, T2 extends IDataItem> {
filterMode: FilterMode;
useData: (filter: ListFilterModel) => T;
getData: (data: T) => T2[];
getCount: (data: T) => number;
}
type ScenesQuery = QueryHookResult<FindScenesQuery, FindScenesVariables>;
export const useScenesList = (props:IListHookOptions<ScenesQuery>) => (
useList<ScenesQuery, SlimSceneDataFragment>({
...props,
filterMode: FilterMode.Scenes,
useData: StashService.useFindScenes,
getData: (result:ScenesQuery) => (result?.data?.findScenes?.scenes ?? []),
getCount: (result:ScenesQuery) => (result?.data?.findScenes?.count ?? 0)
})
)
type SceneMarkersQuery = QueryHookResult<FindSceneMarkersQuery, FindSceneMarkersVariables>;
export const useSceneMarkersList = (props:IListHookOptions<SceneMarkersQuery>) => (
useList<SceneMarkersQuery, FindSceneMarkersSceneMarkers>({
...props,
filterMode: FilterMode.SceneMarkers,
useData: StashService.useFindSceneMarkers,
getData: (result:SceneMarkersQuery) => (result?.data?.findSceneMarkers?.scene_markers?? []),
getCount: (result:SceneMarkersQuery) => (result?.data?.findSceneMarkers?.count ?? 0)
})
)
type GalleriesQuery = QueryHookResult<FindGalleriesQuery, FindGalleriesVariables>;
export const useGalleriesList = (props:IListHookOptions<GalleriesQuery>) => (
useList<GalleriesQuery, GalleryDataFragment>({
...props,
filterMode: FilterMode.Galleries,
useData: StashService.useFindGalleries,
getData: (result:GalleriesQuery) => (result?.data?.findGalleries?.galleries ?? []),
getCount: (result:GalleriesQuery) => (result?.data?.findGalleries?.count ?? 0)
})
)
type StudiosQuery = QueryHookResult<FindStudiosQuery, FindStudiosVariables>;
export const useStudiosList = (props:IListHookOptions<StudiosQuery>) => (
useList<StudiosQuery, StudioDataFragment>({
...props,
filterMode: FilterMode.Studios,
useData: StashService.useFindStudios,
getData: (result:StudiosQuery) => (result?.data?.findStudios?.studios ?? []),
getCount: (result:StudiosQuery) => (result?.data?.findStudios?.count ?? 0)
})
)
type PerformersQuery = QueryHookResult<FindPerformersQuery, FindPerformersVariables>;
export const usePerformersList = (props:IListHookOptions<PerformersQuery>) => (
useList<PerformersQuery, PerformerDataFragment>({
...props,
filterMode: FilterMode.Performers,
useData: StashService.useFindPerformers,
getData: (result:PerformersQuery) => (result?.data?.findPerformers?.performers ?? []),
getCount: (result:PerformersQuery) => (result?.data?.findPerformers?.count ?? 0)
})
)
const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
options: (IListHookOptions<QueryResult> & IQuery<QueryResult, QueryData>)
): IListHookData => {
const history = useHistory();
const [filter, setFilter] = useState<ListFilterModel>(new ListFilterModel(options.filterMode, queryString.parse(history.location.search)));
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set()); const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
const [lastClickedId, setLastClickedId] = useState<string | undefined>(undefined); const [lastClickedId, setLastClickedId] = useState<string | undefined>();
const [totalCount, setTotalCount] = useState<number>(0);
const [zoomIndex, setZoomIndex] = useState<number>(1); const [zoomIndex, setZoomIndex] = useState<number>(1);
// Update the filter when the query parameters change const result = options.useData(filter);
useEffect(() => { const totalCount = options.getCount(result);
const queryParams = queryString.parse(options.props.location.search); const items = options.getData(result);
const newFilter = _.cloneDeep(filter);
newFilter.configureFromQueryParameters(queryParams);
setFilter(newFilter);
// TODO: Need this side effect to update the query params properly function updateQueryParams(filter:ListFilterModel) {
filter.configureFromQueryParameters(queryParams); const newLocation = Object.assign({}, history.location);
}, [options.props.location.search]); newLocation.search = filter.makeQueryParameters();
history.replace(newLocation);
let result: QueryHookResult<any, any>;
let getData: (filter : ListFilterModel) => QueryHookResult<any, any>;
let getItems: () => any[];
let getCount: () => number;
switch (options.filterMode) {
case FilterMode.Scenes: {
getData = (filter : ListFilterModel) => { return StashService.useFindScenes(filter); }
getItems = () => { return !!result.data && !!result.data.findScenes ? result.data.findScenes.scenes : []; }
getCount = () => { return !!result.data && !!result.data.findScenes ? result.data.findScenes.count : 0; }
break;
} }
case FilterMode.SceneMarkers: {
getData = (filter : ListFilterModel) => { return StashService.useFindSceneMarkers(filter); }
getItems = () => { return !!result.data && !!result.data.findSceneMarkers ? result.data.findSceneMarkers.scene_markers : []; }
getCount = () => { return !!result.data && !!result.data.findSceneMarkers ? result.data.findSceneMarkers.count : 0; }
break;
}
case FilterMode.Galleries: {
getData = (filter : ListFilterModel) => { return StashService.useFindGalleries(filter); }
getItems = () => { return !!result.data && !!result.data.findGalleries ? result.data.findGalleries.galleries : []; }
getCount = () => { return !!result.data && !!result.data.findGalleries ? result.data.findGalleries.count : 0; }
break;
}
case FilterMode.Studios: {
getData = (filter : ListFilterModel) => { return StashService.useFindStudios(filter); }
getItems = () => { return !!result.data && !!result.data.findStudios ? result.data.findStudios.studios : []; }
getCount = () => { return !!result.data && !!result.data.findStudios ? result.data.findStudios.count : 0; }
break;
}
case FilterMode.Performers: {
getData = (filter : ListFilterModel) => { return StashService.useFindPerformers(filter); }
getItems = () => { return !!result.data && !!result.data.findPerformers ? result.data.findPerformers.performers : []; }
getCount = () => { return !!result.data && !!result.data.findPerformers ? result.data.findPerformers.count : 0; }
break;
}
default: {
console.error("REMOVE DEFAULT IN LIST HOOK");
getData = (filter : ListFilterModel) => { return StashService.useFindScenes(filter); }
getItems = () => { return !!result.data && !!result.data.findScenes ? result.data.findScenes.scenes : []; }
getCount = () => { return !!result.data && !!result.data.findScenes ? result.data.findScenes.count : 0; }
break;
}
}
result = getData(filter);
useEffect(() => {
setTotalCount(getCount());
// select none when data changes
onSelectNone();
setLastClickedId(undefined);
}, [result.data])
// Update the query parameters when the data changes
useEffect(() => {
const location = Object.assign({}, options.props.history.location);
location.search = filter.makeQueryParameters();
options.props.history.replace(location);
}, [result.data, filter.displayMode]);
// Update the total count
useEffect(() => {
const newFilter = _.cloneDeep(filter);
newFilter.totalCount = totalCount;
setFilter(newFilter);
}, [totalCount]);
function onChangePageSize(pageSize: number) { function onChangePageSize(pageSize: number) {
const newFilter = _.cloneDeep(filter); const newFilter = _.cloneDeep(filter);
newFilter.itemsPerPage = pageSize; newFilter.itemsPerPage = pageSize;
newFilter.currentPage = 1; newFilter.currentPage = 1;
setFilter(newFilter); setFilter(newFilter);
updateQueryParams(newFilter);
} }
function onChangeQuery(query: string) { function onChangeQuery(query: string) {
@@ -133,12 +149,14 @@ export class ListHook {
newFilter.searchTerm = query; newFilter.searchTerm = query;
newFilter.currentPage = 1; newFilter.currentPage = 1;
setFilter(newFilter); setFilter(newFilter);
updateQueryParams(newFilter);
} }
function onChangeSortDirection(sortDirection: "asc" | "desc") { function onChangeSortDirection(sortDirection: "asc" | "desc") {
const newFilter = _.cloneDeep(filter); const newFilter = _.cloneDeep(filter);
newFilter.sortDirection = sortDirection; newFilter.sortDirection = sortDirection;
setFilter(newFilter); setFilter(newFilter);
updateQueryParams(newFilter);
} }
function onChangeSortBy(sortBy: string) { function onChangeSortBy(sortBy: string) {
@@ -146,12 +164,14 @@ export class ListHook {
newFilter.sortBy = sortBy; newFilter.sortBy = sortBy;
newFilter.currentPage = 1; newFilter.currentPage = 1;
setFilter(newFilter); setFilter(newFilter);
updateQueryParams(newFilter);
} }
function onChangeDisplayMode(displayMode: DisplayMode) { function onChangeDisplayMode(displayMode: DisplayMode) {
const newFilter = _.cloneDeep(filter); const newFilter = _.cloneDeep(filter);
newFilter.displayMode = displayMode; newFilter.displayMode = displayMode;
setFilter(newFilter); setFilter(newFilter);
updateQueryParams(newFilter);
} }
function onAddCriterion(criterion: Criterion, oldId?: string) { function onAddCriterion(criterion: Criterion, oldId?: string) {
@@ -176,6 +196,7 @@ export class ListHook {
newFilter.currentPage = 1; newFilter.currentPage = 1;
setFilter(newFilter); setFilter(newFilter);
updateQueryParams(newFilter);
} }
function onRemoveCriterion(removedCriterion: Criterion) { function onRemoveCriterion(removedCriterion: Criterion) {
@@ -183,17 +204,19 @@ export class ListHook {
newFilter.criteria = newFilter.criteria.filter((criterion) => criterion.getId() !== removedCriterion.getId()); newFilter.criteria = newFilter.criteria.filter((criterion) => criterion.getId() !== removedCriterion.getId());
newFilter.currentPage = 1; newFilter.currentPage = 1;
setFilter(newFilter); setFilter(newFilter);
updateQueryParams(newFilter);
} }
function onChangePage(page: number) { function onChangePage(page: number) {
const newFilter = _.cloneDeep(filter); const newFilter = _.cloneDeep(filter);
newFilter.currentPage = page; newFilter.currentPage = page;
setFilter(newFilter); setFilter(newFilter);
updateQueryParams(newFilter);
} }
function onSelectChange(id: string, selected : boolean, shiftKey: boolean) { function onSelectChange(id: string, selected : boolean, shiftKey: boolean) {
if (shiftKey) { if (shiftKey) {
multiSelect(id, selected); multiSelect(id);
} else { } else {
singleSelect(id, selected); singleSelect(id, selected);
} }
@@ -212,17 +235,17 @@ export class ListHook {
setSelectedIds(newSelectedIds); setSelectedIds(newSelectedIds);
} }
function multiSelect(id: string, selected : boolean) { function multiSelect(id: string) {
let startIndex = 0; let startIndex = 0;
let thisIndex = -1; let thisIndex = -1;
if (!!lastClickedId) { if (lastClickedId) {
startIndex = getItems().findIndex((item) => { startIndex = items.findIndex((item) => {
return item.id === lastClickedId; return item.id === lastClickedId;
}); });
} }
thisIndex = getItems().findIndex((item) => { thisIndex = items.findIndex((item) => {
return item.id === id; return item.id === id;
}); });
@@ -236,7 +259,7 @@ export class ListHook {
endIndex = tmp; endIndex = tmp;
} }
const subset = getItems().slice(startIndex, endIndex + 1); const subset = items.slice(startIndex, endIndex + 1);
const newSelectedIds : Set<string> = new Set(); const newSelectedIds : Set<string> = new Set();
subset.forEach((item) => { subset.forEach((item) => {
@@ -248,7 +271,7 @@ export class ListHook {
function onSelectAll() { function onSelectAll() {
const newSelectedIds : Set<string> = new Set(); const newSelectedIds : Set<string> = new Set();
getItems().forEach((item) => { items.forEach((item) => {
newSelectedIds.add(item.id); newSelectedIds.add(item.id);
}); });
@@ -301,10 +324,10 @@ export class ListHook {
currentPage={filter.currentPage} currentPage={filter.currentPage}
totalItems={totalCount} totalItems={totalCount}
onChangePage={onChangePage} onChangePage={onChangePage}
loading={result.loading}
/> />
</div> </div>
); );
return { filter, template, options, onSelectChange }; return { filter, template, onSelectChange };
}
} }

View File

@@ -1,4 +1,4 @@
export { default as useToast } from './Toast'; export { default as useToast } from './Toast';
export { useInterfaceLocalForage } from './LocalForage'; export { useInterfaceLocalForage } from './LocalForage';
export { VideoHoverHook } from './VideoHover'; export { VideoHoverHook } from './VideoHover';
export { ListHook } from './ListHook'; export { useScenesList, useSceneMarkersList, useGalleriesList, useStudiosList, usePerformersList } from './ListHook';

View File

@@ -1,2 +1 @@
export * from "./base-props"; export * from "./base-props";
export * from "./types";

View File

@@ -45,9 +45,8 @@ export class ListFilterModel {
public displayModeOptions: DisplayMode[] = []; public displayModeOptions: DisplayMode[] = [];
public criterionOptions: ICriterionOption[] = []; public criterionOptions: ICriterionOption[] = [];
public criteria: Array<Criterion<any, any>> = []; public criteria: Array<Criterion<any, any>> = [];
public totalCount: number = 0;
public constructor(filterMode: FilterMode) { public constructor(filterMode: FilterMode, rawParms?: any) {
switch (filterMode) { switch (filterMode) {
case FilterMode.Scenes: case FilterMode.Scenes:
if (!!this.sortBy === false) { this.sortBy = "date"; } if (!!this.sortBy === false) { this.sortBy = "date"; }
@@ -142,6 +141,8 @@ export class ListFilterModel {
} }
if (!!this.displayMode === false) { this.displayMode = this.displayModeOptions[0]; } if (!!this.displayMode === false) { this.displayMode = this.displayModeOptions[0]; }
this.sortByOptions = [...this.sortByOptions, "created_at", "updated_at"]; this.sortByOptions = [...this.sortByOptions, "created_at", "updated_at"];
if(rawParms)
this.configureFromQueryParameters(rawParms);
} }
public configureFromQueryParameters(rawParms: any) { public configureFromQueryParameters(rawParms: any) {

View File

@@ -1,5 +0,0 @@
declare module "react-images" {
// typing module default export as `any` will allow you to access its members without compiler warning
var Lightbox: any;
export default Lightbox;
}

View File

@@ -1 +0,0 @@
export type HTMLInputProps = React.InputHTMLAttributes<HTMLInputElement>;

View File

@@ -1559,6 +1559,13 @@
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react-images@^0.5.1":
version "0.5.1"
resolved "https://registry.yarnpkg.com/@types/react-images/-/react-images-0.5.1.tgz#e00ccce7221a03ed7b43072cefdc0e5a66fc5a08"
integrity sha512-n2guyR+kblfNEAr1TA3GnrpEdt0/2dHxMOFaFcCgI62NRQaQClaNdgidsim3JRVWLlUiWilDjzZnJUSR0kMncQ==
dependencies:
"@types/react" "*"
"@types/react-router-bootstrap@^0.24.5": "@types/react-router-bootstrap@^0.24.5":
version "0.24.5" version "0.24.5"
resolved "https://registry.yarnpkg.com/@types/react-router-bootstrap/-/react-router-bootstrap-0.24.5.tgz#9257ba3dfb01cda201aac9fa05cde3eb09ea5b27" resolved "https://registry.yarnpkg.com/@types/react-router-bootstrap/-/react-router-bootstrap-0.24.5.tgz#9257ba3dfb01cda201aac9fa05cde3eb09ea5b27"