Skip to content

Commit

Permalink
wip: implement content rating frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
xi committed Jul 24, 2024
1 parent 79a696a commit 952ac4a
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 1 deletion.
105 changes: 105 additions & 0 deletions frontend/src/components/application/ContentRating.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import clsx from "clsx"
import { useTranslation } from "next-i18next"
import { FunctionComponent, createElement, useState } from "react"
import {
getContentRating,
contentRatingToColor,
contentRatingToIcon,
} from "src/contentRating"
import {
Appstream,
ContentRatingAttribute,
ContentRatingLevel,
} from "src/types/Appstream"
import { StackedListBox } from "./StackedListBox"
import Modal from "../Modal"

interface Props {
data: Appstream
summary: Summary
}

const ContentRatingIcon = ({
attr,
level,
}: {
attr: ContentRatingAttribute
level: ContentRatingLevel
}) => {
return (
<div
className={clsx(
size === "small" ? "h-10 w-10" : "h-16 w-16",
"rounded-full p-2",
contentRatingToColor(level),
)}
>
{icon
? createElement(icon, {
className: "w-full h-full",
})
: contentRatingToIcon(attr)}
</div>
)
}

const ContentRating: FunctionComponent<Props> = ({ data }) => {
const { t } = useTranslation()
const [isOpen, setIsOpen] = useState(false)

const contentRating = getContentRating(data)

return (
<>
<button
className={clsx(
"flex w-full flex-col items-center gap-1 p-4 duration-500 hover:bg-flathub-gainsborow/20 justify-center",
"active:bg-flathub-gainsborow/40 active:shadow-sm hover:dark:bg-flathub-dark-gunmetal/20 active:dark:bg-flathub-arsenic",
"text-flathub-arsenic dark:text-flathub-gainsborow",
)}
onClick={() => setIsOpen(true)}
>
<div className="text-lg font-bold">
{contentRating.minimumAge}
</div>
</button>

<Modal
shown={isOpen}
centerTitle
onClose={() => setIsOpen(false)}
aboveTitle={
<div className="flex flex-col items-center pb-2">
{minimumAgeFormatted}
</div>
}
>
<>
<div className="w-full">
<StackedListBox
items={contentRating.attrs
.map(
(
{
attr,
level,
description,
},
i,
) => ({
id: i,
header: description,
icon: (
<ContentRatingIcon attr={attr} level={level} />
),
}),
)}
/>
</div>
</>
</Modal>
</>
)
}

export default ContentRating
7 changes: 7 additions & 0 deletions frontend/src/components/application/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from "src/meilisearch"
import Tags from "./Tags"
import SafetyRating from "./SafetyRating"
import ContentRating from "./ContentRating"
import "yet-another-react-lightbox/plugins/captions.css"
import { CarouselStrip } from "./CarouselStrip"
import { useQuery } from "@tanstack/react-query"
Expand Down Expand Up @@ -108,6 +109,12 @@ const Details: FunctionComponent<Props> = ({

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

if (contentRating !== null) {
children.unshift(
<ContentRating key={"content-rating"} data={app} summary={summary} />,
)
}

if (summary !== null && summary.metadata !== null) {
children.unshift(
<SafetyRating key={"safety-rating"} data={app} summary={summary} />,
Expand Down
46 changes: 46 additions & 0 deletions frontend/src/contentRating.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { FaGun } from "react-icons/fa6";
import {
Appstream,
ContentRatingAttribute,
ContentRatingLevel,
} from "src/types/Appstream"

interface ContentRatingDetails {
minimumAge: string
attrs: ContentRatingDetailsItem[]
}

interface ContentRatingDetailsItem {
attr: ContentRatingAttribute
level: ContentRatingLevel
description: string
}

export async function getContentRating(data: Appstream): Promise<ContentRatingDisplay> {
// TODO
}

export function contentRatingToColor(level: ContentRatingLevel): string {
switch (level) {
case ContentRatingLevel.none:
return `text-flathub-status-green bg-flathub-status-green/25 dark:bg-flathub-status-green-dark/25 dark:text-flathub-status-green-dark`
case ContentRatingLevel.mild:
return `text-flathub-status-yellow bg-flathub-status-yellow/25 dark:bg-flathub-status-yellow-dark/25 dark:text-flathub-status-yellow-dark`
case ContentRatingLevel.moderate:
return `text-flathub-status-orange bg-flathub-status-orange/25 dark:bg-flathub-status-orange-dark/25 dark:text-flathub-status-orange-dark`
case ContentRatingLevel.intense:
return `text-flathub-status-red bg-flathub-status-red/25 dark:bg-flathub-status-red-dark/25 dark:text-flathub-status-red-dark`
case ContentRatingLevel.unknown:
// TODO
return ''
}
}

export function contentRatingToIcon(attr: ContentRatingAttribute): JSX.Element {
// TODO
switch (attr) {
default:
return React.createElement(FaGun, {
className: "w-full h-full",
})
}
31 changes: 30 additions & 1 deletion frontend/src/types/Appstream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,36 @@ interface ContentRating {
"money-gambling": ContentRatingLevel
}

type ContentRatingLevel = "none" | "mild" | "moderate" | "intense"
export type ContentRatingLevel = "none" | "mild" | "moderate" | "intense"

export interface ContentRatingAttribute =
| "violence-cartoon"
| "violence-fantasy"
| "violence-realistic"
| "violence-bloodshed"
| "violence-sexual"
| "violence-desecration"
| "violence-slavery"
| "violence-worship"
| "drugs-alcohol"
| "drugs-narcotics"
| "drugs-tobacco"
| "sex-nudity"
| "sex-themes"
| "sex-homosexuality"
| "sex-prostitution"
| "sex-adultery"
| "sex-appearance"
| "language-profanity"
| "language-humor"
| "language-discrimination"
| "social-chat"
| "social-info"
| "social-audio"
| "social-location"
| "social-contacts"
| "money-purchasing"
| "money-gambling"

export interface Urls {
bugtracker: string
Expand Down

0 comments on commit 952ac4a

Please sign in to comment.