v3: lots of small UI tweaks and fixes
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 46s

This commit is contained in:
khannurien
2026-04-11 08:35:16 +00:00
parent 362967472c
commit b822f861ed
22 changed files with 673 additions and 500 deletions

View File

@@ -1288,6 +1288,36 @@ body.has-player .fab-new {
gap: 1.5rem; gap: 1.5rem;
} }
.profile-info {
position: relative;
min-width: 0;
}
.profile-info-scroll {
overflow-x: auto;
scrollbar-width: none;
padding-bottom: 0.25rem;
padding-right: 0.25rem;
}
.profile-info-scroll::-webkit-scrollbar {
display: none;
}
/* Fade hint only at narrow viewports where the info column can actually overflow */
@media (max-width: 600px) {
.profile-info::after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 2rem;
background: linear-gradient(to right, transparent, var(--color-bg));
pointer-events: none;
}
}
.profile-tabs-scroller { .profile-tabs-scroller {
margin-top: 0.5rem; margin-top: 0.5rem;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
@@ -1854,6 +1884,33 @@ body.has-player .fab-new {
.app-header-center { .app-header-center {
display: flex; display: flex;
} }
.app-header-center .search-bar-btn {
display: none;
}
}
/* Below-header search row — small viewports only */
.header-search-below {
display: flex;
align-items: center;
padding: 0.5rem 1rem;
background: var(--color-surface);
border-bottom: 2px solid var(--color-border);
}
.header-search-below .search-bar {
flex: 1;
}
.header-search-below .search-bar-input {
max-width: 100%;
}
@media (min-width: 860px) {
.header-search-below {
display: none;
}
} }
/* When the search bar is expanded, immediately clear the rest of the center — /* When the search bar is expanded, immediately clear the rest of the center —
@@ -1917,12 +1974,32 @@ body.has-player .fab-new {
.app-header-nav button { .app-header-nav button {
padding: 0.35rem 0.85rem; padding: 0.35rem 0.85rem;
font-size: 0.95rem; font-size: 0.95rem;
font-family: inherit;
line-height: inherit;
} }
.app-header-nav .btn-primary { .app-header-nav .btn-primary {
padding: 0.35rem 1rem; padding: 0.35rem 1rem;
} }
/* Ghost search button — always visible except when the center search bar is shown */
.nav-search-btn {
display: flex;
align-items: center;
padding: 0.35rem;
background: transparent;
border: none;
color: inherit;
text-decoration: none;
font-size: 1rem;
cursor: pointer;
border-radius: 6px;
}
.nav-search-btn:hover {
background: var(--color-surface);
}
/* Text links — visible only at wide viewports */ /* Text links — visible only at wide viewports */
.nav-links { .nav-links {
display: none; display: none;
@@ -2204,6 +2281,7 @@ body.has-player .fab-new {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 0.25em;
white-space: nowrap; white-space: nowrap;
cursor: pointer; cursor: pointer;
border-radius: 8px; border-radius: 8px;
@@ -2360,7 +2438,7 @@ body.has-player .fab-new {
/* Right-edge gradient: absolutely positioned over the scroll container, /* Right-edge gradient: absolutely positioned over the scroll container,
always at the viewport-right edge regardless of scroll position */ always at the viewport-right edge regardless of scroll position */
.feed-sort-scroller::after { .feed-sort-scroller::after {
content: ''; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
@@ -2387,6 +2465,7 @@ body.has-player .fab-new {
overflow-x: auto; overflow-x: auto;
scrollbar-width: none; scrollbar-width: none;
padding-bottom: 0.3rem; padding-bottom: 0.3rem;
padding-right: 0.25rem;
min-width: 0; min-width: 0;
} }
@@ -2525,7 +2604,6 @@ body.has-player .fab-new {
order: 0; order: 0;
} }
/* Left column: preview + vote stacked with fixed gap, independent of body height */ /* Left column: preview + vote stacked with fixed gap, independent of body height */
.dump-card-left { .dump-card-left {
display: flex; display: flex;
@@ -3052,7 +3130,6 @@ body.has-player .fab-new {
opacity: 1; opacity: 1;
} }
.playlist-detail-content { .playlist-detail-content {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
@@ -3907,6 +3984,7 @@ body.has-player .fab-new {
border: none; border: none;
cursor: pointer; cursor: pointer;
font-size: 0.95rem; font-size: 0.95rem;
font-weight: 600;
padding: 0.35rem 0.85rem; padding: 0.35rem 0.85rem;
border-radius: 8px; border-radius: 8px;
transition: background 0.15s; transition: background 0.15s;
@@ -4398,12 +4476,9 @@ body.has-player .fab-new {
} }
.search-tabs { .search-tabs {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 1rem 1.25rem 0;
width: 100%; width: 100%;
max-width: 860px; max-width: 860px;
padding: 1rem 1.25rem 0;
box-sizing: border-box; box-sizing: border-box;
} }

View File

@@ -62,6 +62,9 @@ const ResetPassword = lazy(() =>
default: m.ResetPassword, default: m.ResetPassword,
})) }))
); );
const NotFound = lazy(() =>
import("./pages/NotFound.tsx").then((m) => ({ default: m.NotFound }))
);
function AppRoutes() { function AppRoutes() {
return ( return (
@@ -111,6 +114,7 @@ function AppRoutes() {
</RestrictedLoggedIn> </RestrictedLoggedIn>
} }
/> />
<Route path="*" element={<NotFound />} />
</Routes> </Routes>
</Suspense> </Suspense>
); );

View File

@@ -1,11 +1,12 @@
import { lazy, type ReactNode, Suspense, useState } from "react"; import { lazy, type ReactNode, Suspense, useState } from "react";
import { Link, useNavigate } from "react-router"; import { Link, useLocation, useNavigate } from "react-router";
import { t } from "@lingui/core/macro"; import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro"; import { Trans } from "@lingui/react/macro";
import { useAuth } from "../hooks/useAuth.ts"; import { useAuth } from "../hooks/useAuth.ts";
import { useWS } from "../hooks/useWS.ts"; import { useWS } from "../hooks/useWS.ts";
import { NotificationBell } from "./NotificationBell.tsx"; import { NotificationBell } from "./NotificationBell.tsx";
import { UserMenu } from "./UserMenu.tsx"; import { UserMenu } from "./UserMenu.tsx";
import { SearchBar } from "./SearchBar.tsx";
const DumpCreateModal = lazy(() => const DumpCreateModal = lazy(() =>
import("./DumpCreateModal.tsx").then((m) => ({ default: m.DumpCreateModal })) import("./DumpCreateModal.tsx").then((m) => ({ default: m.DumpCreateModal }))
@@ -21,21 +22,51 @@ export function AppHeader(
const { user } = useAuth(); const { user } = useAuth();
const { wsStatus, wsErrorMessage } = useWS(); const { wsStatus, wsErrorMessage } = useWS();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation();
const isSearchPage = location.pathname === "/search";
const [searchExpanded, setSearchExpanded] = useState(isSearchPage);
const [createModalOpen, setCreateModalOpen] = useState(!!initialDumpUrl); const [createModalOpen, setCreateModalOpen] = useState(!!initialDumpUrl);
function handleSearchToggle() {
if (searchExpanded) {
if (isSearchPage) {
navigate(-1);
} else {
setSearchExpanded(false);
}
} else {
setSearchExpanded(true);
}
}
return ( return (
<> <>
<header <header className="app-header app-header--has-center">
className={`app-header${centerSlot ? " app-header--has-center" : ""}`}
>
<Link to="/?tab=hot" className="app-header-brand"> <Link to="/?tab=hot" className="app-header-brand">
🚚<span className="app-header-brand-name">{" "}{document.querySelector<HTMLMetaElement>('meta[name="site-name"]') 🚚<span className="app-header-brand-name">
?.content ?? "gerbeur"}</span> {" "}
{document.querySelector<HTMLMetaElement>('meta[name="site-name"]')
?.content ?? "gerbeur"}
</span>
</Link> </Link>
{centerSlot && <div className="app-header-center">{centerSlot}</div>} <div className="app-header-center">
{centerSlot}
<SearchBar
expanded={searchExpanded}
onExpandedChange={setSearchExpanded}
/>
</div>
<nav className="app-header-nav"> <nav className="app-header-nav">
<button
type="button"
className="nav-search-btn"
aria-label={searchExpanded ? t`Cancel search` : t`Search`}
onClick={handleSearchToggle}
>
{searchExpanded ? "✕" : "🔍"}
</button>
{user {user
? ( ? (
<> <>
@@ -68,7 +99,9 @@ export function AppHeader(
disabled={disableNew} disabled={disableNew}
title={disableNew ? t`Server unreachable` : undefined} title={disableNew ? t`Server unreachable` : undefined}
> >
+<span className="btn-new-label"><Trans> Dump</Trans></span> +<span className="btn-new-label">
<Trans>Dump</Trans>
</span>
</button> </button>
</> </>
) )
@@ -82,6 +115,15 @@ export function AppHeader(
</nav> </nav>
</header> </header>
{searchExpanded && (
<div className="header-search-below">
<SearchBar
expanded={searchExpanded}
onExpandedChange={setSearchExpanded}
/>
</div>
)}
{wsStatus === "disconnected" && wsErrorMessage && ( {wsStatus === "disconnected" && wsErrorMessage && (
<div className="app-header-status" role="alert"> <div className="app-header-status" role="alert">
<strong> <strong>

View File

@@ -106,7 +106,6 @@ export function DumpCard(
)} )}
</div> </div>
</div> </div>
</div> </div>
</li> </li>
); );

View File

@@ -2,6 +2,7 @@ import { useLocation, useNavigate } from "react-router";
import { Trans } from "@lingui/react/macro"; import { Trans } from "@lingui/react/macro";
import { useAuth } from "../hooks/useAuth.ts"; import { useAuth } from "../hooks/useAuth.ts";
import { type FeedTab, VALID_TABS } from "../config/feedTabs.ts"; import { type FeedTab, VALID_TABS } from "../config/feedTabs.ts";
import { TabBar } from "./TabBar.tsx";
export function FeedTabBar() { export function FeedTabBar() {
const location = useLocation(); const location = useLocation();
@@ -11,44 +12,20 @@ export function FeedTabBar() {
const rawTab = new URLSearchParams(location.search).get("tab") ?? "hot"; const rawTab = new URLSearchParams(location.search).get("tab") ?? "hot";
const tab: FeedTab = VALID_TABS.has(rawTab) ? (rawTab as FeedTab) : "hot"; const tab: FeedTab = VALID_TABS.has(rawTab) ? (rawTab as FeedTab) : "hot";
function setTab(t: FeedTab) { const tabs = [
navigate(`/?tab=${t}`, { replace: true }); { key: "hot" as FeedTab, label: <Trans>Hot</Trans> },
} { key: "new" as FeedTab, label: <Trans>New</Trans> },
{ key: "journal" as FeedTab, label: <Trans>Journal</Trans> },
...(user
? [{ key: "followed" as FeedTab, label: <Trans>Followed</Trans> }]
: []),
];
return ( return (
<div className="feed-sort-scroller"> <TabBar
<div className="feed-sort"> tabs={tabs}
<button activeTab={tab}
type="button" onChange={(t) => navigate(`/?tab=${t}`, { replace: true })}
className={`feed-sort-btn${tab === "hot" ? " active" : ""}`} />
onClick={() => setTab("hot")}
>
<Trans>Hot</Trans>
</button>
<button
type="button"
className={`feed-sort-btn${tab === "new" ? " active" : ""}`}
onClick={() => setTab("new")}
>
<Trans>New</Trans>
</button>
<button
type="button"
className={`feed-sort-btn${tab === "journal" ? " active" : ""}`}
onClick={() => setTab("journal")}
>
<Trans>Journal</Trans>
</button>
{user && (
<button
type="button"
className={`feed-sort-btn${tab === "followed" ? " active" : ""}`}
onClick={() => setTab("followed")}
>
<Trans>Followed</Trans>
</button>
)}
</div>
</div>
); );
} }

View File

@@ -1,5 +1,6 @@
import { useContext } from "react"; import { useContext } from "react";
import { Link, useNavigate } from "react-router"; import { Link, useNavigate } from "react-router";
import { Plural, Trans } from "@lingui/react/macro";
import type { Dump } from "../model.ts"; import type { Dump } from "../model.ts";
import { API_URL } from "../config/api.ts"; import { API_URL } from "../config/api.ts";
import { relativeTime } from "../utils/relativeTime.ts"; import { relativeTime } from "../utils/relativeTime.ts";
@@ -73,12 +74,18 @@ export function JournalCard(
</time> </time>
</Tooltip> </Tooltip>
{dump.commentCount > 0 && ( {dump.commentCount > 0 && (
<span> <span className="dump-card-comment-count">
{dump.commentCount} {dump.commentCount === 1 ? "comment" : "comments"} <Plural
value={dump.commentCount}
one="# comment"
other="# comments"
/>
</span> </span>
)} )}
{dump.isPrivate && isOwner && ( {dump.isPrivate && isOwner && (
<span className="dump-card-private-badge">private</span> <span className="dump-card-private-badge">
<Trans>private</Trans>
</span>
)} )}
</div> </div>
); );

View File

@@ -27,7 +27,13 @@ export function NewPlaylistForm(
className={toggleClassName} className={toggleClassName}
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
> >
{toggleLabel ?? <>+<span className="btn-new-label"><Trans> New playlist</Trans></span></>} {toggleLabel ?? (
<>
+<span className="btn-new-label">
<Trans>New playlist</Trans>
</span>
</>
)}
</button> </button>
{open && ( {open && (

View File

@@ -1,6 +1,5 @@
import { type ReactNode } from "react"; import { type ReactNode } from "react";
import { AppHeader } from "./AppHeader.tsx"; import { AppHeader } from "./AppHeader.tsx";
import { SearchBar } from "./SearchBar.tsx";
interface PageShellProps { interface PageShellProps {
children: ReactNode; children: ReactNode;
@@ -14,7 +13,7 @@ export function PageShell(
) { ) {
return ( return (
<div className="page-shell"> <div className="page-shell">
<AppHeader centerSlot={centerSlot ?? <SearchBar />} /> <AppHeader centerSlot={centerSlot} />
<main <main
className={`page-content${centered ? " page-content--centered" : ""}`} className={`page-content${centered ? " page-content--centered" : ""}`}
> >

View File

@@ -4,22 +4,35 @@ import { t } from "@lingui/core/macro";
interface SearchBarProps { interface SearchBarProps {
collapsible?: boolean; collapsible?: boolean;
expanded?: boolean;
onExpandedChange?: (v: boolean) => void;
} }
export function SearchBar({ collapsible = false }: SearchBarProps) { export function SearchBar(
const [value, setValue] = useState( { collapsible = false, expanded: expandedProp, onExpandedChange }:
() => new URLSearchParams(location.search).get("q") ?? "", SearchBarProps,
); ) {
const [expanded, setExpanded] = useState(!collapsible); const isControlled = expandedProp !== undefined;
const [expandedState, setExpandedState] = useState(!collapsible);
const expanded = isControlled ? expandedProp! : expandedState;
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const navigate = useNavigate(); const navigate = useNavigate();
const [value, setValue] = useState(
() => new URLSearchParams(location.search).get("q") ?? "",
);
function setExpanded(v: boolean) {
if (!isControlled) setExpandedState(v);
onExpandedChange?.(v);
}
useEffect(() => { useEffect(() => {
if (collapsible && expanded) inputRef.current?.focus(); if ((collapsible || isControlled) && expanded) inputRef.current?.focus();
}, [expanded, collapsible]); }, [expanded, collapsible, isControlled]);
function handleIconClick() { function handleIconClick() {
if (!collapsible) return; if (!collapsible && !isControlled) return;
if (expanded) { if (expanded) {
setExpanded(false); setExpanded(false);
setValue(""); setValue("");
@@ -33,14 +46,14 @@ export function SearchBar({ collapsible = false }: SearchBarProps) {
const q = value.trim(); const q = value.trim();
if (!q) return; if (!q) return;
navigate(`/search?q=${encodeURIComponent(q)}&tab=dumps`); navigate(`/search?q=${encodeURIComponent(q)}&tab=dumps`);
if (collapsible) { if (collapsible || isControlled) {
setExpanded(false); setExpanded(false);
setValue(""); setValue("");
} }
} }
function handleKeyDown(e: React.KeyboardEvent) { function handleKeyDown(e: React.KeyboardEvent) {
if (e.key === "Escape" && collapsible) { if (e.key === "Escape" && (collapsible || isControlled)) {
setExpanded(false); setExpanded(false);
setValue(""); setValue("");
} }
@@ -48,9 +61,9 @@ export function SearchBar({ collapsible = false }: SearchBarProps) {
return ( return (
<form <form
className={`search-bar${collapsible ? " search-bar--collapsible" : ""}${ className={`search-bar${
expanded ? " search-bar--expanded" : "" collapsible || isControlled ? " search-bar--collapsible" : ""
}`} }${expanded ? " search-bar--expanded" : ""}`}
onSubmit={handleSubmit} onSubmit={handleSubmit}
role="search" role="search"
> >
@@ -66,10 +79,12 @@ export function SearchBar({ collapsible = false }: SearchBarProps) {
tabIndex={expanded ? 0 : -1} tabIndex={expanded ? 0 : -1}
/> />
<button <button
type={expanded && !collapsible ? "submit" : "button"} type={expanded ? "submit" : "button"}
className="search-bar-btn" className="search-bar-btn"
aria-label={expanded ? t`Submit search` : t`Open search`} aria-label={expanded ? t`Submit search` : t`Open search`}
onClick={collapsible ? handleIconClick : undefined} onClick={!expanded && (collapsible || isControlled)
? handleIconClick
: undefined}
> >
🔍 🔍
</button> </button>

35
src/components/TabBar.tsx Normal file
View File

@@ -0,0 +1,35 @@
import { type ReactNode } from "react";
interface Tab<T extends string> {
key: T;
label: ReactNode;
}
interface TabBarProps<T extends string> {
tabs: Tab<T>[];
activeTab: T;
onChange: (key: T) => void;
className?: string;
innerClassName?: string;
}
export function TabBar<T extends string>(
{ tabs, activeTab, onChange, className, innerClassName }: TabBarProps<T>,
) {
return (
<div className={`feed-sort-scroller${className ? ` ${className}` : ""}`}>
<div className={`feed-sort${innerClassName ? ` ${innerClassName}` : ""}`}>
{tabs.map(({ key, label }) => (
<button
key={key}
type="button"
className={`feed-sort-btn${activeTab === key ? " active" : ""}`}
onClick={() => onChange(key)}
>
{label}
</button>
))}
</div>
</div>
);
}

File diff suppressed because one or more lines are too long

View File

@@ -13,25 +13,13 @@ msgstr ""
"Language-Team: \n" "Language-Team: \n"
"Plural-Forms: \n" "Plural-Forms: \n"
#: src/components/AppHeader.tsx:71
msgid " Dump"
msgstr " Dump"
#: src/pages/UserDumps.tsx:114
#: src/pages/UserPublicProfile.tsx:1332
msgid " New dump"
msgstr " New dump"
#: src/components/NewPlaylistForm.tsx:30
msgid " New playlist"
msgstr " New playlist"
#: src/components/CommentThread.tsx:176 #: src/components/CommentThread.tsx:176
msgid "[deleted]" msgid "[deleted]"
msgstr "[deleted]" msgstr "[deleted]"
#. placeholder {0}: dump.commentCount #. placeholder {0}: dump.commentCount
#: src/components/DumpCard.tsx:95 #: src/components/DumpCard.tsx:95
#: src/components/JournalCard.tsx:78
msgid "{0, plural, one {# comment} other {# comments}}" msgid "{0, plural, one {# comment} other {# comments}}"
msgstr "{0, plural, one {# comment} other {# comments}}" msgstr "{0, plural, one {# comment} other {# comments}}"
@@ -83,14 +71,10 @@ msgstr "← Back to all dumps"
msgid "← Back to profile" msgid "← Back to profile"
msgstr "← Back to profile" msgstr "← Back to profile"
#: src/pages/UserPublicProfile.tsx:100 #: src/pages/UserPublicProfile.tsx:101
msgid "+ Invite someone" msgid "+ Invite someone"
msgstr "+ Invite someone" msgstr "+ Invite someone"
#: src/components/AppHeader.tsx:71
msgid "+ New"
msgstr "+ New"
#: src/pages/UserDumps.tsx:114 #: src/pages/UserDumps.tsx:114
#: src/pages/UserPublicProfile.tsx:1332 #: src/pages/UserPublicProfile.tsx:1332
msgid "+ New dump" msgid "+ New dump"
@@ -152,7 +136,7 @@ msgstr "a comment"
msgid "a post" msgid "a post"
msgstr "a post" msgstr "a post"
#: src/pages/UserPublicProfile.tsx:1217 #: src/pages/UserPublicProfile.tsx:1193
msgid "Account" msgid "Account"
msgstr "Account" msgstr "Account"
@@ -160,7 +144,7 @@ msgstr "Account"
msgid "Add a comment…" msgid "Add a comment…"
msgstr "Add a comment…" msgstr "Add a comment…"
#: src/pages/UserPublicProfile.tsx:859 #: src/pages/UserPublicProfile.tsx:860
msgid "Add email…" msgid "Add email…"
msgstr "Add email…" msgstr "Add email…"
@@ -181,7 +165,7 @@ msgstr "All {0, plural, one {# upvoted dump} other {# upvoted dumps}} loaded."
msgid "Already have an account? <0>Log in</0>" msgid "Already have an account? <0>Log in</0>"
msgstr "Already have an account? <0>Log in</0>" msgstr "Already have an account? <0>Log in</0>"
#: src/pages/UserPublicProfile.tsx:1236 #: src/pages/UserPublicProfile.tsx:1212
msgid "Appearance" msgid "Appearance"
msgstr "Appearance" msgstr "Appearance"
@@ -191,7 +175,7 @@ msgstr "Appearance"
msgid "At least {0} characters" msgid "At least {0} characters"
msgstr "At least {0} characters" msgstr "At least {0} characters"
#: src/pages/UserPublicProfile.tsx:1270 #: src/pages/UserPublicProfile.tsx:1246
msgid "Auto" msgid "Auto"
msgstr "Auto" msgstr "Auto"
@@ -214,8 +198,8 @@ msgstr "Can't connect to the live updates server. Upvotes and notifications may
#: src/components/PlaylistCreateForm.tsx:112 #: src/components/PlaylistCreateForm.tsx:112
#: src/pages/DumpEdit.tsx:299 #: src/pages/DumpEdit.tsx:299
#: src/pages/PlaylistDetail.tsx:680 #: src/pages/PlaylistDetail.tsx:680
#: src/pages/UserPublicProfile.tsx:841 #: src/pages/UserPublicProfile.tsx:842
#: src/pages/UserPublicProfile.tsx:919 #: src/pages/UserPublicProfile.tsx:921
msgid "Cancel" msgid "Cancel"
msgstr "Cancel" msgstr "Cancel"
@@ -223,6 +207,10 @@ msgstr "Cancel"
msgid "Cancel removal" msgid "Cancel removal"
msgstr "Cancel removal" msgstr "Cancel removal"
#: src/components/AppHeader.tsx:62
msgid "Cancel search"
msgstr "Cancel search"
#: src/pages/UserPublicProfile.tsx:772 #: src/pages/UserPublicProfile.tsx:772
msgid "Change avatar" msgid "Change avatar"
msgstr "Change avatar" msgstr "Change avatar"
@@ -232,7 +220,7 @@ msgstr "Change avatar"
msgid "Change password" msgid "Change password"
msgstr "Change password" msgstr "Change password"
#: src/pages/UserPublicProfile.tsx:1229 #: src/pages/UserPublicProfile.tsx:1205
msgid "Change password…" msgid "Change password…"
msgstr "Change password…" msgstr "Change password…"
@@ -245,7 +233,7 @@ msgstr "Checking invite…"
msgid "Close" msgid "Close"
msgstr "Close" msgstr "Close"
#: src/pages/UserPublicProfile.tsx:1262 #: src/pages/UserPublicProfile.tsx:1238
msgid "Color scheme" msgid "Color scheme"
msgstr "Color scheme" msgstr "Color scheme"
@@ -254,11 +242,11 @@ msgstr "Color scheme"
msgid "Confirm new password" msgid "Confirm new password"
msgstr "Confirm new password" msgstr "Confirm new password"
#: src/pages/UserPublicProfile.tsx:91 #: src/pages/UserPublicProfile.tsx:92
msgid "Copied!" msgid "Copied!"
msgstr "Copied!" msgstr "Copied!"
#: src/pages/UserPublicProfile.tsx:91 #: src/pages/UserPublicProfile.tsx:92
msgid "Copy" msgid "Copy"
msgstr "Copy" msgstr "Copy"
@@ -299,7 +287,7 @@ msgstr "Creating…"
msgid "Current password" msgid "Current password"
msgstr "Current password" msgstr "Current password"
#: src/pages/UserPublicProfile.tsx:1284 #: src/pages/UserPublicProfile.tsx:1260
msgid "Dark" msgid "Dark"
msgstr "Dark" msgstr "Dark"
@@ -351,6 +339,10 @@ msgstr "Drop a file here"
msgid "Drop a replacement here" msgid "Drop a replacement here"
msgstr "Drop a replacement here" msgstr "Drop a replacement here"
#: src/components/AppHeader.tsx:99
msgid "Dump"
msgstr "Dump"
#: src/components/DumpCreateModal.tsx:427 #: src/components/DumpCreateModal.tsx:427
msgid "Dump it" msgid "Dump it"
msgstr "Dump it" msgstr "Dump it"
@@ -361,13 +353,13 @@ msgstr "Dumped!"
#: src/pages/Search.tsx:172 #: src/pages/Search.tsx:172
#: src/pages/UserDumps.tsx:107 #: src/pages/UserDumps.tsx:107
#: src/pages/UserPublicProfile.tsx:968 #: src/pages/UserPublicProfile.tsx:965
msgid "Dumps" msgid "Dumps"
msgstr "Dumps" msgstr "Dumps"
#. placeholder {0}: dumps.items.length #. placeholder {0}: dumps.items.length
#. placeholder {1}: dumps.hasMore ? "+" : "" #. placeholder {1}: dumps.hasMore ? "+" : ""
#: src/pages/UserPublicProfile.tsx:1006 #: src/pages/UserPublicProfile.tsx:982
msgid "Dumps ({0}{1})" msgid "Dumps ({0}{1})"
msgstr "Dumps ({0}{1})" msgstr "Dumps ({0}{1})"
@@ -407,7 +399,7 @@ msgstr "Editing"
msgid "Email address" msgid "Email address"
msgstr "Email address" msgstr "Email address"
#: src/pages/Search.tsx:207 #: src/pages/Search.tsx:198
msgid "Enter a query to search." msgid "Enter a query to search."
msgstr "Enter a query to search." msgstr "Enter a query to search."
@@ -420,20 +412,20 @@ msgstr "Failed to change password"
msgid "Failed to create playlist" msgid "Failed to create playlist"
msgstr "Failed to create playlist" msgstr "Failed to create playlist"
#: src/pages/UserPublicProfile.tsx:72 #: src/pages/UserPublicProfile.tsx:73
#: src/pages/UserPublicProfile.tsx:75 #: src/pages/UserPublicProfile.tsx:76
#: src/pages/UserPublicProfile.tsx:103 #: src/pages/UserPublicProfile.tsx:104
msgid "Failed to generate invite" msgid "Failed to generate invite"
msgstr "Failed to generate invite" msgstr "Failed to generate invite"
#: src/pages/index/FollowedFeed.tsx:81 #: src/pages/index/FollowedFeed.tsx:82
#: src/pages/index/HotFeed.tsx:36 #: src/pages/index/HotFeed.tsx:36
#: src/pages/index/JournalFeed.tsx:48 #: src/pages/index/JournalFeed.tsx:48
#: src/pages/index/NewFeed.tsx:36 #: src/pages/index/NewFeed.tsx:36
#: src/pages/Notifications.tsx:342 #: src/pages/Notifications.tsx:342
#: src/pages/UserPublicProfile.tsx:1108 #: src/pages/UserPublicProfile.tsx:1084
#: src/pages/UserPublicProfile.tsx:1150 #: src/pages/UserPublicProfile.tsx:1126
#: src/pages/UserPublicProfile.tsx:1195 #: src/pages/UserPublicProfile.tsx:1171
msgid "Failed to load" msgid "Failed to load"
msgstr "Failed to load" msgstr "Failed to load"
@@ -452,8 +444,8 @@ msgstr "Failed to post reply"
#: src/pages/PlaylistDetail.tsx:789 #: src/pages/PlaylistDetail.tsx:789
#: src/pages/UserPublicProfile.tsx:680 #: src/pages/UserPublicProfile.tsx:680
#: src/pages/UserPublicProfile.tsx:718 #: src/pages/UserPublicProfile.tsx:718
#: src/pages/UserPublicProfile.tsx:845 #: src/pages/UserPublicProfile.tsx:846
#: src/pages/UserPublicProfile.tsx:922 #: src/pages/UserPublicProfile.tsx:924
msgid "Failed to save" msgid "Failed to save"
msgstr "Failed to save" msgstr "Failed to save"
@@ -461,7 +453,7 @@ msgstr "Failed to save"
msgid "Failed to save edit" msgid "Failed to save edit"
msgstr "Failed to save edit" msgstr "Failed to save edit"
#: src/pages/UserPublicProfile.tsx:868 #: src/pages/UserPublicProfile.tsx:869
msgid "Failed to update avatar" msgid "Failed to update avatar"
msgstr "Failed to update avatar" msgstr "Failed to update avatar"
@@ -495,16 +487,16 @@ msgstr "Follow {targetUsername}"
msgid "Follow playlist" msgid "Follow playlist"
msgstr "Follow playlist" msgstr "Follow playlist"
#: src/pages/index/FollowedFeed.tsx:371 #: src/pages/index/FollowedFeed.tsx:365
msgid "Follow some public playlists to see their dumps here." msgid "Follow some public playlists to see their dumps here."
msgstr "Follow some public playlists to see their dumps here." msgstr "Follow some public playlists to see their dumps here."
#: src/pages/index/FollowedFeed.tsx:357 #: src/pages/index/FollowedFeed.tsx:351
msgid "Follow some users to see their dumps here." msgid "Follow some users to see their dumps here."
msgstr "Follow some users to see their dumps here." msgstr "Follow some users to see their dumps here."
#: src/components/FeedTabBar.tsx:48 #: src/components/FeedTabBar.tsx:19
#: src/pages/UserPublicProfile.tsx:982 #: src/pages/UserPublicProfile.tsx:967
msgid "Followed" msgid "Followed"
msgstr "Followed" msgstr "Followed"
@@ -514,13 +506,13 @@ msgstr "Followed"
msgid "Followed ({0}{1})" msgid "Followed ({0}{1})"
msgstr "Followed ({0}{1})" msgstr "Followed ({0}{1})"
#: src/pages/UserPublicProfile.tsx:1139 #: src/pages/UserPublicProfile.tsx:1115
msgid "Followed playlists" msgid "Followed playlists"
msgstr "Followed playlists" msgstr "Followed playlists"
#: src/components/FollowButton.tsx:37 #: src/components/FollowButton.tsx:37
#: src/components/FollowButton.tsx:64 #: src/components/FollowButton.tsx:64
#: src/pages/UserPublicProfile.tsx:1097 #: src/pages/UserPublicProfile.tsx:1073
msgid "Following" msgid "Following"
msgstr "Following" msgstr "Following"
@@ -528,19 +520,23 @@ msgstr "Following"
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Forgot password?" msgstr "Forgot password?"
#: src/pages/index/FollowedFeed.tsx:337 #: src/pages/index/FollowedFeed.tsx:334
msgid "From people" msgid "From people"
msgstr "From people" msgstr "From people"
#: src/pages/index/FollowedFeed.tsx:344 #: src/pages/index/FollowedFeed.tsx:335
msgid "From playlists" msgid "From playlists"
msgstr "From playlists" msgstr "From playlists"
#: src/pages/NotFound.tsx:13
msgid "Go home"
msgstr "Go home"
#: src/pages/ResetPassword.tsx:66 #: src/pages/ResetPassword.tsx:66
msgid "Go to login" msgid "Go to login"
msgstr "Go to login" msgstr "Go to login"
#: src/components/FeedTabBar.tsx:26 #: src/components/FeedTabBar.tsx:16
msgid "Hot" msgid "Hot"
msgstr "Hot" msgstr "Hot"
@@ -556,16 +552,16 @@ msgstr "Invalid invite"
msgid "Invalid link" msgid "Invalid link"
msgstr "Invalid link" msgstr "Invalid link"
#: src/pages/UserPublicProfile.tsx:790 #: src/pages/UserPublicProfile.tsx:791
msgid "invited by" msgid "invited by"
msgstr "invited by" msgstr "invited by"
#: src/pages/UserPublicProfile.tsx:989 #: src/pages/UserPublicProfile.tsx:968
#: src/pages/UserPublicProfile.tsx:1184 #: src/pages/UserPublicProfile.tsx:1160
msgid "Invitees" msgid "Invitees"
msgstr "Invitees" msgstr "Invitees"
#: src/components/FeedTabBar.tsx:40 #: src/components/FeedTabBar.tsx:18
msgid "Journal" msgid "Journal"
msgstr "Journal" msgstr "Journal"
@@ -573,7 +569,7 @@ msgstr "Journal"
msgid "just now" msgid "just now"
msgstr "just now" msgstr "just now"
#: src/pages/UserPublicProfile.tsx:1277 #: src/pages/UserPublicProfile.tsx:1253
msgid "Light" msgid "Light"
msgstr "Light" msgstr "Light"
@@ -581,7 +577,7 @@ msgstr "Light"
msgid "Live updates are temporarily disconnected. Trying to reconnect…" msgid "Live updates are temporarily disconnected. Trying to reconnect…"
msgstr "Live updates are temporarily disconnected. Trying to reconnect…" msgstr "Live updates are temporarily disconnected. Trying to reconnect…"
#: src/components/AppHeader.tsx:88 #: src/components/AppHeader.tsx:125
msgid "Live updates unavailable." msgid "Live updates unavailable."
msgstr "Live updates unavailable." msgstr "Live updates unavailable."
@@ -594,11 +590,11 @@ msgstr "Load more"
msgid "Loading dump…" msgid "Loading dump…"
msgstr "Loading dump…" msgstr "Loading dump…"
#: src/pages/index/FollowedFeed.tsx:109 #: src/pages/index/FollowedFeed.tsx:110
#: src/pages/index/HotFeed.tsx:64 #: src/pages/index/HotFeed.tsx:64
#: src/pages/index/JournalFeed.tsx:77 #: src/pages/index/JournalFeed.tsx:77
#: src/pages/index/NewFeed.tsx:64 #: src/pages/index/NewFeed.tsx:64
#: src/pages/Search.tsx:244 #: src/pages/Search.tsx:235
#: src/pages/UserDumps.tsx:93 #: src/pages/UserDumps.tsx:93
#: src/pages/UserPlaylists.tsx:417 #: src/pages/UserPlaylists.tsx:417
#: src/pages/UserPlaylists.tsx:452 #: src/pages/UserPlaylists.tsx:452
@@ -616,7 +612,7 @@ msgstr "Loading profile…"
#: src/components/PlaylistMembershipPanel.tsx:28 #: src/components/PlaylistMembershipPanel.tsx:28
#: src/components/TextEditor.tsx:289 #: src/components/TextEditor.tsx:289
#: src/pages/index/FollowedFeed.tsx:76 #: src/pages/index/FollowedFeed.tsx:77
#: src/pages/index/HotFeed.tsx:32 #: src/pages/index/HotFeed.tsx:32
#: src/pages/index/JournalFeed.tsx:44 #: src/pages/index/JournalFeed.tsx:44
#: src/pages/index/NewFeed.tsx:32 #: src/pages/index/NewFeed.tsx:32
@@ -624,21 +620,21 @@ msgstr "Loading profile…"
#: src/pages/Notifications.tsx:414 #: src/pages/Notifications.tsx:414
#: src/pages/UserDumps.tsx:51 #: src/pages/UserDumps.tsx:51
#: src/pages/UserPlaylists.tsx:342 #: src/pages/UserPlaylists.tsx:342
#: src/pages/UserPublicProfile.tsx:1102 #: src/pages/UserPublicProfile.tsx:1078
#: src/pages/UserPublicProfile.tsx:1144 #: src/pages/UserPublicProfile.tsx:1120
#: src/pages/UserPublicProfile.tsx:1189 #: src/pages/UserPublicProfile.tsx:1165
#: src/pages/UserUpvoted.tsx:123 #: src/pages/UserUpvoted.tsx:123
msgid "Loading…" msgid "Loading…"
msgstr "Loading…" msgstr "Loading…"
#: src/components/AppHeader.tsx:78 #: src/components/AppHeader.tsx:106
#: src/pages/UserLogin.tsx:87 #: src/pages/UserLogin.tsx:87
#: src/pages/UserLogin.tsx:117 #: src/pages/UserLogin.tsx:117
msgid "Log in" msgid "Log in"
msgstr "Log in" msgstr "Log in"
#: src/pages/UserPublicProfile.tsx:749 #: src/pages/UserPublicProfile.tsx:749
#: src/pages/UserPublicProfile.tsx:882 #: src/pages/UserPublicProfile.tsx:883
msgid "Log out" msgid "Log out"
msgstr "Log out" msgstr "Log out"
@@ -658,11 +654,13 @@ msgstr "Max 50 MB"
msgid "new" msgid "new"
msgstr "new" msgstr "new"
#: src/components/FeedTabBar.tsx:33 #: src/components/FeedTabBar.tsx:17
msgid "New" msgid "New"
msgstr "New" msgstr "New"
#: src/components/DumpCreateModal.tsx:277 #: src/components/DumpCreateModal.tsx:277
#: src/pages/UserDumps.tsx:114
#: src/pages/UserPublicProfile.tsx:1308
msgid "New dump" msgid "New dump"
msgstr "New dump" msgstr "New dump"
@@ -671,6 +669,7 @@ msgstr "New dump"
msgid "New password" msgid "New password"
msgstr "New password" msgstr "New password"
#: src/components/NewPlaylistForm.tsx:30
#: src/components/NewPlaylistForm.tsx:34 #: src/components/NewPlaylistForm.tsx:34
msgid "New playlist" msgid "New playlist"
msgstr "New playlist" msgstr "New playlist"
@@ -679,7 +678,7 @@ msgstr "New playlist"
msgid "No dumps in this playlist yet." msgid "No dumps in this playlist yet."
msgstr "No dumps in this playlist yet." msgstr "No dumps in this playlist yet."
#: src/pages/Search.tsx:224 #: src/pages/Search.tsx:215
msgid "No dumps match \"{q}\"." msgid "No dumps match \"{q}\"."
msgstr "No dumps match \"{q}\"." msgstr "No dumps match \"{q}\"."
@@ -694,36 +693,36 @@ msgid "No emoji found."
msgstr "No emoji found." msgstr "No emoji found."
#: src/pages/UserPlaylists.tsx:439 #: src/pages/UserPlaylists.tsx:439
#: src/pages/UserPublicProfile.tsx:1157 #: src/pages/UserPublicProfile.tsx:1133
msgid "No followed playlists yet." msgid "No followed playlists yet."
msgstr "No followed playlists yet." msgstr "No followed playlists yet."
#: src/pages/UserPublicProfile.tsx:1202 #: src/pages/UserPublicProfile.tsx:1178
msgid "No invitees yet." msgid "No invitees yet."
msgstr "No invitees yet." msgstr "No invitees yet."
#: src/pages/Search.tsx:283 #: src/pages/Search.tsx:274
msgid "No playlists match \"{q}\"." msgid "No playlists match \"{q}\"."
msgstr "No playlists match \"{q}\"." msgstr "No playlists match \"{q}\"."
#: src/components/PlaylistMembershipPanel.tsx:34 #: src/components/PlaylistMembershipPanel.tsx:34
#: src/pages/UserPlaylists.tsx:397 #: src/pages/UserPlaylists.tsx:397
#: src/pages/UserPublicProfile.tsx:1068 #: src/pages/UserPublicProfile.tsx:1044
msgid "No playlists yet." msgid "No playlists yet."
msgstr "No playlists yet." msgstr "No playlists yet."
#: src/pages/Search.tsx:257 #: src/pages/Search.tsx:248
msgid "No users match \"{q}\"." msgid "No users match \"{q}\"."
msgstr "No users match \"{q}\"." msgstr "No users match \"{q}\"."
#: src/pages/UserPublicProfile.tsx:1115 #: src/pages/UserPublicProfile.tsx:1091
msgid "Not following anyone yet." msgid "Not following anyone yet."
msgstr "Not following anyone yet." msgstr "Not following anyone yet."
#: src/pages/Notifications.tsx:349 #: src/pages/Notifications.tsx:349
#: src/pages/UserDumps.tsx:123 #: src/pages/UserDumps.tsx:123
#: src/pages/UserPublicProfile.tsx:1342 #: src/pages/UserPublicProfile.tsx:1318
#: src/pages/UserPublicProfile.tsx:1465 #: src/pages/UserPublicProfile.tsx:1441
#: src/pages/UserUpvoted.tsx:195 #: src/pages/UserUpvoted.tsx:195
msgid "Nothing here yet." msgid "Nothing here yet."
msgstr "Nothing here yet." msgstr "Nothing here yet."
@@ -737,7 +736,7 @@ msgstr "Notifications"
msgid "Notifications ({unreadNotificationCount} unread)" msgid "Notifications ({unreadNotificationCount} unread)"
msgstr "Notifications ({unreadNotificationCount} unread)" msgstr "Notifications ({unreadNotificationCount} unread)"
#: src/components/SearchBar.tsx:71 #: src/components/SearchBar.tsx:83
msgid "Open search" msgid "Open search"
msgstr "Open search" msgstr "Open search"
@@ -746,7 +745,7 @@ msgid "or <0>browse files</0>"
msgstr "or <0>browse files</0>" msgstr "or <0>browse files</0>"
#: src/pages/UserLogin.tsx:106 #: src/pages/UserLogin.tsx:106
#: src/pages/UserPublicProfile.tsx:1222 #: src/pages/UserPublicProfile.tsx:1198
msgid "Password" msgid "Password"
msgstr "Password" msgstr "Password"
@@ -768,17 +767,17 @@ msgstr "Password updated"
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Passwords do not match" msgstr "Passwords do not match"
#: src/components/AppHeader.tsx:54 #: src/components/AppHeader.tsx:82
#: src/components/UserMenu.tsx:62 #: src/components/UserMenu.tsx:62
#: src/pages/Search.tsx:175 #: src/pages/Search.tsx:175
#: src/pages/UserPlaylists.tsx:368 #: src/pages/UserPlaylists.tsx:368
#: src/pages/UserPublicProfile.tsx:975 #: src/pages/UserPublicProfile.tsx:966
msgid "Playlists" msgid "Playlists"
msgstr "Playlists" msgstr "Playlists"
#. placeholder {0}: playlists.items.length #. placeholder {0}: playlists.items.length
#. placeholder {1}: playlists.hasMore ? "+" : "" #. placeholder {1}: playlists.hasMore ? "+" : ""
#: src/pages/UserPublicProfile.tsx:1037 #: src/pages/UserPublicProfile.tsx:1013
msgid "Playlists ({0}{1})" msgid "Playlists ({0}{1})"
msgstr "Playlists ({0}{1})" msgstr "Playlists ({0}{1})"
@@ -800,6 +799,7 @@ msgid "Posting…"
msgstr "Posting…" msgstr "Posting…"
#: src/components/DumpCard.tsx:104 #: src/components/DumpCard.tsx:104
#: src/components/JournalCard.tsx:87
#: src/components/PlaylistCard.tsx:73 #: src/components/PlaylistCard.tsx:73
#: src/components/PlaylistMembershipPanel.tsx:55 #: src/components/PlaylistMembershipPanel.tsx:55
#: src/pages/Dump.tsx:287 #: src/pages/Dump.tsx:287
@@ -879,8 +879,8 @@ msgstr "Retry"
#: src/components/CommentThread.tsx:270 #: src/components/CommentThread.tsx:270
#: src/pages/DumpEdit.tsx:306 #: src/pages/DumpEdit.tsx:306
#: src/pages/PlaylistDetail.tsx:673 #: src/pages/PlaylistDetail.tsx:673
#: src/pages/UserPublicProfile.tsx:833 #: src/pages/UserPublicProfile.tsx:834
#: src/pages/UserPublicProfile.tsx:911 #: src/pages/UserPublicProfile.tsx:913
msgid "Save" msgid "Save"
msgstr "Save" msgstr "Save"
@@ -888,24 +888,25 @@ msgstr "Save"
#: src/components/CommentThread.tsx:269 #: src/components/CommentThread.tsx:269
#: src/pages/PlaylistDetail.tsx:673 #: src/pages/PlaylistDetail.tsx:673
#: src/pages/ResetPassword.tsx:152 #: src/pages/ResetPassword.tsx:152
#: src/pages/UserPublicProfile.tsx:832 #: src/pages/UserPublicProfile.tsx:833
#: src/pages/UserPublicProfile.tsx:911 #: src/pages/UserPublicProfile.tsx:913
msgid "Saving…" msgid "Saving…"
msgstr "Saving…" msgstr "Saving…"
#: src/components/SearchBar.tsx:65 #: src/components/AppHeader.tsx:62
#: src/components/SearchBar.tsx:77
msgid "Search" msgid "Search"
msgstr "Search" msgstr "Search"
#: src/components/SearchBar.tsx:61 #: src/components/SearchBar.tsx:73
msgid "Search dumps, users, playlists…" msgid "Search dumps, users, playlists…"
msgstr "Search dumps, users, playlists…" msgstr "Search dumps, users, playlists…"
#: src/pages/Search.tsx:218 #: src/pages/Search.tsx:209
msgid "Search failed" msgid "Search failed"
msgstr "Search failed" msgstr "Search failed"
#: src/pages/Search.tsx:213 #: src/pages/Search.tsx:204
msgid "Searching…" msgid "Searching…"
msgstr "Searching…" msgstr "Searching…"
@@ -917,7 +918,7 @@ msgstr "Send reset link"
msgid "Sending…" msgid "Sending…"
msgstr "Sending…" msgstr "Sending…"
#: src/components/AppHeader.tsx:69 #: src/components/AppHeader.tsx:97
msgid "Server unreachable" msgid "Server unreachable"
msgstr "Server unreachable" msgstr "Server unreachable"
@@ -926,7 +927,7 @@ msgstr "Server unreachable"
msgid "Set new password" msgid "Set new password"
msgstr "Set new password" msgstr "Set new password"
#: src/pages/UserPublicProfile.tsx:997 #: src/pages/UserPublicProfile.tsx:970
msgid "Settings" msgid "Settings"
msgstr "Settings" msgstr "Settings"
@@ -934,11 +935,11 @@ msgstr "Settings"
msgid "Something went wrong" msgid "Something went wrong"
msgstr "Something went wrong" msgstr "Something went wrong"
#: src/pages/UserPublicProfile.tsx:1241 #: src/pages/UserPublicProfile.tsx:1217
msgid "Style" msgid "Style"
msgstr "Style" msgstr "Style"
#: src/components/SearchBar.tsx:71 #: src/components/SearchBar.tsx:83
msgid "Submit search" msgid "Submit search"
msgstr "Submit search" msgstr "Submit search"
@@ -950,6 +951,10 @@ msgstr "This invite link is missing, expired, or already used."
msgid "This is a mirage." msgid "This is a mirage."
msgstr "This is a mirage." msgstr "This is a mirage."
#: src/pages/NotFound.tsx:10
msgid "This page does not exist."
msgstr "This page does not exist."
#: src/pages/ResetPassword.tsx:37 #: src/pages/ResetPassword.tsx:37
msgid "This reset link is missing or malformed." msgid "This reset link is missing or malformed."
msgstr "This reset link is missing or malformed." msgstr "This reset link is missing or malformed."
@@ -993,7 +998,7 @@ msgstr "Upvoted"
#. placeholder {0}: votes.items.length #. placeholder {0}: votes.items.length
#. placeholder {1}: votes.hasMore ? "+" : "" #. placeholder {1}: votes.hasMore ? "+" : ""
#: src/pages/UserPublicProfile.tsx:1017 #: src/pages/UserPublicProfile.tsx:993
msgid "Upvoted ({0}{1})" msgid "Upvoted ({0}{1})"
msgstr "Upvoted ({0}{1})" msgstr "Upvoted ({0}{1})"
@@ -1019,11 +1024,11 @@ msgstr "Username"
msgid "Users" msgid "Users"
msgstr "Users" msgstr "Users"
#: src/pages/UserPublicProfile.tsx:1087 #: src/pages/UserPublicProfile.tsx:1063
#: src/pages/UserPublicProfile.tsx:1130 #: src/pages/UserPublicProfile.tsx:1106
#: src/pages/UserPublicProfile.tsx:1172 #: src/pages/UserPublicProfile.tsx:1148
#: src/pages/UserPublicProfile.tsx:1363 #: src/pages/UserPublicProfile.tsx:1339
#: src/pages/UserPublicProfile.tsx:1495 #: src/pages/UserPublicProfile.tsx:1471
msgid "View all →" msgid "View all →"
msgstr "View all →" msgstr "View all →"
@@ -1036,8 +1041,8 @@ msgstr "View dump →"
msgid "What makes it worth it?" msgid "What makes it worth it?"
msgstr "What makes it worth it?" msgstr "What makes it worth it?"
#: src/pages/UserPublicProfile.tsx:899 #: src/pages/UserPublicProfile.tsx:901
#: src/pages/UserPublicProfile.tsx:948 #: src/pages/UserPublicProfile.tsx:950
msgid "Who am I?" msgid "Who am I?"
msgstr "Who am I?" msgstr "Who am I?"
@@ -1058,11 +1063,11 @@ msgstr "Yesterday"
msgid "You'll be notified when someone follows your playlists, upvotes your dumps, or posts new content." msgid "You'll be notified when someone follows your playlists, upvotes your dumps, or posts new content."
msgstr "You'll be notified when someone follows your playlists, upvotes your dumps, or posts new content." msgstr "You'll be notified when someone follows your playlists, upvotes your dumps, or posts new content."
#: src/pages/index/FollowedFeed.tsx:114 #: src/pages/index/FollowedFeed.tsx:115
#: src/pages/index/HotFeed.tsx:69 #: src/pages/index/HotFeed.tsx:69
#: src/pages/index/JournalFeed.tsx:82 #: src/pages/index/JournalFeed.tsx:82
#: src/pages/index/NewFeed.tsx:69 #: src/pages/index/NewFeed.tsx:69
#: src/pages/Search.tsx:249 #: src/pages/Search.tsx:240
#: src/pages/UserDumps.tsx:98 #: src/pages/UserDumps.tsx:98
#: src/pages/UserPlaylists.tsx:422 #: src/pages/UserPlaylists.tsx:422
#: src/pages/UserPlaylists.tsx:457 #: src/pages/UserPlaylists.tsx:457

File diff suppressed because one or more lines are too long

View File

@@ -13,25 +13,13 @@ msgstr ""
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
#: src/components/AppHeader.tsx:71
msgid " Dump"
msgstr " Reco"
#: src/pages/UserDumps.tsx:114
#: src/pages/UserPublicProfile.tsx:1332
msgid " New dump"
msgstr " Nouvelle reco"
#: src/components/NewPlaylistForm.tsx:30
msgid " New playlist"
msgstr " Nouvelle collection"
#: src/components/CommentThread.tsx:176 #: src/components/CommentThread.tsx:176
msgid "[deleted]" msgid "[deleted]"
msgstr "[supprimé]" msgstr "[supprimé]"
#. placeholder {0}: dump.commentCount #. placeholder {0}: dump.commentCount
#: src/components/DumpCard.tsx:95 #: src/components/DumpCard.tsx:95
#: src/components/JournalCard.tsx:78
msgid "{0, plural, one {# comment} other {# comments}}" msgid "{0, plural, one {# comment} other {# comments}}"
msgstr "{0, plural, one {# commentaire} other {# commentaires}}" msgstr "{0, plural, one {# commentaire} other {# commentaires}}"
@@ -83,14 +71,10 @@ msgstr "← Retour à toutes les recos"
msgid "← Back to profile" msgid "← Back to profile"
msgstr "← Retour au profil" msgstr "← Retour au profil"
#: src/pages/UserPublicProfile.tsx:100 #: src/pages/UserPublicProfile.tsx:101
msgid "+ Invite someone" msgid "+ Invite someone"
msgstr "+ Inviter quelqu'un" msgstr "+ Inviter quelqu'un"
#: src/components/AppHeader.tsx:71
msgid "+ New"
msgstr "+ Nouveau"
#: src/pages/UserDumps.tsx:114 #: src/pages/UserDumps.tsx:114
#: src/pages/UserPublicProfile.tsx:1332 #: src/pages/UserPublicProfile.tsx:1332
msgid "+ New dump" msgid "+ New dump"
@@ -152,7 +136,7 @@ msgstr "un commentaire"
msgid "a post" msgid "a post"
msgstr "une publication" msgstr "une publication"
#: src/pages/UserPublicProfile.tsx:1217 #: src/pages/UserPublicProfile.tsx:1193
msgid "Account" msgid "Account"
msgstr "Compte" msgstr "Compte"
@@ -160,7 +144,7 @@ msgstr "Compte"
msgid "Add a comment…" msgid "Add a comment…"
msgstr "Ajouter un commentaire…" msgstr "Ajouter un commentaire…"
#: src/pages/UserPublicProfile.tsx:859 #: src/pages/UserPublicProfile.tsx:860
msgid "Add email…" msgid "Add email…"
msgstr "Ajouter un e-mail…" msgstr "Ajouter un e-mail…"
@@ -181,7 +165,7 @@ msgstr "Toutes les {0, plural, one {# reco votée} other {# recos votées}} char
msgid "Already have an account? <0>Log in</0>" msgid "Already have an account? <0>Log in</0>"
msgstr "Vous avez déjà un compte ? <0>Se connecter</0>" msgstr "Vous avez déjà un compte ? <0>Se connecter</0>"
#: src/pages/UserPublicProfile.tsx:1236 #: src/pages/UserPublicProfile.tsx:1212
msgid "Appearance" msgid "Appearance"
msgstr "Apparence" msgstr "Apparence"
@@ -191,7 +175,7 @@ msgstr "Apparence"
msgid "At least {0} characters" msgid "At least {0} characters"
msgstr "Au moins {0} caractères" msgstr "Au moins {0} caractères"
#: src/pages/UserPublicProfile.tsx:1270 #: src/pages/UserPublicProfile.tsx:1246
msgid "Auto" msgid "Auto"
msgstr "Auto" msgstr "Auto"
@@ -214,8 +198,8 @@ msgstr "Impossible de se connecter au serveur de mises à jour en direct. Les vo
#: src/components/PlaylistCreateForm.tsx:112 #: src/components/PlaylistCreateForm.tsx:112
#: src/pages/DumpEdit.tsx:299 #: src/pages/DumpEdit.tsx:299
#: src/pages/PlaylistDetail.tsx:680 #: src/pages/PlaylistDetail.tsx:680
#: src/pages/UserPublicProfile.tsx:841 #: src/pages/UserPublicProfile.tsx:842
#: src/pages/UserPublicProfile.tsx:919 #: src/pages/UserPublicProfile.tsx:921
msgid "Cancel" msgid "Cancel"
msgstr "Annuler" msgstr "Annuler"
@@ -223,6 +207,10 @@ msgstr "Annuler"
msgid "Cancel removal" msgid "Cancel removal"
msgstr "Annuler la suppression" msgstr "Annuler la suppression"
#: src/components/AppHeader.tsx:62
msgid "Cancel search"
msgstr "Annuler la recherche"
#: src/pages/UserPublicProfile.tsx:772 #: src/pages/UserPublicProfile.tsx:772
msgid "Change avatar" msgid "Change avatar"
msgstr "Changer l'avatar" msgstr "Changer l'avatar"
@@ -232,7 +220,7 @@ msgstr "Changer l'avatar"
msgid "Change password" msgid "Change password"
msgstr "Changer le mot de passe" msgstr "Changer le mot de passe"
#: src/pages/UserPublicProfile.tsx:1229 #: src/pages/UserPublicProfile.tsx:1205
msgid "Change password…" msgid "Change password…"
msgstr "Changer le mot de passe…" msgstr "Changer le mot de passe…"
@@ -245,7 +233,7 @@ msgstr "Vérification de l'invitation…"
msgid "Close" msgid "Close"
msgstr "Fermer" msgstr "Fermer"
#: src/pages/UserPublicProfile.tsx:1262 #: src/pages/UserPublicProfile.tsx:1238
msgid "Color scheme" msgid "Color scheme"
msgstr "Thème de couleur" msgstr "Thème de couleur"
@@ -254,11 +242,11 @@ msgstr "Thème de couleur"
msgid "Confirm new password" msgid "Confirm new password"
msgstr "Confirmer le nouveau mot de passe" msgstr "Confirmer le nouveau mot de passe"
#: src/pages/UserPublicProfile.tsx:91 #: src/pages/UserPublicProfile.tsx:92
msgid "Copied!" msgid "Copied!"
msgstr "Copié !" msgstr "Copié !"
#: src/pages/UserPublicProfile.tsx:91 #: src/pages/UserPublicProfile.tsx:92
msgid "Copy" msgid "Copy"
msgstr "Copier" msgstr "Copier"
@@ -299,7 +287,7 @@ msgstr "Création…"
msgid "Current password" msgid "Current password"
msgstr "Mot de passe actuel" msgstr "Mot de passe actuel"
#: src/pages/UserPublicProfile.tsx:1284 #: src/pages/UserPublicProfile.tsx:1260
msgid "Dark" msgid "Dark"
msgstr "Sombre" msgstr "Sombre"
@@ -351,6 +339,10 @@ msgstr "Déposez un fichier ici"
msgid "Drop a replacement here" msgid "Drop a replacement here"
msgstr "Déposez un fichier de remplacement ici" msgstr "Déposez un fichier de remplacement ici"
#: src/components/AppHeader.tsx:99
msgid "Dump"
msgstr "Reco"
#: src/components/DumpCreateModal.tsx:427 #: src/components/DumpCreateModal.tsx:427
msgid "Dump it" msgid "Dump it"
msgstr "Recommander" msgstr "Recommander"
@@ -361,13 +353,13 @@ msgstr "Recommandé !"
#: src/pages/Search.tsx:172 #: src/pages/Search.tsx:172
#: src/pages/UserDumps.tsx:107 #: src/pages/UserDumps.tsx:107
#: src/pages/UserPublicProfile.tsx:968 #: src/pages/UserPublicProfile.tsx:965
msgid "Dumps" msgid "Dumps"
msgstr "Recos" msgstr "Recos"
#. placeholder {0}: dumps.items.length #. placeholder {0}: dumps.items.length
#. placeholder {1}: dumps.hasMore ? "+" : "" #. placeholder {1}: dumps.hasMore ? "+" : ""
#: src/pages/UserPublicProfile.tsx:1006 #: src/pages/UserPublicProfile.tsx:982
msgid "Dumps ({0}{1})" msgid "Dumps ({0}{1})"
msgstr "Recos ({0}{1})" msgstr "Recos ({0}{1})"
@@ -407,7 +399,7 @@ msgstr "Modification"
msgid "Email address" msgid "Email address"
msgstr "Adresse e-mail" msgstr "Adresse e-mail"
#: src/pages/Search.tsx:207 #: src/pages/Search.tsx:198
msgid "Enter a query to search." msgid "Enter a query to search."
msgstr "Saisissez une recherche." msgstr "Saisissez une recherche."
@@ -420,20 +412,20 @@ msgstr "Impossible de changer le mot de passe"
msgid "Failed to create playlist" msgid "Failed to create playlist"
msgstr "Impossible de créer la collection" msgstr "Impossible de créer la collection"
#: src/pages/UserPublicProfile.tsx:72 #: src/pages/UserPublicProfile.tsx:73
#: src/pages/UserPublicProfile.tsx:75 #: src/pages/UserPublicProfile.tsx:76
#: src/pages/UserPublicProfile.tsx:103 #: src/pages/UserPublicProfile.tsx:104
msgid "Failed to generate invite" msgid "Failed to generate invite"
msgstr "Impossible de générer une invitation" msgstr "Impossible de générer une invitation"
#: src/pages/index/FollowedFeed.tsx:81 #: src/pages/index/FollowedFeed.tsx:82
#: src/pages/index/HotFeed.tsx:36 #: src/pages/index/HotFeed.tsx:36
#: src/pages/index/JournalFeed.tsx:48 #: src/pages/index/JournalFeed.tsx:48
#: src/pages/index/NewFeed.tsx:36 #: src/pages/index/NewFeed.tsx:36
#: src/pages/Notifications.tsx:342 #: src/pages/Notifications.tsx:342
#: src/pages/UserPublicProfile.tsx:1108 #: src/pages/UserPublicProfile.tsx:1084
#: src/pages/UserPublicProfile.tsx:1150 #: src/pages/UserPublicProfile.tsx:1126
#: src/pages/UserPublicProfile.tsx:1195 #: src/pages/UserPublicProfile.tsx:1171
msgid "Failed to load" msgid "Failed to load"
msgstr "Chargement échoué" msgstr "Chargement échoué"
@@ -452,8 +444,8 @@ msgstr "Impossible de publier la réponse"
#: src/pages/PlaylistDetail.tsx:789 #: src/pages/PlaylistDetail.tsx:789
#: src/pages/UserPublicProfile.tsx:680 #: src/pages/UserPublicProfile.tsx:680
#: src/pages/UserPublicProfile.tsx:718 #: src/pages/UserPublicProfile.tsx:718
#: src/pages/UserPublicProfile.tsx:845 #: src/pages/UserPublicProfile.tsx:846
#: src/pages/UserPublicProfile.tsx:922 #: src/pages/UserPublicProfile.tsx:924
msgid "Failed to save" msgid "Failed to save"
msgstr "Enregistrement échoué" msgstr "Enregistrement échoué"
@@ -461,7 +453,7 @@ msgstr "Enregistrement échoué"
msgid "Failed to save edit" msgid "Failed to save edit"
msgstr "Impossible d'enregistrer la modification" msgstr "Impossible d'enregistrer la modification"
#: src/pages/UserPublicProfile.tsx:868 #: src/pages/UserPublicProfile.tsx:869
msgid "Failed to update avatar" msgid "Failed to update avatar"
msgstr "Impossible de mettre à jour l'avatar" msgstr "Impossible de mettre à jour l'avatar"
@@ -495,16 +487,16 @@ msgstr "Suivre {targetUsername}"
msgid "Follow playlist" msgid "Follow playlist"
msgstr "Suivre la collection" msgstr "Suivre la collection"
#: src/pages/index/FollowedFeed.tsx:371 #: src/pages/index/FollowedFeed.tsx:365
msgid "Follow some public playlists to see their dumps here." msgid "Follow some public playlists to see their dumps here."
msgstr "Suivez des collections publiques pour voir leurs recos ici." msgstr "Suivez des collections publiques pour voir leurs recos ici."
#: src/pages/index/FollowedFeed.tsx:357 #: src/pages/index/FollowedFeed.tsx:351
msgid "Follow some users to see their dumps here." msgid "Follow some users to see their dumps here."
msgstr "Suivez des utilisateurs pour voir leurs recos ici." msgstr "Suivez des utilisateurs pour voir leurs recos ici."
#: src/components/FeedTabBar.tsx:48 #: src/components/FeedTabBar.tsx:19
#: src/pages/UserPublicProfile.tsx:982 #: src/pages/UserPublicProfile.tsx:967
msgid "Followed" msgid "Followed"
msgstr "Suivi" msgstr "Suivi"
@@ -514,13 +506,13 @@ msgstr "Suivi"
msgid "Followed ({0}{1})" msgid "Followed ({0}{1})"
msgstr "Suivies ({0}{1})" msgstr "Suivies ({0}{1})"
#: src/pages/UserPublicProfile.tsx:1139 #: src/pages/UserPublicProfile.tsx:1115
msgid "Followed playlists" msgid "Followed playlists"
msgstr "Collections suivies" msgstr "Collections suivies"
#: src/components/FollowButton.tsx:37 #: src/components/FollowButton.tsx:37
#: src/components/FollowButton.tsx:64 #: src/components/FollowButton.tsx:64
#: src/pages/UserPublicProfile.tsx:1097 #: src/pages/UserPublicProfile.tsx:1073
msgid "Following" msgid "Following"
msgstr "Abonné" msgstr "Abonné"
@@ -528,19 +520,23 @@ msgstr "Abonné"
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Mot de passe oublié ?" msgstr "Mot de passe oublié ?"
#: src/pages/index/FollowedFeed.tsx:337 #: src/pages/index/FollowedFeed.tsx:334
msgid "From people" msgid "From people"
msgstr "De personnes" msgstr "De personnes"
#: src/pages/index/FollowedFeed.tsx:344 #: src/pages/index/FollowedFeed.tsx:335
msgid "From playlists" msgid "From playlists"
msgstr "De collections" msgstr "De collections"
#: src/pages/NotFound.tsx:13
msgid "Go home"
msgstr "Accueil"
#: src/pages/ResetPassword.tsx:66 #: src/pages/ResetPassword.tsx:66
msgid "Go to login" msgid "Go to login"
msgstr "Aller à la connexion" msgstr "Aller à la connexion"
#: src/components/FeedTabBar.tsx:26 #: src/components/FeedTabBar.tsx:16
msgid "Hot" msgid "Hot"
msgstr "Tendances" msgstr "Tendances"
@@ -556,16 +552,16 @@ msgstr "Invitation invalide"
msgid "Invalid link" msgid "Invalid link"
msgstr "Lien invalide" msgstr "Lien invalide"
#: src/pages/UserPublicProfile.tsx:790 #: src/pages/UserPublicProfile.tsx:791
msgid "invited by" msgid "invited by"
msgstr "invité par" msgstr "invité par"
#: src/pages/UserPublicProfile.tsx:989 #: src/pages/UserPublicProfile.tsx:968
#: src/pages/UserPublicProfile.tsx:1184 #: src/pages/UserPublicProfile.tsx:1160
msgid "Invitees" msgid "Invitees"
msgstr "Invités" msgstr "Invités"
#: src/components/FeedTabBar.tsx:40 #: src/components/FeedTabBar.tsx:18
msgid "Journal" msgid "Journal"
msgstr "Journal" msgstr "Journal"
@@ -573,7 +569,7 @@ msgstr "Journal"
msgid "just now" msgid "just now"
msgstr "à l'instant" msgstr "à l'instant"
#: src/pages/UserPublicProfile.tsx:1277 #: src/pages/UserPublicProfile.tsx:1253
msgid "Light" msgid "Light"
msgstr "Clair" msgstr "Clair"
@@ -581,7 +577,7 @@ msgstr "Clair"
msgid "Live updates are temporarily disconnected. Trying to reconnect…" msgid "Live updates are temporarily disconnected. Trying to reconnect…"
msgstr "Les mises à jour en direct sont temporairement interrompues. Tentative de reconnexion…" msgstr "Les mises à jour en direct sont temporairement interrompues. Tentative de reconnexion…"
#: src/components/AppHeader.tsx:88 #: src/components/AppHeader.tsx:125
msgid "Live updates unavailable." msgid "Live updates unavailable."
msgstr "Mises à jour en direct indisponibles." msgstr "Mises à jour en direct indisponibles."
@@ -594,11 +590,11 @@ msgstr "Charger plus"
msgid "Loading dump…" msgid "Loading dump…"
msgstr "Chargement de la reco…" msgstr "Chargement de la reco…"
#: src/pages/index/FollowedFeed.tsx:109 #: src/pages/index/FollowedFeed.tsx:110
#: src/pages/index/HotFeed.tsx:64 #: src/pages/index/HotFeed.tsx:64
#: src/pages/index/JournalFeed.tsx:77 #: src/pages/index/JournalFeed.tsx:77
#: src/pages/index/NewFeed.tsx:64 #: src/pages/index/NewFeed.tsx:64
#: src/pages/Search.tsx:244 #: src/pages/Search.tsx:235
#: src/pages/UserDumps.tsx:93 #: src/pages/UserDumps.tsx:93
#: src/pages/UserPlaylists.tsx:417 #: src/pages/UserPlaylists.tsx:417
#: src/pages/UserPlaylists.tsx:452 #: src/pages/UserPlaylists.tsx:452
@@ -616,7 +612,7 @@ msgstr "Chargement du profil…"
#: src/components/PlaylistMembershipPanel.tsx:28 #: src/components/PlaylistMembershipPanel.tsx:28
#: src/components/TextEditor.tsx:289 #: src/components/TextEditor.tsx:289
#: src/pages/index/FollowedFeed.tsx:76 #: src/pages/index/FollowedFeed.tsx:77
#: src/pages/index/HotFeed.tsx:32 #: src/pages/index/HotFeed.tsx:32
#: src/pages/index/JournalFeed.tsx:44 #: src/pages/index/JournalFeed.tsx:44
#: src/pages/index/NewFeed.tsx:32 #: src/pages/index/NewFeed.tsx:32
@@ -624,21 +620,21 @@ msgstr "Chargement du profil…"
#: src/pages/Notifications.tsx:414 #: src/pages/Notifications.tsx:414
#: src/pages/UserDumps.tsx:51 #: src/pages/UserDumps.tsx:51
#: src/pages/UserPlaylists.tsx:342 #: src/pages/UserPlaylists.tsx:342
#: src/pages/UserPublicProfile.tsx:1102 #: src/pages/UserPublicProfile.tsx:1078
#: src/pages/UserPublicProfile.tsx:1144 #: src/pages/UserPublicProfile.tsx:1120
#: src/pages/UserPublicProfile.tsx:1189 #: src/pages/UserPublicProfile.tsx:1165
#: src/pages/UserUpvoted.tsx:123 #: src/pages/UserUpvoted.tsx:123
msgid "Loading…" msgid "Loading…"
msgstr "Chargement…" msgstr "Chargement…"
#: src/components/AppHeader.tsx:78 #: src/components/AppHeader.tsx:106
#: src/pages/UserLogin.tsx:87 #: src/pages/UserLogin.tsx:87
#: src/pages/UserLogin.tsx:117 #: src/pages/UserLogin.tsx:117
msgid "Log in" msgid "Log in"
msgstr "Se connecter" msgstr "Se connecter"
#: src/pages/UserPublicProfile.tsx:749 #: src/pages/UserPublicProfile.tsx:749
#: src/pages/UserPublicProfile.tsx:882 #: src/pages/UserPublicProfile.tsx:883
msgid "Log out" msgid "Log out"
msgstr "Se déconnecter" msgstr "Se déconnecter"
@@ -658,11 +654,13 @@ msgstr "Max 50 Mo"
msgid "new" msgid "new"
msgstr "nouveau" msgstr "nouveau"
#: src/components/FeedTabBar.tsx:33 #: src/components/FeedTabBar.tsx:17
msgid "New" msgid "New"
msgstr "Nouveau" msgstr "Nouveau"
#: src/components/DumpCreateModal.tsx:277 #: src/components/DumpCreateModal.tsx:277
#: src/pages/UserDumps.tsx:114
#: src/pages/UserPublicProfile.tsx:1308
msgid "New dump" msgid "New dump"
msgstr "Nouvelle reco" msgstr "Nouvelle reco"
@@ -671,6 +669,7 @@ msgstr "Nouvelle reco"
msgid "New password" msgid "New password"
msgstr "Nouveau mot de passe" msgstr "Nouveau mot de passe"
#: src/components/NewPlaylistForm.tsx:30
#: src/components/NewPlaylistForm.tsx:34 #: src/components/NewPlaylistForm.tsx:34
msgid "New playlist" msgid "New playlist"
msgstr "Nouvelle collection" msgstr "Nouvelle collection"
@@ -679,7 +678,7 @@ msgstr "Nouvelle collection"
msgid "No dumps in this playlist yet." msgid "No dumps in this playlist yet."
msgstr "Aucune reco dans cette collection pour l'instant." msgstr "Aucune reco dans cette collection pour l'instant."
#: src/pages/Search.tsx:224 #: src/pages/Search.tsx:215
msgid "No dumps match \"{q}\"." msgid "No dumps match \"{q}\"."
msgstr "Aucune reco ne correspond à « {q} »." msgstr "Aucune reco ne correspond à « {q} »."
@@ -694,36 +693,36 @@ msgid "No emoji found."
msgstr "Aucun emoji trouvé." msgstr "Aucun emoji trouvé."
#: src/pages/UserPlaylists.tsx:439 #: src/pages/UserPlaylists.tsx:439
#: src/pages/UserPublicProfile.tsx:1157 #: src/pages/UserPublicProfile.tsx:1133
msgid "No followed playlists yet." msgid "No followed playlists yet."
msgstr "Pas encore de collections suivies." msgstr "Pas encore de collections suivies."
#: src/pages/UserPublicProfile.tsx:1202 #: src/pages/UserPublicProfile.tsx:1178
msgid "No invitees yet." msgid "No invitees yet."
msgstr "Aucun invité pour le moment." msgstr "Aucun invité pour le moment."
#: src/pages/Search.tsx:283 #: src/pages/Search.tsx:274
msgid "No playlists match \"{q}\"." msgid "No playlists match \"{q}\"."
msgstr "Aucune collection ne correspond à « {q} »." msgstr "Aucune collection ne correspond à « {q} »."
#: src/components/PlaylistMembershipPanel.tsx:34 #: src/components/PlaylistMembershipPanel.tsx:34
#: src/pages/UserPlaylists.tsx:397 #: src/pages/UserPlaylists.tsx:397
#: src/pages/UserPublicProfile.tsx:1068 #: src/pages/UserPublicProfile.tsx:1044
msgid "No playlists yet." msgid "No playlists yet."
msgstr "Pas encore de collections." msgstr "Pas encore de collections."
#: src/pages/Search.tsx:257 #: src/pages/Search.tsx:248
msgid "No users match \"{q}\"." msgid "No users match \"{q}\"."
msgstr "Aucun utilisateur ne correspond à « {q} »." msgstr "Aucun utilisateur ne correspond à « {q} »."
#: src/pages/UserPublicProfile.tsx:1115 #: src/pages/UserPublicProfile.tsx:1091
msgid "Not following anyone yet." msgid "Not following anyone yet."
msgstr "Aucun abonnement pour le moment." msgstr "Aucun abonnement pour le moment."
#: src/pages/Notifications.tsx:349 #: src/pages/Notifications.tsx:349
#: src/pages/UserDumps.tsx:123 #: src/pages/UserDumps.tsx:123
#: src/pages/UserPublicProfile.tsx:1342 #: src/pages/UserPublicProfile.tsx:1318
#: src/pages/UserPublicProfile.tsx:1465 #: src/pages/UserPublicProfile.tsx:1441
#: src/pages/UserUpvoted.tsx:195 #: src/pages/UserUpvoted.tsx:195
msgid "Nothing here yet." msgid "Nothing here yet."
msgstr "Rien ici pour l'instant." msgstr "Rien ici pour l'instant."
@@ -737,7 +736,7 @@ msgstr "Notifications"
msgid "Notifications ({unreadNotificationCount} unread)" msgid "Notifications ({unreadNotificationCount} unread)"
msgstr "Notifications ({unreadNotificationCount} non lues)" msgstr "Notifications ({unreadNotificationCount} non lues)"
#: src/components/SearchBar.tsx:71 #: src/components/SearchBar.tsx:83
msgid "Open search" msgid "Open search"
msgstr "Ouvrir la recherche" msgstr "Ouvrir la recherche"
@@ -746,7 +745,7 @@ msgid "or <0>browse files</0>"
msgstr "ou <0>parcourir les fichiers</0>" msgstr "ou <0>parcourir les fichiers</0>"
#: src/pages/UserLogin.tsx:106 #: src/pages/UserLogin.tsx:106
#: src/pages/UserPublicProfile.tsx:1222 #: src/pages/UserPublicProfile.tsx:1198
msgid "Password" msgid "Password"
msgstr "Mot de passe" msgstr "Mot de passe"
@@ -768,17 +767,17 @@ msgstr "Mot de passe mis à jour"
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Les mots de passe ne correspondent pas" msgstr "Les mots de passe ne correspondent pas"
#: src/components/AppHeader.tsx:54 #: src/components/AppHeader.tsx:82
#: src/components/UserMenu.tsx:62 #: src/components/UserMenu.tsx:62
#: src/pages/Search.tsx:175 #: src/pages/Search.tsx:175
#: src/pages/UserPlaylists.tsx:368 #: src/pages/UserPlaylists.tsx:368
#: src/pages/UserPublicProfile.tsx:975 #: src/pages/UserPublicProfile.tsx:966
msgid "Playlists" msgid "Playlists"
msgstr "Collections" msgstr "Collections"
#. placeholder {0}: playlists.items.length #. placeholder {0}: playlists.items.length
#. placeholder {1}: playlists.hasMore ? "+" : "" #. placeholder {1}: playlists.hasMore ? "+" : ""
#: src/pages/UserPublicProfile.tsx:1037 #: src/pages/UserPublicProfile.tsx:1013
msgid "Playlists ({0}{1})" msgid "Playlists ({0}{1})"
msgstr "Collections ({0}{1})" msgstr "Collections ({0}{1})"
@@ -800,6 +799,7 @@ msgid "Posting…"
msgstr "Publication…" msgstr "Publication…"
#: src/components/DumpCard.tsx:104 #: src/components/DumpCard.tsx:104
#: src/components/JournalCard.tsx:87
#: src/components/PlaylistCard.tsx:73 #: src/components/PlaylistCard.tsx:73
#: src/components/PlaylistMembershipPanel.tsx:55 #: src/components/PlaylistMembershipPanel.tsx:55
#: src/pages/Dump.tsx:287 #: src/pages/Dump.tsx:287
@@ -879,8 +879,8 @@ msgstr "Réessayer"
#: src/components/CommentThread.tsx:270 #: src/components/CommentThread.tsx:270
#: src/pages/DumpEdit.tsx:306 #: src/pages/DumpEdit.tsx:306
#: src/pages/PlaylistDetail.tsx:673 #: src/pages/PlaylistDetail.tsx:673
#: src/pages/UserPublicProfile.tsx:833 #: src/pages/UserPublicProfile.tsx:834
#: src/pages/UserPublicProfile.tsx:911 #: src/pages/UserPublicProfile.tsx:913
msgid "Save" msgid "Save"
msgstr "Enregistrer" msgstr "Enregistrer"
@@ -888,24 +888,25 @@ msgstr "Enregistrer"
#: src/components/CommentThread.tsx:269 #: src/components/CommentThread.tsx:269
#: src/pages/PlaylistDetail.tsx:673 #: src/pages/PlaylistDetail.tsx:673
#: src/pages/ResetPassword.tsx:152 #: src/pages/ResetPassword.tsx:152
#: src/pages/UserPublicProfile.tsx:832 #: src/pages/UserPublicProfile.tsx:833
#: src/pages/UserPublicProfile.tsx:911 #: src/pages/UserPublicProfile.tsx:913
msgid "Saving…" msgid "Saving…"
msgstr "Enregistrement…" msgstr "Enregistrement…"
#: src/components/SearchBar.tsx:65 #: src/components/AppHeader.tsx:62
#: src/components/SearchBar.tsx:77
msgid "Search" msgid "Search"
msgstr "Rechercher" msgstr "Rechercher"
#: src/components/SearchBar.tsx:61 #: src/components/SearchBar.tsx:73
msgid "Search dumps, users, playlists…" msgid "Search dumps, users, playlists…"
msgstr "Rechercher des recos, utilisateurs, collections…" msgstr "Rechercher des recos, utilisateurs, collections…"
#: src/pages/Search.tsx:218 #: src/pages/Search.tsx:209
msgid "Search failed" msgid "Search failed"
msgstr "Recherche échouée" msgstr "Recherche échouée"
#: src/pages/Search.tsx:213 #: src/pages/Search.tsx:204
msgid "Searching…" msgid "Searching…"
msgstr "Recherche…" msgstr "Recherche…"
@@ -917,7 +918,7 @@ msgstr "Envoyer le lien de réinitialisation"
msgid "Sending…" msgid "Sending…"
msgstr "Envoi…" msgstr "Envoi…"
#: src/components/AppHeader.tsx:69 #: src/components/AppHeader.tsx:97
msgid "Server unreachable" msgid "Server unreachable"
msgstr "Serveur inaccessible" msgstr "Serveur inaccessible"
@@ -926,7 +927,7 @@ msgstr "Serveur inaccessible"
msgid "Set new password" msgid "Set new password"
msgstr "Définir un nouveau mot de passe" msgstr "Définir un nouveau mot de passe"
#: src/pages/UserPublicProfile.tsx:997 #: src/pages/UserPublicProfile.tsx:970
msgid "Settings" msgid "Settings"
msgstr "Paramètres" msgstr "Paramètres"
@@ -934,11 +935,11 @@ msgstr "Paramètres"
msgid "Something went wrong" msgid "Something went wrong"
msgstr "Une erreur est survenue" msgstr "Une erreur est survenue"
#: src/pages/UserPublicProfile.tsx:1241 #: src/pages/UserPublicProfile.tsx:1217
msgid "Style" msgid "Style"
msgstr "Style" msgstr "Style"
#: src/components/SearchBar.tsx:71 #: src/components/SearchBar.tsx:83
msgid "Submit search" msgid "Submit search"
msgstr "Lancer la recherche" msgstr "Lancer la recherche"
@@ -950,6 +951,10 @@ msgstr "Ce lien d'invitation est manquant, expiré ou déjà utilisé."
msgid "This is a mirage." msgid "This is a mirage."
msgstr "C'est un mirage." msgstr "C'est un mirage."
#: src/pages/NotFound.tsx:10
msgid "This page does not exist."
msgstr "Rien à voir, circulez."
#: src/pages/ResetPassword.tsx:37 #: src/pages/ResetPassword.tsx:37
msgid "This reset link is missing or malformed." msgid "This reset link is missing or malformed."
msgstr "Ce lien de réinitialisation est absent ou malformé." msgstr "Ce lien de réinitialisation est absent ou malformé."
@@ -993,7 +998,7 @@ msgstr "Voté"
#. placeholder {0}: votes.items.length #. placeholder {0}: votes.items.length
#. placeholder {1}: votes.hasMore ? "+" : "" #. placeholder {1}: votes.hasMore ? "+" : ""
#: src/pages/UserPublicProfile.tsx:1017 #: src/pages/UserPublicProfile.tsx:993
msgid "Upvoted ({0}{1})" msgid "Upvoted ({0}{1})"
msgstr "Votés ({0}{1})" msgstr "Votés ({0}{1})"
@@ -1019,11 +1024,11 @@ msgstr "Nom d'utilisateur"
msgid "Users" msgid "Users"
msgstr "Utilisateurs" msgstr "Utilisateurs"
#: src/pages/UserPublicProfile.tsx:1087 #: src/pages/UserPublicProfile.tsx:1063
#: src/pages/UserPublicProfile.tsx:1130 #: src/pages/UserPublicProfile.tsx:1106
#: src/pages/UserPublicProfile.tsx:1172 #: src/pages/UserPublicProfile.tsx:1148
#: src/pages/UserPublicProfile.tsx:1363 #: src/pages/UserPublicProfile.tsx:1339
#: src/pages/UserPublicProfile.tsx:1495 #: src/pages/UserPublicProfile.tsx:1471
msgid "View all →" msgid "View all →"
msgstr "Tout voir →" msgstr "Tout voir →"
@@ -1036,8 +1041,8 @@ msgstr "Voir la reco →"
msgid "What makes it worth it?" msgid "What makes it worth it?"
msgstr "Pourquoi on en voudrait ?" msgstr "Pourquoi on en voudrait ?"
#: src/pages/UserPublicProfile.tsx:899 #: src/pages/UserPublicProfile.tsx:901
#: src/pages/UserPublicProfile.tsx:948 #: src/pages/UserPublicProfile.tsx:950
msgid "Who am I?" msgid "Who am I?"
msgstr "Qui suis-je ?" msgstr "Qui suis-je ?"
@@ -1058,11 +1063,11 @@ msgstr "Hier"
msgid "You'll be notified when someone follows your playlists, upvotes your dumps, or posts new content." msgid "You'll be notified when someone follows your playlists, upvotes your dumps, or posts new content."
msgstr "Vous serez notifié lorsque quelqu'un suit vos collections, vote pour vos recos ou publie du nouveau contenu." msgstr "Vous serez notifié lorsque quelqu'un suit vos collections, vote pour vos recos ou publie du nouveau contenu."
#: src/pages/index/FollowedFeed.tsx:114 #: src/pages/index/FollowedFeed.tsx:115
#: src/pages/index/HotFeed.tsx:69 #: src/pages/index/HotFeed.tsx:69
#: src/pages/index/JournalFeed.tsx:82 #: src/pages/index/JournalFeed.tsx:82
#: src/pages/index/NewFeed.tsx:69 #: src/pages/index/NewFeed.tsx:69
#: src/pages/Search.tsx:249 #: src/pages/Search.tsx:240
#: src/pages/UserDumps.tsx:98 #: src/pages/UserDumps.tsx:98
#: src/pages/UserPlaylists.tsx:422 #: src/pages/UserPlaylists.tsx:422
#: src/pages/UserPlaylists.tsx:457 #: src/pages/UserPlaylists.tsx:457

View File

@@ -252,42 +252,42 @@ export function Dump() {
)} )}
</div> </div>
<div className="dump-header-right"> <div className="dump-header-right">
<h1 className="dump-title">{dump.title}</h1> <h1 className="dump-title">{dump.title}</h1>
<div className="dump-op"> <div className="dump-op">
<Avatar <Avatar
userId={dump.userId} userId={dump.userId}
username={op?.username ?? "?"} username={op?.username ?? "?"}
hasAvatar={!!op?.avatarMime} hasAvatar={!!op?.avatarMime}
size={22} size={22}
/> />
{op {op
? ( ? (
<Link to={`/users/${op.username}`} className="dump-op-link"> <Link to={`/users/${op.username}`} className="dump-op-link">
{op.username} {op.username}
</Link> </Link>
) )
: <span className="dump-op-link"></span>} : <span className="dump-op-link"></span>}
<Tooltip text={dump.createdAt.toLocaleString()}> <Tooltip text={dump.createdAt.toLocaleString()}>
<time <time
className="dump-card-date" className="dump-card-date"
dateTime={dump.createdAt.toISOString()} dateTime={dump.createdAt.toISOString()}
> >
{relativeTime(dump.createdAt)} {relativeTime(dump.createdAt)}
</time> </time>
</Tooltip>
{dump.updatedAt && (
<Tooltip text={t`Edited ${dump.updatedAt.toLocaleString()}`}>
<span className="dump-edited-label">
<Trans>edited {relativeTime(dump.updatedAt)}</Trans>
</span>
</Tooltip> </Tooltip>
)} {dump.updatedAt && (
{dump.isPrivate && ( <Tooltip text={t`Edited ${dump.updatedAt.toLocaleString()}`}>
<span className="dump-card-private-badge"> <span className="dump-edited-label">
<Trans>private</Trans> <Trans>edited {relativeTime(dump.updatedAt)}</Trans>
</span> </span>
)} </Tooltip>
</div> )}
{dump.isPrivate && (
<span className="dump-card-private-badge">
<Trans>private</Trans>
</span>
)}
</div>
</div> </div>
</div> </div>

View File

@@ -9,7 +9,6 @@ import {
import { useLocation } from "react-router"; import { useLocation } from "react-router";
import { AppHeader } from "../components/AppHeader.tsx"; import { AppHeader } from "../components/AppHeader.tsx";
import { SearchBar } from "../components/SearchBar.tsx";
import { PresenceRow } from "../components/PresenceRow.tsx"; import { PresenceRow } from "../components/PresenceRow.tsx";
import { FeedTabBar } from "../components/FeedTabBar.tsx"; import { FeedTabBar } from "../components/FeedTabBar.tsx";
import { type FeedTab, VALID_TABS } from "../config/feedTabs.ts"; import { type FeedTab, VALID_TABS } from "../config/feedTabs.ts";
@@ -249,7 +248,6 @@ export function Index() {
<div className="header-center-slot"> <div className="header-center-slot">
<PresenceRow /> <PresenceRow />
<FeedTabBar /> <FeedTabBar />
<SearchBar collapsible />
</div> </div>
} }
disableNew={dumpsState.status === "error"} disableNew={dumpsState.status === "error"}

17
src/pages/NotFound.tsx Normal file
View File

@@ -0,0 +1,17 @@
import { Link } from "react-router";
import { Trans } from "@lingui/react/macro";
import { PageShell } from "../components/PageShell.tsx";
export function NotFound() {
return (
<PageShell centered>
<h1>404</h1>
<p>
<Trans>This page does not exist.</Trans>
</p>
<Link to="/" className="btn-primary">
<Trans>Go home</Trans>
</Link>
</PageShell>
);
}

View File

@@ -3,7 +3,7 @@ import { Link, useSearchParams } from "react-router";
import { t } from "@lingui/core/macro"; import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro"; import { Trans } from "@lingui/react/macro";
import { AppHeader } from "../components/AppHeader.tsx"; import { AppHeader } from "../components/AppHeader.tsx";
import { SearchBar } from "../components/SearchBar.tsx"; import { TabBar } from "../components/TabBar.tsx";
import { DumpCard } from "../components/DumpCard.tsx"; import { DumpCard } from "../components/DumpCard.tsx";
import { PlaylistCard } from "../components/PlaylistCard.tsx"; import { PlaylistCard } from "../components/PlaylistCard.tsx";
import { ErrorCard } from "../components/ErrorCard.tsx"; import { ErrorCard } from "../components/ErrorCard.tsx";
@@ -178,28 +178,26 @@ export function Search() {
return ( return (
<div className="page-shell"> <div className="page-shell">
<AppHeader centerSlot={<SearchBar />} /> <AppHeader />
<main className="search-page"> <main className="search-page">
{q && ( {q && (
<div className="search-tabs"> <TabBar
{(["dumps", "users", "playlists"] as Tab[]).map((tabKey) => ( tabs={(["dumps", "users", "playlists"] as Tab[]).map((key) => ({
<button key,
key={tabKey} label: tabLabel(
type="button" key,
className={`feed-sort-btn${tab === tabKey ? " active" : ""}`} key === "dumps"
onClick={() => setTab(tabKey)} ? dumpCount
> : key === "users"
{tabLabel( ? userCount
tabKey, : playlistCount,
tabKey === "dumps" ),
? dumpCount }))}
: tabKey === "users" activeTab={tab}
? userCount onChange={setTab}
: playlistCount, className="search-tabs"
)} innerClassName="search-tabs-inner"
</button> />
))}
</div>
)} )}
{state.status === "idle" && ( {state.status === "idle" && (

View File

@@ -111,7 +111,9 @@ export function UserDumps() {
className="new-playlist-toggle" className="new-playlist-toggle"
onClick={() => setCreateModalOpen(true)} onClick={() => setCreateModalOpen(true)}
> >
+<span className="btn-new-label"><Trans> New dump</Trans></span> +<span className="btn-new-label">
<Trans>New dump</Trans>
</span>
</button> </button>
)} )}
/> />

View File

@@ -53,6 +53,7 @@ import { friendlyFetchError } from "../utils/apiError.ts";
import { TextEditor } from "../components/TextEditor.tsx"; import { TextEditor } from "../components/TextEditor.tsx";
import { Markdown } from "../components/Markdown.tsx"; import { Markdown } from "../components/Markdown.tsx";
import { ChangePasswordModal } from "../components/ChangePasswordModal.tsx"; import { ChangePasswordModal } from "../components/ChangePasswordModal.tsx";
import { TabBar } from "../components/TabBar.tsx";
function InviteButton() { function InviteButton() {
const { authFetch } = useAuth(); const { authFetch } = useAuth();
@@ -145,6 +146,7 @@ type InviteTreeState =
| { status: "loaded"; entries: InviteTreeEntry[] }; | { status: "loaded"; entries: InviteTreeEntry[] };
type InviteTreeNode = InviteTreeEntry & { children: InviteTreeNode[] }; type InviteTreeNode = InviteTreeEntry & { children: InviteTreeNode[] };
type ProfileTab = "dumps" | "playlists" | "followed" | "invitees" | "settings";
function buildInviteTree( function buildInviteTree(
entries: InviteTreeEntry[], entries: InviteTreeEntry[],
@@ -282,9 +284,7 @@ export function UserPublicProfile() {
const [emailError, setEmailError] = useState<string | null>(null); const [emailError, setEmailError] = useState<string | null>(null);
const prevMyVotesRef = useRef<Set<string> | null>(null); const prevMyVotesRef = useRef<Set<string> | null>(null);
const [tab, setTab] = useState< const [tab, setTab] = useState<ProfileTab>("dumps");
"dumps" | "playlists" | "followed" | "invitees" | "settings"
>("dumps");
const [changePasswordOpen, setChangePasswordOpen] = useState(false); const [changePasswordOpen, setChangePasswordOpen] = useState(false);
const [followedState, setFollowedState] = useState<FollowedState>(null); const [followedState, setFollowedState] = useState<FollowedState>(null);
const [inviteTreeState, setInviteTreeState] = useState<InviteTreeState>(null); const [inviteTreeState, setInviteTreeState] = useState<InviteTreeState>(null);
@@ -782,107 +782,112 @@ export function UserPublicProfile() {
</label> </label>
)} )}
</div> </div>
<div> <div className="profile-info">
<h1 className="profile-username">{profileUser.username}</h1> <div className="profile-info-scroll">
{profileUser.invitedByUsername <h1 className="profile-username">{profileUser.username}</h1>
? ( {profileUser.invitedByUsername
<p className="profile-invited-by">
<Trans>invited by</Trans>{" "}
<Link
to={`/users/${profileUser.invitedByUsername}`}
className="profile-invited-by-link"
>
@{profileUser.invitedByUsername}
</Link>
</p>
)
: (
<p className="profile-invited-by profile-invited-by--founding">
O.G.
</p>
)}
{isOwnProfile && (
emailEditing
? ( ? (
<form <p className="profile-invited-by">
className="profile-email-editor" <Trans>invited by</Trans>{" "}
onSubmit={(e) => { <Link
e.preventDefault(); to={`/users/${profileUser.invitedByUsername}`}
handleEmailSave(); className="profile-invited-by-link"
}} >
> @{profileUser.invitedByUsername}
<input </Link>
type="email"
className="profile-email-input"
value={emailDraft}
onChange={(e) => setEmailDraft(e.currentTarget.value)}
onKeyDown={(e) => {
if (e.key === "Escape") setEmailEditing(false);
}}
disabled={emailSaving}
autoFocus
/>
<div className="profile-email-actions">
<button
type="submit"
className="profile-email-btn profile-email-btn--save"
disabled={emailSaving || !emailDraft.trim()}
>
{emailSaving
? <Trans>Saving</Trans>
: <Trans>Save</Trans>}
</button>
<button
type="button"
className="profile-email-btn profile-email-btn--cancel"
onClick={() => setEmailEditing(false)}
disabled={emailSaving}
>
<Trans>Cancel</Trans>
</button>
</div>
{emailError && (
<ErrorCard title={t`Failed to save`} message={emailError} />
)}
</form>
)
: (
<p
className="profile-email-display"
onClick={() => {
setEmailDraft(me?.email ?? "");
setEmailError(null);
setEmailEditing(true);
}}
title="Edit email"
>
{me?.email ?? t`Add email…`}
<span className="profile-description-edit-btn" aria-hidden>
</span>
</p> </p>
) )
)} : (
{avatarError && ( <p className="profile-invited-by profile-invited-by--founding">
<ErrorCard O.G.
title={t`Failed to update avatar`} </p>
message={avatarError} )}
/> {isOwnProfile && (
)} emailEditing
{!isOwnProfile && ( ? (
<FollowUserButton <form
targetUserId={profileUser.id} className="profile-email-editor"
targetUsername={profileUser.username} onSubmit={(e) => {
/> e.preventDefault();
)} handleEmailSave();
{isOwnProfile && ( }}
<div className="profile-own-actions"> >
<InviteButton /> <input
<button type="button" className="btn-border" onClick={logout}> type="email"
<Trans>Log out</Trans> className="profile-email-input"
</button> value={emailDraft}
</div> onChange={(e) => setEmailDraft(e.currentTarget.value)}
)} onKeyDown={(e) => {
if (e.key === "Escape") setEmailEditing(false);
}}
disabled={emailSaving}
autoFocus
/>
<div className="profile-email-actions">
<button
type="submit"
className="profile-email-btn profile-email-btn--save"
disabled={emailSaving || !emailDraft.trim()}
>
{emailSaving
? <Trans>Saving</Trans>
: <Trans>Save</Trans>}
</button>
<button
type="button"
className="profile-email-btn profile-email-btn--cancel"
onClick={() => setEmailEditing(false)}
disabled={emailSaving}
>
<Trans>Cancel</Trans>
</button>
</div>
{emailError && (
<ErrorCard
title={t`Failed to save`}
message={emailError}
/>
)}
</form>
)
: (
<p
className="profile-email-display"
onClick={() => {
setEmailDraft(me?.email ?? "");
setEmailError(null);
setEmailEditing(true);
}}
title="Edit email"
>
{me?.email ?? t`Add email…`}
<span className="profile-description-edit-btn" aria-hidden>
</span>
</p>
)
)}
{avatarError && (
<ErrorCard
title={t`Failed to update avatar`}
message={avatarError}
/>
)}
{!isOwnProfile && (
<FollowUserButton
targetUserId={profileUser.id}
targetUsername={profileUser.username}
/>
)}
{isOwnProfile && (
<div className="profile-own-actions">
<InviteButton />
<button type="button" className="btn-border" onClick={logout}>
<Trans>Log out</Trans>
</button>
</div>
)}
</div>
</div> </div>
</div> </div>
@@ -958,47 +963,21 @@ export function UserPublicProfile() {
</div> </div>
)} )}
<div className="feed-sort-scroller profile-tabs-scroller"> <TabBar
<div className="profile-tabs feed-sort"> tabs={[
<button { key: "dumps", label: <Trans>Dumps</Trans> },
type="button" { key: "playlists", label: <Trans>Playlists</Trans> },
className={`feed-sort-btn${tab === "dumps" ? " active" : ""}`} { key: "followed", label: <Trans>Followed</Trans> },
onClick={() => setTab("dumps")} { key: "invitees", label: <Trans>Invitees</Trans> },
> ...(isOwnProfile
<Trans>Dumps</Trans> ? [{ key: "settings" as const, label: <Trans>Settings</Trans> }]
</button> : []),
<button ]}
type="button" activeTab={tab}
className={`feed-sort-btn${tab === "playlists" ? " active" : ""}`} onChange={(key) => setTab(key)}
onClick={() => setTab("playlists")} className="profile-tabs-scroller"
> innerClassName="profile-tabs"
<Trans>Playlists</Trans> />
</button>
<button
type="button"
className={`feed-sort-btn${tab === "followed" ? " active" : ""}`}
onClick={() => setTab("followed")}
>
<Trans>Followed</Trans>
</button>
<button
type="button"
className={`feed-sort-btn${tab === "invitees" ? " active" : ""}`}
onClick={() => setTab("invitees")}
>
<Trans>Invitees</Trans>
</button>
{isOwnProfile && (
<button
type="button"
className={`feed-sort-btn${tab === "settings" ? " active" : ""}`}
onClick={() => setTab("settings")}
>
<Trans>Settings</Trans>
</button>
)}
</div>
</div>
{tab === "dumps" && ( {tab === "dumps" && (
<div className="profile-columns"> <div className="profile-columns">
@@ -1329,7 +1308,9 @@ function DumpList(
className="new-playlist-toggle" className="new-playlist-toggle"
onClick={() => setCreateModalOpen(true)} onClick={() => setCreateModalOpen(true)}
> >
+<span className="btn-new-label"><Trans> New dump</Trans></span> +<span className="btn-new-label">
<Trans>New dump</Trans>
</span>
</button> </button>
)} )}
</div> </div>

View File

@@ -1,4 +1,5 @@
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { TabBar } from "../../components/TabBar.tsx";
import { t } from "@lingui/core/macro"; import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro"; import { Trans } from "@lingui/react/macro";
import { DumpCard } from "../../components/DumpCard.tsx"; import { DumpCard } from "../../components/DumpCard.tsx";
@@ -328,22 +329,15 @@ export function FollowedFeed({
return ( return (
<div className="followed-feed"> <div className="followed-feed">
<div className="feed-sort followed-sub-nav"> <TabBar
<button tabs={[
type="button" { key: "users", label: <Trans>From people</Trans> },
className={`feed-sort-btn${section === "users" ? " active" : ""}`} { key: "playlists", label: <Trans>From playlists</Trans> },
onClick={() => setSection("users")} ]}
> activeTab={section}
<Trans>From people</Trans> onChange={setSection}
</button> innerClassName="followed-sub-nav"
<button />
type="button"
className={`feed-sort-btn${section === "playlists" ? " active" : ""}`}
onClick={() => setSection("playlists")}
>
<Trans>From playlists</Trans>
</button>
</div>
{section === "users" && ( {section === "users" && (
<FollowedSubFeed <FollowedSubFeed

View File

@@ -174,13 +174,19 @@
[data-style="brutalist"] .audio-player-btn, [data-style="brutalist"] .audio-player-btn,
[data-style="brutalist"] .audio-player-vol-btn, [data-style="brutalist"] .audio-player-vol-btn,
[data-style="brutalist"] .rich-content-thumbnail-btn, [data-style="brutalist"] .rich-content-thumbnail-btn,
[data-style="brutalist"] .user-menu-trigger { [data-style="brutalist"] .user-menu-trigger,
[data-style="brutalist"] .nav-search-btn,
[data-style="brutalist"] .auth-link-btn {
border: none; border: none;
box-shadow: none; box-shadow: none;
} }
[data-style="brutalist"] .user-menu-trigger:hover:not(:disabled), [data-style="brutalist"] .user-menu-trigger:hover:not(:disabled),
[data-style="brutalist"] .user-menu-trigger:active { [data-style="brutalist"] .user-menu-trigger:active,
[data-style="brutalist"] .nav-search-btn:hover:not(:disabled),
[data-style="brutalist"] .nav-search-btn:active,
[data-style="brutalist"] .auth-link-btn:hover:not(:disabled),
[data-style="brutalist"] .auth-link-btn:active {
transform: none; transform: none;
box-shadow: none; box-shadow: none;
} }