Added screenshots/previews to tagger list (#939)

* Added screenshots/previews to tagger list
* Move errors and stashids to subcontent container, and tweak layout
* Fix search-result margin
Co-authored-by: Infinite <infinitekittens@protonmail.com>
This commit is contained in:
JoeSmithStarkers 2020-11-24 08:02:31 +11:00 committed by GitHub
parent abf0281903
commit f0ec37c343
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 54 deletions

View file

@ -14,7 +14,7 @@ interface IScenePreviewProps {
soundActive: boolean;
}
const ScenePreview: React.FC<IScenePreviewProps> = ({
export const ScenePreview: React.FC<IScenePreviewProps> = ({
image,
video,
isPortrait,

View file

@ -196,6 +196,7 @@ textarea.scene-description {
&-video {
height: 100%;
object-fit: cover;
object-position: top;
width: 100%;
}

View file

@ -313,7 +313,7 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
setSaveState("");
};
const classname = cx("row no-gutters mt-2 search-result", {
const classname = cx("row mx-0 mt-2 search-result", {
"selected-result": isActive,
});
@ -336,6 +336,11 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
Object.keys(performers ?? []).every((id) => performers?.[id].type) &&
saveState === "";
const endpointBase = endpoint.match(/https?:\/\/.*?\//)?.[0];
const stashBoxURL = endpointBase
? `${endpointBase}scenes/${scene.stash_id}`
: "";
return (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
<li
@ -345,11 +350,13 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
>
<div className="col-lg-6">
<div className="row">
<img
src={scene.images[0]}
alt=""
className="align-self-center scene-image"
/>
<a href={stashBoxURL} target="_blank" rel="noopener noreferrer">
<img
src={scene.images[0]}
alt=""
className="align-self-center scene-image"
/>
</a>
<div className="d-flex flex-column justify-content-center scene-metadata">
<h4 className="text-truncate" title={scene?.title ?? ""}>
{sceneTitle}

View file

@ -2,6 +2,7 @@ import React, { useState } from "react";
import { Button, Card, Form, InputGroup } from "react-bootstrap";
import { Link } from "react-router-dom";
import { HashLink } from "react-router-hash-link";
import { ScenePreview } from "src/components/Scenes/SceneCard";
import * as GQL from "src/core/generated-graphql";
import { LoadingIndicator } from "src/components/Shared";
@ -233,14 +234,19 @@ const TaggerList: React.FC<ITaggerListProps> = ({
null;
const isTagged = taggedScenes[scene.id];
const hasStashIDs = scene.stash_ids.length > 0;
const width = scene.file.width ? scene.file.width : 0;
const height = scene.file.height ? scene.file.height : 0;
const isPortrait = height > width;
let maincontent;
let mainContent;
if (!isTagged && hasStashIDs) {
maincontent = (
<h5 className="text-right text-bold">Scene already tagged</h5>
mainContent = (
<div className="text-right">
<h5 className="text-bold">Scene already tagged</h5>
</div>
);
} else if (!isTagged && !hasStashIDs) {
maincontent = (
mainContent = (
<InputGroup>
<Form.Control
className="text-input"
@ -275,27 +281,52 @@ const TaggerList: React.FC<ITaggerListProps> = ({
</InputGroup>
);
} else if (isTagged) {
maincontent = (
<h5 className="row no-gutters">
<b className="col-4">Scene successfully tagged:</b>
<Link
className="offset-1 col-7 text-right"
to={`/scenes/${scene.id}`}
>
{taggedScenes[scene.id].title}
</Link>
</h5>
mainContent = (
<div className="d-flex flex-column text-right">
<h5>Scene successfully tagged:</h5>
<h6>
<Link className="bold" to={`/scenes/${scene.id}`}>
{taggedScenes[scene.id].title}
</Link>
</h6>
</div>
);
}
let searchResult;
if (searchErrors[scene.id]) {
searchResult = (
let subContent;
if (scene.stash_ids.length > 0) {
const stashLinks = scene.stash_ids.map((stashID) => {
const base = stashID.endpoint.match(/https?:\/\/.*?\//)?.[0];
const link = base ? (
<a
className="small d-block"
href={`${base}scenes/${stashID.stash_id}`}
target="_blank"
rel="noopener noreferrer"
>
{stashID.stash_id}
</a>
) : (
<div className="small">{stashID.stash_id}</div>
);
return link;
});
subContent = <>{stashLinks}</>;
} else if (searchErrors[scene.id]) {
subContent = (
<div className="text-danger font-weight-bold">
{searchErrors[scene.id]}
</div>
);
} else if (fingerprintMatch && !isTagged && !hasStashIDs) {
} else if (searchResults[scene.id]?.length === 0) {
subContent = (
<div className="text-danger font-weight-bold">No results found.</div>
);
}
let searchResult;
if (fingerprintMatch && !isTagged && !hasStashIDs) {
searchResult = (
<StashSearchResult
showMales={config.showMales}
@ -317,7 +348,7 @@ const TaggerList: React.FC<ITaggerListProps> = ({
!fingerprintMatch
) {
searchResult = (
<ul className="pl-0 mt-4">
<ul className="pl-0 mt-3 mb-0">
{sortScenesByDuration(
searchResults[scene.id],
scene.file.duration ?? undefined
@ -347,19 +378,25 @@ const TaggerList: React.FC<ITaggerListProps> = ({
)}
</ul>
);
} else if (searchResults[scene.id]?.length === 0) {
searchResult = (
<div className="text-danger font-weight-bold">No results found.</div>
);
}
return (
<div key={scene.id} className="my-2 search-item">
<div className="row">
<div className="col-md-6 my-1 text-truncate align-self-center">
<div className="col col-lg-6 overflow-hidden align-items-center d-flex flex-column flex-sm-row">
<div className="scene-card mr-3">
<Link to={`/scenes/${scene.id}`}>
<ScenePreview
image={scene.paths.screenshot ?? undefined}
video={scene.paths.preview ?? undefined}
isPortrait={isPortrait}
soundActive={false}
/>
</Link>
</div>
<Link
to={`/scenes/${scene.id}`}
className="scene-link"
className="scene-link text-truncate w-100"
title={scene.path}
>
{originalDir}
@ -367,7 +404,10 @@ const TaggerList: React.FC<ITaggerListProps> = ({
{`${file}.${ext}`}
</Link>
</div>
<div className="col-md-6 my-1">{maincontent}</div>
<div className="col-md-6 my-1 align-self-center">
{mainContent}
<div className="sub-content text-right">{subContent}</div>
</div>
</div>
{searchResult}
</div>
@ -376,9 +416,9 @@ const TaggerList: React.FC<ITaggerListProps> = ({
return (
<Card className="tagger-table">
<div className="tagger-table-header row flex-nowrap mb-4 align-items-center">
<div className="col-md-6">
<b>Path</b>
<div className="tagger-table-header d-flex flex-nowrap align-items-center">
<div className="col-md-6 pl-0">
<b>Scene</b>
</div>
<div className="col-md-2">
<b>Query</b>
@ -400,18 +440,14 @@ const TaggerList: React.FC<ITaggerListProps> = ({
</Button>
)}
</div>
<div className="mr-2">
<Button
onClick={handleFingerprintSearch}
disabled={!canFingerprintSearch() && !loadingFingerprints}
>
{canFingerprintSearch() && <span>Match Fingerprints</span>}
{!canFingerprintSearch() && getFingerprintCount()}
{loadingFingerprints && (
<LoadingIndicator message="" inline small />
)}
</Button>
</div>
<Button
onClick={handleFingerprintSearch}
disabled={!canFingerprintSearch() && !loadingFingerprints}
>
{canFingerprintSearch() && <span>Match Fingerprints</span>}
{!canFingerprintSearch() && getFingerprintCount()}
{loadingFingerprints && <LoadingIndicator message="" inline small />}
</Button>
</div>
{renderScenes()}
</Card>
@ -467,7 +503,7 @@ export const Tagger: React.FC<ITaggerProps> = ({ scenes }) => {
onClose={() => setShowManual(false)}
defaultActiveTab="Tagger.md"
/>
<div className="tagger-container mx-auto">
<div className="tagger-container row mx-md-auto">
{selectedEndpointIndex !== -1 && selectedEndpoint ? (
<>
<div className="row mb-2 no-gutters">

View file

@ -1,6 +1,21 @@
.tagger-container {
max-width: 1400px;
// min-width: 1200px;
max-width: 1600px;
.scene-card-preview {
border-radius: 3px;
margin-bottom: 0;
max-height: 100px;
overflow: hidden;
width: 150px;
&-video {
background-color: #495b68;
}
}
.sub-content {
min-height: 1.5rem;
}
}
.tagger-table {
@ -9,14 +24,13 @@
.search-item {
background-color: #495b68;
margin-left: -20px;
margin-right: -20px;
border-radius: 3px;
padding: 1rem;
}
.search-result {
background-color: rgba(61, 80, 92, 0.3);
padding: 0.5rem 1rem;
padding: 1rem 0;
&:hover {
background-color: hsl(204, 20, 30);
@ -43,6 +57,7 @@
max-height: 10rem;
max-width: 14rem;
min-width: 168px;
object-fit: contain;
padding: 0 1rem;
}