Skip to content

Commit

Permalink
Add Icons to tags if they have parent/child tags (stashapp#3931)
Browse files Browse the repository at this point in the history
* Add Icons to tags if they have parent/child tags
* Refactor TagLink
---------
Co-authored-by: WithoutPants <[email protected]>
  • Loading branch information
elkorol authored Sep 20, 2023
1 parent 36e9ed7 commit 636b0a3
Show file tree
Hide file tree
Showing 27 changed files with 412 additions and 128 deletions.
2 changes: 0 additions & 2 deletions graphql/documents/data/scene-marker.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ fragment SceneMarkerData on SceneMarker {
primary_tag {
id
name
aliases
}

tags {
id
name
aliases
}
}
2 changes: 2 additions & 0 deletions graphql/documents/data/tag-slim.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ fragment SlimTagData on Tag {
name
aliases
image_path
parent_count
child_count
}
3 changes: 3 additions & 0 deletions graphql/schema/types/tag.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ type Tag {
performer_count(depth: Int): Int! # Resolver
parents: [Tag!]!
children: [Tag!]!

parent_count: Int! # Resolver
child_count: Int! # Resolver
}

input TagCreateInput {
Expand Down
22 changes: 22 additions & 0 deletions internal/api/resolver_model_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,25 @@ func (r *tagResolver) ImagePath(ctx context.Context, obj *models.Tag) (*string,
imagePath := urlbuilders.NewTagURLBuilder(baseURL, obj).GetTagImageURL(hasImage)
return &imagePath, nil
}

func (r *tagResolver) ParentCount(ctx context.Context, obj *models.Tag) (ret int, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Tag.CountByParentTagID(ctx, obj.ID)
return err
}); err != nil {
return ret, err
}

return ret, nil
}

func (r *tagResolver) ChildCount(ctx context.Context, obj *models.Tag) (ret int, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Tag.CountByChildTagID(ctx, obj.ID)
return err
}); err != nil {
return ret, err
}

return ret, nil
}
42 changes: 42 additions & 0 deletions pkg/models/mocks/TagReaderWriter.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/models/repository_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type TagAutoTagQueryer interface {
// TagCounter provides methods to count tags.
type TagCounter interface {
Count(ctx context.Context) (int, error)
CountByParentTagID(ctx context.Context, parentID int) (int, error)
CountByChildTagID(ctx context.Context, childID int) (int, error)
}

// TagCreator provides methods to create tags.
Expand Down
14 changes: 14 additions & 0 deletions pkg/sqlite/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,20 @@ func (qb *TagStore) FindByChildTagID(ctx context.Context, parentID int) ([]*mode
return qb.queryTags(ctx, query, args)
}

func (qb *TagStore) CountByParentTagID(ctx context.Context, parentID int) (int, error) {
q := dialect.Select(goqu.COUNT("*")).From(goqu.T("tags")).
InnerJoin(goqu.T("tags_relations"), goqu.On(goqu.I("tags_relations.parent_id").Eq(goqu.I("tags.id")))).
Where(goqu.I("tags_relations.child_id").Eq(goqu.V(parentID))) // Pass the parentID here
return count(ctx, q)
}

func (qb *TagStore) CountByChildTagID(ctx context.Context, childID int) (int, error) {
q := dialect.Select(goqu.COUNT("*")).From(goqu.T("tags")).
InnerJoin(goqu.T("tags_relations"), goqu.On(goqu.I("tags_relations.child_id").Eq(goqu.I("tags.id")))).
Where(goqu.I("tags_relations.parent_id").Eq(goqu.V(childID))) // Pass the childID here
return count(ctx, q)
}

func (qb *TagStore) Count(ctx context.Context) (int, error) {
q := dialect.Select(goqu.COUNT("*")).From(qb.table())
return count(ctx, q)
Expand Down
6 changes: 3 additions & 3 deletions ui/v2.5/src/components/Galleries/GalleryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as GQL from "src/core/generated-graphql";
import { GridCard } from "../Shared/GridCard";
import { HoverPopover } from "../Shared/HoverPopover";
import { Icon } from "../Shared/Icon";
import { TagLink } from "../Shared/TagLink";
import { SceneLink, TagLink } from "../Shared/TagLink";
import { TruncatedText } from "../Shared/TruncatedText";
import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton";
import { PopoverCountButton } from "../Shared/PopoverCountButton";
Expand All @@ -31,7 +31,7 @@ export const GalleryCard: React.FC<IProps> = (props) => {
if (props.gallery.scenes.length === 0) return;

const popoverContent = props.gallery.scenes.map((scene) => (
<TagLink key={scene.id} scene={scene} />
<SceneLink key={scene.id} scene={scene} />
));

return (
Expand All @@ -52,7 +52,7 @@ export const GalleryCard: React.FC<IProps> = (props) => {
if (props.gallery.tags.length <= 0) return;

const popoverContent = props.gallery.tags.map((tag) => (
<TagLink key={tag.id} tag={tag} tagType="gallery" />
<TagLink key={tag.id} tag={tag} linkType="gallery" />
));

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const GalleryDetailPanel: React.FC<IGalleryDetailProps> = ({
function renderTags() {
if (gallery.tags.length === 0) return;
const tags = gallery.tags.map((tag) => (
<TagLink key={tag.id} tag={tag} tagType="gallery" />
<TagLink key={tag.id} tag={tag} linkType="gallery" />
));
return (
<>
Expand Down
6 changes: 3 additions & 3 deletions ui/v2.5/src/components/Images/ImageCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Button, ButtonGroup } from "react-bootstrap";
import cx from "classnames";
import * as GQL from "src/core/generated-graphql";
import { Icon } from "src/components/Shared/Icon";
import { TagLink } from "src/components/Shared/TagLink";
import { GalleryLink, TagLink } from "src/components/Shared/TagLink";
import { HoverPopover } from "src/components/Shared/HoverPopover";
import { SweatDrops } from "src/components/Shared/SweatDrops";
import { PerformerPopoverButton } from "src/components/Shared/PerformerPopoverButton";
Expand Down Expand Up @@ -41,7 +41,7 @@ export const ImageCard: React.FC<IImageCardProps> = (
if (props.image.tags.length <= 0) return;

const popoverContent = props.image.tags.map((tag) => (
<TagLink key={tag.id} tag={tag} tagType="image" />
<TagLink key={tag.id} tag={tag} linkType="image" />
));

return (
Expand Down Expand Up @@ -83,7 +83,7 @@ export const ImageCard: React.FC<IImageCardProps> = (
if (props.image.galleries.length <= 0) return;

const popoverContent = props.image.galleries.map((gallery) => (
<TagLink key={gallery.id} gallery={gallery} />
<GalleryLink key={gallery.id} gallery={gallery} />
));

return (
Expand Down
10 changes: 5 additions & 5 deletions ui/v2.5/src/components/Images/ImageDetails/ImageDetailPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useMemo } from "react";
import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import TextUtils from "src/utils/text";
import { TagLink } from "src/components/Shared/TagLink";
import { GalleryLink, TagLink } from "src/components/Shared/TagLink";
import { TruncatedText } from "src/components/Shared/TruncatedText";
import { PerformerCard } from "src/components/Performers/PerformerCard";
import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
Expand All @@ -24,7 +24,7 @@ export const ImageDetailPanel: React.FC<IImageDetailProps> = (props) => {
function renderTags() {
if (props.image.tags.length === 0) return;
const tags = props.image.tags.map((tag) => (
<TagLink key={tag.id} tag={tag} tagType="image" />
<TagLink key={tag.id} tag={tag} linkType="image" />
));
return (
<>
Expand Down Expand Up @@ -67,8 +67,8 @@ export const ImageDetailPanel: React.FC<IImageDetailProps> = (props) => {

function renderGalleries() {
if (props.image.galleries.length === 0) return;
const tags = props.image.galleries.map((gallery) => (
<TagLink key={gallery.id} gallery={gallery} />
const galleries = props.image.galleries.map((gallery) => (
<GalleryLink key={gallery.id} gallery={gallery} />
));
return (
<>
Expand All @@ -78,7 +78,7 @@ export const ImageDetailPanel: React.FC<IImageDetailProps> = (props) => {
values={{ count: props.image.galleries.length }}
/>
</h6>
{tags}
{galleries}
</>
);
}
Expand Down
4 changes: 2 additions & 2 deletions ui/v2.5/src/components/Movies/MovieCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as GQL from "src/core/generated-graphql";
import { GridCard } from "../Shared/GridCard";
import { HoverPopover } from "../Shared/HoverPopover";
import { Icon } from "../Shared/Icon";
import { TagLink } from "../Shared/TagLink";
import { SceneLink } from "../Shared/TagLink";
import { TruncatedText } from "../Shared/TruncatedText";
import { FormattedMessage } from "react-intl";
import { RatingBanner } from "../Shared/RatingBanner";
Expand Down Expand Up @@ -36,7 +36,7 @@ export const MovieCard: React.FC<IProps> = (props: IProps) => {
if (props.movie.scenes.length === 0) return;

const popoverContent = props.movie.scenes.map((scene) => (
<TagLink key={scene.id} scene={scene} />
<SceneLink key={scene.id} scene={scene} />
));

return (
Expand Down
2 changes: 1 addition & 1 deletion ui/v2.5/src/components/Performers/PerformerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export const PerformerCard: React.FC<IPerformerCardProps> = ({
if (performer.tags.length <= 0) return;

const popoverContent = performer.tags.map((tag) => (
<TagLink key={tag.id} tagType="performer" tag={tag} />
<TagLink key={tag.id} linkType="performer" tag={tag} />
));

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
return (
<ul className="pl-0">
{(performer.tags ?? []).map((tag) => (
<TagLink key={tag.id} tagType="performer" tag={tag} />
<TagLink key={tag.id} linkType="performer" tag={tag} />
))}
</ul>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ import { LoadingIndicator } from "../Shared/LoadingIndicator";
import { ErrorMessage } from "../Shared/ErrorMessage";
import { HoverPopover } from "../Shared/HoverPopover";
import { Icon } from "../Shared/Icon";
import { TagLink } from "../Shared/TagLink";
import {
GalleryLink,
MovieLink,
SceneMarkerLink,
TagLink,
} from "../Shared/TagLink";
import { SweatDrops } from "../Shared/SweatDrops";
import { Pagination } from "src/components/List/Pagination";
import TextUtils from "src/utils/text";
Expand Down Expand Up @@ -349,7 +354,7 @@ export const SceneDuplicateChecker: React.FC = () => {
src={sceneMovie.movie.front_image_path ?? ""}
/>
</Link>
<TagLink
<MovieLink
key={sceneMovie.movie.id}
movie={sceneMovie.movie}
className="d-block"
Expand Down Expand Up @@ -377,8 +382,8 @@ export const SceneDuplicateChecker: React.FC = () => {
if (scene.scene_markers.length <= 0) return;

const popoverContent = scene.scene_markers.map((marker) => {
const markerPopover = { ...marker, scene: { id: scene.id } };
return <TagLink key={marker.id} marker={markerPopover} />;
const markerWithScene = { ...marker, scene: { id: scene.id } };
return <SceneMarkerLink key={marker.id} marker={markerWithScene} />;
});

return (
Expand Down Expand Up @@ -410,7 +415,7 @@ export const SceneDuplicateChecker: React.FC = () => {
if (scene.galleries.length <= 0) return;

const popoverContent = scene.galleries.map((gallery) => (
<TagLink key={gallery.id} gallery={gallery} />
<GalleryLink key={gallery.id} gallery={gallery} />
));

return (
Expand Down
15 changes: 10 additions & 5 deletions ui/v2.5/src/components/Scenes/SceneCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { Link, useHistory } from "react-router-dom";
import cx from "classnames";
import * as GQL from "src/core/generated-graphql";
import { Icon } from "../Shared/Icon";
import { TagLink } from "../Shared/TagLink";
import {
GalleryLink,
TagLink,
MovieLink,
SceneMarkerLink,
} from "../Shared/TagLink";
import { HoverPopover } from "../Shared/HoverPopover";
import { SweatDrops } from "../Shared/SweatDrops";
import { TruncatedText } from "../Shared/TruncatedText";
Expand Down Expand Up @@ -219,7 +224,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
src={sceneMovie.movie.front_image_path ?? ""}
/>
</Link>
<TagLink
<MovieLink
key={sceneMovie.movie.id}
movie={sceneMovie.movie}
className="d-block"
Expand All @@ -245,8 +250,8 @@ export const SceneCard: React.FC<ISceneCardProps> = (
if (props.scene.scene_markers.length <= 0) return;

const popoverContent = props.scene.scene_markers.map((marker) => {
const markerPopover = { ...marker, scene: { id: props.scene.id } };
return <TagLink key={marker.id} marker={markerPopover} />;
const markerWithScene = { ...marker, scene: { id: props.scene.id } };
return <SceneMarkerLink key={marker.id} marker={markerWithScene} />;
});

return (
Expand Down Expand Up @@ -282,7 +287,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
if (props.scene.galleries.length <= 0) return;

const popoverContent = props.scene.galleries.map((gallery) => (
<TagLink key={gallery.id} gallery={gallery} />
<GalleryLink key={gallery.id} gallery={gallery} />
));

return (
Expand Down
19 changes: 10 additions & 9 deletions ui/v2.5/src/components/Scenes/SceneDetails/PrimaryTags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@ export const PrimaryTags: React.FC<IPrimaryTags> = ({
}) => {
if (!sceneMarkers?.length) return <div />;

const primaries: Record<string, GQL.SlimTagDataFragment> = {};
const primaryTags: Record<string, GQL.SceneMarkerDataFragment[]> = {};
const primaryTagNames: Record<string, string> = {};
const markersByTag: Record<string, GQL.SceneMarkerDataFragment[]> = {};
sceneMarkers.forEach((m) => {
if (primaryTags[m.primary_tag.id]) primaryTags[m.primary_tag.id].push(m);
else {
primaryTags[m.primary_tag.id] = [m];
primaries[m.primary_tag.id] = m.primary_tag;
if (primaryTagNames[m.primary_tag.id]) {
markersByTag[m.primary_tag.id].push(m);
} else {
primaryTagNames[m.primary_tag.id] = m.primary_tag.name;
markersByTag[m.primary_tag.id] = [m];
}
});

const primaryCards = Object.keys(primaryTags).map((id) => {
const markers = primaryTags[id].map((marker) => {
const primaryCards = Object.keys(markersByTag).map((id) => {
const markers = markersByTag[id].map((marker) => {
const tags = marker.tags.map((tag) => (
<Badge key={tag.id} variant="secondary" className="tag-item">
{tag.name}
Expand Down Expand Up @@ -59,7 +60,7 @@ export const PrimaryTags: React.FC<IPrimaryTags> = ({

return (
<Card className="primary-card col-12 col-sm-6 col-xl-6" key={id}>
<h3>{primaries[id].name}</h3>
<h3>{primaryTagNames[id]}</h3>
<Card.Body className="primary-card-body">{markers}</Card.Body>
</Card>
);
Expand Down
Loading

0 comments on commit 636b0a3

Please sign in to comment.