Skip to content

Commit 0f70802

Browse files
xirazzeee
authored andcommitted
wip: implement content rating frontend
1 parent 6a7b75f commit 0f70802

File tree

4 files changed

+188
-1
lines changed

4 files changed

+188
-1
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import clsx from "clsx"
2+
import { useTranslation } from "next-i18next"
3+
import { FunctionComponent, createElement, useState } from "react"
4+
import {
5+
getContentRating,
6+
contentRatingToColor,
7+
contentRatingToIcon,
8+
} from "src/contentRating"
9+
import {
10+
Appstream,
11+
ContentRatingAttribute,
12+
ContentRatingLevel,
13+
} from "src/types/Appstream"
14+
import { StackedListBox } from "./StackedListBox"
15+
import Modal from "../Modal"
16+
17+
interface Props {
18+
data: Appstream
19+
summary: Summary
20+
}
21+
22+
const ContentRatingIcon = ({
23+
attr,
24+
level,
25+
}: {
26+
attr: ContentRatingAttribute
27+
level: ContentRatingLevel
28+
}) => {
29+
return (
30+
<div
31+
className={clsx(
32+
size === "small" ? "h-10 w-10" : "h-16 w-16",
33+
"rounded-full p-2",
34+
contentRatingToColor(level),
35+
)}
36+
>
37+
{icon
38+
? createElement(icon, {
39+
className: "w-full h-full",
40+
})
41+
: contentRatingToIcon(attr)}
42+
</div>
43+
)
44+
}
45+
46+
const ContentRating: FunctionComponent<Props> = ({ data }) => {
47+
const { t } = useTranslation()
48+
const [isOpen, setIsOpen] = useState(false)
49+
50+
const contentRating = getContentRating(data)
51+
52+
return (
53+
<>
54+
<button
55+
className={clsx(
56+
"flex w-full flex-col items-center gap-1 p-4 duration-500 hover:bg-flathub-gainsborow/20 justify-center",
57+
"active:bg-flathub-gainsborow/40 active:shadow-sm hover:dark:bg-flathub-dark-gunmetal/20 active:dark:bg-flathub-arsenic",
58+
"text-flathub-arsenic dark:text-flathub-gainsborow",
59+
)}
60+
onClick={() => setIsOpen(true)}
61+
>
62+
<div className="text-lg font-bold">
63+
{contentRating.minimumAge}
64+
</div>
65+
</button>
66+
67+
<Modal
68+
shown={isOpen}
69+
centerTitle
70+
onClose={() => setIsOpen(false)}
71+
aboveTitle={
72+
<div className="flex flex-col items-center pb-2">
73+
{minimumAgeFormatted}
74+
</div>
75+
}
76+
>
77+
<>
78+
<div className="w-full">
79+
<StackedListBox
80+
items={contentRating.attrs
81+
.map(
82+
(
83+
{
84+
attr,
85+
level,
86+
description,
87+
},
88+
i,
89+
) => ({
90+
id: i,
91+
header: description,
92+
icon: (
93+
<ContentRatingIcon attr={attr} level={level} />
94+
),
95+
}),
96+
)}
97+
/>
98+
</div>
99+
</>
100+
</Modal>
101+
</>
102+
)
103+
}
104+
105+
export default ContentRating

frontend/src/components/application/Details.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
} from "src/meilisearch"
2727
import Tags from "./Tags"
2828
import SafetyRating from "./SafetyRating"
29+
import ContentRating from "./ContentRating"
2930
import "yet-another-react-lightbox/plugins/captions.css"
3031
import { CarouselStrip } from "./CarouselStrip"
3132
import { useQuery } from "@tanstack/react-query"
@@ -108,6 +109,12 @@ const Details: FunctionComponent<Props> = ({
108109

109110
const children = [<LicenseInfo key={"license-info"} app={app} />]
110111

112+
if (contentRating !== null) {
113+
children.unshift(
114+
<ContentRating key={"content-rating"} data={app} summary={summary} />,
115+
)
116+
}
117+
111118
if (summary !== null && summary.metadata !== null) {
112119
children.unshift(
113120
<SafetyRating key={"safety-rating"} data={app} summary={summary} />,

frontend/src/contentRating.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { FaGun } from "react-icons/fa6";
2+
import {
3+
Appstream,
4+
ContentRatingAttribute,
5+
ContentRatingLevel,
6+
} from "src/types/Appstream"
7+
8+
interface ContentRatingDetails {
9+
minimumAge: string
10+
attrs: ContentRatingDetailsItem[]
11+
}
12+
13+
interface ContentRatingDetailsItem {
14+
attr: ContentRatingAttribute
15+
level: ContentRatingLevel
16+
description: string
17+
}
18+
19+
export async function getContentRating(data: Appstream): Promise<ContentRatingDisplay> {
20+
// TODO
21+
}
22+
23+
export function contentRatingToColor(level: ContentRatingLevel): string {
24+
switch (level) {
25+
case ContentRatingLevel.none:
26+
return `text-flathub-status-green bg-flathub-status-green/25 dark:bg-flathub-status-green-dark/25 dark:text-flathub-status-green-dark`
27+
case ContentRatingLevel.mild:
28+
return `text-flathub-status-yellow bg-flathub-status-yellow/25 dark:bg-flathub-status-yellow-dark/25 dark:text-flathub-status-yellow-dark`
29+
case ContentRatingLevel.moderate:
30+
return `text-flathub-status-orange bg-flathub-status-orange/25 dark:bg-flathub-status-orange-dark/25 dark:text-flathub-status-orange-dark`
31+
case ContentRatingLevel.intense:
32+
return `text-flathub-status-red bg-flathub-status-red/25 dark:bg-flathub-status-red-dark/25 dark:text-flathub-status-red-dark`
33+
case ContentRatingLevel.unknown:
34+
// TODO
35+
return ''
36+
}
37+
}
38+
39+
export function contentRatingToIcon(attr: ContentRatingAttribute): JSX.Element {
40+
// TODO
41+
switch (attr) {
42+
default:
43+
return React.createElement(FaGun, {
44+
className: "w-full h-full",
45+
})
46+
}

frontend/src/types/Appstream.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,36 @@ interface ContentRating {
9494
"money-gambling": ContentRatingLevel
9595
}
9696

97-
type ContentRatingLevel = "none" | "mild" | "moderate" | "intense"
97+
export type ContentRatingLevel = "none" | "mild" | "moderate" | "intense"
98+
99+
export type ContentRatingAttribute =
100+
| "violence-cartoon"
101+
| "violence-fantasy"
102+
| "violence-realistic"
103+
| "violence-bloodshed"
104+
| "violence-sexual"
105+
| "violence-desecration"
106+
| "violence-slavery"
107+
| "violence-worship"
108+
| "drugs-alcohol"
109+
| "drugs-narcotics"
110+
| "drugs-tobacco"
111+
| "sex-nudity"
112+
| "sex-themes"
113+
| "sex-homosexuality"
114+
| "sex-prostitution"
115+
| "sex-adultery"
116+
| "sex-appearance"
117+
| "language-profanity"
118+
| "language-humor"
119+
| "language-discrimination"
120+
| "social-chat"
121+
| "social-info"
122+
| "social-audio"
123+
| "social-location"
124+
| "social-contacts"
125+
| "money-purchasing"
126+
| "money-gambling"
98127

99128
export interface Urls {
100129
bugtracker: string

0 commit comments

Comments
 (0)