v3: consistent tabs url across app, maxy small fixes
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 42s
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 42s
This commit is contained in:
@@ -73,7 +73,8 @@ async function loadIndexHtml(): Promise<string | null> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DUMP_RE = /^\/dumps\/([^/]+)$/;
|
const DUMP_RE = /^\/dumps\/([^/]+)$/;
|
||||||
const USER_RE = /^\/users\/([^/]+)$/;
|
// Allow an optional `/~/<tab>` suffix so linkable profile tabs keep OG meta.
|
||||||
|
const USER_RE = /^\/users\/([^/]+)(?:\/~\/[^/]+)?$/;
|
||||||
const PLAYLIST_RE = /^\/playlists\/([^/]+)$/;
|
const PLAYLIST_RE = /^\/playlists\/([^/]+)$/;
|
||||||
|
|
||||||
export async function ogMiddleware(ctx: Context, next: Next) {
|
export async function ogMiddleware(ctx: Context, next: Next) {
|
||||||
|
|||||||
@@ -83,6 +83,20 @@ export interface DumpRow {
|
|||||||
[key: string]: SQLOutputValue; // Index signature
|
[key: string]: SQLOutputValue; // Index signature
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shared `dumps` column list — keep in sync with DumpRow/isDumpRow above.
|
||||||
|
// Services import these instead of repeating the column list themselves.
|
||||||
|
export const DUMP_COLUMNS =
|
||||||
|
"id, kind, title, slug, comment, user_id, created_at, updated_at, url, rich_content, file_name, file_mime, file_size, vote_count, is_private, custom_thumbnail_mime";
|
||||||
|
|
||||||
|
export const DUMP_COLUMNS_ALIASED =
|
||||||
|
"d.id, d.kind, d.title, d.slug, d.comment, d.user_id, d.created_at, d.updated_at, d.url, d.rich_content, d.file_name, d.file_mime, d.file_size, d.vote_count, d.is_private, d.custom_thumbnail_mime";
|
||||||
|
|
||||||
|
export const DUMP_SELECT_COLUMNS = `${DUMP_COLUMNS},
|
||||||
|
(SELECT COUNT(*) FROM comments WHERE dump_id = dumps.id AND deleted = 0) as comment_count`;
|
||||||
|
|
||||||
|
export const DUMP_SELECT_COLUMNS_ALIASED = `${DUMP_COLUMNS_ALIASED},
|
||||||
|
(SELECT COUNT(*) FROM comments WHERE dump_id = d.id AND deleted = 0) as comment_count`;
|
||||||
|
|
||||||
export interface UserRow {
|
export interface UserRow {
|
||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { type Dump } from "../model/interfaces.ts";
|
import { type Dump } from "../model/interfaces.ts";
|
||||||
import { db, dumpRowToApi, isDumpRow } from "../model/db.ts";
|
import {
|
||||||
|
db,
|
||||||
|
DUMP_SELECT_COLUMNS as SELECT_COLS,
|
||||||
|
dumpRowToApi,
|
||||||
|
isDumpRow,
|
||||||
|
} from "../model/db.ts";
|
||||||
import { UUID_RE } from "../lib/slugify.ts";
|
import { UUID_RE } from "../lib/slugify.ts";
|
||||||
|
|
||||||
// Mirrors dump-service SELECT_COLS — kept local to avoid a circular import
|
|
||||||
// (dump-service.ts must import relinkBacklinks from this file).
|
|
||||||
const SELECT_COLS =
|
|
||||||
"id, kind, title, slug, comment, user_id, created_at, updated_at, url, rich_content, file_name, file_mime, file_size, vote_count, is_private, custom_thumbnail_mime," +
|
|
||||||
" (SELECT COUNT(*) FROM comments WHERE dump_id = dumps.id AND deleted = 0) as comment_count";
|
|
||||||
|
|
||||||
// Matches http(s) URLs in free text — stops at whitespace or markdown-link
|
// Matches http(s) URLs in free text — stops at whitespace or markdown-link
|
||||||
// delimiters (so `[text](url)` and `<url>` don't swallow the closing char).
|
// delimiters (so `[text](url)` and `<url>` don't swallow the closing char).
|
||||||
const URL_RE = /https?:\/\/[^\s)\]<>"']+/g;
|
const URL_RE = /https?:\/\/[^\s)\]<>"']+/g;
|
||||||
|
|||||||
@@ -5,7 +5,14 @@ import {
|
|||||||
type Dump,
|
type Dump,
|
||||||
type UpdateDumpRequest,
|
type UpdateDumpRequest,
|
||||||
} from "../model/interfaces.ts";
|
} from "../model/interfaces.ts";
|
||||||
import { db, dumpApiToRow, dumpRowToApi, isDumpRow } from "../model/db.ts";
|
import {
|
||||||
|
db,
|
||||||
|
DUMP_SELECT_COLUMNS as SELECT_COLS,
|
||||||
|
DUMP_SELECT_COLUMNS_ALIASED as SELECT_COLS_ALIASED,
|
||||||
|
dumpApiToRow,
|
||||||
|
dumpRowToApi,
|
||||||
|
isDumpRow,
|
||||||
|
} from "../model/db.ts";
|
||||||
import { fetchRichContent, isValidHttpUrl } from "./rich-content-service.ts";
|
import { fetchRichContent, isValidHttpUrl } from "./rich-content-service.ts";
|
||||||
import {
|
import {
|
||||||
broadcastDumpDeleted,
|
broadcastDumpDeleted,
|
||||||
@@ -40,16 +47,6 @@ function titleFromUrl(url: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const BASE_COLS =
|
|
||||||
"id, kind, title, slug, comment, user_id, created_at, updated_at, url, rich_content, file_name, file_mime, file_size, vote_count, is_private, custom_thumbnail_mime";
|
|
||||||
|
|
||||||
const SELECT_COLS = `${BASE_COLS},
|
|
||||||
(SELECT COUNT(*) FROM comments WHERE dump_id = dumps.id AND deleted = 0) as comment_count`;
|
|
||||||
|
|
||||||
const SELECT_COLS_ALIASED =
|
|
||||||
"d.id, d.kind, d.title, d.slug, d.comment, d.user_id, d.created_at, d.updated_at, d.url, d.rich_content, d.file_name, d.file_mime, d.file_size, d.vote_count, d.is_private, d.custom_thumbnail_mime," +
|
|
||||||
" (SELECT COUNT(*) FROM comments WHERE dump_id = d.id AND deleted = 0) as comment_count";
|
|
||||||
|
|
||||||
export async function createUrlDump(
|
export async function createUrlDump(
|
||||||
request: CreateUrlDumpRequest,
|
request: CreateUrlDumpRequest,
|
||||||
userId: string,
|
userId: string,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
} from "./notification-service.ts";
|
} from "./notification-service.ts";
|
||||||
import {
|
import {
|
||||||
db,
|
db,
|
||||||
|
DUMP_SELECT_COLUMNS_ALIASED as SELECT_COLS_ALIASED,
|
||||||
dumpRowToApi,
|
dumpRowToApi,
|
||||||
isDumpRow,
|
isDumpRow,
|
||||||
isFollowRow,
|
isFollowRow,
|
||||||
@@ -21,12 +22,6 @@ import {
|
|||||||
userRowToApi,
|
userRowToApi,
|
||||||
} from "../model/db.ts";
|
} from "../model/db.ts";
|
||||||
|
|
||||||
// Mirrors dump-service SELECT_COLS_ALIASED — kept local to avoid circular imports
|
|
||||||
const SELECT_COLS_ALIASED =
|
|
||||||
"d.id, d.kind, d.title, d.slug, d.comment, d.user_id, d.created_at, d.updated_at, d.url, d.rich_content, " +
|
|
||||||
"d.file_name, d.file_mime, d.file_size, d.vote_count, d.is_private, d.custom_thumbnail_mime," +
|
|
||||||
" (SELECT COUNT(*) FROM comments WHERE dump_id = d.id AND deleted = 0) as comment_count";
|
|
||||||
|
|
||||||
// ── Follow / unfollow a user ──────────────────────────────────────────────────
|
// ── Follow / unfollow a user ──────────────────────────────────────────────────
|
||||||
|
|
||||||
export function followUser(followerId: string, followedUserId: string): void {
|
export function followUser(followerId: string, followedUserId: string): void {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from "../model/interfaces.ts";
|
} from "../model/interfaces.ts";
|
||||||
import {
|
import {
|
||||||
db,
|
db,
|
||||||
|
DUMP_COLUMNS_ALIASED,
|
||||||
dumpRowToApi,
|
dumpRowToApi,
|
||||||
isDumpRow,
|
isDumpRow,
|
||||||
isPlaylistRow,
|
isPlaylistRow,
|
||||||
@@ -28,9 +29,6 @@ import {
|
|||||||
import { makeSlug, UUID_RE } from "../lib/slugify.ts";
|
import { makeSlug, UUID_RE } from "../lib/slugify.ts";
|
||||||
import { linkAttachments } from "./attachment-service.ts";
|
import { linkAttachments } from "./attachment-service.ts";
|
||||||
|
|
||||||
const DUMP_SELECT_COLS =
|
|
||||||
"id, kind, title, slug, comment, user_id, created_at, updated_at, url, rich_content, file_name, file_mime, file_size, vote_count, is_private, custom_thumbnail_mime";
|
|
||||||
|
|
||||||
const PLAYLIST_SELECT = `p.*, u.username as owner_username,
|
const PLAYLIST_SELECT = `p.*, u.username as owner_username,
|
||||||
(SELECT COUNT(*) FROM playlist_dumps pd WHERE pd.playlist_id = p.id) as dump_count
|
(SELECT COUNT(*) FROM playlist_dumps pd WHERE pd.playlist_id = p.id) as dump_count
|
||||||
FROM playlists p LEFT JOIN users u ON u.id = p.user_id`;
|
FROM playlists p LEFT JOIN users u ON u.id = p.user_id`;
|
||||||
@@ -91,12 +89,11 @@ export function getPlaylist(
|
|||||||
throw new APIException(APIErrorCode.NOT_FOUND, 404, "Playlist not found");
|
throw new APIException(APIErrorCode.NOT_FOUND, 404, "Playlist not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const dumpCols = DUMP_SELECT_COLS.split(", ").map((c) => `d.${c}`).join(", ");
|
|
||||||
const isOwner = requestingUserId === playlist.userId;
|
const isOwner = requestingUserId === playlist.userId;
|
||||||
|
|
||||||
// For public playlists (or when viewed by non-owner), filter out private dumps
|
// For public playlists (or when viewed by non-owner), filter out private dumps
|
||||||
const rows = db.prepare(
|
const rows = db.prepare(
|
||||||
`SELECT ${dumpCols},
|
`SELECT ${DUMP_COLUMNS_ALIASED},
|
||||||
(SELECT COUNT(*) FROM comments WHERE dump_id = d.id AND deleted = 0) as comment_count
|
(SELECT COUNT(*) FROM comments WHERE dump_id = d.id AND deleted = 0) as comment_count
|
||||||
FROM dumps d
|
FROM dumps d
|
||||||
INNER JOIN playlist_dumps pd ON d.id = pd.dump_id
|
INNER JOIN playlist_dumps pd ON d.id = pd.dump_id
|
||||||
|
|||||||
10
src/App.tsx
10
src/App.tsx
@@ -1,5 +1,5 @@
|
|||||||
import { lazy, Suspense } from "react";
|
import { lazy, Suspense } from "react";
|
||||||
import { BrowserRouter, Route, Routes } from "react-router";
|
import { BrowserRouter, Navigate, Route, Routes } from "react-router";
|
||||||
|
|
||||||
import { RestrictedGuest } from "./pages/RestrictedGuest.tsx";
|
import { RestrictedGuest } from "./pages/RestrictedGuest.tsx";
|
||||||
import { RestrictedLoggedIn } from "./pages/RestrictedLoggedIn.tsx";
|
import { RestrictedLoggedIn } from "./pages/RestrictedLoggedIn.tsx";
|
||||||
@@ -70,7 +70,8 @@ function AppRoutes() {
|
|||||||
return (
|
return (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Index />} />
|
<Route path="/" element={<Navigate to="/~/hot" replace />} />
|
||||||
|
<Route path="/~/:feedTab" element={<Index />} />
|
||||||
<Route path="/dumps/:selectedDump" element={<Dump />} />
|
<Route path="/dumps/:selectedDump" element={<Dump />} />
|
||||||
<Route
|
<Route
|
||||||
path="/dumps/:selectedDump/edit"
|
path="/dumps/:selectedDump/edit"
|
||||||
@@ -97,6 +98,10 @@ function AppRoutes() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route path="/users/:username" element={<UserPublicProfile />} />
|
<Route path="/users/:username" element={<UserPublicProfile />} />
|
||||||
|
<Route
|
||||||
|
path="/users/:username/~/:profileTab"
|
||||||
|
element={<UserPublicProfile />}
|
||||||
|
/>
|
||||||
<Route path="/users/:username/dumps" element={<UserDumps />} />
|
<Route path="/users/:username/dumps" element={<UserDumps />} />
|
||||||
<Route path="/users/:username/upvoted" element={<UserUpvoted />} />
|
<Route path="/users/:username/upvoted" element={<UserUpvoted />} />
|
||||||
<Route
|
<Route
|
||||||
@@ -105,6 +110,7 @@ function AppRoutes() {
|
|||||||
/>
|
/>
|
||||||
<Route path="/playlists/:playlistId" element={<PlaylistDetail />} />
|
<Route path="/playlists/:playlistId" element={<PlaylistDetail />} />
|
||||||
<Route path="/search" element={<Search />} />
|
<Route path="/search" element={<Search />} />
|
||||||
|
<Route path="/search/~/:searchTab" element={<Search />} />
|
||||||
<Route path="/reset-password" element={<ResetPassword />} />
|
<Route path="/reset-password" element={<ResetPassword />} />
|
||||||
<Route
|
<Route
|
||||||
path="/notifications"
|
path="/notifications"
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export function AppHeader(
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header className="app-header app-header--has-center">
|
<header className="app-header app-header--has-center">
|
||||||
<Link to="/?tab=hot" className="app-header-brand">
|
<Link to="/" className="app-header-brand">
|
||||||
🚚<span className="app-header-brand-name">
|
🚚<span className="app-header-brand-name">
|
||||||
{" "}
|
{" "}
|
||||||
{document.querySelector<HTMLMetaElement>('meta[name="site-name"]')
|
{document.querySelector<HTMLMetaElement>('meta[name="site-name"]')
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function ChangePasswordModal({ onClose }: ChangePasswordModalProps) {
|
|||||||
const tooShort = newPassword.length > 0 &&
|
const tooShort = newPassword.length > 0 &&
|
||||||
newPassword.length < VALIDATION.PASSWORD_MIN;
|
newPassword.length < VALIDATION.PASSWORD_MIN;
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.SubmitEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (mismatch || tooShort || !currentPassword || !newPassword) return;
|
if (mismatch || tooShort || !currentPassword || !newPassword) return;
|
||||||
|
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ export function DumpCreateModal(
|
|||||||
return () => globalThis.removeEventListener("paste", handler);
|
return () => globalThis.removeEventListener("paste", handler);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.SubmitEvent<HTMLFormElement>) => {
|
const handleSubmit = async (e: React.SubmitEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (comment.length > VALIDATION.DUMP_COMMENT_MAX) return;
|
if (comment.length > VALIDATION.DUMP_COMMENT_MAX) return;
|
||||||
setSubmitState({ status: "submitting" });
|
setSubmitState({ status: "submitting" });
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
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, FEED_TABS } from "../config/feedTabs.ts";
|
||||||
|
import { useTabParam } from "../hooks/useTabParam.ts";
|
||||||
import { TabBar } from "./TabBar.tsx";
|
import { TabBar } from "./TabBar.tsx";
|
||||||
|
|
||||||
export function FeedTabBar() {
|
export function FeedTabBar() {
|
||||||
const location = useLocation();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const [tab, setTab] = useTabParam<FeedTab>(FEED_TABS, "hot");
|
||||||
const rawTab = new URLSearchParams(location.search).get("tab") ?? "hot";
|
|
||||||
const tab: FeedTab = VALID_TABS.has(rawTab) ? (rawTab as FeedTab) : "hot";
|
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ key: "hot" as FeedTab, label: <Trans>Hot</Trans> },
|
{ key: "hot" as FeedTab, label: <Trans>Hot</Trans> },
|
||||||
@@ -25,7 +21,7 @@ export function FeedTabBar() {
|
|||||||
<TabBar
|
<TabBar
|
||||||
tabs={tabs}
|
tabs={tabs}
|
||||||
activeTab={tab}
|
activeTab={tab}
|
||||||
onChange={(t) => navigate(`/?tab=${t}`, { replace: true })}
|
onChange={setTab}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type FormEvent, useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import { t } from "@lingui/core/macro";
|
import { t } from "@lingui/core/macro";
|
||||||
|
|
||||||
@@ -41,11 +41,11 @@ export function SearchBar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSubmit(e: FormEvent) {
|
function handleSubmit(e: React.SubmitEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
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)}`);
|
||||||
if (collapsible || isControlled) {
|
if (collapsible || isControlled) {
|
||||||
setExpanded(false);
|
setExpanded(false);
|
||||||
setValue("");
|
setValue("");
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export const FEED_TABS = ["hot", "new", "journal", "followed"] as const;
|
export const FEED_TABS = ["hot", "new", "journal", "followed"] as const;
|
||||||
export type FeedTab = (typeof FEED_TABS)[number];
|
export type FeedTab = (typeof FEED_TABS)[number];
|
||||||
export const VALID_TABS = new Set<string>(FEED_TABS);
|
|
||||||
|
|||||||
47
src/hooks/useTabParam.ts
Normal file
47
src/hooks/useTabParam.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { useCallback } from "react";
|
||||||
|
import { useLocation, useNavigate } from "react-router";
|
||||||
|
|
||||||
|
const SEP = "/~/";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflects the active tab in the URL path using a `/~/` delimiter, e.g.
|
||||||
|
* `/users/vincent/~/playlists` or `/~/new`. The segment before `/~/` is the
|
||||||
|
* base path and the segment after is the tab. When the base is non-empty the
|
||||||
|
* default tab collapses to the clean base path (`/users/vincent`); at the root
|
||||||
|
* the base is empty, so the default stays explicit (`/~/hot`). Generic and
|
||||||
|
* path-agnostic: the base is derived from the current location, so the same
|
||||||
|
* hook works for any route.
|
||||||
|
*
|
||||||
|
* Returns the validated active tab (falling back to `defaultTab`) and a setter
|
||||||
|
* that navigates to the matching path (replacing history).
|
||||||
|
*/
|
||||||
|
export function useTabParam<T extends string>(
|
||||||
|
validTabs: Iterable<T>,
|
||||||
|
defaultTab: T,
|
||||||
|
): [T, (tab: T) => void] {
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const valid = new Set<string>(validTabs as Iterable<string>);
|
||||||
|
const path = location.pathname;
|
||||||
|
const sepAt = path.indexOf(SEP);
|
||||||
|
|
||||||
|
let base = sepAt === -1 ? path : path.slice(0, sepAt);
|
||||||
|
if (base.endsWith("/")) base = base.slice(0, -1); // root "/" → ""
|
||||||
|
|
||||||
|
const raw = sepAt === -1
|
||||||
|
? null
|
||||||
|
: path.slice(sepAt + SEP.length).replace(/\/$/, "");
|
||||||
|
const tab = (raw && valid.has(raw) ? raw : defaultTab) as T;
|
||||||
|
|
||||||
|
const setTab = useCallback(
|
||||||
|
(t: T) => {
|
||||||
|
const target = t === defaultTab && base ? base : `${base}${SEP}${t}`;
|
||||||
|
// Preserve any unrelated query string (e.g. the search page's `?q=`).
|
||||||
|
navigate(`${target}${location.search}`, { replace: true });
|
||||||
|
},
|
||||||
|
[navigate, base, defaultTab, location.search],
|
||||||
|
);
|
||||||
|
|
||||||
|
return [tab, setTab];
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@ msgstr "{days}d ago"
|
|||||||
msgid "{hrs}h ago"
|
msgid "{hrs}h ago"
|
||||||
msgstr "{hrs}h ago"
|
msgstr "{hrs}h ago"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:182
|
#: src/pages/Search.tsx:176
|
||||||
msgid "{label} ({count})"
|
msgid "{label} ({count})"
|
||||||
msgstr "{label} ({count})"
|
msgstr "{label} ({count})"
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ msgid "{visibleCount, plural, one {# comment} other {# comments}}"
|
|||||||
msgstr "{visibleCount, plural, one {# comment} other {# comments}}"
|
msgstr "{visibleCount, plural, one {# comment} other {# comments}}"
|
||||||
|
|
||||||
#: src/pages/PlaylistDetail.tsx:617
|
#: src/pages/PlaylistDetail.tsx:617
|
||||||
#: src/pages/UserPublicProfile.tsx:753
|
#: src/pages/UserPublicProfile.tsx:766
|
||||||
msgid "← Back"
|
msgid "← Back"
|
||||||
msgstr "← Back"
|
msgstr "← Back"
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ 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:101
|
#: src/pages/UserPublicProfile.tsx:102
|
||||||
msgid "+ Invite someone"
|
msgid "+ Invite someone"
|
||||||
msgstr "+ Invite someone"
|
msgstr "+ Invite someone"
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ msgstr "a comment"
|
|||||||
msgid "a post"
|
msgid "a post"
|
||||||
msgstr "a post"
|
msgstr "a post"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1204
|
#: src/pages/UserPublicProfile.tsx:1217
|
||||||
msgid "Account"
|
msgid "Account"
|
||||||
msgstr "Account"
|
msgstr "Account"
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ msgstr "Account"
|
|||||||
msgid "Add a comment…"
|
msgid "Add a comment…"
|
||||||
msgstr "Add a comment…"
|
msgstr "Add a comment…"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:871
|
#: src/pages/UserPublicProfile.tsx:884
|
||||||
msgid "Add email…"
|
msgid "Add email…"
|
||||||
msgstr "Add email…"
|
msgstr "Add email…"
|
||||||
|
|
||||||
@@ -171,7 +171,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:1223
|
#: src/pages/UserPublicProfile.tsx:1236
|
||||||
msgid "Appearance"
|
msgid "Appearance"
|
||||||
msgstr "Appearance"
|
msgstr "Appearance"
|
||||||
|
|
||||||
@@ -181,7 +181,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:1257
|
#: src/pages/UserPublicProfile.tsx:1270
|
||||||
msgid "Auto"
|
msgid "Auto"
|
||||||
msgstr "Auto"
|
msgstr "Auto"
|
||||||
|
|
||||||
@@ -205,8 +205,8 @@ msgstr "Can't connect to the live updates server. Upvotes and notifications may
|
|||||||
#: src/pages/Dump.tsx:355
|
#: src/pages/Dump.tsx:355
|
||||||
#: src/pages/DumpEdit.tsx:391
|
#: src/pages/DumpEdit.tsx:391
|
||||||
#: src/pages/PlaylistDetail.tsx:686
|
#: src/pages/PlaylistDetail.tsx:686
|
||||||
#: src/pages/UserPublicProfile.tsx:850
|
#: src/pages/UserPublicProfile.tsx:863
|
||||||
#: src/pages/UserPublicProfile.tsx:932
|
#: src/pages/UserPublicProfile.tsx:945
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Cancel"
|
msgstr "Cancel"
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ msgstr "Cancel removal"
|
|||||||
msgid "Cancel search"
|
msgid "Cancel search"
|
||||||
msgstr "Cancel search"
|
msgstr "Cancel search"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:780
|
#: src/pages/UserPublicProfile.tsx:793
|
||||||
msgid "Change avatar"
|
msgid "Change avatar"
|
||||||
msgstr "Change avatar"
|
msgstr "Change avatar"
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ msgstr "Change avatar"
|
|||||||
msgid "Change password"
|
msgid "Change password"
|
||||||
msgstr "Change password"
|
msgstr "Change password"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1216
|
#: src/pages/UserPublicProfile.tsx:1229
|
||||||
msgid "Change password…"
|
msgid "Change password…"
|
||||||
msgstr "Change password…"
|
msgstr "Change password…"
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ msgstr "Checking invite…"
|
|||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr "Close"
|
msgstr "Close"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1249
|
#: src/pages/UserPublicProfile.tsx:1262
|
||||||
msgid "Color scheme"
|
msgid "Color scheme"
|
||||||
msgstr "Color scheme"
|
msgstr "Color scheme"
|
||||||
|
|
||||||
@@ -249,11 +249,11 @@ msgstr "Color scheme"
|
|||||||
msgid "Confirm new password"
|
msgid "Confirm new password"
|
||||||
msgstr "Confirm new password"
|
msgstr "Confirm new password"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:92
|
#: src/pages/UserPublicProfile.tsx:93
|
||||||
msgid "Copied!"
|
msgid "Copied!"
|
||||||
msgstr "Copied!"
|
msgstr "Copied!"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:92
|
#: src/pages/UserPublicProfile.tsx:93
|
||||||
msgid "Copy"
|
msgid "Copy"
|
||||||
msgstr "Copy"
|
msgstr "Copy"
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ msgstr "Creating…"
|
|||||||
msgid "Current password"
|
msgid "Current password"
|
||||||
msgstr "Current password"
|
msgstr "Current password"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1271
|
#: src/pages/UserPublicProfile.tsx:1284
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr "Dark"
|
msgstr "Dark"
|
||||||
|
|
||||||
@@ -362,15 +362,15 @@ msgstr "Dump it"
|
|||||||
msgid "Dumped!"
|
msgid "Dumped!"
|
||||||
msgstr "Dumped!"
|
msgstr "Dumped!"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:178
|
#: src/pages/Search.tsx:172
|
||||||
#: src/pages/UserDumps.tsx:107
|
#: src/pages/UserDumps.tsx:107
|
||||||
#: src/pages/UserPublicProfile.tsx:976
|
#: src/pages/UserPublicProfile.tsx:989
|
||||||
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:993
|
#: src/pages/UserPublicProfile.tsx:1006
|
||||||
msgid "Dumps ({0}{1})"
|
msgid "Dumps ({0}{1})"
|
||||||
msgstr "Dumps ({0}{1})"
|
msgstr "Dumps ({0}{1})"
|
||||||
|
|
||||||
@@ -414,7 +414,7 @@ msgstr "Editing"
|
|||||||
msgid "Email address"
|
msgid "Email address"
|
||||||
msgstr "Email address"
|
msgstr "Email address"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:211
|
#: src/pages/Search.tsx:205
|
||||||
msgid "Enter a query to search."
|
msgid "Enter a query to search."
|
||||||
msgstr "Enter a query to search."
|
msgstr "Enter a query to search."
|
||||||
|
|
||||||
@@ -427,9 +427,9 @@ 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:73
|
#: src/pages/UserPublicProfile.tsx:74
|
||||||
#: src/pages/UserPublicProfile.tsx:76
|
#: src/pages/UserPublicProfile.tsx:77
|
||||||
#: src/pages/UserPublicProfile.tsx:104
|
#: src/pages/UserPublicProfile.tsx:105
|
||||||
msgid "Failed to generate invite"
|
msgid "Failed to generate invite"
|
||||||
msgstr "Failed to generate invite"
|
msgstr "Failed to generate invite"
|
||||||
|
|
||||||
@@ -438,9 +438,9 @@ msgstr "Failed to generate invite"
|
|||||||
#: 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:367
|
#: src/pages/Notifications.tsx:367
|
||||||
#: src/pages/UserPublicProfile.tsx:1095
|
#: src/pages/UserPublicProfile.tsx:1108
|
||||||
#: src/pages/UserPublicProfile.tsx:1137
|
#: src/pages/UserPublicProfile.tsx:1150
|
||||||
#: src/pages/UserPublicProfile.tsx:1182
|
#: src/pages/UserPublicProfile.tsx:1195
|
||||||
msgid "Failed to load"
|
msgid "Failed to load"
|
||||||
msgstr "Failed to load"
|
msgstr "Failed to load"
|
||||||
|
|
||||||
@@ -457,10 +457,10 @@ msgid "Failed to post reply"
|
|||||||
msgstr "Failed to post reply"
|
msgstr "Failed to post reply"
|
||||||
|
|
||||||
#: src/pages/PlaylistDetail.tsx:795
|
#: src/pages/PlaylistDetail.tsx:795
|
||||||
#: src/pages/UserPublicProfile.tsx:688
|
#: src/pages/UserPublicProfile.tsx:701
|
||||||
#: src/pages/UserPublicProfile.tsx:726
|
#: src/pages/UserPublicProfile.tsx:739
|
||||||
#: src/pages/UserPublicProfile.tsx:855
|
#: src/pages/UserPublicProfile.tsx:868
|
||||||
#: src/pages/UserPublicProfile.tsx:935
|
#: src/pages/UserPublicProfile.tsx:948
|
||||||
msgid "Failed to save"
|
msgid "Failed to save"
|
||||||
msgstr "Failed to save"
|
msgstr "Failed to save"
|
||||||
|
|
||||||
@@ -468,7 +468,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:880
|
#: src/pages/UserPublicProfile.tsx:893
|
||||||
msgid "Failed to update avatar"
|
msgid "Failed to update avatar"
|
||||||
msgstr "Failed to update avatar"
|
msgstr "Failed to update avatar"
|
||||||
|
|
||||||
@@ -510,8 +510,8 @@ msgstr "Follow some public playlists to see their dumps here."
|
|||||||
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:20
|
#: src/components/FeedTabBar.tsx:16
|
||||||
#: src/pages/UserPublicProfile.tsx:978
|
#: src/pages/UserPublicProfile.tsx:991
|
||||||
msgid "Followed"
|
msgid "Followed"
|
||||||
msgstr "Followed"
|
msgstr "Followed"
|
||||||
|
|
||||||
@@ -521,13 +521,13 @@ msgstr "Followed"
|
|||||||
msgid "Followed ({0}{1})"
|
msgid "Followed ({0}{1})"
|
||||||
msgstr "Followed ({0}{1})"
|
msgstr "Followed ({0}{1})"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1126
|
#: src/pages/UserPublicProfile.tsx:1139
|
||||||
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:1084
|
#: src/pages/UserPublicProfile.tsx:1097
|
||||||
msgid "Following"
|
msgid "Following"
|
||||||
msgstr "Following"
|
msgstr "Following"
|
||||||
|
|
||||||
@@ -551,7 +551,7 @@ msgstr "Go home"
|
|||||||
msgid "Go to login"
|
msgid "Go to login"
|
||||||
msgstr "Go to login"
|
msgstr "Go to login"
|
||||||
|
|
||||||
#: src/components/FeedTabBar.tsx:16
|
#: src/components/FeedTabBar.tsx:12
|
||||||
msgid "Hot"
|
msgid "Hot"
|
||||||
msgstr "Hot"
|
msgstr "Hot"
|
||||||
|
|
||||||
@@ -567,16 +567,16 @@ msgstr "Invalid invite"
|
|||||||
msgid "Invalid link"
|
msgid "Invalid link"
|
||||||
msgstr "Invalid link"
|
msgstr "Invalid link"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:799
|
#: src/pages/UserPublicProfile.tsx:812
|
||||||
msgid "invited by"
|
msgid "invited by"
|
||||||
msgstr "invited by"
|
msgstr "invited by"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:979
|
#: src/pages/UserPublicProfile.tsx:992
|
||||||
#: src/pages/UserPublicProfile.tsx:1171
|
#: src/pages/UserPublicProfile.tsx:1184
|
||||||
msgid "Invitees"
|
msgid "Invitees"
|
||||||
msgstr "Invitees"
|
msgstr "Invitees"
|
||||||
|
|
||||||
#: src/components/FeedTabBar.tsx:18
|
#: src/components/FeedTabBar.tsx:14
|
||||||
msgid "Journal"
|
msgid "Journal"
|
||||||
msgstr "Journal"
|
msgstr "Journal"
|
||||||
|
|
||||||
@@ -584,7 +584,7 @@ msgstr "Journal"
|
|||||||
msgid "just now"
|
msgid "just now"
|
||||||
msgstr "just now"
|
msgstr "just now"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1264
|
#: src/pages/UserPublicProfile.tsx:1277
|
||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr "Light"
|
msgstr "Light"
|
||||||
|
|
||||||
@@ -614,7 +614,7 @@ msgstr "Loading dump…"
|
|||||||
#: 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:248
|
#: src/pages/Search.tsx:242
|
||||||
#: 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
|
||||||
@@ -626,7 +626,7 @@ msgstr "Loading more…"
|
|||||||
msgid "Loading playlist…"
|
msgid "Loading playlist…"
|
||||||
msgstr "Loading playlist…"
|
msgstr "Loading playlist…"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:736
|
#: src/pages/UserPublicProfile.tsx:749
|
||||||
msgid "Loading profile…"
|
msgid "Loading profile…"
|
||||||
msgstr "Loading profile…"
|
msgstr "Loading profile…"
|
||||||
|
|
||||||
@@ -642,9 +642,9 @@ msgstr "Loading profile…"
|
|||||||
#: src/pages/Notifications.tsx:439
|
#: src/pages/Notifications.tsx:439
|
||||||
#: src/pages/UserDumps.tsx:51
|
#: src/pages/UserDumps.tsx:51
|
||||||
#: src/pages/UserPlaylists.tsx:342
|
#: src/pages/UserPlaylists.tsx:342
|
||||||
#: src/pages/UserPublicProfile.tsx:1089
|
#: src/pages/UserPublicProfile.tsx:1102
|
||||||
#: src/pages/UserPublicProfile.tsx:1131
|
#: src/pages/UserPublicProfile.tsx:1144
|
||||||
#: src/pages/UserPublicProfile.tsx:1176
|
#: src/pages/UserPublicProfile.tsx:1189
|
||||||
#: src/pages/UserUpvoted.tsx:122
|
#: src/pages/UserUpvoted.tsx:122
|
||||||
msgid "Loading…"
|
msgid "Loading…"
|
||||||
msgstr "Loading…"
|
msgstr "Loading…"
|
||||||
@@ -663,8 +663,8 @@ msgstr "Log in to like"
|
|||||||
msgid "Log in to vote"
|
msgid "Log in to vote"
|
||||||
msgstr "Log in to vote"
|
msgstr "Log in to vote"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:757
|
#: src/pages/UserPublicProfile.tsx:770
|
||||||
#: src/pages/UserPublicProfile.tsx:894
|
#: src/pages/UserPublicProfile.tsx:907
|
||||||
msgid "Log out"
|
msgid "Log out"
|
||||||
msgstr "Log out"
|
msgstr "Log out"
|
||||||
|
|
||||||
@@ -684,13 +684,13 @@ msgstr "Max 50 MB"
|
|||||||
msgid "new"
|
msgid "new"
|
||||||
msgstr "new"
|
msgstr "new"
|
||||||
|
|
||||||
#: src/components/FeedTabBar.tsx:17
|
#: src/components/FeedTabBar.tsx:13
|
||||||
msgid "New"
|
msgid "New"
|
||||||
msgstr "New"
|
msgstr "New"
|
||||||
|
|
||||||
#: src/components/DumpCreateModal.tsx:302
|
#: src/components/DumpCreateModal.tsx:302
|
||||||
#: src/pages/UserDumps.tsx:115
|
#: src/pages/UserDumps.tsx:115
|
||||||
#: src/pages/UserPublicProfile.tsx:1320
|
#: src/pages/UserPublicProfile.tsx:1333
|
||||||
msgid "New dump"
|
msgid "New dump"
|
||||||
msgstr "New dump"
|
msgstr "New dump"
|
||||||
|
|
||||||
@@ -708,7 +708,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:228
|
#: src/pages/Search.tsx:222
|
||||||
msgid "No dumps match \"{q}\"."
|
msgid "No dumps match \"{q}\"."
|
||||||
msgstr "No dumps match \"{q}\"."
|
msgstr "No dumps match \"{q}\"."
|
||||||
|
|
||||||
@@ -723,11 +723,11 @@ 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:1144
|
#: src/pages/UserPublicProfile.tsx:1157
|
||||||
msgid "No followed playlists yet."
|
msgid "No followed playlists yet."
|
||||||
msgstr "No followed playlists yet."
|
msgstr "No followed playlists yet."
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1189
|
#: src/pages/UserPublicProfile.tsx:1202
|
||||||
msgid "No invitees yet."
|
msgid "No invitees yet."
|
||||||
msgstr "No invitees yet."
|
msgstr "No invitees yet."
|
||||||
|
|
||||||
@@ -735,28 +735,28 @@ msgstr "No invitees yet."
|
|||||||
msgid "No one yet."
|
msgid "No one yet."
|
||||||
msgstr "No one yet."
|
msgstr "No one yet."
|
||||||
|
|
||||||
#: src/pages/Search.tsx:287
|
#: src/pages/Search.tsx:281
|
||||||
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:1055
|
#: src/pages/UserPublicProfile.tsx:1068
|
||||||
msgid "No playlists yet."
|
msgid "No playlists yet."
|
||||||
msgstr "No playlists yet."
|
msgstr "No playlists yet."
|
||||||
|
|
||||||
#: src/pages/Search.tsx:261
|
#: src/pages/Search.tsx:255
|
||||||
msgid "No users match \"{q}\"."
|
msgid "No users match \"{q}\"."
|
||||||
msgstr "No users match \"{q}\"."
|
msgstr "No users match \"{q}\"."
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1102
|
#: src/pages/UserPublicProfile.tsx:1115
|
||||||
msgid "Not following anyone yet."
|
msgid "Not following anyone yet."
|
||||||
msgstr "Not following anyone yet."
|
msgstr "Not following anyone yet."
|
||||||
|
|
||||||
#: src/pages/Notifications.tsx:374
|
#: src/pages/Notifications.tsx:374
|
||||||
#: src/pages/UserDumps.tsx:125
|
#: src/pages/UserDumps.tsx:125
|
||||||
#: src/pages/UserPublicProfile.tsx:1331
|
#: src/pages/UserPublicProfile.tsx:1344
|
||||||
#: src/pages/UserPublicProfile.tsx:1453
|
#: src/pages/UserPublicProfile.tsx:1466
|
||||||
#: src/pages/UserUpvoted.tsx:194
|
#: src/pages/UserUpvoted.tsx:194
|
||||||
msgid "Nothing here yet."
|
msgid "Nothing here yet."
|
||||||
msgstr "Nothing here yet."
|
msgstr "Nothing here yet."
|
||||||
@@ -779,7 +779,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:1209
|
#: src/pages/UserPublicProfile.tsx:1222
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Password"
|
msgstr "Password"
|
||||||
|
|
||||||
@@ -803,15 +803,15 @@ msgstr "Passwords do not match"
|
|||||||
|
|
||||||
#: src/components/AppHeader.tsx:85
|
#: src/components/AppHeader.tsx:85
|
||||||
#: src/components/UserMenu.tsx:62
|
#: src/components/UserMenu.tsx:62
|
||||||
#: src/pages/Search.tsx:181
|
#: src/pages/Search.tsx:175
|
||||||
#: src/pages/UserPlaylists.tsx:368
|
#: src/pages/UserPlaylists.tsx:368
|
||||||
#: src/pages/UserPublicProfile.tsx:977
|
#: src/pages/UserPublicProfile.tsx:990
|
||||||
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:1024
|
#: src/pages/UserPublicProfile.tsx:1037
|
||||||
msgid "Playlists ({0}{1})"
|
msgid "Playlists ({0}{1})"
|
||||||
msgstr "Playlists ({0}{1})"
|
msgstr "Playlists ({0}{1})"
|
||||||
|
|
||||||
@@ -930,8 +930,8 @@ msgstr "Retry"
|
|||||||
#: src/pages/Dump.tsx:347
|
#: src/pages/Dump.tsx:347
|
||||||
#: src/pages/DumpEdit.tsx:400
|
#: src/pages/DumpEdit.tsx:400
|
||||||
#: src/pages/PlaylistDetail.tsx:679
|
#: src/pages/PlaylistDetail.tsx:679
|
||||||
#: src/pages/UserPublicProfile.tsx:842
|
#: src/pages/UserPublicProfile.tsx:855
|
||||||
#: src/pages/UserPublicProfile.tsx:924
|
#: src/pages/UserPublicProfile.tsx:937
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Save"
|
msgstr "Save"
|
||||||
|
|
||||||
@@ -940,8 +940,8 @@ msgstr "Save"
|
|||||||
#: src/pages/Dump.tsx:347
|
#: src/pages/Dump.tsx:347
|
||||||
#: src/pages/PlaylistDetail.tsx:679
|
#: src/pages/PlaylistDetail.tsx:679
|
||||||
#: src/pages/ResetPassword.tsx:152
|
#: src/pages/ResetPassword.tsx:152
|
||||||
#: src/pages/UserPublicProfile.tsx:841
|
#: src/pages/UserPublicProfile.tsx:854
|
||||||
#: src/pages/UserPublicProfile.tsx:924
|
#: src/pages/UserPublicProfile.tsx:937
|
||||||
msgid "Saving…"
|
msgid "Saving…"
|
||||||
msgstr "Saving…"
|
msgstr "Saving…"
|
||||||
|
|
||||||
@@ -954,11 +954,11 @@ msgstr "Search"
|
|||||||
msgid "Search dumps, users, playlists…"
|
msgid "Search dumps, users, playlists…"
|
||||||
msgstr "Search dumps, users, playlists…"
|
msgstr "Search dumps, users, playlists…"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:222
|
#: src/pages/Search.tsx:216
|
||||||
msgid "Search failed"
|
msgid "Search failed"
|
||||||
msgstr "Search failed"
|
msgstr "Search failed"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:217
|
#: src/pages/Search.tsx:211
|
||||||
msgid "Searching…"
|
msgid "Searching…"
|
||||||
msgstr "Searching…"
|
msgstr "Searching…"
|
||||||
|
|
||||||
@@ -979,7 +979,7 @@ msgstr "Server unreachable"
|
|||||||
msgid "Set new password"
|
msgid "Set new password"
|
||||||
msgstr "Set new password"
|
msgstr "Set new password"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:981
|
#: src/pages/UserPublicProfile.tsx:994
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr "Settings"
|
msgstr "Settings"
|
||||||
|
|
||||||
@@ -987,7 +987,7 @@ msgstr "Settings"
|
|||||||
msgid "Something went wrong"
|
msgid "Something went wrong"
|
||||||
msgstr "Something went wrong"
|
msgstr "Something went wrong"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1228
|
#: src/pages/UserPublicProfile.tsx:1241
|
||||||
msgid "Style"
|
msgid "Style"
|
||||||
msgstr "Style"
|
msgstr "Style"
|
||||||
|
|
||||||
@@ -1042,7 +1042,7 @@ msgstr "Unfollow playlist"
|
|||||||
msgid "Unknown error"
|
msgid "Unknown error"
|
||||||
msgstr "Unknown error"
|
msgstr "Unknown error"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:657
|
#: src/pages/UserPublicProfile.tsx:670
|
||||||
msgid "Upload failed"
|
msgid "Upload failed"
|
||||||
msgstr "Upload failed"
|
msgstr "Upload failed"
|
||||||
|
|
||||||
@@ -1060,7 +1060,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:1004
|
#: src/pages/UserPublicProfile.tsx:1017
|
||||||
msgid "Upvoted ({0}{1})"
|
msgid "Upvoted ({0}{1})"
|
||||||
msgstr "Upvoted ({0}{1})"
|
msgstr "Upvoted ({0}{1})"
|
||||||
|
|
||||||
@@ -1082,15 +1082,15 @@ msgstr "User menu"
|
|||||||
msgid "Username"
|
msgid "Username"
|
||||||
msgstr "Username"
|
msgstr "Username"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:180
|
#: src/pages/Search.tsx:174
|
||||||
msgid "Users"
|
msgid "Users"
|
||||||
msgstr "Users"
|
msgstr "Users"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1074
|
#: src/pages/UserPublicProfile.tsx:1087
|
||||||
#: src/pages/UserPublicProfile.tsx:1117
|
#: src/pages/UserPublicProfile.tsx:1130
|
||||||
#: src/pages/UserPublicProfile.tsx:1159
|
#: src/pages/UserPublicProfile.tsx:1172
|
||||||
#: src/pages/UserPublicProfile.tsx:1352
|
#: src/pages/UserPublicProfile.tsx:1365
|
||||||
#: src/pages/UserPublicProfile.tsx:1483
|
#: src/pages/UserPublicProfile.tsx:1496
|
||||||
msgid "View all →"
|
msgid "View all →"
|
||||||
msgstr "View all →"
|
msgstr "View all →"
|
||||||
|
|
||||||
@@ -1103,8 +1103,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:912
|
#: src/pages/UserPublicProfile.tsx:925
|
||||||
#: src/pages/UserPublicProfile.tsx:961
|
#: src/pages/UserPublicProfile.tsx:974
|
||||||
msgid "Who am I?"
|
msgid "Who am I?"
|
||||||
msgstr "Who am I?"
|
msgstr "Who am I?"
|
||||||
|
|
||||||
@@ -1129,7 +1129,7 @@ msgstr "You'll be notified when someone follows your playlists, upvotes your dum
|
|||||||
#: 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:253
|
#: src/pages/Search.tsx:247
|
||||||
#: 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
@@ -42,7 +42,7 @@ msgstr "il y a {days}j"
|
|||||||
msgid "{hrs}h ago"
|
msgid "{hrs}h ago"
|
||||||
msgstr "il y a {hrs}h"
|
msgstr "il y a {hrs}h"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:182
|
#: src/pages/Search.tsx:176
|
||||||
msgid "{label} ({count})"
|
msgid "{label} ({count})"
|
||||||
msgstr "{label} ({count})"
|
msgstr "{label} ({count})"
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ msgid "{visibleCount, plural, one {# comment} other {# comments}}"
|
|||||||
msgstr "{visibleCount, plural, one {# commentaire} other {# commentaires}}"
|
msgstr "{visibleCount, plural, one {# commentaire} other {# commentaires}}"
|
||||||
|
|
||||||
#: src/pages/PlaylistDetail.tsx:617
|
#: src/pages/PlaylistDetail.tsx:617
|
||||||
#: src/pages/UserPublicProfile.tsx:753
|
#: src/pages/UserPublicProfile.tsx:766
|
||||||
msgid "← Back"
|
msgid "← Back"
|
||||||
msgstr "← Retour"
|
msgstr "← Retour"
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ 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:101
|
#: src/pages/UserPublicProfile.tsx:102
|
||||||
msgid "+ Invite someone"
|
msgid "+ Invite someone"
|
||||||
msgstr "+ Inviter quelqu'un"
|
msgstr "+ Inviter quelqu'un"
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ msgstr "un commentaire"
|
|||||||
msgid "a post"
|
msgid "a post"
|
||||||
msgstr "une publication"
|
msgstr "une publication"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1204
|
#: src/pages/UserPublicProfile.tsx:1217
|
||||||
msgid "Account"
|
msgid "Account"
|
||||||
msgstr "Compte"
|
msgstr "Compte"
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ msgstr "Compte"
|
|||||||
msgid "Add a comment…"
|
msgid "Add a comment…"
|
||||||
msgstr "Ajouter un commentaire…"
|
msgstr "Ajouter un commentaire…"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:871
|
#: src/pages/UserPublicProfile.tsx:884
|
||||||
msgid "Add email…"
|
msgid "Add email…"
|
||||||
msgstr "Ajouter un e-mail…"
|
msgstr "Ajouter un e-mail…"
|
||||||
|
|
||||||
@@ -171,7 +171,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:1223
|
#: src/pages/UserPublicProfile.tsx:1236
|
||||||
msgid "Appearance"
|
msgid "Appearance"
|
||||||
msgstr "Apparence"
|
msgstr "Apparence"
|
||||||
|
|
||||||
@@ -181,7 +181,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:1257
|
#: src/pages/UserPublicProfile.tsx:1270
|
||||||
msgid "Auto"
|
msgid "Auto"
|
||||||
msgstr "Auto"
|
msgstr "Auto"
|
||||||
|
|
||||||
@@ -205,8 +205,8 @@ msgstr "Impossible de se connecter au serveur de mises à jour en direct. Les vo
|
|||||||
#: src/pages/Dump.tsx:355
|
#: src/pages/Dump.tsx:355
|
||||||
#: src/pages/DumpEdit.tsx:391
|
#: src/pages/DumpEdit.tsx:391
|
||||||
#: src/pages/PlaylistDetail.tsx:686
|
#: src/pages/PlaylistDetail.tsx:686
|
||||||
#: src/pages/UserPublicProfile.tsx:850
|
#: src/pages/UserPublicProfile.tsx:863
|
||||||
#: src/pages/UserPublicProfile.tsx:932
|
#: src/pages/UserPublicProfile.tsx:945
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Annuler"
|
msgstr "Annuler"
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ msgstr "Annuler la suppression"
|
|||||||
msgid "Cancel search"
|
msgid "Cancel search"
|
||||||
msgstr "Annuler la recherche"
|
msgstr "Annuler la recherche"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:780
|
#: src/pages/UserPublicProfile.tsx:793
|
||||||
msgid "Change avatar"
|
msgid "Change avatar"
|
||||||
msgstr "Changer l'avatar"
|
msgstr "Changer l'avatar"
|
||||||
|
|
||||||
@@ -227,7 +227,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:1216
|
#: src/pages/UserPublicProfile.tsx:1229
|
||||||
msgid "Change password…"
|
msgid "Change password…"
|
||||||
msgstr "Changer le mot de passe…"
|
msgstr "Changer le mot de passe…"
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ msgstr "Vérification de l'invitation…"
|
|||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr "Fermer"
|
msgstr "Fermer"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1249
|
#: src/pages/UserPublicProfile.tsx:1262
|
||||||
msgid "Color scheme"
|
msgid "Color scheme"
|
||||||
msgstr "Thème de couleur"
|
msgstr "Thème de couleur"
|
||||||
|
|
||||||
@@ -249,11 +249,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:92
|
#: src/pages/UserPublicProfile.tsx:93
|
||||||
msgid "Copied!"
|
msgid "Copied!"
|
||||||
msgstr "Copié !"
|
msgstr "Copié !"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:92
|
#: src/pages/UserPublicProfile.tsx:93
|
||||||
msgid "Copy"
|
msgid "Copy"
|
||||||
msgstr "Copier"
|
msgstr "Copier"
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ msgstr "Création…"
|
|||||||
msgid "Current password"
|
msgid "Current password"
|
||||||
msgstr "Mot de passe actuel"
|
msgstr "Mot de passe actuel"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1271
|
#: src/pages/UserPublicProfile.tsx:1284
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr "Sombre"
|
msgstr "Sombre"
|
||||||
|
|
||||||
@@ -362,15 +362,15 @@ msgstr "Recommander"
|
|||||||
msgid "Dumped!"
|
msgid "Dumped!"
|
||||||
msgstr "Recommandé !"
|
msgstr "Recommandé !"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:178
|
#: src/pages/Search.tsx:172
|
||||||
#: src/pages/UserDumps.tsx:107
|
#: src/pages/UserDumps.tsx:107
|
||||||
#: src/pages/UserPublicProfile.tsx:976
|
#: src/pages/UserPublicProfile.tsx:989
|
||||||
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:993
|
#: src/pages/UserPublicProfile.tsx:1006
|
||||||
msgid "Dumps ({0}{1})"
|
msgid "Dumps ({0}{1})"
|
||||||
msgstr "Recos ({0}{1})"
|
msgstr "Recos ({0}{1})"
|
||||||
|
|
||||||
@@ -414,7 +414,7 @@ msgstr "Modification"
|
|||||||
msgid "Email address"
|
msgid "Email address"
|
||||||
msgstr "Adresse e-mail"
|
msgstr "Adresse e-mail"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:211
|
#: src/pages/Search.tsx:205
|
||||||
msgid "Enter a query to search."
|
msgid "Enter a query to search."
|
||||||
msgstr "Saisissez une recherche."
|
msgstr "Saisissez une recherche."
|
||||||
|
|
||||||
@@ -427,9 +427,9 @@ 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:73
|
#: src/pages/UserPublicProfile.tsx:74
|
||||||
#: src/pages/UserPublicProfile.tsx:76
|
#: src/pages/UserPublicProfile.tsx:77
|
||||||
#: src/pages/UserPublicProfile.tsx:104
|
#: src/pages/UserPublicProfile.tsx:105
|
||||||
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"
|
||||||
|
|
||||||
@@ -438,9 +438,9 @@ msgstr "Impossible de générer une invitation"
|
|||||||
#: 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:367
|
#: src/pages/Notifications.tsx:367
|
||||||
#: src/pages/UserPublicProfile.tsx:1095
|
#: src/pages/UserPublicProfile.tsx:1108
|
||||||
#: src/pages/UserPublicProfile.tsx:1137
|
#: src/pages/UserPublicProfile.tsx:1150
|
||||||
#: src/pages/UserPublicProfile.tsx:1182
|
#: src/pages/UserPublicProfile.tsx:1195
|
||||||
msgid "Failed to load"
|
msgid "Failed to load"
|
||||||
msgstr "Chargement échoué"
|
msgstr "Chargement échoué"
|
||||||
|
|
||||||
@@ -457,10 +457,10 @@ msgid "Failed to post reply"
|
|||||||
msgstr "Impossible de publier la réponse"
|
msgstr "Impossible de publier la réponse"
|
||||||
|
|
||||||
#: src/pages/PlaylistDetail.tsx:795
|
#: src/pages/PlaylistDetail.tsx:795
|
||||||
#: src/pages/UserPublicProfile.tsx:688
|
#: src/pages/UserPublicProfile.tsx:701
|
||||||
#: src/pages/UserPublicProfile.tsx:726
|
#: src/pages/UserPublicProfile.tsx:739
|
||||||
#: src/pages/UserPublicProfile.tsx:855
|
#: src/pages/UserPublicProfile.tsx:868
|
||||||
#: src/pages/UserPublicProfile.tsx:935
|
#: src/pages/UserPublicProfile.tsx:948
|
||||||
msgid "Failed to save"
|
msgid "Failed to save"
|
||||||
msgstr "Enregistrement échoué"
|
msgstr "Enregistrement échoué"
|
||||||
|
|
||||||
@@ -468,7 +468,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:880
|
#: src/pages/UserPublicProfile.tsx:893
|
||||||
msgid "Failed to update avatar"
|
msgid "Failed to update avatar"
|
||||||
msgstr "Impossible de mettre à jour l'avatar"
|
msgstr "Impossible de mettre à jour l'avatar"
|
||||||
|
|
||||||
@@ -510,8 +510,8 @@ msgstr "Suivez des collections publiques pour voir leurs recos ici."
|
|||||||
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:20
|
#: src/components/FeedTabBar.tsx:16
|
||||||
#: src/pages/UserPublicProfile.tsx:978
|
#: src/pages/UserPublicProfile.tsx:991
|
||||||
msgid "Followed"
|
msgid "Followed"
|
||||||
msgstr "Suivi"
|
msgstr "Suivi"
|
||||||
|
|
||||||
@@ -521,13 +521,13 @@ msgstr "Suivi"
|
|||||||
msgid "Followed ({0}{1})"
|
msgid "Followed ({0}{1})"
|
||||||
msgstr "Suivies ({0}{1})"
|
msgstr "Suivies ({0}{1})"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1126
|
#: src/pages/UserPublicProfile.tsx:1139
|
||||||
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:1084
|
#: src/pages/UserPublicProfile.tsx:1097
|
||||||
msgid "Following"
|
msgid "Following"
|
||||||
msgstr "Abonné"
|
msgstr "Abonné"
|
||||||
|
|
||||||
@@ -551,7 +551,7 @@ msgstr "Accueil"
|
|||||||
msgid "Go to login"
|
msgid "Go to login"
|
||||||
msgstr "Aller à la connexion"
|
msgstr "Aller à la connexion"
|
||||||
|
|
||||||
#: src/components/FeedTabBar.tsx:16
|
#: src/components/FeedTabBar.tsx:12
|
||||||
msgid "Hot"
|
msgid "Hot"
|
||||||
msgstr "Tendances"
|
msgstr "Tendances"
|
||||||
|
|
||||||
@@ -567,16 +567,16 @@ msgstr "Invitation invalide"
|
|||||||
msgid "Invalid link"
|
msgid "Invalid link"
|
||||||
msgstr "Lien invalide"
|
msgstr "Lien invalide"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:799
|
#: src/pages/UserPublicProfile.tsx:812
|
||||||
msgid "invited by"
|
msgid "invited by"
|
||||||
msgstr "invité par"
|
msgstr "invité par"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:979
|
#: src/pages/UserPublicProfile.tsx:992
|
||||||
#: src/pages/UserPublicProfile.tsx:1171
|
#: src/pages/UserPublicProfile.tsx:1184
|
||||||
msgid "Invitees"
|
msgid "Invitees"
|
||||||
msgstr "Invités"
|
msgstr "Invités"
|
||||||
|
|
||||||
#: src/components/FeedTabBar.tsx:18
|
#: src/components/FeedTabBar.tsx:14
|
||||||
msgid "Journal"
|
msgid "Journal"
|
||||||
msgstr "Journal"
|
msgstr "Journal"
|
||||||
|
|
||||||
@@ -584,7 +584,7 @@ msgstr "Journal"
|
|||||||
msgid "just now"
|
msgid "just now"
|
||||||
msgstr "à l'instant"
|
msgstr "à l'instant"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1264
|
#: src/pages/UserPublicProfile.tsx:1277
|
||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr "Clair"
|
msgstr "Clair"
|
||||||
|
|
||||||
@@ -614,7 +614,7 @@ msgstr "Chargement de la reco…"
|
|||||||
#: 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:248
|
#: src/pages/Search.tsx:242
|
||||||
#: 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
|
||||||
@@ -626,7 +626,7 @@ msgstr "Chargement…"
|
|||||||
msgid "Loading playlist…"
|
msgid "Loading playlist…"
|
||||||
msgstr "Chargement de la collection…"
|
msgstr "Chargement de la collection…"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:736
|
#: src/pages/UserPublicProfile.tsx:749
|
||||||
msgid "Loading profile…"
|
msgid "Loading profile…"
|
||||||
msgstr "Chargement du profil…"
|
msgstr "Chargement du profil…"
|
||||||
|
|
||||||
@@ -642,9 +642,9 @@ msgstr "Chargement du profil…"
|
|||||||
#: src/pages/Notifications.tsx:439
|
#: src/pages/Notifications.tsx:439
|
||||||
#: src/pages/UserDumps.tsx:51
|
#: src/pages/UserDumps.tsx:51
|
||||||
#: src/pages/UserPlaylists.tsx:342
|
#: src/pages/UserPlaylists.tsx:342
|
||||||
#: src/pages/UserPublicProfile.tsx:1089
|
#: src/pages/UserPublicProfile.tsx:1102
|
||||||
#: src/pages/UserPublicProfile.tsx:1131
|
#: src/pages/UserPublicProfile.tsx:1144
|
||||||
#: src/pages/UserPublicProfile.tsx:1176
|
#: src/pages/UserPublicProfile.tsx:1189
|
||||||
#: src/pages/UserUpvoted.tsx:122
|
#: src/pages/UserUpvoted.tsx:122
|
||||||
msgid "Loading…"
|
msgid "Loading…"
|
||||||
msgstr "Chargement…"
|
msgstr "Chargement…"
|
||||||
@@ -663,8 +663,8 @@ msgstr "Se connecter pour aimer"
|
|||||||
msgid "Log in to vote"
|
msgid "Log in to vote"
|
||||||
msgstr "Se connecter pour voter"
|
msgstr "Se connecter pour voter"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:757
|
#: src/pages/UserPublicProfile.tsx:770
|
||||||
#: src/pages/UserPublicProfile.tsx:894
|
#: src/pages/UserPublicProfile.tsx:907
|
||||||
msgid "Log out"
|
msgid "Log out"
|
||||||
msgstr "Se déconnecter"
|
msgstr "Se déconnecter"
|
||||||
|
|
||||||
@@ -684,13 +684,13 @@ msgstr "Max 50 Mo"
|
|||||||
msgid "new"
|
msgid "new"
|
||||||
msgstr "nouveau"
|
msgstr "nouveau"
|
||||||
|
|
||||||
#: src/components/FeedTabBar.tsx:17
|
#: src/components/FeedTabBar.tsx:13
|
||||||
msgid "New"
|
msgid "New"
|
||||||
msgstr "Nouveau"
|
msgstr "Nouveau"
|
||||||
|
|
||||||
#: src/components/DumpCreateModal.tsx:302
|
#: src/components/DumpCreateModal.tsx:302
|
||||||
#: src/pages/UserDumps.tsx:115
|
#: src/pages/UserDumps.tsx:115
|
||||||
#: src/pages/UserPublicProfile.tsx:1320
|
#: src/pages/UserPublicProfile.tsx:1333
|
||||||
msgid "New dump"
|
msgid "New dump"
|
||||||
msgstr "Nouvelle reco"
|
msgstr "Nouvelle reco"
|
||||||
|
|
||||||
@@ -708,7 +708,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:228
|
#: src/pages/Search.tsx:222
|
||||||
msgid "No dumps match \"{q}\"."
|
msgid "No dumps match \"{q}\"."
|
||||||
msgstr "Aucune reco ne correspond à « {q} »."
|
msgstr "Aucune reco ne correspond à « {q} »."
|
||||||
|
|
||||||
@@ -723,11 +723,11 @@ 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:1144
|
#: src/pages/UserPublicProfile.tsx:1157
|
||||||
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:1189
|
#: src/pages/UserPublicProfile.tsx:1202
|
||||||
msgid "No invitees yet."
|
msgid "No invitees yet."
|
||||||
msgstr "Aucun invité pour le moment."
|
msgstr "Aucun invité pour le moment."
|
||||||
|
|
||||||
@@ -735,28 +735,28 @@ msgstr "Aucun invité pour le moment."
|
|||||||
msgid "No one yet."
|
msgid "No one yet."
|
||||||
msgstr "Personne pour le moment."
|
msgstr "Personne pour le moment."
|
||||||
|
|
||||||
#: src/pages/Search.tsx:287
|
#: src/pages/Search.tsx:281
|
||||||
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:1055
|
#: src/pages/UserPublicProfile.tsx:1068
|
||||||
msgid "No playlists yet."
|
msgid "No playlists yet."
|
||||||
msgstr "Pas encore de collections."
|
msgstr "Pas encore de collections."
|
||||||
|
|
||||||
#: src/pages/Search.tsx:261
|
#: src/pages/Search.tsx:255
|
||||||
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:1102
|
#: src/pages/UserPublicProfile.tsx:1115
|
||||||
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:374
|
#: src/pages/Notifications.tsx:374
|
||||||
#: src/pages/UserDumps.tsx:125
|
#: src/pages/UserDumps.tsx:125
|
||||||
#: src/pages/UserPublicProfile.tsx:1331
|
#: src/pages/UserPublicProfile.tsx:1344
|
||||||
#: src/pages/UserPublicProfile.tsx:1453
|
#: src/pages/UserPublicProfile.tsx:1466
|
||||||
#: src/pages/UserUpvoted.tsx:194
|
#: src/pages/UserUpvoted.tsx:194
|
||||||
msgid "Nothing here yet."
|
msgid "Nothing here yet."
|
||||||
msgstr "Rien ici pour l'instant."
|
msgstr "Rien ici pour l'instant."
|
||||||
@@ -779,7 +779,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:1209
|
#: src/pages/UserPublicProfile.tsx:1222
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Mot de passe"
|
msgstr "Mot de passe"
|
||||||
|
|
||||||
@@ -803,15 +803,15 @@ msgstr "Les mots de passe ne correspondent pas"
|
|||||||
|
|
||||||
#: src/components/AppHeader.tsx:85
|
#: src/components/AppHeader.tsx:85
|
||||||
#: src/components/UserMenu.tsx:62
|
#: src/components/UserMenu.tsx:62
|
||||||
#: src/pages/Search.tsx:181
|
#: src/pages/Search.tsx:175
|
||||||
#: src/pages/UserPlaylists.tsx:368
|
#: src/pages/UserPlaylists.tsx:368
|
||||||
#: src/pages/UserPublicProfile.tsx:977
|
#: src/pages/UserPublicProfile.tsx:990
|
||||||
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:1024
|
#: src/pages/UserPublicProfile.tsx:1037
|
||||||
msgid "Playlists ({0}{1})"
|
msgid "Playlists ({0}{1})"
|
||||||
msgstr "Collections ({0}{1})"
|
msgstr "Collections ({0}{1})"
|
||||||
|
|
||||||
@@ -883,7 +883,7 @@ msgstr "Inscription échouée"
|
|||||||
|
|
||||||
#: src/pages/Dump.tsx:467
|
#: src/pages/Dump.tsx:467
|
||||||
msgid "Related"
|
msgid "Related"
|
||||||
msgstr "Apparentés"
|
msgstr "Connexe"
|
||||||
|
|
||||||
#: src/components/FileDropZone.tsx:115
|
#: src/components/FileDropZone.tsx:115
|
||||||
msgid "Remove file"
|
msgid "Remove file"
|
||||||
@@ -930,8 +930,8 @@ msgstr "Réessayer"
|
|||||||
#: src/pages/Dump.tsx:347
|
#: src/pages/Dump.tsx:347
|
||||||
#: src/pages/DumpEdit.tsx:400
|
#: src/pages/DumpEdit.tsx:400
|
||||||
#: src/pages/PlaylistDetail.tsx:679
|
#: src/pages/PlaylistDetail.tsx:679
|
||||||
#: src/pages/UserPublicProfile.tsx:842
|
#: src/pages/UserPublicProfile.tsx:855
|
||||||
#: src/pages/UserPublicProfile.tsx:924
|
#: src/pages/UserPublicProfile.tsx:937
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Enregistrer"
|
msgstr "Enregistrer"
|
||||||
|
|
||||||
@@ -940,8 +940,8 @@ msgstr "Enregistrer"
|
|||||||
#: src/pages/Dump.tsx:347
|
#: src/pages/Dump.tsx:347
|
||||||
#: src/pages/PlaylistDetail.tsx:679
|
#: src/pages/PlaylistDetail.tsx:679
|
||||||
#: src/pages/ResetPassword.tsx:152
|
#: src/pages/ResetPassword.tsx:152
|
||||||
#: src/pages/UserPublicProfile.tsx:841
|
#: src/pages/UserPublicProfile.tsx:854
|
||||||
#: src/pages/UserPublicProfile.tsx:924
|
#: src/pages/UserPublicProfile.tsx:937
|
||||||
msgid "Saving…"
|
msgid "Saving…"
|
||||||
msgstr "Enregistrement…"
|
msgstr "Enregistrement…"
|
||||||
|
|
||||||
@@ -954,11 +954,11 @@ msgstr "Rechercher"
|
|||||||
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:222
|
#: src/pages/Search.tsx:216
|
||||||
msgid "Search failed"
|
msgid "Search failed"
|
||||||
msgstr "Recherche échouée"
|
msgstr "Recherche échouée"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:217
|
#: src/pages/Search.tsx:211
|
||||||
msgid "Searching…"
|
msgid "Searching…"
|
||||||
msgstr "Recherche…"
|
msgstr "Recherche…"
|
||||||
|
|
||||||
@@ -979,7 +979,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:981
|
#: src/pages/UserPublicProfile.tsx:994
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr "Paramètres"
|
msgstr "Paramètres"
|
||||||
|
|
||||||
@@ -987,7 +987,7 @@ 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:1228
|
#: src/pages/UserPublicProfile.tsx:1241
|
||||||
msgid "Style"
|
msgid "Style"
|
||||||
msgstr "Style"
|
msgstr "Style"
|
||||||
|
|
||||||
@@ -1042,7 +1042,7 @@ msgstr "Ne plus suivre la collection"
|
|||||||
msgid "Unknown error"
|
msgid "Unknown error"
|
||||||
msgstr "Erreur inconnue"
|
msgstr "Erreur inconnue"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:657
|
#: src/pages/UserPublicProfile.tsx:670
|
||||||
msgid "Upload failed"
|
msgid "Upload failed"
|
||||||
msgstr "Envoi échoué"
|
msgstr "Envoi échoué"
|
||||||
|
|
||||||
@@ -1060,7 +1060,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:1004
|
#: src/pages/UserPublicProfile.tsx:1017
|
||||||
msgid "Upvoted ({0}{1})"
|
msgid "Upvoted ({0}{1})"
|
||||||
msgstr "Votés ({0}{1})"
|
msgstr "Votés ({0}{1})"
|
||||||
|
|
||||||
@@ -1082,15 +1082,15 @@ msgstr "Menu utilisateur"
|
|||||||
msgid "Username"
|
msgid "Username"
|
||||||
msgstr "Nom d'utilisateur"
|
msgstr "Nom d'utilisateur"
|
||||||
|
|
||||||
#: src/pages/Search.tsx:180
|
#: src/pages/Search.tsx:174
|
||||||
msgid "Users"
|
msgid "Users"
|
||||||
msgstr "Utilisateurs"
|
msgstr "Utilisateurs"
|
||||||
|
|
||||||
#: src/pages/UserPublicProfile.tsx:1074
|
#: src/pages/UserPublicProfile.tsx:1087
|
||||||
#: src/pages/UserPublicProfile.tsx:1117
|
#: src/pages/UserPublicProfile.tsx:1130
|
||||||
#: src/pages/UserPublicProfile.tsx:1159
|
#: src/pages/UserPublicProfile.tsx:1172
|
||||||
#: src/pages/UserPublicProfile.tsx:1352
|
#: src/pages/UserPublicProfile.tsx:1365
|
||||||
#: src/pages/UserPublicProfile.tsx:1483
|
#: src/pages/UserPublicProfile.tsx:1496
|
||||||
msgid "View all →"
|
msgid "View all →"
|
||||||
msgstr "Tout voir →"
|
msgstr "Tout voir →"
|
||||||
|
|
||||||
@@ -1103,8 +1103,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:912
|
#: src/pages/UserPublicProfile.tsx:925
|
||||||
#: src/pages/UserPublicProfile.tsx:961
|
#: src/pages/UserPublicProfile.tsx:974
|
||||||
msgid "Who am I?"
|
msgid "Who am I?"
|
||||||
msgstr "Qui suis-je ?"
|
msgstr "Qui suis-je ?"
|
||||||
|
|
||||||
@@ -1129,7 +1129,7 @@ msgstr "Vous serez notifié lorsque quelqu'un suit vos collections, vote pour vo
|
|||||||
#: 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:253
|
#: src/pages/Search.tsx:247
|
||||||
#: 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
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import { useLocation } from "react-router";
|
|||||||
import { AppHeader } from "../components/AppHeader.tsx";
|
import { AppHeader } from "../components/AppHeader.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, FEED_TABS } from "../config/feedTabs.ts";
|
||||||
|
import { useTabParam } from "../hooks/useTabParam.ts";
|
||||||
|
|
||||||
import { API_URL, DEFAULT_PAGE_SIZE } from "../config/api.ts";
|
import { API_URL, DEFAULT_PAGE_SIZE } from "../config/api.ts";
|
||||||
|
|
||||||
@@ -82,20 +83,20 @@ export function Index() {
|
|||||||
);
|
);
|
||||||
const mainFetchDone = useRef(false);
|
const mainFetchDone = useRef(false);
|
||||||
|
|
||||||
const searchParams = new URLSearchParams(location.search);
|
const [tab] = useTabParam<FeedTab>(FEED_TABS, "hot");
|
||||||
const rawTab = searchParams.get("tab") ?? "hot";
|
|
||||||
const tab: FeedTab = VALID_TABS.has(rawTab) ? rawTab as FeedTab : "hot";
|
|
||||||
|
|
||||||
// Web Share Target: Android share sheet navigates to /?share_url=...
|
// Web Share Target: Android share sheet navigates to /?share_url=...
|
||||||
|
const searchParams = new URLSearchParams(location.search);
|
||||||
const shareUrl = searchParams.get("share_url") ??
|
const shareUrl = searchParams.get("share_url") ??
|
||||||
searchParams.get("share_text") ?? "";
|
searchParams.get("share_text") ?? "";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!shareUrl) return;
|
if (!shareUrl) return;
|
||||||
// Clean share params from the URL so a refresh doesn't re-open the modal
|
// Clean share params from the URL so a refresh doesn't re-open the modal.
|
||||||
const clean = tab !== "hot" ? `?tab=${tab}` : "";
|
// The active tab already lives in the pathname (e.g. /~/new), so just drop
|
||||||
globalThis.history.replaceState({}, "", location.pathname + clean);
|
// the query string.
|
||||||
}, [shareUrl, tab, location.pathname]);
|
globalThis.history.replaceState({}, "", location.pathname);
|
||||||
|
}, [shareUrl, location.pathname]);
|
||||||
|
|
||||||
// ── Main feed fetch ──
|
// ── Main feed fetch ──
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export function ResetPassword() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.SubmitEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (mismatch || tooShort || !newPassword) return;
|
if (mismatch || tooShort || !newPassword) return;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { ErrorCard } from "../components/ErrorCard.tsx";
|
|||||||
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 { useInfiniteScroll } from "../hooks/useInfiniteScroll.ts";
|
import { useInfiniteScroll } from "../hooks/useInfiniteScroll.ts";
|
||||||
|
import { useTabParam } from "../hooks/useTabParam.ts";
|
||||||
import {
|
import {
|
||||||
deserializeDump,
|
deserializeDump,
|
||||||
deserializePlaylist,
|
deserializePlaylist,
|
||||||
@@ -23,7 +24,8 @@ import {
|
|||||||
} from "../model.ts";
|
} from "../model.ts";
|
||||||
import { DEFAULT_PAGE_SIZE, SEARCH_URL } from "../config/api.ts";
|
import { DEFAULT_PAGE_SIZE, SEARCH_URL } from "../config/api.ts";
|
||||||
|
|
||||||
type Tab = "dumps" | "users" | "playlists";
|
const SEARCH_TABS = ["dumps", "users", "playlists"] as const;
|
||||||
|
type Tab = (typeof SEARCH_TABS)[number];
|
||||||
|
|
||||||
type SearchState =
|
type SearchState =
|
||||||
| { status: "idle" }
|
| { status: "idle" }
|
||||||
@@ -44,9 +46,9 @@ type SearchState =
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function Search() {
|
export function Search() {
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const q = searchParams.get("q") ?? "";
|
const q = searchParams.get("q") ?? "";
|
||||||
const tab = (searchParams.get("tab") ?? "dumps") as Tab;
|
const [tab, setTab] = useTabParam<Tab>(SEARCH_TABS, "dumps");
|
||||||
|
|
||||||
const { token, user } = useAuth();
|
const { token, user } = useAuth();
|
||||||
const { voteCounts, myVotes, castVote, removeVote } = useWS();
|
const { voteCounts, myVotes, castVote, removeVote } = useWS();
|
||||||
@@ -159,14 +161,6 @@ export function Search() {
|
|||||||
!state.dumps.loadingMore,
|
!state.dumps.loadingMore,
|
||||||
);
|
);
|
||||||
|
|
||||||
function setTab(tab: Tab) {
|
|
||||||
setSearchParams((prev) => {
|
|
||||||
const next = new URLSearchParams(prev);
|
|
||||||
next.set("tab", tab);
|
|
||||||
return next;
|
|
||||||
}, { replace: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const dumpCount = state.status === "loaded" ? state.dumps.total : null;
|
const dumpCount = state.status === "loaded" ? state.dumps.total : null;
|
||||||
const userCount = state.status === "loaded" ? state.users.length : null;
|
const userCount = state.status === "loaded" ? state.users.length : null;
|
||||||
const playlistCount = state.status === "loaded"
|
const playlistCount = state.status === "loaded"
|
||||||
@@ -188,7 +182,7 @@ export function Search() {
|
|||||||
<main className="search-page">
|
<main className="search-page">
|
||||||
{q && (
|
{q && (
|
||||||
<TabBar
|
<TabBar
|
||||||
tabs={(["dumps", "users", "playlists"] as Tab[]).map((key) => ({
|
tabs={SEARCH_TABS.map((key) => ({
|
||||||
key,
|
key,
|
||||||
label: tabLabel(
|
label: tabLabel(
|
||||||
key,
|
key,
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export function UserLogin() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResetRequest = async (e: React.FormEvent) => {
|
const handleResetRequest = async (e: React.SubmitEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!resetEmail.trim()) return;
|
if (!resetEmail.trim()) return;
|
||||||
setResetState({ status: "submitting" });
|
setResetState({ status: "submitting" });
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ 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";
|
import { TabBar } from "../components/TabBar.tsx";
|
||||||
|
import { useTabParam } from "../hooks/useTabParam.ts";
|
||||||
|
|
||||||
function InviteButton() {
|
function InviteButton() {
|
||||||
const { authFetch } = useAuth();
|
const { authFetch } = useAuth();
|
||||||
@@ -146,7 +147,14 @@ 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";
|
const PROFILE_TABS = [
|
||||||
|
"dumps",
|
||||||
|
"playlists",
|
||||||
|
"followed",
|
||||||
|
"invitees",
|
||||||
|
"settings",
|
||||||
|
] as const;
|
||||||
|
type ProfileTab = (typeof PROFILE_TABS)[number];
|
||||||
|
|
||||||
function buildInviteTree(
|
function buildInviteTree(
|
||||||
entries: InviteTreeEntry[],
|
entries: InviteTreeEntry[],
|
||||||
@@ -193,6 +201,13 @@ export function UserPublicProfile() {
|
|||||||
const profileUserId = state.status === "loaded" ? state.user.id : null;
|
const profileUserId = state.status === "loaded" ? state.user.id : null;
|
||||||
const isOwnProfile = me?.id === profileUserId;
|
const isOwnProfile = me?.id === profileUserId;
|
||||||
|
|
||||||
|
// Active tab is driven by the `/~/<tab>` URL path (linkable / refresh-safe).
|
||||||
|
// `settings` is only valid on your own profile; otherwise it falls back to "dumps".
|
||||||
|
const availableTabs: ProfileTab[] = isOwnProfile
|
||||||
|
? [...PROFILE_TABS]
|
||||||
|
: PROFILE_TABS.filter((t) => t !== "settings");
|
||||||
|
const [tab, setTab] = useTabParam<ProfileTab>(availableTabs, "dumps");
|
||||||
|
|
||||||
const setDumps = useCallback((fn: (prev: Dump[]) => Dump[]) => {
|
const setDumps = useCallback((fn: (prev: Dump[]) => Dump[]) => {
|
||||||
setState((s) =>
|
setState((s) =>
|
||||||
s.status !== "loaded"
|
s.status !== "loaded"
|
||||||
@@ -287,7 +302,6 @@ 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<ProfileTab>("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);
|
||||||
@@ -301,7 +315,6 @@ export function UserPublicProfile() {
|
|||||||
if (prevUsername !== username) {
|
if (prevUsername !== username) {
|
||||||
setPrevUsername(username);
|
setPrevUsername(username);
|
||||||
setState({ status: "loading" });
|
setState({ status: "loading" });
|
||||||
setTab("dumps");
|
|
||||||
setFollowedState(null);
|
setFollowedState(null);
|
||||||
setInviteTreeState(null);
|
setInviteTreeState(null);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user