mirror of
https://github.com/stashapp/stash.git
synced 2025-12-14 20:33:16 +01:00
Merge pull request #142 from WithoutPants/performer_list_view
Add performer list view.
This commit is contained in:
commit
0a8e272c97
5 changed files with 234 additions and 88 deletions
|
|
@ -57,7 +57,6 @@ export const Stats: FunctionComponent = () => {
|
|||
* Filters for performers and studios only supports one item, even though it's a multi select.
|
||||
|
||||
TODO:
|
||||
* List view for scenes / performers
|
||||
* Websocket connection to display logs in the UI
|
||||
`}
|
||||
</pre>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { IBaseProps } from "../../models/base-props";
|
|||
import { ListFilterModel } from "../../models/list-filter/filter";
|
||||
import { DisplayMode, FilterMode } from "../../models/list-filter/types";
|
||||
import { PerformerCard } from "./PerformerCard";
|
||||
import { PerformerListTable } from "./PerformerListTable";
|
||||
|
||||
interface IPerformerListProps extends IBaseProps {}
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ export const PerformerList: FunctionComponent<IPerformerListProps> = (props: IPe
|
|||
</div>
|
||||
);
|
||||
} else if (filter.displayMode === DisplayMode.List) {
|
||||
return <h1>TODO</h1>;
|
||||
return <PerformerListTable performers={result.data.findPerformers.performers}/>;
|
||||
} else if (filter.displayMode === DisplayMode.Wall) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
107
ui/v2/src/components/performers/PerformerListTable.tsx
Normal file
107
ui/v2/src/components/performers/PerformerListTable.tsx
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import {
|
||||
HTMLTable,
|
||||
H5,
|
||||
H6,
|
||||
Button,
|
||||
} from "@blueprintjs/core";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import * as GQL from "../../core/generated-graphql";
|
||||
import { NavigationUtils } from "../../utils/navigation";
|
||||
|
||||
interface IPerformerListTableProps {
|
||||
performers: GQL.PerformerDataFragment[];
|
||||
}
|
||||
|
||||
export const PerformerListTable: FunctionComponent<IPerformerListTableProps> = (props: IPerformerListTableProps) => {
|
||||
|
||||
function maybeRenderFavoriteHeart(performer : GQL.PerformerDataFragment) {
|
||||
if (!performer.favorite) { return; }
|
||||
return (
|
||||
<Button
|
||||
icon="heart"
|
||||
disabled={true}
|
||||
className="favorite"
|
||||
minimal={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function renderPerformerImage(performer : GQL.PerformerDataFragment) {
|
||||
const style: React.CSSProperties = {
|
||||
backgroundImage: `url('${performer.image_path}')`,
|
||||
lineHeight: 5,
|
||||
backgroundSize: "contain",
|
||||
display: "inline-block",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
};
|
||||
|
||||
return (
|
||||
<Link
|
||||
className="performer-list-thumbnail"
|
||||
to={`/performers/${performer.id}`}
|
||||
style={style}/>
|
||||
)
|
||||
}
|
||||
|
||||
function renderPerformerRow(performer : GQL.PerformerDataFragment) {
|
||||
return (
|
||||
<>
|
||||
<tr>
|
||||
<td>
|
||||
{renderPerformerImage(performer)}
|
||||
</td>
|
||||
<td style={{textAlign: "left"}}>
|
||||
<Link to={`/performers/${performer.id}`}>
|
||||
<H5 style={{textOverflow: "ellipsis", overflow: "hidden"}}>
|
||||
{performer.name}
|
||||
</H5>
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
{performer.aliases ? performer.aliases : ''}
|
||||
</td>
|
||||
<td>
|
||||
{maybeRenderFavoriteHeart(performer)}
|
||||
</td>
|
||||
<td>
|
||||
<Link to={NavigationUtils.makePerformerScenesUrl(performer)}>
|
||||
<H6>{performer.scene_count}</H6>
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
{performer.birthdate}
|
||||
</td>
|
||||
<td>
|
||||
{performer.height}
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid">
|
||||
<HTMLTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>Aliases</th>
|
||||
<th>Favourite</th>
|
||||
<th>Scene Count</th>
|
||||
<th>Birthdate</th>
|
||||
<th>Height</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{props.performers.map(renderPerformerRow)}
|
||||
</tbody>
|
||||
</HTMLTable>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -1,104 +1,124 @@
|
|||
import {
|
||||
H4,
|
||||
HTMLTable,
|
||||
H5,
|
||||
H6,
|
||||
} from "@blueprintjs/core";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import * as GQL from "../../core/generated-graphql";
|
||||
import { TextUtils } from "../../utils/text";
|
||||
import { TagLink } from "../Shared/TagLink";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import * as GQL from "../../core/generated-graphql";
|
||||
import { TextUtils } from "../../utils/text";
|
||||
import { NavigationUtils } from "../../utils/navigation";
|
||||
|
||||
interface ISceneListTableProps {
|
||||
scenes: GQL.SlimSceneDataFragment[];
|
||||
interface ISceneListTableProps {
|
||||
scenes: GQL.SlimSceneDataFragment[];
|
||||
}
|
||||
|
||||
export const SceneListTable: FunctionComponent<ISceneListTableProps> = (props: ISceneListTableProps) => {
|
||||
|
||||
function renderSceneImage(scene : GQL.SlimSceneDataFragment) {
|
||||
const style: React.CSSProperties = {
|
||||
backgroundImage: `url('${scene.paths.screenshot}')`,
|
||||
lineHeight: 5,
|
||||
backgroundSize: "contain",
|
||||
display: "inline-block",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
};
|
||||
|
||||
return (
|
||||
<Link
|
||||
className="scene-list-thumbnail"
|
||||
to={`/performers/${scene.id}`}
|
||||
style={style}/>
|
||||
)
|
||||
}
|
||||
|
||||
export const SceneListTable: FunctionComponent<ISceneListTableProps> = (props: ISceneListTableProps) => {
|
||||
|
||||
function renderDuration(scene : GQL.SlimSceneDataFragment) {
|
||||
if (scene.file.duration === undefined) { return; }
|
||||
return TextUtils.secondsToTimestamp(scene.file.duration);
|
||||
}
|
||||
|
||||
function renderTags(tags : GQL.SlimSceneDataTags[]) {
|
||||
return tags.map((tag) => (
|
||||
<Link to={NavigationUtils.makeTagScenesUrl(tag)}>
|
||||
<H6>{tag.name}</H6>
|
||||
</Link>
|
||||
));
|
||||
}
|
||||
function renderDuration(scene : GQL.SlimSceneDataFragment) {
|
||||
if (scene.file.duration === undefined) { return; }
|
||||
return TextUtils.secondsToTimestamp(scene.file.duration);
|
||||
}
|
||||
|
||||
function renderPerformers(performers : GQL.SlimSceneDataPerformers[]) {
|
||||
return performers.map((performer) => (
|
||||
<Link to={NavigationUtils.makePerformerScenesUrl(performer)}>
|
||||
<H6>{performer.name}</H6>
|
||||
</Link>
|
||||
));
|
||||
}
|
||||
function renderTags(tags : GQL.SlimSceneDataTags[]) {
|
||||
return tags.map((tag) => (
|
||||
<Link to={NavigationUtils.makeTagScenesUrl(tag)}>
|
||||
<H6>{tag.name}</H6>
|
||||
</Link>
|
||||
));
|
||||
}
|
||||
|
||||
function renderStudio(studio : GQL.SlimSceneDataStudio | undefined) {
|
||||
if (!!studio) {
|
||||
return (
|
||||
<Link to={NavigationUtils.makeStudioScenesUrl(studio)}>
|
||||
<H6>{studio.name}</H6>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
function renderPerformers(performers : GQL.SlimSceneDataPerformers[]) {
|
||||
return performers.map((performer) => (
|
||||
<Link to={NavigationUtils.makePerformerScenesUrl(performer)}>
|
||||
<H6>{performer.name}</H6>
|
||||
</Link>
|
||||
));
|
||||
}
|
||||
|
||||
function renderSceneRow(scene : GQL.SlimSceneDataFragment) {
|
||||
function renderStudio(studio : GQL.SlimSceneDataStudio | undefined) {
|
||||
if (!!studio) {
|
||||
return (
|
||||
<>
|
||||
<tr>
|
||||
<td>
|
||||
<Link to={`/scenes/${scene.id}`}>
|
||||
<H5 style={{textOverflow: "ellipsis", overflow: "hidden"}}>
|
||||
{!!scene.title ? scene.title : TextUtils.fileNameFromPath(scene.path)}
|
||||
</H5>
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
{scene.rating ? scene.rating : ''}
|
||||
</td>
|
||||
<td>
|
||||
{renderDuration(scene)}
|
||||
</td>
|
||||
<td>
|
||||
{renderTags(scene.tags)}
|
||||
</td>
|
||||
<td>
|
||||
{renderPerformers(scene.performers)}
|
||||
</td>
|
||||
<td>
|
||||
{renderStudio(scene.studio)}
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
)
|
||||
<Link to={NavigationUtils.makeStudioScenesUrl(studio)}>
|
||||
<H6>{studio.name}</H6>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function renderSceneRow(scene : GQL.SlimSceneDataFragment) {
|
||||
return (
|
||||
<>
|
||||
<div className="grid">
|
||||
<HTMLTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Rating</th>
|
||||
<th>Duration</th>
|
||||
<th>Tags</th>
|
||||
<th>Performers</th>
|
||||
<th>Studio</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{props.scenes.map(renderSceneRow)}
|
||||
</tbody>
|
||||
</HTMLTable>
|
||||
</div>
|
||||
<tr>
|
||||
<td>
|
||||
{renderSceneImage(scene)}
|
||||
</td>
|
||||
<td style={{textAlign: "left"}}>
|
||||
<Link to={`/scenes/${scene.id}`}>
|
||||
<H5 style={{textOverflow: "ellipsis", overflow: "hidden"}}>
|
||||
{!!scene.title ? scene.title : TextUtils.fileNameFromPath(scene.path)}
|
||||
</H5>
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
{scene.rating ? scene.rating : ''}
|
||||
</td>
|
||||
<td>
|
||||
{renderDuration(scene)}
|
||||
</td>
|
||||
<td>
|
||||
{renderTags(scene.tags)}
|
||||
</td>
|
||||
<td>
|
||||
{renderPerformers(scene.performers)}
|
||||
</td>
|
||||
<td>
|
||||
{renderStudio(scene.studio)}
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid">
|
||||
<HTMLTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Title</th>
|
||||
<th>Rating</th>
|
||||
<th>Duration</th>
|
||||
<th>Tags</th>
|
||||
<th>Performers</th>
|
||||
<th>Studio</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{props.scenes.map(renderSceneRow)}
|
||||
</tbody>
|
||||
</HTMLTable>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -44,6 +44,25 @@ code {
|
|||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& .bp3-button.favorite .bp3-icon {
|
||||
color: #ff7373 !important
|
||||
}
|
||||
|
||||
& .performer-list-thumbnail {
|
||||
min-width: 50px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
& .scene-list-thumbnail {
|
||||
width: 150px;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
& table td {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
|
|
|
|||
Loading…
Reference in a new issue