mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Fix various console errors and graphql loading issues (#760)
* Refactor listhook to resolve loading issues * Fix graphql loading race conditions * Various console spam * Fix scene card overlay hierarchy * Fix modal and manual borders
This commit is contained in:
@@ -132,7 +132,7 @@ export const Manual: React.FC<IManualProps> = ({ show, onClose }) => {
|
|||||||
<Nav variant="pills" className="flex-column">
|
<Nav variant="pills" className="flex-column">
|
||||||
{content.map((c) => {
|
{content.map((c) => {
|
||||||
return (
|
return (
|
||||||
<Nav.Item>
|
<Nav.Item key={`${c.key}-nav`}>
|
||||||
<Nav.Link className={c.className} eventKey={c.key}>
|
<Nav.Link className={c.className} eventKey={c.key}>
|
||||||
{c.title}
|
{c.title}
|
||||||
</Nav.Link>
|
</Nav.Link>
|
||||||
@@ -146,7 +146,11 @@ export const Manual: React.FC<IManualProps> = ({ show, onClose }) => {
|
|||||||
<Tab.Content>
|
<Tab.Content>
|
||||||
{content.map((c) => {
|
{content.map((c) => {
|
||||||
return (
|
return (
|
||||||
<Tab.Pane eventKey={c.key} onClick={interceptLinkClick}>
|
<Tab.Pane
|
||||||
|
eventKey={c.key}
|
||||||
|
key={`${c.key}-pane`}
|
||||||
|
onClick={interceptLinkClick}
|
||||||
|
>
|
||||||
<Page page={c.content} />
|
<Page page={c.content} />
|
||||||
</Tab.Pane>
|
</Tab.Pane>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
.manual {
|
.manual {
|
||||||
background-color: #30404d;
|
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
|
|
||||||
.close {
|
.close {
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.manual-container {
|
&-container {
|
||||||
padding-left: 1px;
|
padding-left: 1px;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header,
|
&-header,
|
||||||
.modal-body {
|
&-body {
|
||||||
background-color: #30404d;
|
background-color: #30404d;
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
@@ -21,22 +20,22 @@
|
|||||||
.indent-1 {
|
.indent-1 {
|
||||||
padding-left: 2rem;
|
padding-left: 2rem;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.manual .manual-content,
|
.manual-content,
|
||||||
.manual .manual-toc {
|
.manual-toc {
|
||||||
max-height: calc(100vh - 10rem);
|
max-height: calc(100vh - 10rem);
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 992px) {
|
|
||||||
.manual .modal-body {
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.manual-content,
|
@media (max-width: 992px) {
|
||||||
.manual-toc {
|
.modal-body {
|
||||||
max-height: inherit;
|
overflow-y: auto;
|
||||||
overflow-y: hidden;
|
|
||||||
|
.manual-content,
|
||||||
|
.manual-toc {
|
||||||
|
max-height: inherit;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,6 +285,10 @@ export const Performer: React.FC = () => {
|
|||||||
|
|
||||||
const photos = [{ src: activeImage, caption: "Image" }];
|
const photos = [{ src: activeImage, caption: "Image" }];
|
||||||
|
|
||||||
|
if (!performer.id) {
|
||||||
|
return <LoadingIndicator />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="performer-page" className="row">
|
<div id="performer-page" className="row">
|
||||||
<div className="image-container col-md-4 text-center">
|
<div className="image-container col-md-4 text-center">
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
|||||||
if (props.scene.performers.length <= 0) return;
|
if (props.scene.performers.length <= 0) return;
|
||||||
|
|
||||||
const popoverContent = props.scene.performers.map((performer) => (
|
const popoverContent = props.scene.performers.map((performer) => (
|
||||||
<div className="performer-tag-container row" key="performer">
|
<div className="performer-tag-container row" key={performer.id}>
|
||||||
<Link
|
<Link
|
||||||
to={`/performers/${performer.id}`}
|
to={`/performers/${performer.id}`}
|
||||||
className="performer-tag col m-auto zoom-2"
|
className="performer-tag col m-auto zoom-2"
|
||||||
@@ -151,7 +151,11 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
|||||||
));
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HoverPopover placement="bottom" content={popoverContent}>
|
<HoverPopover
|
||||||
|
placement="bottom"
|
||||||
|
content={popoverContent}
|
||||||
|
className="tag-tooltip"
|
||||||
|
>
|
||||||
<Button className="minimal">
|
<Button className="minimal">
|
||||||
<Icon icon="film" />
|
<Icon icon="film" />
|
||||||
<span>{props.scene.movies.length}</span>
|
<span>{props.scene.movies.length}</span>
|
||||||
@@ -285,26 +289,28 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Link
|
<div className="video-section">
|
||||||
to={`/scenes/${props.scene.id}`}
|
<Link
|
||||||
className="scene-card-link"
|
to={`/scenes/${props.scene.id}`}
|
||||||
onClick={handleSceneClick}
|
className="scene-card-link"
|
||||||
onDragStart={handleDrag}
|
onClick={handleSceneClick}
|
||||||
onDragOver={handleDragOver}
|
onDragStart={handleDrag}
|
||||||
draggable={props.selecting}
|
onDragOver={handleDragOver}
|
||||||
>
|
draggable={props.selecting}
|
||||||
{maybeRenderRatingBanner()}
|
|
||||||
{maybeRenderSceneStudioOverlay()}
|
|
||||||
{maybeRenderSceneSpecsOverlay()}
|
|
||||||
<video
|
|
||||||
loop
|
|
||||||
className={cx("scene-card-video", { portrait: isPortrait() })}
|
|
||||||
poster={props.scene.paths.screenshot || ""}
|
|
||||||
ref={hoverHandler.videoEl}
|
|
||||||
>
|
>
|
||||||
{previewPath ? <source src={previewPath} /> : ""}
|
{maybeRenderRatingBanner()}
|
||||||
</video>
|
{maybeRenderSceneSpecsOverlay()}
|
||||||
</Link>
|
<video
|
||||||
|
loop
|
||||||
|
className={cx("scene-card-video", { portrait: isPortrait() })}
|
||||||
|
poster={props.scene.paths.screenshot || ""}
|
||||||
|
ref={hoverHandler.videoEl}
|
||||||
|
>
|
||||||
|
{previewPath ? <source src={previewPath} /> : ""}
|
||||||
|
</video>
|
||||||
|
</Link>
|
||||||
|
{maybeRenderSceneStudioOverlay()}
|
||||||
|
</div>
|
||||||
<div className="card-section">
|
<div className="card-section">
|
||||||
<h5 className="card-section-title">
|
<h5 className="card-section-title">
|
||||||
{props.scene.title
|
{props.scene.title
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ export const RatingStars: React.FC<IRatingStarsProps> = (
|
|||||||
onFocus={() => onMouseOver(rating)}
|
onFocus={() => onMouseOver(rating)}
|
||||||
onBlur={() => onMouseOut(rating)}
|
onBlur={() => onMouseOut(rating)}
|
||||||
title={getTooltip(rating)}
|
title={getTooltip(rating)}
|
||||||
|
key={`star-${rating}`}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon={[getIconPrefix(rating), "star"]}
|
icon={[getIconPrefix(rating), "star"]}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.video-section {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.card-section {
|
.card-section {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding: 0.5rem 1rem 0 1rem;
|
padding: 0.5rem 1rem 0 1rem;
|
||||||
@@ -174,10 +178,6 @@ textarea.scene-description {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-link {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-card-check {
|
.scene-card-check {
|
||||||
left: 0.5rem;
|
left: 0.5rem;
|
||||||
margin-top: -12px;
|
margin-top: -12px;
|
||||||
|
|||||||
@@ -455,8 +455,10 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
className="col col-sm-6 text-input"
|
className="col col-sm-6 text-input"
|
||||||
type="number"
|
type="number"
|
||||||
value={previewSegments.toString()}
|
value={previewSegments.toString()}
|
||||||
onInput={(e: React.FormEvent<HTMLInputElement>) =>
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
setPreviewSegments(Number.parseInt(e.currentTarget.value, 10))
|
setPreviewSegments(
|
||||||
|
Number.parseInt(e.currentTarget.value || "0", 10)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Form.Text className="text-muted">
|
<Form.Text className="text-muted">
|
||||||
@@ -470,9 +472,9 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
className="col col-sm-6 text-input"
|
className="col col-sm-6 text-input"
|
||||||
type="number"
|
type="number"
|
||||||
value={previewSegmentDuration.toString()}
|
value={previewSegmentDuration.toString()}
|
||||||
onInput={(e: React.FormEvent<HTMLInputElement>) =>
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
setPreviewSegmentDuration(
|
setPreviewSegmentDuration(
|
||||||
Number.parseFloat(e.currentTarget.value)
|
Number.parseFloat(e.currentTarget.value || "0")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -583,8 +585,10 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
className="col col-sm-6 text-input"
|
className="col col-sm-6 text-input"
|
||||||
type="number"
|
type="number"
|
||||||
value={maxSessionAge.toString()}
|
value={maxSessionAge.toString()}
|
||||||
onInput={(e: React.FormEvent<HTMLInputElement>) =>
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
setMaxSessionAge(Number.parseInt(e.currentTarget.value, 10))
|
setMaxSessionAge(
|
||||||
|
Number.parseInt(e.currentTarget.value || "0", 10)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Form.Text className="text-muted">
|
<Form.Text className="text-muted">
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export const HoverPopover: React.FC<IHoverPopover> = ({
|
|||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
id="popover"
|
id="popover"
|
||||||
|
className="hover-popover-content"
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Badge } from "react-bootstrap";
|
import { Badge } from "react-bootstrap";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import cx from "classnames";
|
||||||
import {
|
import {
|
||||||
PerformerDataFragment,
|
PerformerDataFragment,
|
||||||
SceneMarkerDataFragment,
|
SceneMarkerDataFragment,
|
||||||
@@ -43,7 +44,7 @@ export const TagLink: React.FC<IProps> = (props: IProps) => {
|
|||||||
: TextUtils.fileNameFromPath(props.scene.path ?? "");
|
: TextUtils.fileNameFromPath(props.scene.path ?? "");
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Badge className={`tag-item ${props.className}`} variant="secondary">
|
<Badge className={cx("tag-item", props.className)} variant="secondary">
|
||||||
<Link to={link}>{title}</Link>
|
<Link to={link}>{title}</Link>
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -128,3 +128,8 @@ button.collapse-button.btn-primary:not(:disabled):not(.disabled):active {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
color: #f5f8fa;
|
color: #f5f8fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hover-popover-content {
|
||||||
|
max-width: 32rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export const Studio: React.FC = () => {
|
|||||||
const imageEncoding = ImageUtils.usePasteImage(onImageLoad, isEditing);
|
const imageEncoding = ImageUtils.usePasteImage(onImageLoad, isEditing);
|
||||||
|
|
||||||
if (!isNew && !isEditing) {
|
if (!isNew && !isEditing) {
|
||||||
if (!data?.findStudio || loading) return <LoadingIndicator />;
|
if (!data?.findStudio || loading || !studio.id) return <LoadingIndicator />;
|
||||||
if (error) return <div>{error.message}</div>;
|
if (error) return <div>{error.message}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const Tag: React.FC = () => {
|
|||||||
const [name, setName] = useState<string>();
|
const [name, setName] = useState<string>();
|
||||||
|
|
||||||
// Tag state
|
// Tag state
|
||||||
const [tag, setTag] = useState<Partial<GQL.TagDataFragment>>({});
|
const [tag, setTag] = useState<GQL.TagDataFragment | undefined>();
|
||||||
const [imagePreview, setImagePreview] = useState<string>();
|
const [imagePreview, setImagePreview] = useState<string>();
|
||||||
|
|
||||||
const { data, error, loading } = useFindTag(id);
|
const { data, error, loading } = useFindTag(id);
|
||||||
@@ -71,11 +71,11 @@ export const Tag: React.FC = () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateTagEditState(state: Partial<GQL.TagDataFragment>) {
|
function updateTagEditState(state: GQL.TagDataFragment) {
|
||||||
setName(state.name);
|
setName(state.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTagData(tagData: Partial<GQL.TagDataFragment>) {
|
function updateTagData(tagData: GQL.TagDataFragment) {
|
||||||
setImage(undefined);
|
setImage(undefined);
|
||||||
updateTagEditState(tagData);
|
updateTagEditState(tagData);
|
||||||
setImagePreview(tagData.image_path ?? undefined);
|
setImagePreview(tagData.image_path ?? undefined);
|
||||||
@@ -104,15 +104,17 @@ export const Tag: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTagInput() {
|
function getTagInput() {
|
||||||
const input: Partial<GQL.TagCreateInput | GQL.TagUpdateInput> = {
|
if (!isNew) {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
image,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
name,
|
name,
|
||||||
image,
|
image,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isNew) {
|
|
||||||
(input as GQL.TagUpdateInput).id = id;
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSave() {
|
async function onSave() {
|
||||||
@@ -136,7 +138,7 @@ export const Tag: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onAutoTag() {
|
async function onAutoTag() {
|
||||||
if (!tag.id) return;
|
if (!tag?.id) return;
|
||||||
try {
|
try {
|
||||||
await mutateMetadataAutoTag({ tags: [tag.id] });
|
await mutateMetadataAutoTag({ tags: [tag.id] });
|
||||||
Toast.success({ content: "Started auto tagging" });
|
Toast.success({ content: "Started auto tagging" });
|
||||||
@@ -175,13 +177,15 @@ export const Tag: React.FC = () => {
|
|||||||
|
|
||||||
function onToggleEdit() {
|
function onToggleEdit() {
|
||||||
setIsEditing(!isEditing);
|
setIsEditing(!isEditing);
|
||||||
updateTagData(tag);
|
if (tag) {
|
||||||
|
updateTagData(tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClearImage() {
|
function onClearImage() {
|
||||||
setImage(null);
|
setImage(null);
|
||||||
setImagePreview(
|
setImagePreview(
|
||||||
tag.image_path ? `${tag.image_path}?default=true` : undefined
|
tag?.image_path ? `${tag.image_path}?default=true` : undefined
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +230,7 @@ export const Tag: React.FC = () => {
|
|||||||
acceptSVG
|
acceptSVG
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!isNew && (
|
{!isNew && tag && (
|
||||||
<div className="col col-md-8">
|
<div className="col col-md-8">
|
||||||
<Tabs
|
<Tabs
|
||||||
id="tag-tabs"
|
id="tag-tabs"
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import { SceneList } from "src/components/Scenes/SceneList";
|
|||||||
import { TagsCriterion } from "src/models/list-filter/criteria/tags";
|
import { TagsCriterion } from "src/models/list-filter/criteria/tags";
|
||||||
|
|
||||||
interface ITagScenesPanel {
|
interface ITagScenesPanel {
|
||||||
tag: Partial<GQL.TagDataFragment>;
|
tag: GQL.TagDataFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TagScenesPanel: React.FC<ITagScenesPanel> = ({ tag }) => {
|
export const TagScenesPanel: React.FC<ITagScenesPanel> = ({ tag }) => {
|
||||||
function filterHook(filter: ListFilterModel) {
|
function filterHook(filter: ListFilterModel) {
|
||||||
const tagValue = { id: tag.id!, label: tag.name! };
|
const tagValue = { id: tag.id, label: tag.name };
|
||||||
// if tag is already present, then we modify it, otherwise add
|
// if tag is already present, then we modify it, otherwise add
|
||||||
let tagCriterion = filter.criteria.find((c) => {
|
let tagCriterion = filter.criteria.find((c) => {
|
||||||
return c.type === "tags";
|
return c.type === "tags";
|
||||||
|
|||||||
@@ -38,6 +38,24 @@ import {
|
|||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
import { FilterMode } from "src/models/list-filter/types";
|
import { FilterMode } from "src/models/list-filter/types";
|
||||||
|
|
||||||
|
const getSelectedData = <I extends IDataItem>(
|
||||||
|
result: I[],
|
||||||
|
selectedIds: Set<string>
|
||||||
|
) => {
|
||||||
|
// find the selected items from the ids
|
||||||
|
const selectedResults: I[] = [];
|
||||||
|
|
||||||
|
selectedIds.forEach((id) => {
|
||||||
|
const item = result.find((s) => s.id === id);
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
selectedResults.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return selectedResults;
|
||||||
|
};
|
||||||
|
|
||||||
interface IListHookData {
|
interface IListHookData {
|
||||||
filter: ListFilterModel;
|
filter: ListFilterModel;
|
||||||
template: JSX.Element;
|
template: JSX.Element;
|
||||||
@@ -99,34 +117,45 @@ interface IQuery<T extends IQueryResult, T2 extends IDataItem> {
|
|||||||
filterMode: FilterMode;
|
filterMode: FilterMode;
|
||||||
useData: (filter: ListFilterModel) => T;
|
useData: (filter: ListFilterModel) => T;
|
||||||
getData: (data: T) => T2[];
|
getData: (data: T) => T2[];
|
||||||
getSelectedData: (data: T, selectedIds: Set<string>) => T2[];
|
|
||||||
getCount: (data: T) => number;
|
getCount: (data: T) => number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
interface IRenderListProps {
|
||||||
options: IListHookOptions<QueryResult, QueryData> &
|
filter: ListFilterModel;
|
||||||
IQuery<QueryResult, QueryData>
|
onChangePage: (page: number) => void;
|
||||||
): IListHookData => {
|
updateQueryParams: (filter: ListFilterModel) => void;
|
||||||
const [interfaceState, setInterfaceState] = useInterfaceLocalForage();
|
}
|
||||||
const [forageInitialised, setForageInitialised] = useState(false);
|
|
||||||
const history = useHistory();
|
const RenderList = <
|
||||||
const location = useLocation();
|
QueryResult extends IQueryResult,
|
||||||
const [filter, setFilter] = useState<ListFilterModel>(
|
QueryData extends IDataItem
|
||||||
new ListFilterModel(options.filterMode, queryString.parse(location.search))
|
>({
|
||||||
);
|
defaultZoomIndex,
|
||||||
|
filter,
|
||||||
|
onChangePage,
|
||||||
|
addKeybinds,
|
||||||
|
useData,
|
||||||
|
getCount,
|
||||||
|
getData,
|
||||||
|
otherOperations,
|
||||||
|
renderContent,
|
||||||
|
zoomable,
|
||||||
|
selectable,
|
||||||
|
renderEditDialog,
|
||||||
|
renderDeleteDialog,
|
||||||
|
updateQueryParams,
|
||||||
|
}: IListHookOptions<QueryResult, QueryData> &
|
||||||
|
IQuery<QueryResult, QueryData> &
|
||||||
|
IRenderListProps) => {
|
||||||
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
|
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
|
||||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||||
const [lastClickedId, setLastClickedId] = useState<string | undefined>();
|
const [lastClickedId, setLastClickedId] = useState<string | undefined>();
|
||||||
const [zoomIndex, setZoomIndex] = useState<number>(
|
const [zoomIndex, setZoomIndex] = useState<number>(defaultZoomIndex ?? 1);
|
||||||
options.defaultZoomIndex ?? 1
|
|
||||||
);
|
|
||||||
// Store initial pathname to prevent hooks from operating outside this page
|
|
||||||
const originalPathName = useRef(location.pathname);
|
|
||||||
|
|
||||||
const result = options.useData(getFilter());
|
const result = useData(filter);
|
||||||
const totalCount = options.getCount(result);
|
const totalCount = getCount(result);
|
||||||
const items = options.getData(result);
|
const items = getData(result);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind("right", () => {
|
Mousetrap.bind("right", () => {
|
||||||
@@ -140,7 +169,6 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
|||||||
onChangePage(filter.currentPage - 1);
|
onChangePage(filter.currentPage - 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Mousetrap.bind("shift+right", () => {
|
Mousetrap.bind("shift+right", () => {
|
||||||
const maxPage = totalCount / filter.itemsPerPage + 1;
|
const maxPage = totalCount / filter.itemsPerPage + 1;
|
||||||
onChangePage(Math.min(maxPage, filter.currentPage + 10));
|
onChangePage(Math.min(maxPage, filter.currentPage + 10));
|
||||||
@@ -157,8 +185,8 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let unbindExtras: () => void;
|
let unbindExtras: () => void;
|
||||||
if (options.addKeybinds) {
|
if (addKeybinds) {
|
||||||
unbindExtras = options.addKeybinds(result, filter, selectedIds);
|
unbindExtras = addKeybinds(result, filter, selectedIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -175,102 +203,6 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateInterfaceConfig = useCallback(
|
|
||||||
(updatedFilter: ListFilterModel) => {
|
|
||||||
setInterfaceState((config) => {
|
|
||||||
const data = { ...config } as IInterfaceConfig;
|
|
||||||
data.queries = {
|
|
||||||
[options.filterMode]: {
|
|
||||||
filter: updatedFilter.makeQueryParameters(),
|
|
||||||
itemsPerPage: updatedFilter.itemsPerPage,
|
|
||||||
currentPage: updatedFilter.currentPage,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[options.filterMode, setInterfaceState]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
interfaceState.loading ||
|
|
||||||
// Only update query params on page the hook was mounted on
|
|
||||||
history.location.pathname !== originalPathName.current
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
if (!forageInitialised) setForageInitialised(true);
|
|
||||||
|
|
||||||
if (!options.persistState) return;
|
|
||||||
|
|
||||||
const storedQuery = interfaceState.data?.queries?.[options.filterMode];
|
|
||||||
if (!storedQuery) return;
|
|
||||||
|
|
||||||
const queryFilter = queryString.parse(history.location.search);
|
|
||||||
const storedFilter = queryString.parse(storedQuery.filter);
|
|
||||||
const query = history.location.search
|
|
||||||
? {
|
|
||||||
sortby: storedFilter.sortby,
|
|
||||||
sortdir: storedFilter.sortdir,
|
|
||||||
disp: storedFilter.disp,
|
|
||||||
perPage: storedFilter.perPage,
|
|
||||||
...queryFilter,
|
|
||||||
}
|
|
||||||
: storedFilter;
|
|
||||||
|
|
||||||
const newFilter = new ListFilterModel(options.filterMode, query);
|
|
||||||
|
|
||||||
// Compare constructed filter with current filter.
|
|
||||||
// If different it's the result of navigation, and we update the filter.
|
|
||||||
const newLocation = { ...history.location };
|
|
||||||
newLocation.search = newFilter.makeQueryParameters();
|
|
||||||
if (newLocation.search !== filter.makeQueryParameters()) {
|
|
||||||
setFilter(newFilter);
|
|
||||||
updateInterfaceConfig(newFilter);
|
|
||||||
}
|
|
||||||
// If constructed search is different from current, update it as well
|
|
||||||
if (newLocation.search !== location.search) {
|
|
||||||
newLocation.search = newFilter.makeQueryParameters();
|
|
||||||
history.replace(newLocation);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
filter,
|
|
||||||
interfaceState.data,
|
|
||||||
interfaceState.loading,
|
|
||||||
history,
|
|
||||||
location.search,
|
|
||||||
options.filterMode,
|
|
||||||
forageInitialised,
|
|
||||||
updateInterfaceConfig,
|
|
||||||
options.persistState,
|
|
||||||
]);
|
|
||||||
|
|
||||||
function getFilter() {
|
|
||||||
if (!options.filterHook) {
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make a copy of the filter and call the hook
|
|
||||||
const newFilter = _.cloneDeep(filter);
|
|
||||||
return options.filterHook(newFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateQueryParams(listFilter: ListFilterModel) {
|
|
||||||
setFilter(listFilter);
|
|
||||||
const newLocation = { ...location };
|
|
||||||
newLocation.search = listFilter.makeQueryParameters();
|
|
||||||
history.replace(newLocation);
|
|
||||||
if (options.persistState) {
|
|
||||||
updateInterfaceConfig(listFilter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onChangePage(page: number) {
|
|
||||||
const newFilter = _.cloneDeep(filter);
|
|
||||||
newFilter.currentPage = page;
|
|
||||||
updateQueryParams(newFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
function singleSelect(id: string, selected: boolean) {
|
function singleSelect(id: string, selected: boolean) {
|
||||||
setLastClickedId(id);
|
setLastClickedId(id);
|
||||||
|
|
||||||
@@ -348,54 +280,21 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
|||||||
setZoomIndex(newZoomIndex);
|
setZoomIndex(newZoomIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
const otherOperations = options.otherOperations
|
const operations =
|
||||||
? options.otherOperations.map((o) => {
|
otherOperations &&
|
||||||
return {
|
otherOperations.map((o) => ({
|
||||||
text: o.text,
|
text: o.text,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
o.onClick(result, filter, selectedIds);
|
o.onClick(result, filter, selectedIds);
|
||||||
},
|
},
|
||||||
isDisplayed: () => {
|
isDisplayed: () => {
|
||||||
if (o.isDisplayed) {
|
if (o.isDisplayed) {
|
||||||
return o.isDisplayed(result, filter, selectedIds);
|
return o.isDisplayed(result, filter, selectedIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
}));
|
||||||
})
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
function maybeRenderContent() {
|
|
||||||
if (!result.loading && !result.error) {
|
|
||||||
return options.renderContent(result, filter, selectedIds, zoomIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybeRenderPaginationIndex() {
|
|
||||||
if (!result.loading && !result.error) {
|
|
||||||
return (
|
|
||||||
<PaginationIndex
|
|
||||||
itemsPerPage={filter.itemsPerPage}
|
|
||||||
currentPage={filter.currentPage}
|
|
||||||
totalItems={totalCount}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybeRenderPagination() {
|
|
||||||
if (!result.loading && !result.error) {
|
|
||||||
return (
|
|
||||||
<Pagination
|
|
||||||
itemsPerPage={filter.itemsPerPage}
|
|
||||||
currentPage={filter.currentPage}
|
|
||||||
totalItems={totalCount}
|
|
||||||
onChangePage={onChangePage}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEdit() {
|
function onEdit() {
|
||||||
setIsEditDialogOpen(true);
|
setIsEditDialogOpen(true);
|
||||||
@@ -425,60 +324,189 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
|||||||
result.refetch();
|
result.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = (
|
const renderPagination = () => (
|
||||||
<div>
|
<Pagination
|
||||||
<ListFilter
|
itemsPerPage={filter.itemsPerPage}
|
||||||
onFilterUpdate={updateQueryParams}
|
currentPage={filter.currentPage}
|
||||||
onSelectAll={options.selectable ? onSelectAll : undefined}
|
totalItems={totalCount}
|
||||||
onSelectNone={options.selectable ? onSelectNone : undefined}
|
onChangePage={onChangePage}
|
||||||
zoomIndex={options.zoomable ? zoomIndex : undefined}
|
/>
|
||||||
onChangeZoom={options.zoomable ? onChangeZoom : undefined}
|
|
||||||
otherOperations={otherOperations}
|
|
||||||
itemsSelected={selectedIds.size > 0}
|
|
||||||
onEdit={options.renderEditDialog ? onEdit : undefined}
|
|
||||||
onDelete={options.renderDeleteDialog ? onDelete : undefined}
|
|
||||||
filter={filter}
|
|
||||||
/>
|
|
||||||
{isEditDialogOpen && options.renderEditDialog
|
|
||||||
? options.renderEditDialog(
|
|
||||||
options.getSelectedData(result, selectedIds),
|
|
||||||
(applied) => onEditDialogClosed(applied)
|
|
||||||
)
|
|
||||||
: undefined}
|
|
||||||
{isDeleteDialogOpen && options.renderDeleteDialog
|
|
||||||
? options.renderDeleteDialog(
|
|
||||||
options.getSelectedData(result, selectedIds),
|
|
||||||
(deleted) => onDeleteDialogClosed(deleted)
|
|
||||||
)
|
|
||||||
: undefined}
|
|
||||||
{(result.loading || !forageInitialised) && <LoadingIndicator />}
|
|
||||||
{result.error && <h1>{result.error.message}</h1>}
|
|
||||||
{maybeRenderPagination()}
|
|
||||||
{maybeRenderContent()}
|
|
||||||
{maybeRenderPaginationIndex()}
|
|
||||||
{maybeRenderPagination()}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return { filter, template, onSelectChange };
|
let content;
|
||||||
|
if (result.loading) {
|
||||||
|
content = <LoadingIndicator />;
|
||||||
|
} else if (result.error) {
|
||||||
|
content = <h1>{result.error.message}</h1>;
|
||||||
|
} else {
|
||||||
|
content = (
|
||||||
|
<div>
|
||||||
|
<ListFilter
|
||||||
|
onFilterUpdate={updateQueryParams}
|
||||||
|
onSelectAll={selectable ? onSelectAll : undefined}
|
||||||
|
onSelectNone={selectable ? onSelectNone : undefined}
|
||||||
|
zoomIndex={zoomable ? zoomIndex : undefined}
|
||||||
|
onChangeZoom={zoomable ? onChangeZoom : undefined}
|
||||||
|
otherOperations={operations}
|
||||||
|
itemsSelected={selectedIds.size > 0}
|
||||||
|
onEdit={renderEditDialog ? onEdit : undefined}
|
||||||
|
onDelete={renderDeleteDialog ? onDelete : undefined}
|
||||||
|
filter={filter}
|
||||||
|
/>
|
||||||
|
{isEditDialogOpen &&
|
||||||
|
renderEditDialog &&
|
||||||
|
renderEditDialog(
|
||||||
|
getSelectedData(getData(result), selectedIds),
|
||||||
|
(applied) => onEditDialogClosed(applied)
|
||||||
|
)}
|
||||||
|
{isDeleteDialogOpen &&
|
||||||
|
renderDeleteDialog &&
|
||||||
|
renderDeleteDialog(
|
||||||
|
getSelectedData(getData(result), selectedIds),
|
||||||
|
(deleted) => onDeleteDialogClosed(deleted)
|
||||||
|
)}
|
||||||
|
{renderPagination()}
|
||||||
|
{renderContent(result, filter, selectedIds, zoomIndex)}
|
||||||
|
<PaginationIndex
|
||||||
|
itemsPerPage={filter.itemsPerPage}
|
||||||
|
currentPage={filter.currentPage}
|
||||||
|
totalItems={totalCount}
|
||||||
|
/>
|
||||||
|
{renderPagination()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { contentTemplate: content, onSelectChange };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSelectedData = <I extends IDataItem>(
|
const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||||
result: I[],
|
options: IListHookOptions<QueryResult, QueryData> &
|
||||||
selectedIds: Set<string>
|
IQuery<QueryResult, QueryData>
|
||||||
) => {
|
): IListHookData => {
|
||||||
// find the selected items from the ids
|
const history = useHistory();
|
||||||
const selectedResults: I[] = [];
|
const location = useLocation();
|
||||||
|
const [interfaceState, setInterfaceState] = useInterfaceLocalForage();
|
||||||
|
// If persistState is false we don't care about forage and consider it initialised
|
||||||
|
const [forageInitialised, setForageInitialised] = useState(
|
||||||
|
!options.persistState
|
||||||
|
);
|
||||||
|
// Store initial pathname to prevent hooks from operating outside this page
|
||||||
|
const originalPathName = useRef(location.pathname);
|
||||||
|
|
||||||
selectedIds.forEach((id) => {
|
const [filter, setFilter] = useState<ListFilterModel>(
|
||||||
const item = result.find((s) => s.id === id);
|
new ListFilterModel(options.filterMode, queryString.parse(location.search))
|
||||||
|
);
|
||||||
|
|
||||||
if (item) {
|
const updateInterfaceConfig = useCallback(
|
||||||
selectedResults.push(item);
|
(updatedFilter: ListFilterModel) => {
|
||||||
|
setInterfaceState((config) => {
|
||||||
|
const data = { ...config } as IInterfaceConfig;
|
||||||
|
data.queries = {
|
||||||
|
[options.filterMode]: {
|
||||||
|
filter: updatedFilter.makeQueryParameters(),
|
||||||
|
itemsPerPage: updatedFilter.itemsPerPage,
|
||||||
|
currentPage: updatedFilter.currentPage,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[options.filterMode, setInterfaceState]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
interfaceState.loading ||
|
||||||
|
// Only update query params on page the hook was mounted on
|
||||||
|
history.location.pathname !== originalPathName.current
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!forageInitialised) setForageInitialised(true);
|
||||||
|
|
||||||
|
if (!options.persistState) return;
|
||||||
|
|
||||||
|
const storedQuery = interfaceState.data?.queries?.[options.filterMode];
|
||||||
|
if (!storedQuery) return;
|
||||||
|
|
||||||
|
const queryFilter = queryString.parse(history.location.search);
|
||||||
|
const storedFilter = queryString.parse(storedQuery.filter);
|
||||||
|
const query = history.location.search
|
||||||
|
? {
|
||||||
|
sortby: storedFilter.sortby,
|
||||||
|
sortdir: storedFilter.sortdir,
|
||||||
|
disp: storedFilter.disp,
|
||||||
|
perPage: storedFilter.perPage,
|
||||||
|
...queryFilter,
|
||||||
|
}
|
||||||
|
: storedFilter;
|
||||||
|
|
||||||
|
const newFilter = new ListFilterModel(options.filterMode, query);
|
||||||
|
|
||||||
|
// Compare constructed filter with current filter.
|
||||||
|
// If different it's the result of navigation, and we update the filter.
|
||||||
|
const newLocation = { ...history.location };
|
||||||
|
newLocation.search = newFilter.makeQueryParameters();
|
||||||
|
if (newLocation.search !== filter.makeQueryParameters()) {
|
||||||
|
setFilter(newFilter);
|
||||||
|
updateInterfaceConfig(newFilter);
|
||||||
}
|
}
|
||||||
|
// If constructed search is different from current, update it as well
|
||||||
|
if (newLocation.search !== location.search) {
|
||||||
|
newLocation.search = newFilter.makeQueryParameters();
|
||||||
|
history.replace(newLocation);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
filter,
|
||||||
|
interfaceState.data,
|
||||||
|
interfaceState.loading,
|
||||||
|
history,
|
||||||
|
location.search,
|
||||||
|
options.filterMode,
|
||||||
|
forageInitialised,
|
||||||
|
updateInterfaceConfig,
|
||||||
|
options.persistState,
|
||||||
|
]);
|
||||||
|
|
||||||
|
function updateQueryParams(listFilter: ListFilterModel) {
|
||||||
|
setFilter(listFilter);
|
||||||
|
const newLocation = { ...location };
|
||||||
|
newLocation.search = listFilter.makeQueryParameters();
|
||||||
|
history.replace(newLocation);
|
||||||
|
if (options.persistState) {
|
||||||
|
updateInterfaceConfig(listFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChangePage = (page: number) => {
|
||||||
|
const newFilter = _.cloneDeep(filter);
|
||||||
|
newFilter.currentPage = page;
|
||||||
|
updateQueryParams(newFilter);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderFilter = !options.filterHook
|
||||||
|
? filter
|
||||||
|
: options.filterHook(_.cloneDeep(filter));
|
||||||
|
|
||||||
|
const { contentTemplate, onSelectChange } = RenderList({
|
||||||
|
...options,
|
||||||
|
filter: renderFilter,
|
||||||
|
onChangePage,
|
||||||
|
updateQueryParams,
|
||||||
});
|
});
|
||||||
|
|
||||||
return selectedResults;
|
const template = !forageInitialised ? (
|
||||||
|
<LoadingIndicator />
|
||||||
|
) : (
|
||||||
|
<>{contentTemplate}</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
filter,
|
||||||
|
template,
|
||||||
|
onSelectChange,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useScenesList = (
|
export const useScenesList = (
|
||||||
@@ -492,10 +520,6 @@ export const useScenesList = (
|
|||||||
result?.data?.findScenes?.scenes ?? [],
|
result?.data?.findScenes?.scenes ?? [],
|
||||||
getCount: (result: FindScenesQueryResult) =>
|
getCount: (result: FindScenesQueryResult) =>
|
||||||
result?.data?.findScenes?.count ?? 0,
|
result?.data?.findScenes?.count ?? 0,
|
||||||
getSelectedData: (
|
|
||||||
result: FindScenesQueryResult,
|
|
||||||
selectedIds: Set<string>
|
|
||||||
) => getSelectedData(result?.data?.findScenes?.scenes ?? [], selectedIds),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useSceneMarkersList = (
|
export const useSceneMarkersList = (
|
||||||
@@ -509,14 +533,6 @@ export const useSceneMarkersList = (
|
|||||||
result?.data?.findSceneMarkers?.scene_markers ?? [],
|
result?.data?.findSceneMarkers?.scene_markers ?? [],
|
||||||
getCount: (result: FindSceneMarkersQueryResult) =>
|
getCount: (result: FindSceneMarkersQueryResult) =>
|
||||||
result?.data?.findSceneMarkers?.count ?? 0,
|
result?.data?.findSceneMarkers?.count ?? 0,
|
||||||
getSelectedData: (
|
|
||||||
result: FindSceneMarkersQueryResult,
|
|
||||||
selectedIds: Set<string>
|
|
||||||
) =>
|
|
||||||
getSelectedData(
|
|
||||||
result?.data?.findSceneMarkers?.scene_markers ?? [],
|
|
||||||
selectedIds
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useGalleriesList = (
|
export const useGalleriesList = (
|
||||||
@@ -530,14 +546,6 @@ export const useGalleriesList = (
|
|||||||
result?.data?.findGalleries?.galleries ?? [],
|
result?.data?.findGalleries?.galleries ?? [],
|
||||||
getCount: (result: FindGalleriesQueryResult) =>
|
getCount: (result: FindGalleriesQueryResult) =>
|
||||||
result?.data?.findGalleries?.count ?? 0,
|
result?.data?.findGalleries?.count ?? 0,
|
||||||
getSelectedData: (
|
|
||||||
result: FindGalleriesQueryResult,
|
|
||||||
selectedIds: Set<string>
|
|
||||||
) =>
|
|
||||||
getSelectedData(
|
|
||||||
result?.data?.findGalleries?.galleries ?? [],
|
|
||||||
selectedIds
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useStudiosList = (
|
export const useStudiosList = (
|
||||||
@@ -551,10 +559,6 @@ export const useStudiosList = (
|
|||||||
result?.data?.findStudios?.studios ?? [],
|
result?.data?.findStudios?.studios ?? [],
|
||||||
getCount: (result: FindStudiosQueryResult) =>
|
getCount: (result: FindStudiosQueryResult) =>
|
||||||
result?.data?.findStudios?.count ?? 0,
|
result?.data?.findStudios?.count ?? 0,
|
||||||
getSelectedData: (
|
|
||||||
result: FindStudiosQueryResult,
|
|
||||||
selectedIds: Set<string>
|
|
||||||
) => getSelectedData(result?.data?.findStudios?.studios ?? [], selectedIds),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const usePerformersList = (
|
export const usePerformersList = (
|
||||||
@@ -568,14 +572,6 @@ export const usePerformersList = (
|
|||||||
result?.data?.findPerformers?.performers ?? [],
|
result?.data?.findPerformers?.performers ?? [],
|
||||||
getCount: (result: FindPerformersQueryResult) =>
|
getCount: (result: FindPerformersQueryResult) =>
|
||||||
result?.data?.findPerformers?.count ?? 0,
|
result?.data?.findPerformers?.count ?? 0,
|
||||||
getSelectedData: (
|
|
||||||
result: FindPerformersQueryResult,
|
|
||||||
selectedIds: Set<string>
|
|
||||||
) =>
|
|
||||||
getSelectedData(
|
|
||||||
result?.data?.findPerformers?.performers ?? [],
|
|
||||||
selectedIds
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useMoviesList = (
|
export const useMoviesList = (
|
||||||
@@ -589,10 +585,6 @@ export const useMoviesList = (
|
|||||||
result?.data?.findMovies?.movies ?? [],
|
result?.data?.findMovies?.movies ?? [],
|
||||||
getCount: (result: FindMoviesQueryResult) =>
|
getCount: (result: FindMoviesQueryResult) =>
|
||||||
result?.data?.findMovies?.count ?? 0,
|
result?.data?.findMovies?.count ?? 0,
|
||||||
getSelectedData: (
|
|
||||||
result: FindMoviesQueryResult,
|
|
||||||
selectedIds: Set<string>
|
|
||||||
) => getSelectedData(result?.data?.findMovies?.movies ?? [], selectedIds),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useTagsList = (
|
export const useTagsList = (
|
||||||
@@ -606,13 +598,11 @@ export const useTagsList = (
|
|||||||
result?.data?.findTags?.tags ?? [],
|
result?.data?.findTags?.tags ?? [],
|
||||||
getCount: (result: FindTagsQueryResult) =>
|
getCount: (result: FindTagsQueryResult) =>
|
||||||
result?.data?.findTags?.count ?? 0,
|
result?.data?.findTags?.count ?? 0,
|
||||||
getSelectedData: (result: FindTagsQueryResult, selectedIds: Set<string>) =>
|
|
||||||
getSelectedData(result?.data?.findTags?.tags ?? [], selectedIds),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const showWhenSelected = (
|
export const showWhenSelected = (
|
||||||
result: FindScenesQueryResult,
|
_result: FindScenesQueryResult,
|
||||||
filter: ListFilterModel,
|
_filter: ListFilterModel,
|
||||||
selectedIds: Set<string>
|
selectedIds: Set<string>
|
||||||
) => {
|
) => {
|
||||||
return selectedIds.size > 0;
|
return selectedIds.size > 0;
|
||||||
|
|||||||
@@ -26,10 +26,7 @@ export const useVideoHover = (options: IVideoHoverHookOptions) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (videoTag.paused && !isPlaying.current) {
|
if (videoTag.paused && !isPlaying.current) {
|
||||||
videoTag.play().catch((error) => {
|
videoTag.play().catch(() => {});
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error.message);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -176,10 +176,14 @@ hr {
|
|||||||
color: $text-color;
|
color: $text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header,
|
&-header,
|
||||||
.modal-body,
|
&-body,
|
||||||
.modal-footer {
|
&-footer {
|
||||||
background-color: #30404d;
|
background-color: #30404d;
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user