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:
10
src/App.tsx
10
src/App.tsx
@@ -1,5 +1,5 @@
|
||||
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 { RestrictedLoggedIn } from "./pages/RestrictedLoggedIn.tsx";
|
||||
@@ -70,7 +70,8 @@ function AppRoutes() {
|
||||
return (
|
||||
<Suspense>
|
||||
<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/edit"
|
||||
@@ -97,6 +98,10 @@ function AppRoutes() {
|
||||
}
|
||||
/>
|
||||
<Route path="/users/:username" element={<UserPublicProfile />} />
|
||||
<Route
|
||||
path="/users/:username/~/:profileTab"
|
||||
element={<UserPublicProfile />}
|
||||
/>
|
||||
<Route path="/users/:username/dumps" element={<UserDumps />} />
|
||||
<Route path="/users/:username/upvoted" element={<UserUpvoted />} />
|
||||
<Route
|
||||
@@ -105,6 +110,7 @@ function AppRoutes() {
|
||||
/>
|
||||
<Route path="/playlists/:playlistId" element={<PlaylistDetail />} />
|
||||
<Route path="/search" element={<Search />} />
|
||||
<Route path="/search/~/:searchTab" element={<Search />} />
|
||||
<Route path="/reset-password" element={<ResetPassword />} />
|
||||
<Route
|
||||
path="/notifications"
|
||||
|
||||
@@ -42,7 +42,7 @@ export function AppHeader(
|
||||
return (
|
||||
<>
|
||||
<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">
|
||||
{" "}
|
||||
{document.querySelector<HTMLMetaElement>('meta[name="site-name"]')
|
||||
|
||||
@@ -24,7 +24,7 @@ export function ChangePasswordModal({ onClose }: ChangePasswordModalProps) {
|
||||
const tooShort = newPassword.length > 0 &&
|
||||
newPassword.length < VALIDATION.PASSWORD_MIN;
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
const handleSubmit = async (e: React.SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
if (mismatch || tooShort || !currentPassword || !newPassword) return;
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ export function DumpCreateModal(
|
||||
return () => globalThis.removeEventListener("paste", handler);
|
||||
}, []);
|
||||
|
||||
const handleSubmit = async (e: React.SubmitEvent<HTMLFormElement>) => {
|
||||
const handleSubmit = async (e: React.SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
if (comment.length > VALIDATION.DUMP_COMMENT_MAX) return;
|
||||
setSubmitState({ status: "submitting" });
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
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";
|
||||
|
||||
export function FeedTabBar() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAuth();
|
||||
|
||||
const rawTab = new URLSearchParams(location.search).get("tab") ?? "hot";
|
||||
const tab: FeedTab = VALID_TABS.has(rawTab) ? (rawTab as FeedTab) : "hot";
|
||||
const [tab, setTab] = useTabParam<FeedTab>(FEED_TABS, "hot");
|
||||
|
||||
const tabs = [
|
||||
{ key: "hot" as FeedTab, label: <Trans>Hot</Trans> },
|
||||
@@ -25,7 +21,7 @@ export function FeedTabBar() {
|
||||
<TabBar
|
||||
tabs={tabs}
|
||||
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 { t } from "@lingui/core/macro";
|
||||
|
||||
@@ -41,11 +41,11 @@ export function SearchBar(
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubmit(e: FormEvent) {
|
||||
function handleSubmit(e: React.SubmitEvent) {
|
||||
e.preventDefault();
|
||||
const q = value.trim();
|
||||
if (!q) return;
|
||||
navigate(`/search?q=${encodeURIComponent(q)}&tab=dumps`);
|
||||
navigate(`/search?q=${encodeURIComponent(q)}`);
|
||||
if (collapsible || isControlled) {
|
||||
setExpanded(false);
|
||||
setValue("");
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export const FEED_TABS = ["hot", "new", "journal", "followed"] as const;
|
||||
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"
|
||||
msgstr "{hrs}h ago"
|
||||
|
||||
#: src/pages/Search.tsx:182
|
||||
#: src/pages/Search.tsx:176
|
||||
msgid "{label} ({count})"
|
||||
msgstr "{label} ({count})"
|
||||
|
||||
@@ -55,7 +55,7 @@ msgid "{visibleCount, plural, one {# comment} other {# comments}}"
|
||||
msgstr "{visibleCount, plural, one {# comment} other {# comments}}"
|
||||
|
||||
#: src/pages/PlaylistDetail.tsx:617
|
||||
#: src/pages/UserPublicProfile.tsx:753
|
||||
#: src/pages/UserPublicProfile.tsx:766
|
||||
msgid "← Back"
|
||||
msgstr "← Back"
|
||||
|
||||
@@ -71,7 +71,7 @@ msgstr "← Back to all dumps"
|
||||
msgid "← Back to profile"
|
||||
msgstr "← Back to profile"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:101
|
||||
#: src/pages/UserPublicProfile.tsx:102
|
||||
msgid "+ Invite someone"
|
||||
msgstr "+ Invite someone"
|
||||
|
||||
@@ -142,7 +142,7 @@ msgstr "a comment"
|
||||
msgid "a post"
|
||||
msgstr "a post"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1204
|
||||
#: src/pages/UserPublicProfile.tsx:1217
|
||||
msgid "Account"
|
||||
msgstr "Account"
|
||||
|
||||
@@ -150,7 +150,7 @@ msgstr "Account"
|
||||
msgid "Add a comment…"
|
||||
msgstr "Add a comment…"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:871
|
||||
#: src/pages/UserPublicProfile.tsx:884
|
||||
msgid "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>"
|
||||
msgstr "Already have an account? <0>Log in</0>"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1223
|
||||
#: src/pages/UserPublicProfile.tsx:1236
|
||||
msgid "Appearance"
|
||||
msgstr "Appearance"
|
||||
|
||||
@@ -181,7 +181,7 @@ msgstr "Appearance"
|
||||
msgid "At least {0} characters"
|
||||
msgstr "At least {0} characters"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1257
|
||||
#: src/pages/UserPublicProfile.tsx:1270
|
||||
msgid "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/DumpEdit.tsx:391
|
||||
#: src/pages/PlaylistDetail.tsx:686
|
||||
#: src/pages/UserPublicProfile.tsx:850
|
||||
#: src/pages/UserPublicProfile.tsx:932
|
||||
#: src/pages/UserPublicProfile.tsx:863
|
||||
#: src/pages/UserPublicProfile.tsx:945
|
||||
msgid "Cancel"
|
||||
msgstr "Cancel"
|
||||
|
||||
@@ -218,7 +218,7 @@ msgstr "Cancel removal"
|
||||
msgid "Cancel search"
|
||||
msgstr "Cancel search"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:780
|
||||
#: src/pages/UserPublicProfile.tsx:793
|
||||
msgid "Change avatar"
|
||||
msgstr "Change avatar"
|
||||
|
||||
@@ -227,7 +227,7 @@ msgstr "Change avatar"
|
||||
msgid "Change password"
|
||||
msgstr "Change password"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1216
|
||||
#: src/pages/UserPublicProfile.tsx:1229
|
||||
msgid "Change password…"
|
||||
msgstr "Change password…"
|
||||
|
||||
@@ -240,7 +240,7 @@ msgstr "Checking invite…"
|
||||
msgid "Close"
|
||||
msgstr "Close"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1249
|
||||
#: src/pages/UserPublicProfile.tsx:1262
|
||||
msgid "Color scheme"
|
||||
msgstr "Color scheme"
|
||||
|
||||
@@ -249,11 +249,11 @@ msgstr "Color scheme"
|
||||
msgid "Confirm new password"
|
||||
msgstr "Confirm new password"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:92
|
||||
#: src/pages/UserPublicProfile.tsx:93
|
||||
msgid "Copied!"
|
||||
msgstr "Copied!"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:92
|
||||
#: src/pages/UserPublicProfile.tsx:93
|
||||
msgid "Copy"
|
||||
msgstr "Copy"
|
||||
|
||||
@@ -298,7 +298,7 @@ msgstr "Creating…"
|
||||
msgid "Current password"
|
||||
msgstr "Current password"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1271
|
||||
#: src/pages/UserPublicProfile.tsx:1284
|
||||
msgid "Dark"
|
||||
msgstr "Dark"
|
||||
|
||||
@@ -362,15 +362,15 @@ msgstr "Dump it"
|
||||
msgid "Dumped!"
|
||||
msgstr "Dumped!"
|
||||
|
||||
#: src/pages/Search.tsx:178
|
||||
#: src/pages/Search.tsx:172
|
||||
#: src/pages/UserDumps.tsx:107
|
||||
#: src/pages/UserPublicProfile.tsx:976
|
||||
#: src/pages/UserPublicProfile.tsx:989
|
||||
msgid "Dumps"
|
||||
msgstr "Dumps"
|
||||
|
||||
#. placeholder {0}: dumps.items.length
|
||||
#. placeholder {1}: dumps.hasMore ? "+" : ""
|
||||
#: src/pages/UserPublicProfile.tsx:993
|
||||
#: src/pages/UserPublicProfile.tsx:1006
|
||||
msgid "Dumps ({0}{1})"
|
||||
msgstr "Dumps ({0}{1})"
|
||||
|
||||
@@ -414,7 +414,7 @@ msgstr "Editing"
|
||||
msgid "Email address"
|
||||
msgstr "Email address"
|
||||
|
||||
#: src/pages/Search.tsx:211
|
||||
#: src/pages/Search.tsx:205
|
||||
msgid "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"
|
||||
msgstr "Failed to create playlist"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:73
|
||||
#: src/pages/UserPublicProfile.tsx:76
|
||||
#: src/pages/UserPublicProfile.tsx:104
|
||||
#: src/pages/UserPublicProfile.tsx:74
|
||||
#: src/pages/UserPublicProfile.tsx:77
|
||||
#: src/pages/UserPublicProfile.tsx:105
|
||||
msgid "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/NewFeed.tsx:36
|
||||
#: src/pages/Notifications.tsx:367
|
||||
#: src/pages/UserPublicProfile.tsx:1095
|
||||
#: src/pages/UserPublicProfile.tsx:1137
|
||||
#: src/pages/UserPublicProfile.tsx:1182
|
||||
#: src/pages/UserPublicProfile.tsx:1108
|
||||
#: src/pages/UserPublicProfile.tsx:1150
|
||||
#: src/pages/UserPublicProfile.tsx:1195
|
||||
msgid "Failed to load"
|
||||
msgstr "Failed to load"
|
||||
|
||||
@@ -457,10 +457,10 @@ msgid "Failed to post reply"
|
||||
msgstr "Failed to post reply"
|
||||
|
||||
#: src/pages/PlaylistDetail.tsx:795
|
||||
#: src/pages/UserPublicProfile.tsx:688
|
||||
#: src/pages/UserPublicProfile.tsx:726
|
||||
#: src/pages/UserPublicProfile.tsx:855
|
||||
#: src/pages/UserPublicProfile.tsx:935
|
||||
#: src/pages/UserPublicProfile.tsx:701
|
||||
#: src/pages/UserPublicProfile.tsx:739
|
||||
#: src/pages/UserPublicProfile.tsx:868
|
||||
#: src/pages/UserPublicProfile.tsx:948
|
||||
msgid "Failed to save"
|
||||
msgstr "Failed to save"
|
||||
|
||||
@@ -468,7 +468,7 @@ msgstr "Failed to save"
|
||||
msgid "Failed to save edit"
|
||||
msgstr "Failed to save edit"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:880
|
||||
#: src/pages/UserPublicProfile.tsx:893
|
||||
msgid "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."
|
||||
msgstr "Follow some users to see their dumps here."
|
||||
|
||||
#: src/components/FeedTabBar.tsx:20
|
||||
#: src/pages/UserPublicProfile.tsx:978
|
||||
#: src/components/FeedTabBar.tsx:16
|
||||
#: src/pages/UserPublicProfile.tsx:991
|
||||
msgid "Followed"
|
||||
msgstr "Followed"
|
||||
|
||||
@@ -521,13 +521,13 @@ msgstr "Followed"
|
||||
msgid "Followed ({0}{1})"
|
||||
msgstr "Followed ({0}{1})"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1126
|
||||
#: src/pages/UserPublicProfile.tsx:1139
|
||||
msgid "Followed playlists"
|
||||
msgstr "Followed playlists"
|
||||
|
||||
#: src/components/FollowButton.tsx:37
|
||||
#: src/components/FollowButton.tsx:64
|
||||
#: src/pages/UserPublicProfile.tsx:1084
|
||||
#: src/pages/UserPublicProfile.tsx:1097
|
||||
msgid "Following"
|
||||
msgstr "Following"
|
||||
|
||||
@@ -551,7 +551,7 @@ msgstr "Go home"
|
||||
msgid "Go to login"
|
||||
msgstr "Go to login"
|
||||
|
||||
#: src/components/FeedTabBar.tsx:16
|
||||
#: src/components/FeedTabBar.tsx:12
|
||||
msgid "Hot"
|
||||
msgstr "Hot"
|
||||
|
||||
@@ -567,16 +567,16 @@ msgstr "Invalid invite"
|
||||
msgid "Invalid link"
|
||||
msgstr "Invalid link"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:799
|
||||
#: src/pages/UserPublicProfile.tsx:812
|
||||
msgid "invited by"
|
||||
msgstr "invited by"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:979
|
||||
#: src/pages/UserPublicProfile.tsx:1171
|
||||
#: src/pages/UserPublicProfile.tsx:992
|
||||
#: src/pages/UserPublicProfile.tsx:1184
|
||||
msgid "Invitees"
|
||||
msgstr "Invitees"
|
||||
|
||||
#: src/components/FeedTabBar.tsx:18
|
||||
#: src/components/FeedTabBar.tsx:14
|
||||
msgid "Journal"
|
||||
msgstr "Journal"
|
||||
|
||||
@@ -584,7 +584,7 @@ msgstr "Journal"
|
||||
msgid "just now"
|
||||
msgstr "just now"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1264
|
||||
#: src/pages/UserPublicProfile.tsx:1277
|
||||
msgid "Light"
|
||||
msgstr "Light"
|
||||
|
||||
@@ -614,7 +614,7 @@ msgstr "Loading dump…"
|
||||
#: src/pages/index/HotFeed.tsx:64
|
||||
#: src/pages/index/JournalFeed.tsx:77
|
||||
#: src/pages/index/NewFeed.tsx:64
|
||||
#: src/pages/Search.tsx:248
|
||||
#: src/pages/Search.tsx:242
|
||||
#: src/pages/UserDumps.tsx:93
|
||||
#: src/pages/UserPlaylists.tsx:417
|
||||
#: src/pages/UserPlaylists.tsx:452
|
||||
@@ -626,7 +626,7 @@ msgstr "Loading more…"
|
||||
msgid "Loading playlist…"
|
||||
msgstr "Loading playlist…"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:736
|
||||
#: src/pages/UserPublicProfile.tsx:749
|
||||
msgid "Loading profile…"
|
||||
msgstr "Loading profile…"
|
||||
|
||||
@@ -642,9 +642,9 @@ msgstr "Loading profile…"
|
||||
#: src/pages/Notifications.tsx:439
|
||||
#: src/pages/UserDumps.tsx:51
|
||||
#: src/pages/UserPlaylists.tsx:342
|
||||
#: src/pages/UserPublicProfile.tsx:1089
|
||||
#: src/pages/UserPublicProfile.tsx:1131
|
||||
#: src/pages/UserPublicProfile.tsx:1176
|
||||
#: src/pages/UserPublicProfile.tsx:1102
|
||||
#: src/pages/UserPublicProfile.tsx:1144
|
||||
#: src/pages/UserPublicProfile.tsx:1189
|
||||
#: src/pages/UserUpvoted.tsx:122
|
||||
msgid "Loading…"
|
||||
msgstr "Loading…"
|
||||
@@ -663,8 +663,8 @@ msgstr "Log in to like"
|
||||
msgid "Log in to vote"
|
||||
msgstr "Log in to vote"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:757
|
||||
#: src/pages/UserPublicProfile.tsx:894
|
||||
#: src/pages/UserPublicProfile.tsx:770
|
||||
#: src/pages/UserPublicProfile.tsx:907
|
||||
msgid "Log out"
|
||||
msgstr "Log out"
|
||||
|
||||
@@ -684,13 +684,13 @@ msgstr "Max 50 MB"
|
||||
msgid "new"
|
||||
msgstr "new"
|
||||
|
||||
#: src/components/FeedTabBar.tsx:17
|
||||
#: src/components/FeedTabBar.tsx:13
|
||||
msgid "New"
|
||||
msgstr "New"
|
||||
|
||||
#: src/components/DumpCreateModal.tsx:302
|
||||
#: src/pages/UserDumps.tsx:115
|
||||
#: src/pages/UserPublicProfile.tsx:1320
|
||||
#: src/pages/UserPublicProfile.tsx:1333
|
||||
msgid "New dump"
|
||||
msgstr "New dump"
|
||||
|
||||
@@ -708,7 +708,7 @@ msgstr "New playlist"
|
||||
msgid "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}\"."
|
||||
msgstr "No dumps match \"{q}\"."
|
||||
|
||||
@@ -723,11 +723,11 @@ msgid "No emoji found."
|
||||
msgstr "No emoji found."
|
||||
|
||||
#: src/pages/UserPlaylists.tsx:439
|
||||
#: src/pages/UserPublicProfile.tsx:1144
|
||||
#: src/pages/UserPublicProfile.tsx:1157
|
||||
msgid "No followed playlists yet."
|
||||
msgstr "No followed playlists yet."
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1189
|
||||
#: src/pages/UserPublicProfile.tsx:1202
|
||||
msgid "No invitees yet."
|
||||
msgstr "No invitees yet."
|
||||
|
||||
@@ -735,28 +735,28 @@ msgstr "No invitees yet."
|
||||
msgid "No one yet."
|
||||
msgstr "No one yet."
|
||||
|
||||
#: src/pages/Search.tsx:287
|
||||
#: src/pages/Search.tsx:281
|
||||
msgid "No playlists match \"{q}\"."
|
||||
msgstr "No playlists match \"{q}\"."
|
||||
|
||||
#: src/components/PlaylistMembershipPanel.tsx:34
|
||||
#: src/pages/UserPlaylists.tsx:397
|
||||
#: src/pages/UserPublicProfile.tsx:1055
|
||||
#: src/pages/UserPublicProfile.tsx:1068
|
||||
msgid "No playlists yet."
|
||||
msgstr "No playlists yet."
|
||||
|
||||
#: src/pages/Search.tsx:261
|
||||
#: src/pages/Search.tsx:255
|
||||
msgid "No users match \"{q}\"."
|
||||
msgstr "No users match \"{q}\"."
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1102
|
||||
#: src/pages/UserPublicProfile.tsx:1115
|
||||
msgid "Not following anyone yet."
|
||||
msgstr "Not following anyone yet."
|
||||
|
||||
#: src/pages/Notifications.tsx:374
|
||||
#: src/pages/UserDumps.tsx:125
|
||||
#: src/pages/UserPublicProfile.tsx:1331
|
||||
#: src/pages/UserPublicProfile.tsx:1453
|
||||
#: src/pages/UserPublicProfile.tsx:1344
|
||||
#: src/pages/UserPublicProfile.tsx:1466
|
||||
#: src/pages/UserUpvoted.tsx:194
|
||||
msgid "Nothing here yet."
|
||||
msgstr "Nothing here yet."
|
||||
@@ -779,7 +779,7 @@ msgid "or <0>browse files</0>"
|
||||
msgstr "or <0>browse files</0>"
|
||||
|
||||
#: src/pages/UserLogin.tsx:106
|
||||
#: src/pages/UserPublicProfile.tsx:1209
|
||||
#: src/pages/UserPublicProfile.tsx:1222
|
||||
msgid "Password"
|
||||
msgstr "Password"
|
||||
|
||||
@@ -803,15 +803,15 @@ msgstr "Passwords do not match"
|
||||
|
||||
#: src/components/AppHeader.tsx:85
|
||||
#: src/components/UserMenu.tsx:62
|
||||
#: src/pages/Search.tsx:181
|
||||
#: src/pages/Search.tsx:175
|
||||
#: src/pages/UserPlaylists.tsx:368
|
||||
#: src/pages/UserPublicProfile.tsx:977
|
||||
#: src/pages/UserPublicProfile.tsx:990
|
||||
msgid "Playlists"
|
||||
msgstr "Playlists"
|
||||
|
||||
#. placeholder {0}: playlists.items.length
|
||||
#. placeholder {1}: playlists.hasMore ? "+" : ""
|
||||
#: src/pages/UserPublicProfile.tsx:1024
|
||||
#: src/pages/UserPublicProfile.tsx:1037
|
||||
msgid "Playlists ({0}{1})"
|
||||
msgstr "Playlists ({0}{1})"
|
||||
|
||||
@@ -930,8 +930,8 @@ msgstr "Retry"
|
||||
#: src/pages/Dump.tsx:347
|
||||
#: src/pages/DumpEdit.tsx:400
|
||||
#: src/pages/PlaylistDetail.tsx:679
|
||||
#: src/pages/UserPublicProfile.tsx:842
|
||||
#: src/pages/UserPublicProfile.tsx:924
|
||||
#: src/pages/UserPublicProfile.tsx:855
|
||||
#: src/pages/UserPublicProfile.tsx:937
|
||||
msgid "Save"
|
||||
msgstr "Save"
|
||||
|
||||
@@ -940,8 +940,8 @@ msgstr "Save"
|
||||
#: src/pages/Dump.tsx:347
|
||||
#: src/pages/PlaylistDetail.tsx:679
|
||||
#: src/pages/ResetPassword.tsx:152
|
||||
#: src/pages/UserPublicProfile.tsx:841
|
||||
#: src/pages/UserPublicProfile.tsx:924
|
||||
#: src/pages/UserPublicProfile.tsx:854
|
||||
#: src/pages/UserPublicProfile.tsx:937
|
||||
msgid "Saving…"
|
||||
msgstr "Saving…"
|
||||
|
||||
@@ -954,11 +954,11 @@ msgstr "Search"
|
||||
msgid "Search dumps, users, playlists…"
|
||||
msgstr "Search dumps, users, playlists…"
|
||||
|
||||
#: src/pages/Search.tsx:222
|
||||
#: src/pages/Search.tsx:216
|
||||
msgid "Search failed"
|
||||
msgstr "Search failed"
|
||||
|
||||
#: src/pages/Search.tsx:217
|
||||
#: src/pages/Search.tsx:211
|
||||
msgid "Searching…"
|
||||
msgstr "Searching…"
|
||||
|
||||
@@ -979,7 +979,7 @@ msgstr "Server unreachable"
|
||||
msgid "Set new password"
|
||||
msgstr "Set new password"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:981
|
||||
#: src/pages/UserPublicProfile.tsx:994
|
||||
msgid "Settings"
|
||||
msgstr "Settings"
|
||||
|
||||
@@ -987,7 +987,7 @@ msgstr "Settings"
|
||||
msgid "Something went wrong"
|
||||
msgstr "Something went wrong"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1228
|
||||
#: src/pages/UserPublicProfile.tsx:1241
|
||||
msgid "Style"
|
||||
msgstr "Style"
|
||||
|
||||
@@ -1042,7 +1042,7 @@ msgstr "Unfollow playlist"
|
||||
msgid "Unknown error"
|
||||
msgstr "Unknown error"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:657
|
||||
#: src/pages/UserPublicProfile.tsx:670
|
||||
msgid "Upload failed"
|
||||
msgstr "Upload failed"
|
||||
|
||||
@@ -1060,7 +1060,7 @@ msgstr "Upvoted"
|
||||
|
||||
#. placeholder {0}: votes.items.length
|
||||
#. placeholder {1}: votes.hasMore ? "+" : ""
|
||||
#: src/pages/UserPublicProfile.tsx:1004
|
||||
#: src/pages/UserPublicProfile.tsx:1017
|
||||
msgid "Upvoted ({0}{1})"
|
||||
msgstr "Upvoted ({0}{1})"
|
||||
|
||||
@@ -1082,15 +1082,15 @@ msgstr "User menu"
|
||||
msgid "Username"
|
||||
msgstr "Username"
|
||||
|
||||
#: src/pages/Search.tsx:180
|
||||
#: src/pages/Search.tsx:174
|
||||
msgid "Users"
|
||||
msgstr "Users"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1074
|
||||
#: src/pages/UserPublicProfile.tsx:1117
|
||||
#: src/pages/UserPublicProfile.tsx:1159
|
||||
#: src/pages/UserPublicProfile.tsx:1352
|
||||
#: src/pages/UserPublicProfile.tsx:1483
|
||||
#: src/pages/UserPublicProfile.tsx:1087
|
||||
#: src/pages/UserPublicProfile.tsx:1130
|
||||
#: src/pages/UserPublicProfile.tsx:1172
|
||||
#: src/pages/UserPublicProfile.tsx:1365
|
||||
#: src/pages/UserPublicProfile.tsx:1496
|
||||
msgid "View all →"
|
||||
msgstr "View all →"
|
||||
|
||||
@@ -1103,8 +1103,8 @@ msgstr "View dump →"
|
||||
msgid "What makes it worth it?"
|
||||
msgstr "What makes it worth it?"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:912
|
||||
#: src/pages/UserPublicProfile.tsx:961
|
||||
#: src/pages/UserPublicProfile.tsx:925
|
||||
#: src/pages/UserPublicProfile.tsx:974
|
||||
msgid "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/JournalFeed.tsx:82
|
||||
#: src/pages/index/NewFeed.tsx:69
|
||||
#: src/pages/Search.tsx:253
|
||||
#: src/pages/Search.tsx:247
|
||||
#: src/pages/UserDumps.tsx:98
|
||||
#: src/pages/UserPlaylists.tsx:422
|
||||
#: 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"
|
||||
msgstr "il y a {hrs}h"
|
||||
|
||||
#: src/pages/Search.tsx:182
|
||||
#: src/pages/Search.tsx:176
|
||||
msgid "{label} ({count})"
|
||||
msgstr "{label} ({count})"
|
||||
|
||||
@@ -55,7 +55,7 @@ msgid "{visibleCount, plural, one {# comment} other {# comments}}"
|
||||
msgstr "{visibleCount, plural, one {# commentaire} other {# commentaires}}"
|
||||
|
||||
#: src/pages/PlaylistDetail.tsx:617
|
||||
#: src/pages/UserPublicProfile.tsx:753
|
||||
#: src/pages/UserPublicProfile.tsx:766
|
||||
msgid "← Back"
|
||||
msgstr "← Retour"
|
||||
|
||||
@@ -71,7 +71,7 @@ msgstr "← Retour à toutes les recos"
|
||||
msgid "← Back to profile"
|
||||
msgstr "← Retour au profil"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:101
|
||||
#: src/pages/UserPublicProfile.tsx:102
|
||||
msgid "+ Invite someone"
|
||||
msgstr "+ Inviter quelqu'un"
|
||||
|
||||
@@ -142,7 +142,7 @@ msgstr "un commentaire"
|
||||
msgid "a post"
|
||||
msgstr "une publication"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1204
|
||||
#: src/pages/UserPublicProfile.tsx:1217
|
||||
msgid "Account"
|
||||
msgstr "Compte"
|
||||
|
||||
@@ -150,7 +150,7 @@ msgstr "Compte"
|
||||
msgid "Add a comment…"
|
||||
msgstr "Ajouter un commentaire…"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:871
|
||||
#: src/pages/UserPublicProfile.tsx:884
|
||||
msgid "Add email…"
|
||||
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>"
|
||||
msgstr "Vous avez déjà un compte ? <0>Se connecter</0>"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1223
|
||||
#: src/pages/UserPublicProfile.tsx:1236
|
||||
msgid "Appearance"
|
||||
msgstr "Apparence"
|
||||
|
||||
@@ -181,7 +181,7 @@ msgstr "Apparence"
|
||||
msgid "At least {0} characters"
|
||||
msgstr "Au moins {0} caractères"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1257
|
||||
#: src/pages/UserPublicProfile.tsx:1270
|
||||
msgid "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/DumpEdit.tsx:391
|
||||
#: src/pages/PlaylistDetail.tsx:686
|
||||
#: src/pages/UserPublicProfile.tsx:850
|
||||
#: src/pages/UserPublicProfile.tsx:932
|
||||
#: src/pages/UserPublicProfile.tsx:863
|
||||
#: src/pages/UserPublicProfile.tsx:945
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
@@ -218,7 +218,7 @@ msgstr "Annuler la suppression"
|
||||
msgid "Cancel search"
|
||||
msgstr "Annuler la recherche"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:780
|
||||
#: src/pages/UserPublicProfile.tsx:793
|
||||
msgid "Change avatar"
|
||||
msgstr "Changer l'avatar"
|
||||
|
||||
@@ -227,7 +227,7 @@ msgstr "Changer l'avatar"
|
||||
msgid "Change password"
|
||||
msgstr "Changer le mot de passe"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1216
|
||||
#: src/pages/UserPublicProfile.tsx:1229
|
||||
msgid "Change password…"
|
||||
msgstr "Changer le mot de passe…"
|
||||
|
||||
@@ -240,7 +240,7 @@ msgstr "Vérification de l'invitation…"
|
||||
msgid "Close"
|
||||
msgstr "Fermer"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1249
|
||||
#: src/pages/UserPublicProfile.tsx:1262
|
||||
msgid "Color scheme"
|
||||
msgstr "Thème de couleur"
|
||||
|
||||
@@ -249,11 +249,11 @@ msgstr "Thème de couleur"
|
||||
msgid "Confirm new password"
|
||||
msgstr "Confirmer le nouveau mot de passe"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:92
|
||||
#: src/pages/UserPublicProfile.tsx:93
|
||||
msgid "Copied!"
|
||||
msgstr "Copié !"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:92
|
||||
#: src/pages/UserPublicProfile.tsx:93
|
||||
msgid "Copy"
|
||||
msgstr "Copier"
|
||||
|
||||
@@ -298,7 +298,7 @@ msgstr "Création…"
|
||||
msgid "Current password"
|
||||
msgstr "Mot de passe actuel"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1271
|
||||
#: src/pages/UserPublicProfile.tsx:1284
|
||||
msgid "Dark"
|
||||
msgstr "Sombre"
|
||||
|
||||
@@ -362,15 +362,15 @@ msgstr "Recommander"
|
||||
msgid "Dumped!"
|
||||
msgstr "Recommandé !"
|
||||
|
||||
#: src/pages/Search.tsx:178
|
||||
#: src/pages/Search.tsx:172
|
||||
#: src/pages/UserDumps.tsx:107
|
||||
#: src/pages/UserPublicProfile.tsx:976
|
||||
#: src/pages/UserPublicProfile.tsx:989
|
||||
msgid "Dumps"
|
||||
msgstr "Recos"
|
||||
|
||||
#. placeholder {0}: dumps.items.length
|
||||
#. placeholder {1}: dumps.hasMore ? "+" : ""
|
||||
#: src/pages/UserPublicProfile.tsx:993
|
||||
#: src/pages/UserPublicProfile.tsx:1006
|
||||
msgid "Dumps ({0}{1})"
|
||||
msgstr "Recos ({0}{1})"
|
||||
|
||||
@@ -414,7 +414,7 @@ msgstr "Modification"
|
||||
msgid "Email address"
|
||||
msgstr "Adresse e-mail"
|
||||
|
||||
#: src/pages/Search.tsx:211
|
||||
#: src/pages/Search.tsx:205
|
||||
msgid "Enter a query to search."
|
||||
msgstr "Saisissez une recherche."
|
||||
|
||||
@@ -427,9 +427,9 @@ msgstr "Impossible de changer le mot de passe"
|
||||
msgid "Failed to create playlist"
|
||||
msgstr "Impossible de créer la collection"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:73
|
||||
#: src/pages/UserPublicProfile.tsx:76
|
||||
#: src/pages/UserPublicProfile.tsx:104
|
||||
#: src/pages/UserPublicProfile.tsx:74
|
||||
#: src/pages/UserPublicProfile.tsx:77
|
||||
#: src/pages/UserPublicProfile.tsx:105
|
||||
msgid "Failed to generate invite"
|
||||
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/NewFeed.tsx:36
|
||||
#: src/pages/Notifications.tsx:367
|
||||
#: src/pages/UserPublicProfile.tsx:1095
|
||||
#: src/pages/UserPublicProfile.tsx:1137
|
||||
#: src/pages/UserPublicProfile.tsx:1182
|
||||
#: src/pages/UserPublicProfile.tsx:1108
|
||||
#: src/pages/UserPublicProfile.tsx:1150
|
||||
#: src/pages/UserPublicProfile.tsx:1195
|
||||
msgid "Failed to load"
|
||||
msgstr "Chargement échoué"
|
||||
|
||||
@@ -457,10 +457,10 @@ msgid "Failed to post reply"
|
||||
msgstr "Impossible de publier la réponse"
|
||||
|
||||
#: src/pages/PlaylistDetail.tsx:795
|
||||
#: src/pages/UserPublicProfile.tsx:688
|
||||
#: src/pages/UserPublicProfile.tsx:726
|
||||
#: src/pages/UserPublicProfile.tsx:855
|
||||
#: src/pages/UserPublicProfile.tsx:935
|
||||
#: src/pages/UserPublicProfile.tsx:701
|
||||
#: src/pages/UserPublicProfile.tsx:739
|
||||
#: src/pages/UserPublicProfile.tsx:868
|
||||
#: src/pages/UserPublicProfile.tsx:948
|
||||
msgid "Failed to save"
|
||||
msgstr "Enregistrement échoué"
|
||||
|
||||
@@ -468,7 +468,7 @@ msgstr "Enregistrement échoué"
|
||||
msgid "Failed to save edit"
|
||||
msgstr "Impossible d'enregistrer la modification"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:880
|
||||
#: src/pages/UserPublicProfile.tsx:893
|
||||
msgid "Failed to update 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."
|
||||
msgstr "Suivez des utilisateurs pour voir leurs recos ici."
|
||||
|
||||
#: src/components/FeedTabBar.tsx:20
|
||||
#: src/pages/UserPublicProfile.tsx:978
|
||||
#: src/components/FeedTabBar.tsx:16
|
||||
#: src/pages/UserPublicProfile.tsx:991
|
||||
msgid "Followed"
|
||||
msgstr "Suivi"
|
||||
|
||||
@@ -521,13 +521,13 @@ msgstr "Suivi"
|
||||
msgid "Followed ({0}{1})"
|
||||
msgstr "Suivies ({0}{1})"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1126
|
||||
#: src/pages/UserPublicProfile.tsx:1139
|
||||
msgid "Followed playlists"
|
||||
msgstr "Collections suivies"
|
||||
|
||||
#: src/components/FollowButton.tsx:37
|
||||
#: src/components/FollowButton.tsx:64
|
||||
#: src/pages/UserPublicProfile.tsx:1084
|
||||
#: src/pages/UserPublicProfile.tsx:1097
|
||||
msgid "Following"
|
||||
msgstr "Abonné"
|
||||
|
||||
@@ -551,7 +551,7 @@ msgstr "Accueil"
|
||||
msgid "Go to login"
|
||||
msgstr "Aller à la connexion"
|
||||
|
||||
#: src/components/FeedTabBar.tsx:16
|
||||
#: src/components/FeedTabBar.tsx:12
|
||||
msgid "Hot"
|
||||
msgstr "Tendances"
|
||||
|
||||
@@ -567,16 +567,16 @@ msgstr "Invitation invalide"
|
||||
msgid "Invalid link"
|
||||
msgstr "Lien invalide"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:799
|
||||
#: src/pages/UserPublicProfile.tsx:812
|
||||
msgid "invited by"
|
||||
msgstr "invité par"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:979
|
||||
#: src/pages/UserPublicProfile.tsx:1171
|
||||
#: src/pages/UserPublicProfile.tsx:992
|
||||
#: src/pages/UserPublicProfile.tsx:1184
|
||||
msgid "Invitees"
|
||||
msgstr "Invités"
|
||||
|
||||
#: src/components/FeedTabBar.tsx:18
|
||||
#: src/components/FeedTabBar.tsx:14
|
||||
msgid "Journal"
|
||||
msgstr "Journal"
|
||||
|
||||
@@ -584,7 +584,7 @@ msgstr "Journal"
|
||||
msgid "just now"
|
||||
msgstr "à l'instant"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1264
|
||||
#: src/pages/UserPublicProfile.tsx:1277
|
||||
msgid "Light"
|
||||
msgstr "Clair"
|
||||
|
||||
@@ -614,7 +614,7 @@ msgstr "Chargement de la reco…"
|
||||
#: src/pages/index/HotFeed.tsx:64
|
||||
#: src/pages/index/JournalFeed.tsx:77
|
||||
#: src/pages/index/NewFeed.tsx:64
|
||||
#: src/pages/Search.tsx:248
|
||||
#: src/pages/Search.tsx:242
|
||||
#: src/pages/UserDumps.tsx:93
|
||||
#: src/pages/UserPlaylists.tsx:417
|
||||
#: src/pages/UserPlaylists.tsx:452
|
||||
@@ -626,7 +626,7 @@ msgstr "Chargement…"
|
||||
msgid "Loading playlist…"
|
||||
msgstr "Chargement de la collection…"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:736
|
||||
#: src/pages/UserPublicProfile.tsx:749
|
||||
msgid "Loading profile…"
|
||||
msgstr "Chargement du profil…"
|
||||
|
||||
@@ -642,9 +642,9 @@ msgstr "Chargement du profil…"
|
||||
#: src/pages/Notifications.tsx:439
|
||||
#: src/pages/UserDumps.tsx:51
|
||||
#: src/pages/UserPlaylists.tsx:342
|
||||
#: src/pages/UserPublicProfile.tsx:1089
|
||||
#: src/pages/UserPublicProfile.tsx:1131
|
||||
#: src/pages/UserPublicProfile.tsx:1176
|
||||
#: src/pages/UserPublicProfile.tsx:1102
|
||||
#: src/pages/UserPublicProfile.tsx:1144
|
||||
#: src/pages/UserPublicProfile.tsx:1189
|
||||
#: src/pages/UserUpvoted.tsx:122
|
||||
msgid "Loading…"
|
||||
msgstr "Chargement…"
|
||||
@@ -663,8 +663,8 @@ msgstr "Se connecter pour aimer"
|
||||
msgid "Log in to vote"
|
||||
msgstr "Se connecter pour voter"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:757
|
||||
#: src/pages/UserPublicProfile.tsx:894
|
||||
#: src/pages/UserPublicProfile.tsx:770
|
||||
#: src/pages/UserPublicProfile.tsx:907
|
||||
msgid "Log out"
|
||||
msgstr "Se déconnecter"
|
||||
|
||||
@@ -684,13 +684,13 @@ msgstr "Max 50 Mo"
|
||||
msgid "new"
|
||||
msgstr "nouveau"
|
||||
|
||||
#: src/components/FeedTabBar.tsx:17
|
||||
#: src/components/FeedTabBar.tsx:13
|
||||
msgid "New"
|
||||
msgstr "Nouveau"
|
||||
|
||||
#: src/components/DumpCreateModal.tsx:302
|
||||
#: src/pages/UserDumps.tsx:115
|
||||
#: src/pages/UserPublicProfile.tsx:1320
|
||||
#: src/pages/UserPublicProfile.tsx:1333
|
||||
msgid "New dump"
|
||||
msgstr "Nouvelle reco"
|
||||
|
||||
@@ -708,7 +708,7 @@ msgstr "Nouvelle collection"
|
||||
msgid "No dumps in this playlist yet."
|
||||
msgstr "Aucune reco dans cette collection pour l'instant."
|
||||
|
||||
#: src/pages/Search.tsx:228
|
||||
#: src/pages/Search.tsx:222
|
||||
msgid "No dumps match \"{q}\"."
|
||||
msgstr "Aucune reco ne correspond à « {q} »."
|
||||
|
||||
@@ -723,11 +723,11 @@ msgid "No emoji found."
|
||||
msgstr "Aucun emoji trouvé."
|
||||
|
||||
#: src/pages/UserPlaylists.tsx:439
|
||||
#: src/pages/UserPublicProfile.tsx:1144
|
||||
#: src/pages/UserPublicProfile.tsx:1157
|
||||
msgid "No followed playlists yet."
|
||||
msgstr "Pas encore de collections suivies."
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1189
|
||||
#: src/pages/UserPublicProfile.tsx:1202
|
||||
msgid "No invitees yet."
|
||||
msgstr "Aucun invité pour le moment."
|
||||
|
||||
@@ -735,28 +735,28 @@ msgstr "Aucun invité pour le moment."
|
||||
msgid "No one yet."
|
||||
msgstr "Personne pour le moment."
|
||||
|
||||
#: src/pages/Search.tsx:287
|
||||
#: src/pages/Search.tsx:281
|
||||
msgid "No playlists match \"{q}\"."
|
||||
msgstr "Aucune collection ne correspond à « {q} »."
|
||||
|
||||
#: src/components/PlaylistMembershipPanel.tsx:34
|
||||
#: src/pages/UserPlaylists.tsx:397
|
||||
#: src/pages/UserPublicProfile.tsx:1055
|
||||
#: src/pages/UserPublicProfile.tsx:1068
|
||||
msgid "No playlists yet."
|
||||
msgstr "Pas encore de collections."
|
||||
|
||||
#: src/pages/Search.tsx:261
|
||||
#: src/pages/Search.tsx:255
|
||||
msgid "No users match \"{q}\"."
|
||||
msgstr "Aucun utilisateur ne correspond à « {q} »."
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1102
|
||||
#: src/pages/UserPublicProfile.tsx:1115
|
||||
msgid "Not following anyone yet."
|
||||
msgstr "Aucun abonnement pour le moment."
|
||||
|
||||
#: src/pages/Notifications.tsx:374
|
||||
#: src/pages/UserDumps.tsx:125
|
||||
#: src/pages/UserPublicProfile.tsx:1331
|
||||
#: src/pages/UserPublicProfile.tsx:1453
|
||||
#: src/pages/UserPublicProfile.tsx:1344
|
||||
#: src/pages/UserPublicProfile.tsx:1466
|
||||
#: src/pages/UserUpvoted.tsx:194
|
||||
msgid "Nothing here yet."
|
||||
msgstr "Rien ici pour l'instant."
|
||||
@@ -779,7 +779,7 @@ msgid "or <0>browse files</0>"
|
||||
msgstr "ou <0>parcourir les fichiers</0>"
|
||||
|
||||
#: src/pages/UserLogin.tsx:106
|
||||
#: src/pages/UserPublicProfile.tsx:1209
|
||||
#: src/pages/UserPublicProfile.tsx:1222
|
||||
msgid "Password"
|
||||
msgstr "Mot de passe"
|
||||
|
||||
@@ -803,15 +803,15 @@ msgstr "Les mots de passe ne correspondent pas"
|
||||
|
||||
#: src/components/AppHeader.tsx:85
|
||||
#: src/components/UserMenu.tsx:62
|
||||
#: src/pages/Search.tsx:181
|
||||
#: src/pages/Search.tsx:175
|
||||
#: src/pages/UserPlaylists.tsx:368
|
||||
#: src/pages/UserPublicProfile.tsx:977
|
||||
#: src/pages/UserPublicProfile.tsx:990
|
||||
msgid "Playlists"
|
||||
msgstr "Collections"
|
||||
|
||||
#. placeholder {0}: playlists.items.length
|
||||
#. placeholder {1}: playlists.hasMore ? "+" : ""
|
||||
#: src/pages/UserPublicProfile.tsx:1024
|
||||
#: src/pages/UserPublicProfile.tsx:1037
|
||||
msgid "Playlists ({0}{1})"
|
||||
msgstr "Collections ({0}{1})"
|
||||
|
||||
@@ -883,7 +883,7 @@ msgstr "Inscription échouée"
|
||||
|
||||
#: src/pages/Dump.tsx:467
|
||||
msgid "Related"
|
||||
msgstr "Apparentés"
|
||||
msgstr "Connexe"
|
||||
|
||||
#: src/components/FileDropZone.tsx:115
|
||||
msgid "Remove file"
|
||||
@@ -930,8 +930,8 @@ msgstr "Réessayer"
|
||||
#: src/pages/Dump.tsx:347
|
||||
#: src/pages/DumpEdit.tsx:400
|
||||
#: src/pages/PlaylistDetail.tsx:679
|
||||
#: src/pages/UserPublicProfile.tsx:842
|
||||
#: src/pages/UserPublicProfile.tsx:924
|
||||
#: src/pages/UserPublicProfile.tsx:855
|
||||
#: src/pages/UserPublicProfile.tsx:937
|
||||
msgid "Save"
|
||||
msgstr "Enregistrer"
|
||||
|
||||
@@ -940,8 +940,8 @@ msgstr "Enregistrer"
|
||||
#: src/pages/Dump.tsx:347
|
||||
#: src/pages/PlaylistDetail.tsx:679
|
||||
#: src/pages/ResetPassword.tsx:152
|
||||
#: src/pages/UserPublicProfile.tsx:841
|
||||
#: src/pages/UserPublicProfile.tsx:924
|
||||
#: src/pages/UserPublicProfile.tsx:854
|
||||
#: src/pages/UserPublicProfile.tsx:937
|
||||
msgid "Saving…"
|
||||
msgstr "Enregistrement…"
|
||||
|
||||
@@ -954,11 +954,11 @@ msgstr "Rechercher"
|
||||
msgid "Search dumps, users, playlists…"
|
||||
msgstr "Rechercher des recos, utilisateurs, collections…"
|
||||
|
||||
#: src/pages/Search.tsx:222
|
||||
#: src/pages/Search.tsx:216
|
||||
msgid "Search failed"
|
||||
msgstr "Recherche échouée"
|
||||
|
||||
#: src/pages/Search.tsx:217
|
||||
#: src/pages/Search.tsx:211
|
||||
msgid "Searching…"
|
||||
msgstr "Recherche…"
|
||||
|
||||
@@ -979,7 +979,7 @@ msgstr "Serveur inaccessible"
|
||||
msgid "Set new password"
|
||||
msgstr "Définir un nouveau mot de passe"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:981
|
||||
#: src/pages/UserPublicProfile.tsx:994
|
||||
msgid "Settings"
|
||||
msgstr "Paramètres"
|
||||
|
||||
@@ -987,7 +987,7 @@ msgstr "Paramètres"
|
||||
msgid "Something went wrong"
|
||||
msgstr "Une erreur est survenue"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1228
|
||||
#: src/pages/UserPublicProfile.tsx:1241
|
||||
msgid "Style"
|
||||
msgstr "Style"
|
||||
|
||||
@@ -1042,7 +1042,7 @@ msgstr "Ne plus suivre la collection"
|
||||
msgid "Unknown error"
|
||||
msgstr "Erreur inconnue"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:657
|
||||
#: src/pages/UserPublicProfile.tsx:670
|
||||
msgid "Upload failed"
|
||||
msgstr "Envoi échoué"
|
||||
|
||||
@@ -1060,7 +1060,7 @@ msgstr "Voté"
|
||||
|
||||
#. placeholder {0}: votes.items.length
|
||||
#. placeholder {1}: votes.hasMore ? "+" : ""
|
||||
#: src/pages/UserPublicProfile.tsx:1004
|
||||
#: src/pages/UserPublicProfile.tsx:1017
|
||||
msgid "Upvoted ({0}{1})"
|
||||
msgstr "Votés ({0}{1})"
|
||||
|
||||
@@ -1082,15 +1082,15 @@ msgstr "Menu utilisateur"
|
||||
msgid "Username"
|
||||
msgstr "Nom d'utilisateur"
|
||||
|
||||
#: src/pages/Search.tsx:180
|
||||
#: src/pages/Search.tsx:174
|
||||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:1074
|
||||
#: src/pages/UserPublicProfile.tsx:1117
|
||||
#: src/pages/UserPublicProfile.tsx:1159
|
||||
#: src/pages/UserPublicProfile.tsx:1352
|
||||
#: src/pages/UserPublicProfile.tsx:1483
|
||||
#: src/pages/UserPublicProfile.tsx:1087
|
||||
#: src/pages/UserPublicProfile.tsx:1130
|
||||
#: src/pages/UserPublicProfile.tsx:1172
|
||||
#: src/pages/UserPublicProfile.tsx:1365
|
||||
#: src/pages/UserPublicProfile.tsx:1496
|
||||
msgid "View all →"
|
||||
msgstr "Tout voir →"
|
||||
|
||||
@@ -1103,8 +1103,8 @@ msgstr "Voir la reco →"
|
||||
msgid "What makes it worth it?"
|
||||
msgstr "Pourquoi on en voudrait ?"
|
||||
|
||||
#: src/pages/UserPublicProfile.tsx:912
|
||||
#: src/pages/UserPublicProfile.tsx:961
|
||||
#: src/pages/UserPublicProfile.tsx:925
|
||||
#: src/pages/UserPublicProfile.tsx:974
|
||||
msgid "Who am I?"
|
||||
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/JournalFeed.tsx:82
|
||||
#: src/pages/index/NewFeed.tsx:69
|
||||
#: src/pages/Search.tsx:253
|
||||
#: src/pages/Search.tsx:247
|
||||
#: src/pages/UserDumps.tsx:98
|
||||
#: src/pages/UserPlaylists.tsx:422
|
||||
#: src/pages/UserPlaylists.tsx:457
|
||||
|
||||
@@ -11,7 +11,8 @@ import { useLocation } from "react-router";
|
||||
import { AppHeader } from "../components/AppHeader.tsx";
|
||||
import { PresenceRow } from "../components/PresenceRow.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";
|
||||
|
||||
@@ -82,20 +83,20 @@ export function Index() {
|
||||
);
|
||||
const mainFetchDone = useRef(false);
|
||||
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const rawTab = searchParams.get("tab") ?? "hot";
|
||||
const tab: FeedTab = VALID_TABS.has(rawTab) ? rawTab as FeedTab : "hot";
|
||||
const [tab] = useTabParam<FeedTab>(FEED_TABS, "hot");
|
||||
|
||||
// Web Share Target: Android share sheet navigates to /?share_url=...
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const shareUrl = searchParams.get("share_url") ??
|
||||
searchParams.get("share_text") ?? "";
|
||||
|
||||
useEffect(() => {
|
||||
if (!shareUrl) return;
|
||||
// Clean share params from the URL so a refresh doesn't re-open the modal
|
||||
const clean = tab !== "hot" ? `?tab=${tab}` : "";
|
||||
globalThis.history.replaceState({}, "", location.pathname + clean);
|
||||
}, [shareUrl, tab, location.pathname]);
|
||||
// Clean share params from the URL so a refresh doesn't re-open the modal.
|
||||
// The active tab already lives in the pathname (e.g. /~/new), so just drop
|
||||
// the query string.
|
||||
globalThis.history.replaceState({}, "", location.pathname);
|
||||
}, [shareUrl, location.pathname]);
|
||||
|
||||
// ── Main feed fetch ──
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ export function ResetPassword() {
|
||||
);
|
||||
}
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
const handleSubmit = async (e: React.SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
if (mismatch || tooShort || !newPassword) return;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ErrorCard } from "../components/ErrorCard.tsx";
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useWS } from "../hooks/useWS.ts";
|
||||
import { useInfiniteScroll } from "../hooks/useInfiniteScroll.ts";
|
||||
import { useTabParam } from "../hooks/useTabParam.ts";
|
||||
import {
|
||||
deserializeDump,
|
||||
deserializePlaylist,
|
||||
@@ -23,7 +24,8 @@ import {
|
||||
} from "../model.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 =
|
||||
| { status: "idle" }
|
||||
@@ -44,9 +46,9 @@ type SearchState =
|
||||
};
|
||||
|
||||
export function Search() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
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 { voteCounts, myVotes, castVote, removeVote } = useWS();
|
||||
@@ -159,14 +161,6 @@ export function Search() {
|
||||
!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 userCount = state.status === "loaded" ? state.users.length : null;
|
||||
const playlistCount = state.status === "loaded"
|
||||
@@ -188,7 +182,7 @@ export function Search() {
|
||||
<main className="search-page">
|
||||
{q && (
|
||||
<TabBar
|
||||
tabs={(["dumps", "users", "playlists"] as Tab[]).map((key) => ({
|
||||
tabs={SEARCH_TABS.map((key) => ({
|
||||
key,
|
||||
label: tabLabel(
|
||||
key,
|
||||
|
||||
@@ -64,7 +64,7 @@ export function UserLogin() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleResetRequest = async (e: React.FormEvent) => {
|
||||
const handleResetRequest = async (e: React.SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
if (!resetEmail.trim()) return;
|
||||
setResetState({ status: "submitting" });
|
||||
|
||||
@@ -54,6 +54,7 @@ import { TextEditor } from "../components/TextEditor.tsx";
|
||||
import { Markdown } from "../components/Markdown.tsx";
|
||||
import { ChangePasswordModal } from "../components/ChangePasswordModal.tsx";
|
||||
import { TabBar } from "../components/TabBar.tsx";
|
||||
import { useTabParam } from "../hooks/useTabParam.ts";
|
||||
|
||||
function InviteButton() {
|
||||
const { authFetch } = useAuth();
|
||||
@@ -146,7 +147,14 @@ type InviteTreeState =
|
||||
| { status: "loaded"; entries: InviteTreeEntry[] };
|
||||
|
||||
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(
|
||||
entries: InviteTreeEntry[],
|
||||
@@ -193,6 +201,13 @@ export function UserPublicProfile() {
|
||||
const profileUserId = state.status === "loaded" ? state.user.id : null;
|
||||
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[]) => {
|
||||
setState((s) =>
|
||||
s.status !== "loaded"
|
||||
@@ -287,7 +302,6 @@ export function UserPublicProfile() {
|
||||
const [emailError, setEmailError] = useState<string | null>(null);
|
||||
const prevMyVotesRef = useRef<Set<string> | null>(null);
|
||||
|
||||
const [tab, setTab] = useState<ProfileTab>("dumps");
|
||||
const [changePasswordOpen, setChangePasswordOpen] = useState(false);
|
||||
const [followedState, setFollowedState] = useState<FollowedState>(null);
|
||||
const [inviteTreeState, setInviteTreeState] = useState<InviteTreeState>(null);
|
||||
@@ -301,7 +315,6 @@ export function UserPublicProfile() {
|
||||
if (prevUsername !== username) {
|
||||
setPrevUsername(username);
|
||||
setState({ status: "loading" });
|
||||
setTab("dumps");
|
||||
setFollowedState(null);
|
||||
setInviteTreeState(null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user