mirror of
https://github.com/encounter/decomp.me.git
synced 2026-03-30 11:06:27 -07:00
Split ScratchItem* from ScratchList (#1458)
This commit is contained in:
@@ -19,7 +19,7 @@ import { cpp } from "@/lib/codemirror/cpp";
|
||||
import getTranslation from "@/lib/i18n/translate";
|
||||
import { get } from "@/lib/api/request";
|
||||
import type { TerseScratch } from "@/lib/api/types";
|
||||
import { SingleLineScratchItem } from "@/components/ScratchList";
|
||||
import { SingleLineScratchItem } from "@/components/ScratchItem";
|
||||
import { useDebounce } from "use-debounce";
|
||||
|
||||
interface FormLabelProps {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
import ScratchList, { SingleLineScratchItem } from "@/components/ScratchList";
|
||||
import ScratchList from "@/components/ScratchList";
|
||||
import { SingleLineScratchItem } from "@/components/ScratchItem";
|
||||
import YourScratchList from "@/components/YourScratchList";
|
||||
|
||||
import WelcomeInfo from "./WelcomeInfo";
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
import { PlatformIcon } from "@/components/PlatformSelect/PlatformIcon";
|
||||
import ScratchList, { ScratchItemPlatformList } from "@/components/ScratchList";
|
||||
import ScratchList from "@/components/ScratchList";
|
||||
import { ScratchItemPlatformList } from "@/components/ScratchItem";
|
||||
import { get } from "@/lib/api/request";
|
||||
import type { PlatformMetadata } from "@/lib/api/types";
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
import { PlatformIcon } from "@/components/PlatformSelect/PlatformIcon";
|
||||
import ScratchList, { ScratchItemPresetList } from "@/components/ScratchList";
|
||||
import ScratchList from "@/components/ScratchList";
|
||||
import { ScratchItemPresetList } from "@/components/ScratchItem";
|
||||
import { get } from "@/lib/api/request";
|
||||
import type { Preset } from "@/lib/api/types";
|
||||
import getTranslation from "@/lib/i18n/translate";
|
||||
|
||||
@@ -14,7 +14,7 @@ import LoadingSpinner from "../loading.svg";
|
||||
import PlatformLink from "../PlatformLink";
|
||||
import verticalMenuStyles from "../VerticalMenu.module.scss"; // eslint-disable-line css-modules/no-unused-class
|
||||
|
||||
import { getMatchPercentString, ScratchOwnerAvatar } from "../ScratchList";
|
||||
import { getMatchPercentString, ScratchOwnerAvatar } from "../ScratchItem";
|
||||
|
||||
import styles from "./Search.module.scss";
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
.item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 0.5em;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
padding: 1em;
|
||||
|
||||
border: 1px solid inherit;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.link {
|
||||
font-weight: 600;
|
||||
|
||||
&:hover {
|
||||
color: var(--link);
|
||||
}
|
||||
}
|
||||
|
||||
.scratch {
|
||||
line-height: 1.5;
|
||||
|
||||
.header {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
.name {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* If two children, align them to the left and right */
|
||||
&:has(> :last-child:nth-child(2)) {
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
}
|
||||
|
||||
.owner {
|
||||
color: var(--g1200);
|
||||
}
|
||||
|
||||
.metadata {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
color: var(--g900);
|
||||
|
||||
> span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.singleLine {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4em;
|
||||
|
||||
padding: 0.4em 0;
|
||||
|
||||
.icon {
|
||||
flex-shrink: 0;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
}
|
||||
|
||||
.name {
|
||||
flex-grow: 1;
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.metadata {
|
||||
color: var(--g1200);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
import classNames from "classnames";
|
||||
|
||||
import TimeAgo from "@/components/TimeAgo";
|
||||
import * as api from "@/lib/api";
|
||||
import { presetUrl, scratchUrl, userAvatarUrl } from "@/lib/api/urls";
|
||||
|
||||
import getTranslation from "@/lib/i18n/translate";
|
||||
|
||||
import AnonymousFrogAvatar from "./user/AnonymousFrog";
|
||||
import PlatformLink from "./PlatformLink";
|
||||
import { calculateScorePercent, percentToString } from "./ScoreBadge";
|
||||
import styles from "./ScratchItem.module.scss";
|
||||
import UserLink from "./user/UserLink";
|
||||
|
||||
export function getMatchPercentString(scratch: api.TerseScratch) {
|
||||
if (scratch.score === -1) {
|
||||
return "0%";
|
||||
}
|
||||
if (scratch.match_override) {
|
||||
return "100%";
|
||||
}
|
||||
const matchPercent = calculateScorePercent(
|
||||
scratch.score,
|
||||
scratch.max_score,
|
||||
);
|
||||
const matchPercentString = percentToString(matchPercent);
|
||||
|
||||
return matchPercentString;
|
||||
}
|
||||
|
||||
export function ScratchItem({
|
||||
scratch,
|
||||
children,
|
||||
}: { scratch: api.TerseScratch; children?: ReactNode }) {
|
||||
const compilersTranslation = getTranslation("compilers");
|
||||
const compilerName = compilersTranslation.t(scratch.compiler);
|
||||
const matchPercentString = getMatchPercentString(scratch);
|
||||
const preset = api.usePreset(scratch.preset);
|
||||
const presetName = preset?.name;
|
||||
|
||||
const presetOrCompiler = presetName ? (
|
||||
<Link href={presetUrl(preset)} className={styles.link}>
|
||||
{presetName}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{compilerName}</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
<div className={styles.scratch}>
|
||||
<div className={styles.header}>
|
||||
<PlatformLink
|
||||
size={16}
|
||||
scratch={scratch}
|
||||
className={styles.icon}
|
||||
/>
|
||||
<Link
|
||||
href={scratchUrl(scratch)}
|
||||
className={classNames(styles.link, styles.name)}
|
||||
>
|
||||
{scratch.name}
|
||||
</Link>
|
||||
<div className={styles.owner}>
|
||||
{scratch.owner ? (
|
||||
<UserLink user={scratch.owner} />
|
||||
) : (
|
||||
<div>No Owner</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.metadata}>
|
||||
<span>
|
||||
{presetOrCompiler} • {matchPercentString} matched •{" "}
|
||||
<TimeAgo date={scratch.last_updated} />
|
||||
</span>
|
||||
<div className={styles.actions}>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function ScratchItemNoOwner({ scratch }: { scratch: api.TerseScratch }) {
|
||||
const compilersTranslation = getTranslation("compilers");
|
||||
const compilerName = compilersTranslation.t(scratch.compiler);
|
||||
const matchPercentString = getMatchPercentString(scratch);
|
||||
const preset = api.usePreset(scratch.preset);
|
||||
const presetName = preset?.name;
|
||||
|
||||
const presetOrCompiler = presetName ? (
|
||||
<Link href={presetUrl(preset)} className={styles.link}>
|
||||
{presetName}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{compilerName}</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
<div className={styles.scratch}>
|
||||
<div className={styles.header}>
|
||||
<PlatformLink
|
||||
size={16}
|
||||
scratch={scratch}
|
||||
className={styles.icon}
|
||||
/>
|
||||
<Link
|
||||
href={scratchUrl(scratch)}
|
||||
className={classNames(styles.link, styles.name)}
|
||||
>
|
||||
{scratch.name}
|
||||
</Link>
|
||||
<div>{/* empty div for alignment */}</div>
|
||||
</div>
|
||||
<div className={styles.metadata}>
|
||||
<span>
|
||||
{presetOrCompiler} • {matchPercentString} matched •{" "}
|
||||
<TimeAgo date={scratch.last_updated} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function ScratchItemPlatformList({
|
||||
scratch,
|
||||
}: { scratch: api.TerseScratch }) {
|
||||
const compilersTranslation = getTranslation("compilers");
|
||||
const compilerName = compilersTranslation.t(scratch.compiler);
|
||||
const matchPercentString = getMatchPercentString(scratch);
|
||||
const preset = api.usePreset(scratch.preset);
|
||||
const presetName = preset?.name;
|
||||
|
||||
const presetOrCompiler = presetName ? (
|
||||
<Link href={presetUrl(preset)} className={styles.link}>
|
||||
{presetName}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{compilerName}</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
<div className={styles.scratch}>
|
||||
<div className={styles.header}>
|
||||
<Link
|
||||
href={scratchUrl(scratch)}
|
||||
className={classNames(styles.link, styles.name)}
|
||||
>
|
||||
{scratch.name}
|
||||
</Link>
|
||||
<div className={styles.owner}>
|
||||
{scratch.owner ? (
|
||||
<UserLink user={scratch.owner} />
|
||||
) : (
|
||||
<div>No Owner</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.metadata}>
|
||||
<span>
|
||||
{presetOrCompiler} • {matchPercentString} matched •{" "}
|
||||
<TimeAgo date={scratch.last_updated} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function ScratchItemPresetList({
|
||||
scratch,
|
||||
}: { scratch: api.TerseScratch }) {
|
||||
const matchPercentString = getMatchPercentString(scratch);
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
<div className={styles.scratch}>
|
||||
<div className={styles.header}>
|
||||
<Link
|
||||
href={scratchUrl(scratch)}
|
||||
className={classNames(styles.link, styles.name)}
|
||||
>
|
||||
{scratch.name}
|
||||
</Link>
|
||||
<div className={styles.metadata}>
|
||||
<span>
|
||||
{matchPercentString} matched •{" "}
|
||||
<TimeAgo date={scratch.last_updated} />
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.owner}>
|
||||
{scratch.owner ? (
|
||||
<UserLink user={scratch.owner} />
|
||||
) : (
|
||||
<div>No Owner</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function ScratchOwnerAvatar({ scratch }: { scratch: api.TerseScratch }) {
|
||||
return (
|
||||
scratch.owner &&
|
||||
(!api.isAnonUser(scratch.owner) ? (
|
||||
userAvatarUrl(scratch.owner) && (
|
||||
<Image
|
||||
src={userAvatarUrl(scratch.owner)}
|
||||
alt={scratch.owner.username}
|
||||
width={16}
|
||||
height={16}
|
||||
className={styles.scratchOwnerAvatar}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<AnonymousFrogAvatar
|
||||
user={scratch.owner}
|
||||
width={16}
|
||||
height={16}
|
||||
className={styles.scratchOwnerAvatar}
|
||||
/>
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
export function SingleLineScratchItem({
|
||||
scratch,
|
||||
showOwner = false,
|
||||
}: { scratch: api.TerseScratch; showOwner?: boolean }) {
|
||||
const matchPercentString = getMatchPercentString(scratch);
|
||||
|
||||
return (
|
||||
<li className={styles.singleLine}>
|
||||
<PlatformLink size={16} scratch={scratch} className={styles.icon} />
|
||||
<Link
|
||||
href={scratchUrl(scratch)}
|
||||
className={classNames(styles.link, styles.name)}
|
||||
>
|
||||
{scratch.name}
|
||||
</Link>
|
||||
<div className={styles.metadata}>{matchPercentString}</div>
|
||||
{showOwner && <ScratchOwnerAvatar scratch={scratch} />}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -1,14 +1,3 @@
|
||||
.loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
padding: 1em;
|
||||
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style: none;
|
||||
|
||||
@@ -17,20 +6,6 @@
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 0.5em;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
padding: 1em;
|
||||
|
||||
border: 1px solid inherit;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -42,86 +17,3 @@
|
||||
|
||||
grid-column: span var(--num-columns, 1);
|
||||
}
|
||||
|
||||
.link {
|
||||
font-weight: 600;
|
||||
|
||||
&:hover {
|
||||
color: var(--link);
|
||||
}
|
||||
}
|
||||
|
||||
.scratch {
|
||||
line-height: 1.5;
|
||||
|
||||
.header {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
.name {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* If two children, align them to the left and right */
|
||||
&:has(> :last-child:nth-child(2)) {
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
}
|
||||
|
||||
.owner {
|
||||
color: var(--g1200);
|
||||
}
|
||||
|
||||
.metadata {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
color: var(--g900);
|
||||
|
||||
> span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.singleLine {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4em;
|
||||
|
||||
padding: 0.4em 0;
|
||||
|
||||
.icon {
|
||||
flex-shrink: 0;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
}
|
||||
|
||||
.name {
|
||||
flex-grow: 1;
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.metadata {
|
||||
color: var(--g1200);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,24 @@
|
||||
|
||||
import { type ReactNode, useState } from "react";
|
||||
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
import classNames from "classnames";
|
||||
|
||||
import TimeAgo from "@/components/TimeAgo";
|
||||
import * as api from "@/lib/api";
|
||||
import { presetUrl, scratchUrl, userAvatarUrl } from "@/lib/api/urls";
|
||||
|
||||
import getTranslation from "@/lib/i18n/translate";
|
||||
|
||||
import AnonymousFrogAvatar from "./user/AnonymousFrog";
|
||||
import AsyncButton from "./AsyncButton";
|
||||
import Button from "./Button";
|
||||
import PlatformLink from "./PlatformLink";
|
||||
import { calculateScorePercent, percentToString } from "./ScoreBadge";
|
||||
import styles from "./ScratchList.module.scss";
|
||||
import { type TerseScratch, usePaginated } from "@/lib/api";
|
||||
import { scratchUrl } from "@/lib/api/urls";
|
||||
import { ScratchItem } from "./ScratchItem";
|
||||
import Sort, { SortMode } from "./SortScratch";
|
||||
import UserLink from "./user/UserLink";
|
||||
|
||||
import { TextSkeleton, SCRATCH_LIST } from "./TextSkeleton";
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
url?: string;
|
||||
className?: string;
|
||||
item?: ({ scratch }: { scratch: api.TerseScratch }) => JSX.Element;
|
||||
item?: ({ scratch }: { scratch: TerseScratch }) => JSX.Element;
|
||||
emptyButtonLabel?: ReactNode;
|
||||
isSortable?: boolean;
|
||||
}
|
||||
@@ -43,7 +34,7 @@ export default function ScratchList({
|
||||
}: Props) {
|
||||
const [sortMode, setSortMode] = useState(SortMode.NEWEST_FIRST);
|
||||
const { results, isLoading, hasNext, loadNext } =
|
||||
api.usePaginated<api.TerseScratch>(
|
||||
usePaginated<TerseScratch>(
|
||||
`${url || "/scratch"}&ordering=${sortMode.toString()}`,
|
||||
);
|
||||
|
||||
@@ -89,240 +80,3 @@ export default function ScratchList({
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function getMatchPercentString(scratch: api.TerseScratch) {
|
||||
if (scratch.score === -1) {
|
||||
return "0%";
|
||||
}
|
||||
if (scratch.match_override) {
|
||||
return "100%";
|
||||
}
|
||||
const matchPercent = calculateScorePercent(
|
||||
scratch.score,
|
||||
scratch.max_score,
|
||||
);
|
||||
const matchPercentString = percentToString(matchPercent);
|
||||
|
||||
return matchPercentString;
|
||||
}
|
||||
|
||||
export function ScratchItem({
|
||||
scratch,
|
||||
children,
|
||||
}: { scratch: api.TerseScratch; children?: ReactNode }) {
|
||||
const compilersTranslation = getTranslation("compilers");
|
||||
const compilerName = compilersTranslation.t(scratch.compiler);
|
||||
const matchPercentString = getMatchPercentString(scratch);
|
||||
const preset = api.usePreset(scratch.preset);
|
||||
const presetName = preset?.name;
|
||||
|
||||
const presetOrCompiler = presetName ? (
|
||||
<Link href={presetUrl(preset)} className={styles.link}>
|
||||
{presetName}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{compilerName}</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
<div className={styles.scratch}>
|
||||
<div className={styles.header}>
|
||||
<PlatformLink
|
||||
size={16}
|
||||
scratch={scratch}
|
||||
className={styles.icon}
|
||||
/>
|
||||
<Link
|
||||
href={scratchUrl(scratch)}
|
||||
className={classNames(styles.link, styles.name)}
|
||||
>
|
||||
{scratch.name}
|
||||
</Link>
|
||||
<div className={styles.owner}>
|
||||
{scratch.owner ? (
|
||||
<UserLink user={scratch.owner} />
|
||||
) : (
|
||||
<div>No Owner</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.metadata}>
|
||||
<span>
|
||||
{presetOrCompiler} • {matchPercentString} matched •{" "}
|
||||
<TimeAgo date={scratch.last_updated} />
|
||||
</span>
|
||||
<div className={styles.actions}>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function ScratchItemNoOwner({ scratch }: { scratch: api.TerseScratch }) {
|
||||
const compilersTranslation = getTranslation("compilers");
|
||||
const compilerName = compilersTranslation.t(scratch.compiler);
|
||||
const matchPercentString = getMatchPercentString(scratch);
|
||||
const preset = api.usePreset(scratch.preset);
|
||||
const presetName = preset?.name;
|
||||
|
||||
const presetOrCompiler = presetName ? (
|
||||
<Link href={presetUrl(preset)} className={styles.link}>
|
||||
{presetName}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{compilerName}</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
<div className={styles.scratch}>
|
||||
<div className={styles.header}>
|
||||
<PlatformLink
|
||||
size={16}
|
||||
scratch={scratch}
|
||||
className={styles.icon}
|
||||
/>
|
||||
<Link
|
||||
href={scratchUrl(scratch)}
|
||||
className={classNames(styles.link, styles.name)}
|
||||
>
|
||||
{scratch.name}
|
||||
</Link>
|
||||
<div>{/* empty div for alignment */}</div>
|
||||
</div>
|
||||
<div className={styles.metadata}>
|
||||
<span>
|
||||
{presetOrCompiler} • {matchPercentString} matched •{" "}
|
||||
<TimeAgo date={scratch.last_updated} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function ScratchItemPlatformList({
|
||||
scratch,
|
||||
}: { scratch: api.TerseScratch }) {
|
||||
const compilersTranslation = getTranslation("compilers");
|
||||
const compilerName = compilersTranslation.t(scratch.compiler);
|
||||
const matchPercentString = getMatchPercentString(scratch);
|
||||
const preset = api.usePreset(scratch.preset);
|
||||
const presetName = preset?.name;
|
||||
|
||||
const presetOrCompiler = presetName ? (
|
||||
<Link href={presetUrl(preset)} className={styles.link}>
|
||||
{presetName}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{compilerName}</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
<div className={styles.scratch}>
|
||||
<div className={styles.header}>
|
||||
<Link
|
||||
href={scratchUrl(scratch)}
|
||||
className={classNames(styles.link, styles.name)}
|
||||
>
|
||||
{scratch.name}
|
||||
</Link>
|
||||
<div className={styles.owner}>
|
||||
{scratch.owner ? (
|
||||
<UserLink user={scratch.owner} />
|
||||
) : (
|
||||
<div>No Owner</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.metadata}>
|
||||
<span>
|
||||
{presetOrCompiler} • {matchPercentString} matched •{" "}
|
||||
<TimeAgo date={scratch.last_updated} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function ScratchItemPresetList({
|
||||
scratch,
|
||||
}: { scratch: api.TerseScratch }) {
|
||||
const matchPercentString = getMatchPercentString(scratch);
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
<div className={styles.scratch}>
|
||||
<div className={styles.header}>
|
||||
<Link
|
||||
href={scratchUrl(scratch)}
|
||||
className={classNames(styles.link, styles.name)}
|
||||
>
|
||||
{scratch.name}
|
||||
</Link>
|
||||
<div className={styles.metadata}>
|
||||
<span>
|
||||
{matchPercentString} matched •{" "}
|
||||
<TimeAgo date={scratch.last_updated} />
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.owner}>
|
||||
{scratch.owner ? (
|
||||
<UserLink user={scratch.owner} />
|
||||
) : (
|
||||
<div>No Owner</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function ScratchOwnerAvatar({ scratch }: { scratch: api.TerseScratch }) {
|
||||
return (
|
||||
scratch.owner &&
|
||||
(!api.isAnonUser(scratch.owner) ? (
|
||||
userAvatarUrl(scratch.owner) && (
|
||||
<Image
|
||||
src={userAvatarUrl(scratch.owner)}
|
||||
alt={scratch.owner.username}
|
||||
width={16}
|
||||
height={16}
|
||||
className={styles.scratchOwnerAvatar}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<AnonymousFrogAvatar
|
||||
user={scratch.owner}
|
||||
width={16}
|
||||
height={16}
|
||||
className={styles.scratchOwnerAvatar}
|
||||
/>
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
export function SingleLineScratchItem({
|
||||
scratch,
|
||||
showOwner = false,
|
||||
}: { scratch: api.TerseScratch; showOwner?: boolean }) {
|
||||
const matchPercentString = getMatchPercentString(scratch);
|
||||
|
||||
return (
|
||||
<li className={styles.singleLine}>
|
||||
<PlatformLink size={16} scratch={scratch} className={styles.icon} />
|
||||
<Link
|
||||
href={scratchUrl(scratch)}
|
||||
className={classNames(styles.link, styles.name)}
|
||||
>
|
||||
{scratch.name}
|
||||
</Link>
|
||||
<div className={styles.metadata}>{matchPercentString}</div>
|
||||
{showOwner && <ScratchOwnerAvatar scratch={scratch} />}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import ScratchList, { ScratchItemNoOwner } from "@/components/ScratchList";
|
||||
import ScratchList from "@/components/ScratchList";
|
||||
import { ScratchItemNoOwner } from "@/components/ScratchItem";
|
||||
|
||||
import type { User } from "@/lib/api";
|
||||
import { userUrl } from "@/lib/api/urls";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user