v3: lots of small UI tweaks and fixes
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 46s
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 46s
This commit is contained in:
89
src/App.css
89
src/App.css
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -106,7 +106,6 @@ export function DumpCard(
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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 && (
|
||||||
|
|||||||
@@ -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" : ""}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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
35
src/components/TabBar.tsx
Normal 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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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
17
src/pages/NotFound.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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" && (
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user