v4: asking for a password reset link is a page, /reset-password — "Forgot password?" unfolded the request form on /login as a state of the page, which a reload or a link lost, while /reset-password, where the emailed link leads, answered without its token "Invalid link". The page now holds both steps: without a token it asks for the link, with one it sets the new password, and "Forgot password?" links to it. The login page's unfolding panel and its styles are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-10-09 22:24:13 +00:00
parent 9a215b0cc5
commit 71df0268e3
11 changed files with 322 additions and 381 deletions

43
API.md
View File

@@ -22,27 +22,28 @@ has one word, the same as in pages.
## Pages ## Pages
| Path | Views | Access | Page | | Path | Views | Access | Page |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | ------------------------------ | | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | --------------------------- |
| `/` | `/hot`, `/new`, `/journal`, `/follow`, `/follow/playlists`; the bare `/` is the viewer's default tab, the bare `/follow` the users followed | anyone, `/follow` signed in | `Index` | | `/` | `/hot`, `/new`, `/journal`, `/follow`, `/follow/playlists`; the bare `/` is the viewer's default tab, the bare `/follow` the users followed | anyone, `/follow` signed in | `Index` |
| `/<category>` | `/<category>/hot`, `/new`, `/journal`, `/follow`, `/follow/playlists` | anyone, `/follow` signed in | `Index` | | `/<category>` | `/<category>/hot`, `/new`, `/journal`, `/follow`, `/follow/playlists` | anyone, `/follow` signed in | `Index` |
| `/@<name>` | the overview: dumps beside upvoted | anyone | `UserPublicProfile` | | `/@<name>` | the overview: dumps beside upvoted | anyone | `UserPublicProfile` |
| `/@<name>/dumps`, `/upvoted`, `/playlists`, `/invitees` | | anyone | `UserPublicProfile` | | `/@<name>/dumps`, `/upvoted`, `/playlists`, `/invitees` | | anyone | `UserPublicProfile` |
| `/@<name>/follow` | `/@<name>/follow/playlists`; the bare tab lists users | anyone | `UserPublicProfile` | | `/@<name>/follow` | `/@<name>/follow/playlists`; the bare tab lists users | anyone | `UserPublicProfile` |
| `/@<name>/manage` | | `user:manage`, on others' profiles | `UserPublicProfile` | | `/@<name>/manage` | | `user:manage`, on others' profiles | `UserPublicProfile` |
| `/@<name>/dumps/<slug>` | | anyone; a private dump, its poster | `Dump` | | `/@<name>/dumps/<slug>` | | anyone; a private dump, its poster | `Dump` |
| `/@<name>/dumps/<slug>/edit` | | its poster, `dump:moderate` | `DumpEdit` | | `/@<name>/dumps/<slug>/edit` | | its poster, `dump:moderate` | `DumpEdit` |
| `/@<name>/playlists/<slug>` | | anyone; a private playlist, its owner | `PlaylistDetail` | | `/@<name>/playlists/<slug>` | | anyone; a private playlist, its owner | `PlaylistDetail` |
| `/@<name>/playlists/<slug>/edit` | | its owner, `playlist:moderate` | `PlaylistDetail` | | `/@<name>/playlists/<slug>/edit` | | its owner, `playlist:moderate` | `PlaylistDetail` |
| `/search?q=` | `/search/users?q=`, `/search/playlists?q=` | anyone | `Search` | | `/search?q=` | `/search/users?q=`, `/search/playlists?q=` | anyone | `Search` |
| `/notifications` | | signed in | `Notifications` | | `/notifications` | | signed in | `Notifications` |
| `/community` | `/community/monthly`; the bare path is the week | signed in | `Community` | | `/community` | `/community/monthly`; the bare path is the week | signed in | `Community` |
| `/settings` | | signed in | `Settings` | | `/settings` | | signed in | `Settings` |
| `/manage` | | `category:manage`, `federation:manage` | `Manage` | | `/manage` | | `category:manage`, `federation:manage` | `Manage` |
| `/login`, `/register?token=` | `/login?next=<path>` comes back to a page | signed out | `UserLogin`, `UserRegister` | | `/login`, `/register?token=` | `/login?next=<path>` comes back to a page | signed out | `UserLogin`, `UserRegister` |
| `/authorize?client_id=…` | | signed in, else via `/login?next=` | `Authorize` | | `/authorize?client_id=…` | | signed in, else via `/login?next=` | `Authorize` |
| `/federation/callback?code=&state=` | | anyone | `FederationCallback` | | `/federation/callback?code=&state=` | | anyone | `FederationCallback` |
| `/reset-password?token=`, `/unsubscribe?token=` | | anyone | `ResetPassword`, `Unsubscribe` | | `/reset-password` | `/reset-password?token=`, from the emailed link, sets the new password | anyone | `ResetPassword` |
| `/unsubscribe?token=` | | anyone | `Unsubscribe` |
`/?share_url=` opens the create form on a link shared from Android, `/?share_url=` opens the create form on a link shared from Android,
`/?chat=open` opens the chat over any page, and `#comment-<id>` scrolls a dump's `/?chat=open` opens the chat over any page, and `#comment-<id>` scrolls a dump's

View File

@@ -206,14 +206,14 @@ msgstr "# Welcome to {site}!\n"
"Your account has been created successfully. Welcome aboard!" "Your account has been created successfully. Welcome aboard!"
#: src/components/dump/create/DumpCreateModal.tsx:736 #: src/components/dump/create/DumpCreateModal.tsx:736
#: src/pages/PlaylistDetail.tsx:80 #: src/pages/PlaylistDetail.tsx:98
#: src/pages/UserPublicProfile.tsx:125 #: src/pages/UserPublicProfile.tsx:125
msgid "← Back" msgid "← Back"
msgstr "← Back" msgstr "← Back"
#: src/pages/Dump.tsx:325 #: src/pages/Dump.tsx:330
#: src/pages/Dump.tsx:552 #: src/pages/Dump.tsx:557
#: src/pages/DumpEdit.tsx:233 #: src/pages/DumpEdit.tsx:234
msgid "← Back to all dumps" msgid "← Back to all dumps"
msgstr "← Back to all dumps" msgstr "← Back to all dumps"
@@ -225,7 +225,7 @@ msgstr "+ Invite someone"
msgid "+ New playlist" msgid "+ New playlist"
msgstr "+ New playlist" msgstr "+ New playlist"
#: src/pages/Dump.tsx:399 #: src/pages/Dump.tsx:404
msgid "+ Playlist" msgid "+ Playlist"
msgstr "+ Playlist" msgstr "+ Playlist"
@@ -377,7 +377,7 @@ msgstr "Appearance"
#. placeholder {0}: VALIDATION.PASSWORD_MIN #. placeholder {0}: VALIDATION.PASSWORD_MIN
#: src/components/profile/ChangePasswordModal.tsx:101 #: src/components/profile/ChangePasswordModal.tsx:101
#: src/pages/ResetPassword.tsx:124 #: src/pages/ResetPassword.tsx:176
#: src/pages/UserRegister.tsx:168 #: src/pages/UserRegister.tsx:168
msgid "At least {0} characters" msgid "At least {0} characters"
msgstr "At least {0} characters" msgstr "At least {0} characters"
@@ -390,8 +390,8 @@ msgstr "At most {maxLength} characters."
msgid "Auto" msgid "Auto"
msgstr "Auto" msgstr "Auto"
#: src/pages/ResetPassword.tsx:74 #: src/pages/ResetPassword.tsx:101
#: src/pages/ResetPassword.tsx:147 #: src/pages/ResetPassword.tsx:199
msgid "Back to login" msgid "Back to login"
msgstr "Back to login" msgstr "Back to login"
@@ -439,8 +439,8 @@ msgstr "Can't sign in there"
#: src/components/profile/EmailEditor.tsx:83 #: src/components/profile/EmailEditor.tsx:83
#: src/components/shared/ConfirmModal.tsx:63 #: src/components/shared/ConfirmModal.tsx:63
#: src/pages/Authorize.tsx:158 #: src/pages/Authorize.tsx:158
#: src/pages/Dump.tsx:441 #: src/pages/Dump.tsx:446
#: src/pages/DumpEdit.tsx:560 #: src/pages/DumpEdit.tsx:561
msgid "Cancel" msgid "Cancel"
msgstr "Cancel" msgstr "Cancel"
@@ -546,7 +546,7 @@ msgid "digest.community"
msgstr "Community" msgstr "Community"
#: src/components/profile/ChangePasswordModal.tsx:108 #: src/components/profile/ChangePasswordModal.tsx:108
#: src/pages/ResetPassword.tsx:131 #: src/pages/ResetPassword.tsx:183
msgid "Confirm new password" msgid "Confirm new password"
msgstr "Confirm new password" msgstr "Confirm new password"
@@ -582,8 +582,8 @@ msgstr "Could not change password"
msgid "Could not connect to the server. Check your connection and try again." msgid "Could not connect to the server. Check your connection and try again."
msgstr "Could not connect to the server. Check your connection and try again." msgstr "Could not connect to the server. Check your connection and try again."
#: src/components/profile/ProfilePlaylistsTab.tsx:177 #: src/components/profile/ProfilePlaylistsTab.tsx:178
#: src/pages/DumpEdit.tsx:554 #: src/pages/DumpEdit.tsx:555
msgid "Could not delete" msgid "Could not delete"
msgstr "Could not delete" msgstr "Could not delete"
@@ -591,7 +591,7 @@ msgstr "Could not delete"
msgid "Could not rename" msgid "Could not rename"
msgstr "Could not rename" msgstr "Could not rename"
#: src/pages/DumpEdit.tsx:506 #: src/pages/DumpEdit.tsx:507
msgid "Could not save" msgid "Could not save"
msgstr "Could not save" msgstr "Could not save"
@@ -668,20 +668,20 @@ msgstr "Delete category"
msgid "Delete category \"{0}\"? This cannot be undone." msgid "Delete category \"{0}\"? This cannot be undone."
msgstr "Delete category \"{0}\"? This cannot be undone." msgstr "Delete category \"{0}\"? This cannot be undone."
#: src/pages/DumpEdit.tsx:315 #: src/pages/DumpEdit.tsx:316
#: src/pages/DumpEdit.tsx:557 #: src/pages/DumpEdit.tsx:558
msgid "Delete dump" msgid "Delete dump"
msgstr "Delete dump" msgstr "Delete dump"
#. placeholder {0}: res.status #. placeholder {0}: res.status
#: src/pages/DumpEdit.tsx:194 #: src/pages/DumpEdit.tsx:195
msgid "Delete failed ({0})" msgid "Delete failed ({0})"
msgstr "Delete failed ({0})" msgstr "Delete failed ({0})"
#: src/components/playlist/PlaylistCard.tsx:149 #: src/components/playlist/PlaylistCard.tsx:149
#: src/components/playlist/PlaylistEditForm.tsx:213 #: src/components/playlist/PlaylistEditForm.tsx:213
#: src/components/profile/ProfilePlaylistsTab.tsx:207 #: src/components/profile/ProfilePlaylistsTab.tsx:208
#: src/pages/PlaylistDetail.tsx:141 #: src/pages/PlaylistDetail.tsx:172
msgid "Delete playlist" msgid "Delete playlist"
msgstr "Delete playlist" msgstr "Delete playlist"
@@ -689,7 +689,7 @@ msgstr "Delete playlist"
msgid "Delete this comment?" msgid "Delete this comment?"
msgstr "Delete this comment?" msgstr "Delete this comment?"
#: src/pages/DumpEdit.tsx:314 #: src/pages/DumpEdit.tsx:315
msgid "Delete this dump? This cannot be undone." msgid "Delete this dump? This cannot be undone."
msgstr "Delete this dump? This cannot be undone." msgstr "Delete this dump? This cannot be undone."
@@ -697,8 +697,8 @@ msgstr "Delete this dump? This cannot be undone."
msgid "Delete this message?" msgid "Delete this message?"
msgstr "Delete this message?" msgstr "Delete this message?"
#: src/components/profile/ProfilePlaylistsTab.tsx:206 #: src/components/profile/ProfilePlaylistsTab.tsx:207
#: src/pages/PlaylistDetail.tsx:140 #: src/pages/PlaylistDetail.tsx:171
msgid "Delete this playlist? This cannot be undone." msgid "Delete this playlist? This cannot be undone."
msgstr "Delete this playlist? This cannot be undone." msgstr "Delete this playlist? This cannot be undone."
@@ -748,7 +748,7 @@ msgstr "Drop a file here"
msgid "Drop a file here, or paste one" msgid "Drop a file here, or paste one"
msgstr "Drop a file here, or paste one" msgstr "Drop a file here, or paste one"
#: src/pages/DumpEdit.tsx:497 #: src/pages/DumpEdit.tsx:498
msgid "Drop a replacement here" msgid "Drop a replacement here"
msgstr "Drop a replacement here" msgstr "Drop a replacement here"
@@ -788,7 +788,7 @@ msgstr "Earlier"
#: src/components/chat/PendingItem.tsx:93 #: src/components/chat/PendingItem.tsx:93
#: src/components/comment/CommentNode.tsx:256 #: src/components/comment/CommentNode.tsx:256
#: src/components/playlist/PlaylistHeader.tsx:60 #: src/components/playlist/PlaylistHeader.tsx:60
#: src/pages/Dump.tsx:548 #: src/pages/Dump.tsx:553
msgid "Edit" msgid "Edit"
msgstr "Edit" msgstr "Edit"
@@ -806,7 +806,7 @@ msgid "Edit email"
msgstr "Edit email" msgstr "Edit email"
#: src/components/dump/create/TitleField.tsx:69 #: src/components/dump/create/TitleField.tsx:69
#: src/pages/Dump.tsx:458 #: src/pages/Dump.tsx:463
msgid "Edit title" msgid "Edit title"
msgstr "Edit title" msgstr "Edit title"
@@ -820,7 +820,7 @@ msgstr "edited"
#: src/components/chat/ChatMessageItem.tsx:132 #: src/components/chat/ChatMessageItem.tsx:132
#: src/components/comment/CommentNode.tsx:205 #: src/components/comment/CommentNode.tsx:205
#: src/components/playlist/PlaylistHeader.tsx:95 #: src/components/playlist/PlaylistHeader.tsx:95
#: src/pages/Dump.tsx:501 #: src/pages/Dump.tsx:506
msgid "edited {0}" msgid "edited {0}"
msgstr "edited {0}" msgstr "edited {0}"
@@ -830,16 +830,16 @@ msgstr "edited {0}"
#: src/components/chat/ChatMessageItem.tsx:128 #: src/components/chat/ChatMessageItem.tsx:128
#: src/components/comment/CommentNode.tsx:203 #: src/components/comment/CommentNode.tsx:203
#: src/components/playlist/PlaylistHeader.tsx:93 #: src/components/playlist/PlaylistHeader.tsx:93
#: src/pages/Dump.tsx:499 #: src/pages/Dump.tsx:504
msgid "Edited {0}" msgid "Edited {0}"
msgstr "Edited {0}" msgstr "Edited {0}"
#: src/pages/DumpEdit.tsx:258 #: src/pages/DumpEdit.tsx:259
msgid "Editing" msgid "Editing"
msgstr "Editing" msgstr "Editing"
#. placeholder {0}: state.dump.title #. placeholder {0}: state.dump.title
#: src/pages/DumpEdit.tsx:81 #: src/pages/DumpEdit.tsx:82
msgid "Editing {0}" msgid "Editing {0}"
msgstr "Editing {0}" msgstr "Editing {0}"
@@ -942,15 +942,15 @@ msgstr "Follow {targetUsername}"
msgid "Follow playlist" msgid "Follow playlist"
msgstr "Follow playlist" msgstr "Follow playlist"
#: src/components/feed/FollowedFeed.tsx:151 #: src/components/feed/FollowedFeed.tsx:164
msgid "Follow some public playlists to see their dumps here." msgid "Follow some public playlists to see their dumps here."
msgstr "Follow some public playlists to see their dumps here." msgstr "Follow some public playlists to see their dumps here."
#: src/components/feed/FollowedFeed.tsx:143 #: src/components/feed/FollowedFeed.tsx:156
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/feed/FeedTabBar.tsx:44 #: src/components/feed/FeedTabBar.tsx:61
#: src/components/profile/ProfileFollowTab.tsx:140 #: src/components/profile/ProfileFollowTab.tsx:140
#: src/pages/Settings.tsx:57 #: src/pages/Settings.tsx:57
#: src/pages/UserPublicProfile.tsx:163 #: src/pages/UserPublicProfile.tsx:163
@@ -967,11 +967,11 @@ msgstr "Following"
msgid "email.digest.forYou" msgid "email.digest.forYou"
msgstr "For you" msgstr "For you"
#: src/pages/UserLogin.tsx:103 #: src/pages/UserLogin.tsx:92
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Forgot password?" msgstr "Forgot password?"
#: src/components/feed/FollowedFeed.tsx:132 #: src/components/feed/FollowedFeed.tsx:137
msgid "From people" msgid "From people"
msgstr "From people" msgstr "From people"
@@ -980,7 +980,7 @@ msgstr "From people"
msgid "email.digest.network" msgid "email.digest.network"
msgstr "From people you follow" msgstr "From people you follow"
#: src/components/feed/FollowedFeed.tsx:133 #: src/components/feed/FollowedFeed.tsx:138
msgid "From playlists" msgid "From playlists"
msgstr "From playlists" msgstr "From playlists"
@@ -992,7 +992,7 @@ msgstr "Fullscreen"
msgid "Go home" msgid "Go home"
msgstr "Go home" msgstr "Go home"
#: src/pages/ResetPassword.tsx:96 #: src/pages/ResetPassword.tsx:148
msgid "Go to login" msgid "Go to login"
msgstr "Go to login" msgstr "Go to login"
@@ -1001,12 +1001,12 @@ msgstr "Go to login"
msgid "email.reset.hi" msgid "email.reset.hi"
msgstr "Hi {username}," msgstr "Hi {username},"
#: src/components/feed/FeedTabBar.tsx:40 #: src/components/feed/FeedTabBar.tsx:57
#: src/pages/Settings.tsx:54 #: src/pages/Settings.tsx:54
msgid "Hot" msgid "Hot"
msgstr "Hot" msgstr "Hot"
#: src/pages/UserLogin.tsx:144 #: src/pages/ResetPassword.tsx:76
msgid "If that address is registered you'll receive a reset link shortly." msgid "If that address is registered you'll receive a reset link shortly."
msgstr "If that address is registered you'll receive a reset link shortly." msgstr "If that address is registered you'll receive a reset link shortly."
@@ -1024,7 +1024,7 @@ msgstr "If you didn't ask for this, ignore this email. Your password stays the s
msgid "image" msgid "image"
msgstr "image" msgstr "image"
#: src/pages/Dump.tsx:568 #: src/pages/Dump.tsx:573
msgid "In collections" msgid "In collections"
msgstr "In collections" msgstr "In collections"
@@ -1046,7 +1046,6 @@ msgstr "Instances"
msgid "Invalid invite" msgid "Invalid invite"
msgstr "Invalid invite" msgstr "Invalid invite"
#: src/pages/ResetPassword.tsx:64
#: src/pages/Unsubscribe.tsx:66 #: src/pages/Unsubscribe.tsx:66
msgid "Invalid link" msgid "Invalid link"
msgstr "Invalid link" msgstr "Invalid link"
@@ -1075,7 +1074,7 @@ msgstr "invited by you"
msgid "Invitees" msgid "Invitees"
msgstr "Invitees" msgstr "Invitees"
#: src/components/feed/FeedTabBar.tsx:42 #: src/components/feed/FeedTabBar.tsx:59
#: src/pages/Settings.tsx:56 #: src/pages/Settings.tsx:56
msgid "Journal" msgid "Journal"
msgstr "Journal" msgstr "Journal"
@@ -1126,8 +1125,8 @@ msgstr "Load more"
msgid "Load older messages" msgid "Load older messages"
msgstr "Load older messages" msgstr "Load older messages"
#: src/pages/Dump.tsx:301 #: src/pages/Dump.tsx:306
#: src/pages/DumpEdit.tsx:209 #: src/pages/DumpEdit.tsx:210
msgid "Loading dump…" msgid "Loading dump…"
msgstr "Loading dump…" msgstr "Loading dump…"
@@ -1139,7 +1138,7 @@ msgstr "Loading messages…"
msgid "Loading more…" msgid "Loading more…"
msgstr "Loading more…" msgstr "Loading more…"
#: src/pages/PlaylistDetail.tsx:64 #: src/pages/PlaylistDetail.tsx:82
msgid "Loading playlist…" msgid "Loading playlist…"
msgstr "Loading playlist…" msgstr "Loading playlist…"
@@ -1161,9 +1160,9 @@ msgstr "Loading…"
#: src/components/layout/AppHeader.tsx:208 #: src/components/layout/AppHeader.tsx:208
#: src/pages/FederationCallback.tsx:32 #: src/pages/FederationCallback.tsx:32
#: src/pages/UserLogin.tsx:46 #: src/pages/UserLogin.tsx:41
#: src/pages/UserLogin.tsx:73 #: src/pages/UserLogin.tsx:66
#: src/pages/UserLogin.tsx:92 #: src/pages/UserLogin.tsx:85
msgid "Log in" msgid "Log in"
msgstr "Log in" msgstr "Log in"
@@ -1185,12 +1184,12 @@ msgstr "Log in with {0}"
msgid "Log out" msgid "Log out"
msgstr "Log out" msgstr "Log out"
#: src/pages/UserLogin.tsx:91 #: src/pages/UserLogin.tsx:84
msgid "Logging in…" msgid "Logging in…"
msgstr "Logging in…" msgstr "Logging in…"
#: src/pages/FederationCallback.tsx:69 #: src/pages/FederationCallback.tsx:69
#: src/pages/UserLogin.tsx:78 #: src/pages/UserLogin.tsx:71
msgid "Login failed" msgid "Login failed"
msgstr "Login failed" msgstr "Login failed"
@@ -1228,7 +1227,7 @@ msgid "Name"
msgstr "Name" msgstr "Name"
#: src/components/chat/ChatModal.tsx:692 #: src/components/chat/ChatModal.tsx:692
#: src/components/feed/FeedTabBar.tsx:41 #: src/components/feed/FeedTabBar.tsx:58
#: src/pages/Settings.tsx:55 #: src/pages/Settings.tsx:55
msgid "New" msgid "New"
msgstr "New" msgstr "New"
@@ -1246,7 +1245,7 @@ msgid "digest.newMembers"
msgstr "New members" msgstr "New members"
#: src/components/profile/ChangePasswordModal.tsx:94 #: src/components/profile/ChangePasswordModal.tsx:94
#: src/pages/ResetPassword.tsx:116 #: src/pages/ResetPassword.tsx:168
msgid "New password" msgid "New password"
msgstr "New password" msgstr "New password"
@@ -1306,7 +1305,7 @@ msgid "No playlists match \"{q}\"."
msgstr "No playlists match \"{q}\"." msgstr "No playlists match \"{q}\"."
#: src/components/playlist/PlaylistMembershipPanel.tsx:51 #: src/components/playlist/PlaylistMembershipPanel.tsx:51
#: src/components/profile/ProfilePlaylistsTab.tsx:184 #: src/components/profile/ProfilePlaylistsTab.tsx:185
#: src/pages/Playlists.tsx:118 #: src/pages/Playlists.tsx:118
msgid "No playlists yet." msgid "No playlists yet."
msgstr "No playlists yet." msgstr "No playlists yet."
@@ -1369,7 +1368,7 @@ msgstr "Once a week you'll get a digest of what happened here, and once a month
msgid "Only images can be dropped here." msgid "Only images can be dropped here."
msgstr "Only images can be dropped here." msgstr "Only images can be dropped here."
#: src/pages/DumpEdit.tsx:119 #: src/pages/DumpEdit.tsx:120
msgid "Only its poster or a moderator can edit this dump." msgid "Only its poster or a moderator can edit this dump."
msgstr "Only its poster or a moderator can edit this dump." msgstr "Only its poster or a moderator can edit this dump."
@@ -1418,7 +1417,7 @@ msgstr "Page not found"
#: src/pages/Settings.tsx:96 #: src/pages/Settings.tsx:96
#: src/pages/Settings.tsx:106 #: src/pages/Settings.tsx:106
#: src/pages/UserLogin.tsx:88 #: src/pages/UserLogin.tsx:81
msgid "Password" msgid "Password"
msgstr "Password" msgstr "Password"
@@ -1431,12 +1430,12 @@ msgstr "Password (min. {0} characters)"
msgid "Password changed successfully." msgid "Password changed successfully."
msgstr "Password changed successfully." msgstr "Password changed successfully."
#: src/pages/ResetPassword.tsx:86 #: src/pages/ResetPassword.tsx:138
msgid "Password updated" msgid "Password updated"
msgstr "Password updated" msgstr "Password updated"
#: src/components/profile/ChangePasswordModal.tsx:114 #: src/components/profile/ChangePasswordModal.tsx:114
#: src/pages/ResetPassword.tsx:136 #: src/pages/ResetPassword.tsx:188
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Passwords do not match" msgstr "Passwords do not match"
@@ -1471,7 +1470,7 @@ msgstr "Playlist categories"
#: src/components/layout/AppHeader.tsx:172 #: src/components/layout/AppHeader.tsx:172
#: src/components/layout/UserMenu.tsx:92 #: src/components/layout/UserMenu.tsx:92
#: src/components/profile/ProfileFollowTab.tsx:144 #: src/components/profile/ProfileFollowTab.tsx:144
#: src/components/profile/ProfilePlaylistsTab.tsx:155 #: src/components/profile/ProfilePlaylistsTab.tsx:156
#: src/pages/Playlists.tsx:52 #: src/pages/Playlists.tsx:52
#: src/pages/Playlists.tsx:106 #: src/pages/Playlists.tsx:106
#: src/pages/Search.tsx:151 #: src/pages/Search.tsx:151
@@ -1502,7 +1501,7 @@ msgstr "Previous track"
#: src/components/playlist/PlaylistCard.tsx:106 #: src/components/playlist/PlaylistCard.tsx:106
#: src/components/playlist/PlaylistHeader.tsx:77 #: src/components/playlist/PlaylistHeader.tsx:77
#: src/components/playlist/PlaylistMembershipPanel.tsx:81 #: src/components/playlist/PlaylistMembershipPanel.tsx:81
#: src/pages/Dump.tsx:507 #: src/pages/Dump.tsx:512
msgid "private" msgid "private"
msgstr "private" msgstr "private"
@@ -1519,7 +1518,7 @@ msgstr "public"
msgid "Public" msgid "Public"
msgstr "Public" msgstr "Public"
#: src/pages/DumpEdit.tsx:291 #: src/pages/DumpEdit.tsx:292
msgid "Refresh metadata" msgid "Refresh metadata"
msgstr "Refresh metadata" msgstr "Refresh metadata"
@@ -1527,7 +1526,7 @@ msgstr "Refresh metadata"
msgid "Refreshing stream…" msgid "Refreshing stream…"
msgstr "Refreshing stream…" msgstr "Refreshing stream…"
#: src/pages/DumpEdit.tsx:290 #: src/pages/DumpEdit.tsx:291
msgid "Refreshing…" msgid "Refreshing…"
msgstr "Refreshing…" msgstr "Refreshing…"
@@ -1545,7 +1544,7 @@ msgstr "Registering…"
msgid "Registration failed" msgid "Registration failed"
msgstr "Registration failed" msgstr "Registration failed"
#: src/pages/Dump.tsx:559 #: src/pages/Dump.tsx:564
msgid "Related" msgid "Related"
msgstr "Related" msgstr "Related"
@@ -1579,7 +1578,7 @@ msgstr "Rename"
msgid "Renaming…" msgid "Renaming…"
msgstr "Renaming…" msgstr "Renaming…"
#: src/pages/DumpEdit.tsx:489 #: src/pages/DumpEdit.tsx:490
msgid "Replace file" msgid "Replace file"
msgstr "Replace file" msgstr "Replace file"
@@ -1593,11 +1592,11 @@ msgstr "Reply"
msgid "Replying to" msgid "Replying to"
msgstr "Replying to" msgstr "Replying to"
#: src/pages/UserLogin.tsx:156 #: src/pages/ResetPassword.tsx:85
msgid "Request failed" msgid "Request failed"
msgstr "Request failed" msgstr "Request failed"
#: src/pages/ResetPassword.tsx:112 #: src/pages/ResetPassword.tsx:164
msgid "Reset failed" msgid "Reset failed"
msgstr "Reset failed" msgstr "Reset failed"
@@ -1606,12 +1605,13 @@ msgstr "Reset failed"
msgid "email.reset.button" msgid "email.reset.button"
msgstr "Reset password" msgstr "Reset password"
#: src/pages/ResetPassword.tsx:34 #: src/pages/ResetPassword.tsx:40
#: src/pages/ResetPassword.tsx:71
msgid "Reset password" msgid "Reset password"
msgstr "Reset password" msgstr "Reset password"
#: src/pages/DumpEdit.tsx:465 #: src/pages/DumpEdit.tsx:466
#: src/pages/DumpEdit.tsx:520 #: src/pages/DumpEdit.tsx:521
msgid "Reset to default" msgid "Reset to default"
msgstr "Reset to default" msgstr "Reset to default"
@@ -1631,8 +1631,8 @@ msgid "email.reset.title"
msgstr "Reset your password" msgstr "Reset your password"
#: src/components/chat/PendingItem.tsx:86 #: src/components/chat/PendingItem.tsx:86
#: src/pages/Dump.tsx:318 #: src/pages/Dump.tsx:323
#: src/pages/DumpEdit.tsx:226 #: src/pages/DumpEdit.tsx:227
msgid "Retry" msgid "Retry"
msgstr "Retry" msgstr "Retry"
@@ -1646,8 +1646,8 @@ msgstr "Role"
#: src/components/playlist/PlaylistEditForm.tsx:229 #: src/components/playlist/PlaylistEditForm.tsx:229
#: src/components/profile/DescriptionEditor.tsx:89 #: src/components/profile/DescriptionEditor.tsx:89
#: src/components/profile/EmailEditor.tsx:75 #: src/components/profile/EmailEditor.tsx:75
#: src/pages/Dump.tsx:433 #: src/pages/Dump.tsx:438
#: src/pages/DumpEdit.tsx:563 #: src/pages/DumpEdit.tsx:564
msgid "Save" msgid "Save"
msgstr "Save" msgstr "Save"
@@ -1656,8 +1656,8 @@ msgstr "Save"
#: src/components/profile/ChangePasswordModal.tsx:119 #: src/components/profile/ChangePasswordModal.tsx:119
#: src/components/profile/DescriptionEditor.tsx:86 #: src/components/profile/DescriptionEditor.tsx:86
#: src/components/profile/EmailEditor.tsx:72 #: src/components/profile/EmailEditor.tsx:72
#: src/pages/Dump.tsx:432 #: src/pages/Dump.tsx:437
#: src/pages/ResetPassword.tsx:139 #: src/pages/ResetPassword.tsx:191
msgid "Saving…" msgid "Saving…"
msgstr "Saving…" msgstr "Saving…"
@@ -1700,12 +1700,12 @@ msgstr "Seek"
msgid "Send" msgid "Send"
msgstr "Send" msgstr "Send"
#: src/pages/UserLogin.tsx:165 #: src/pages/ResetPassword.tsx:94
msgid "Send reset link" msgid "Send reset link"
msgstr "Send reset link" msgstr "Send reset link"
#: src/components/chat/PendingItem.tsx:104 #: src/components/chat/PendingItem.tsx:104
#: src/pages/UserLogin.tsx:164 #: src/pages/ResetPassword.tsx:93
msgid "Sending…" msgid "Sending…"
msgstr "Sending…" msgstr "Sending…"
@@ -1713,8 +1713,8 @@ msgstr "Sending…"
msgid "Server unreachable" msgid "Server unreachable"
msgstr "Server unreachable" msgstr "Server unreachable"
#: src/pages/ResetPassword.tsx:107 #: src/pages/ResetPassword.tsx:159
#: src/pages/ResetPassword.tsx:140 #: src/pages/ResetPassword.tsx:192
msgid "Set new password" msgid "Set new password"
msgstr "Set new password" msgstr "Set new password"
@@ -1766,7 +1766,7 @@ msgstr "Someone asked to reset the password of your {site} account. The button b
#: src/components/form/FormError.tsx:31 #: src/components/form/FormError.tsx:31
#: src/components/layout/PageError.tsx:29 #: src/components/layout/PageError.tsx:29
#: src/contexts/ws/WSProvider.tsx:732 #: src/contexts/ws/WSProvider.tsx:732
#: src/pages/PlaylistDetail.tsx:124 #: src/pages/PlaylistDetail.tsx:155
msgid "Something went wrong" msgid "Something went wrong"
msgstr "Something went wrong" msgstr "Something went wrong"
@@ -1799,7 +1799,7 @@ msgstr "the chat"
msgid "the chat" msgid "the chat"
msgstr "the chat" msgstr "the chat"
#: src/hooks/playlist/usePlaylist.ts:179 #: src/hooks/playlist/usePlaylist.ts:182
msgid "The dump couldn't be removed. Try again." msgid "The dump couldn't be removed. Try again."
msgstr "The dump couldn't be removed. Try again." msgstr "The dump couldn't be removed. Try again."
@@ -1822,7 +1822,7 @@ msgstr "The month on {site}"
msgid "email.digest.monthly.subject" msgid "email.digest.monthly.subject"
msgstr "The month on {site}, {range}" msgstr "The month on {site}, {range}"
#: src/hooks/playlist/usePlaylist.ts:322 #: src/hooks/playlist/usePlaylist.ts:325
msgid "The new order couldn't be saved. Try again." msgid "The new order couldn't be saved. Try again."
msgstr "The new order couldn't be saved. Try again." msgstr "The new order couldn't be saved. Try again."
@@ -1834,7 +1834,7 @@ msgstr "This comment couldn't be deleted. Try again."
msgid "This invite link is missing, expired, or already used." msgid "This invite link is missing, expired, or already used."
msgstr "This invite link is missing, expired, or already used." msgstr "This invite link is missing, expired, or already used."
#: src/pages/UserLogin.tsx:112 #: src/pages/UserLogin.tsx:99
msgid "This is a mirage." msgid "This is a mirage."
msgstr "This is a mirage." msgstr "This is a mirage."
@@ -1846,10 +1846,6 @@ msgstr "This month"
msgid "This page does not exist." msgid "This page does not exist."
msgstr "This page does not exist." msgstr "This page does not exist."
#: src/pages/ResetPassword.tsx:67
msgid "This reset link is missing or malformed."
msgstr "This reset link is missing or malformed."
#: src/pages/FederationCallback.tsx:40 #: src/pages/FederationCallback.tsx:40
msgid "This sign-in link is incomplete." msgid "This sign-in link is incomplete."
msgstr "This sign-in link is incomplete." msgstr "This sign-in link is incomplete."
@@ -1870,14 +1866,14 @@ msgstr "This unsubscribe link is missing or malformed."
msgid "This week" msgid "This week"
msgstr "This week" msgstr "This week"
#: src/pages/DumpEdit.tsx:449 #: src/pages/DumpEdit.tsx:450
msgid "Thumbnail" msgid "Thumbnail"
msgstr "Thumbnail" msgstr "Thumbnail"
#: src/components/dump/create/TitleField.tsx:45 #: src/components/dump/create/TitleField.tsx:45
#: src/components/playlist/PlaylistCreateForm.tsx:100 #: src/components/playlist/PlaylistCreateForm.tsx:100
#: src/components/playlist/PlaylistEditForm.tsx:175 #: src/components/playlist/PlaylistEditForm.tsx:175
#: src/pages/DumpEdit.tsx:510 #: src/pages/DumpEdit.tsx:511
msgid "Title" msgid "Title"
msgstr "Title" msgstr "Title"
@@ -2007,7 +2003,7 @@ msgstr "Upvoted"
msgid "Upvoted ({0})" msgid "Upvoted ({0})"
msgstr "Upvoted ({0})" msgstr "Upvoted ({0})"
#: src/pages/DumpEdit.tsx:481 #: src/pages/DumpEdit.tsx:482
msgid "URL" msgid "URL"
msgstr "URL" msgstr "URL"
@@ -2023,7 +2019,7 @@ msgstr "URL slug"
msgid "User" msgid "User"
msgstr "User" msgstr "User"
#: src/components/profile/ProfileManageTab.tsx:54 #: src/components/profile/ProfileManageTab.tsx:56
msgid "User management" msgid "User management"
msgstr "User management" msgstr "User management"
@@ -2031,10 +2027,10 @@ msgstr "User management"
msgid "User menu" msgid "User menu"
msgstr "User menu" msgstr "User menu"
#: src/components/profile/ProfileManageTab.tsx:61 #: src/components/profile/ProfileManageTab.tsx:63
#: src/components/profile/UsernameField.tsx:51 #: src/components/profile/UsernameField.tsx:51
#: src/pages/Settings.tsx:91 #: src/pages/Settings.tsx:91
#: src/pages/UserLogin.tsx:81 #: src/pages/UserLogin.tsx:74
#: src/pages/UserRegister.tsx:141 #: src/pages/UserRegister.tsx:141
msgid "Username" msgid "Username"
msgstr "Username" msgstr "Username"
@@ -2079,7 +2075,7 @@ msgid "email.welcome.subject"
msgstr "Welcome to {site}" msgstr "Welcome to {site}"
#: src/components/dump/create/DumpCreateModal.tsx:705 #: src/components/dump/create/DumpCreateModal.tsx:705
#: src/pages/DumpEdit.tsx:538 #: src/pages/DumpEdit.tsx:539
msgid "What makes it worth it?" msgid "What makes it worth it?"
msgstr "What makes it worth it?" msgstr "What makes it worth it?"
@@ -2097,7 +2093,7 @@ msgid "Why & where"
msgstr "Why & where" msgstr "Why & where"
#: src/components/dump/create/DumpCreateModal.tsx:704 #: src/components/dump/create/DumpCreateModal.tsx:704
#: src/pages/DumpEdit.tsx:537 #: src/pages/DumpEdit.tsx:538
msgid "Why?" msgid "Why?"
msgstr "Why?" msgstr "Why?"
@@ -2171,7 +2167,7 @@ msgstr "Your account lives on {home}: sign in to other instances from there."
msgid "Your account lives on {home}: your password, email address, avatar and username change there." msgid "Your account lives on {home}: your password, email address, avatar and username change there."
msgstr "Your account lives on {home}: your password, email address, avatar and username change there." msgstr "Your account lives on {home}: your password, email address, avatar and username change there."
#: src/pages/UserLogin.tsx:160 #: src/pages/ResetPassword.tsx:89
msgid "Your email address" msgid "Your email address"
msgstr "Your email address" msgstr "Your email address"
@@ -2179,7 +2175,7 @@ msgstr "Your email address"
msgid "Your gerbeur's URL" msgid "Your gerbeur's URL"
msgstr "Your gerbeur's URL" msgstr "Your gerbeur's URL"
#: src/pages/ResetPassword.tsx:89 #: src/pages/ResetPassword.tsx:141
msgid "Your password has been changed. You can now log in." msgid "Your password has been changed. You can now log in."
msgstr "Your password has been changed. You can now log in." msgstr "Your password has been changed. You can now log in."

View File

@@ -206,14 +206,14 @@ msgstr "# Bienvenue sur {site} !\n"
"Votre compte a bien été créé. Bienvenue à bord !" "Votre compte a bien été créé. Bienvenue à bord !"
#: src/components/dump/create/DumpCreateModal.tsx:736 #: src/components/dump/create/DumpCreateModal.tsx:736
#: src/pages/PlaylistDetail.tsx:80 #: src/pages/PlaylistDetail.tsx:98
#: src/pages/UserPublicProfile.tsx:125 #: src/pages/UserPublicProfile.tsx:125
msgid "← Back" msgid "← Back"
msgstr "← Retour" msgstr "← Retour"
#: src/pages/Dump.tsx:325 #: src/pages/Dump.tsx:330
#: src/pages/Dump.tsx:552 #: src/pages/Dump.tsx:557
#: src/pages/DumpEdit.tsx:233 #: src/pages/DumpEdit.tsx:234
msgid "← Back to all dumps" msgid "← Back to all dumps"
msgstr "← Retour à toutes les recos" msgstr "← Retour à toutes les recos"
@@ -225,7 +225,7 @@ msgstr "+ Inviter quelqu'un"
msgid "+ New playlist" msgid "+ New playlist"
msgstr "+ Nouvelle collection" msgstr "+ Nouvelle collection"
#: src/pages/Dump.tsx:399 #: src/pages/Dump.tsx:404
msgid "+ Playlist" msgid "+ Playlist"
msgstr "+ Collection" msgstr "+ Collection"
@@ -377,7 +377,7 @@ msgstr "Apparence"
#. placeholder {0}: VALIDATION.PASSWORD_MIN #. placeholder {0}: VALIDATION.PASSWORD_MIN
#: src/components/profile/ChangePasswordModal.tsx:101 #: src/components/profile/ChangePasswordModal.tsx:101
#: src/pages/ResetPassword.tsx:124 #: src/pages/ResetPassword.tsx:176
#: src/pages/UserRegister.tsx:168 #: src/pages/UserRegister.tsx:168
msgid "At least {0} characters" msgid "At least {0} characters"
msgstr "Au moins {0} caractères" msgstr "Au moins {0} caractères"
@@ -390,8 +390,8 @@ msgstr "{maxLength} caractères au maximum."
msgid "Auto" msgid "Auto"
msgstr "Auto" msgstr "Auto"
#: src/pages/ResetPassword.tsx:74 #: src/pages/ResetPassword.tsx:101
#: src/pages/ResetPassword.tsx:147 #: src/pages/ResetPassword.tsx:199
msgid "Back to login" msgid "Back to login"
msgstr "Retour à la connexion" msgstr "Retour à la connexion"
@@ -439,8 +439,8 @@ msgstr "Connexion impossible"
#: src/components/profile/EmailEditor.tsx:83 #: src/components/profile/EmailEditor.tsx:83
#: src/components/shared/ConfirmModal.tsx:63 #: src/components/shared/ConfirmModal.tsx:63
#: src/pages/Authorize.tsx:158 #: src/pages/Authorize.tsx:158
#: src/pages/Dump.tsx:441 #: src/pages/Dump.tsx:446
#: src/pages/DumpEdit.tsx:560 #: src/pages/DumpEdit.tsx:561
msgid "Cancel" msgid "Cancel"
msgstr "Annuler" msgstr "Annuler"
@@ -546,7 +546,7 @@ msgid "digest.community"
msgstr "Communauté" msgstr "Communauté"
#: src/components/profile/ChangePasswordModal.tsx:108 #: src/components/profile/ChangePasswordModal.tsx:108
#: src/pages/ResetPassword.tsx:131 #: src/pages/ResetPassword.tsx:183
msgid "Confirm new password" msgid "Confirm new password"
msgstr "Confirmer le nouveau mot de passe" msgstr "Confirmer le nouveau mot de passe"
@@ -582,8 +582,8 @@ msgstr "Impossible de changer le mot de passe"
msgid "Could not connect to the server. Check your connection and try again." msgid "Could not connect to the server. Check your connection and try again."
msgstr "Impossible de joindre le serveur. Vérifiez votre connexion et réessayez." msgstr "Impossible de joindre le serveur. Vérifiez votre connexion et réessayez."
#: src/components/profile/ProfilePlaylistsTab.tsx:177 #: src/components/profile/ProfilePlaylistsTab.tsx:178
#: src/pages/DumpEdit.tsx:554 #: src/pages/DumpEdit.tsx:555
msgid "Could not delete" msgid "Could not delete"
msgstr "Suppression impossible" msgstr "Suppression impossible"
@@ -591,7 +591,7 @@ msgstr "Suppression impossible"
msgid "Could not rename" msgid "Could not rename"
msgstr "Renommage impossible" msgstr "Renommage impossible"
#: src/pages/DumpEdit.tsx:506 #: src/pages/DumpEdit.tsx:507
msgid "Could not save" msgid "Could not save"
msgstr "Sauvegarde impossible" msgstr "Sauvegarde impossible"
@@ -668,20 +668,20 @@ msgstr "Supprimer la catégorie"
msgid "Delete category \"{0}\"? This cannot be undone." msgid "Delete category \"{0}\"? This cannot be undone."
msgstr "Supprimer la catégorie \"{0}\" ? Cette action est irréversible." msgstr "Supprimer la catégorie \"{0}\" ? Cette action est irréversible."
#: src/pages/DumpEdit.tsx:315 #: src/pages/DumpEdit.tsx:316
#: src/pages/DumpEdit.tsx:557 #: src/pages/DumpEdit.tsx:558
msgid "Delete dump" msgid "Delete dump"
msgstr "Supprimer la reco" msgstr "Supprimer la reco"
#. placeholder {0}: res.status #. placeholder {0}: res.status
#: src/pages/DumpEdit.tsx:194 #: src/pages/DumpEdit.tsx:195
msgid "Delete failed ({0})" msgid "Delete failed ({0})"
msgstr "Échec de la suppression ({0})" msgstr "Échec de la suppression ({0})"
#: src/components/playlist/PlaylistCard.tsx:149 #: src/components/playlist/PlaylistCard.tsx:149
#: src/components/playlist/PlaylistEditForm.tsx:213 #: src/components/playlist/PlaylistEditForm.tsx:213
#: src/components/profile/ProfilePlaylistsTab.tsx:207 #: src/components/profile/ProfilePlaylistsTab.tsx:208
#: src/pages/PlaylistDetail.tsx:141 #: src/pages/PlaylistDetail.tsx:172
msgid "Delete playlist" msgid "Delete playlist"
msgstr "Supprimer la collection" msgstr "Supprimer la collection"
@@ -689,7 +689,7 @@ msgstr "Supprimer la collection"
msgid "Delete this comment?" msgid "Delete this comment?"
msgstr "Supprimer ce commentaire ?" msgstr "Supprimer ce commentaire ?"
#: src/pages/DumpEdit.tsx:314 #: src/pages/DumpEdit.tsx:315
msgid "Delete this dump? This cannot be undone." msgid "Delete this dump? This cannot be undone."
msgstr "Supprimer cette reco ? Cette action est irréversible." msgstr "Supprimer cette reco ? Cette action est irréversible."
@@ -697,8 +697,8 @@ msgstr "Supprimer cette reco ? Cette action est irréversible."
msgid "Delete this message?" msgid "Delete this message?"
msgstr "Supprimer ce message ?" msgstr "Supprimer ce message ?"
#: src/components/profile/ProfilePlaylistsTab.tsx:206 #: src/components/profile/ProfilePlaylistsTab.tsx:207
#: src/pages/PlaylistDetail.tsx:140 #: src/pages/PlaylistDetail.tsx:171
msgid "Delete this playlist? This cannot be undone." msgid "Delete this playlist? This cannot be undone."
msgstr "Supprimer cette collection ? Cette action est irréversible." msgstr "Supprimer cette collection ? Cette action est irréversible."
@@ -748,7 +748,7 @@ msgstr "Déposez un fichier ici"
msgid "Drop a file here, or paste one" msgid "Drop a file here, or paste one"
msgstr "Déposez un fichier ici, ou collez-en un" msgstr "Déposez un fichier ici, ou collez-en un"
#: src/pages/DumpEdit.tsx:497 #: src/pages/DumpEdit.tsx:498
msgid "Drop a replacement here" msgid "Drop a replacement here"
msgstr "Déposez un fichier de remplacement ici" msgstr "Déposez un fichier de remplacement ici"
@@ -788,7 +788,7 @@ msgstr "Plus tôt"
#: src/components/chat/PendingItem.tsx:93 #: src/components/chat/PendingItem.tsx:93
#: src/components/comment/CommentNode.tsx:256 #: src/components/comment/CommentNode.tsx:256
#: src/components/playlist/PlaylistHeader.tsx:60 #: src/components/playlist/PlaylistHeader.tsx:60
#: src/pages/Dump.tsx:548 #: src/pages/Dump.tsx:553
msgid "Edit" msgid "Edit"
msgstr "Modifier" msgstr "Modifier"
@@ -806,7 +806,7 @@ msgid "Edit email"
msgstr "Modifier l'e-mail" msgstr "Modifier l'e-mail"
#: src/components/dump/create/TitleField.tsx:69 #: src/components/dump/create/TitleField.tsx:69
#: src/pages/Dump.tsx:458 #: src/pages/Dump.tsx:463
msgid "Edit title" msgid "Edit title"
msgstr "Modifier le titre" msgstr "Modifier le titre"
@@ -820,7 +820,7 @@ msgstr "modifié"
#: src/components/chat/ChatMessageItem.tsx:132 #: src/components/chat/ChatMessageItem.tsx:132
#: src/components/comment/CommentNode.tsx:205 #: src/components/comment/CommentNode.tsx:205
#: src/components/playlist/PlaylistHeader.tsx:95 #: src/components/playlist/PlaylistHeader.tsx:95
#: src/pages/Dump.tsx:501 #: src/pages/Dump.tsx:506
msgid "edited {0}" msgid "edited {0}"
msgstr "modifié {0}" msgstr "modifié {0}"
@@ -830,16 +830,16 @@ msgstr "modifié {0}"
#: src/components/chat/ChatMessageItem.tsx:128 #: src/components/chat/ChatMessageItem.tsx:128
#: src/components/comment/CommentNode.tsx:203 #: src/components/comment/CommentNode.tsx:203
#: src/components/playlist/PlaylistHeader.tsx:93 #: src/components/playlist/PlaylistHeader.tsx:93
#: src/pages/Dump.tsx:499 #: src/pages/Dump.tsx:504
msgid "Edited {0}" msgid "Edited {0}"
msgstr "Modifié le {0}" msgstr "Modifié le {0}"
#: src/pages/DumpEdit.tsx:258 #: src/pages/DumpEdit.tsx:259
msgid "Editing" msgid "Editing"
msgstr "Modification" msgstr "Modification"
#. placeholder {0}: state.dump.title #. placeholder {0}: state.dump.title
#: src/pages/DumpEdit.tsx:81 #: src/pages/DumpEdit.tsx:82
msgid "Editing {0}" msgid "Editing {0}"
msgstr "Édition de {0}" msgstr "Édition de {0}"
@@ -942,15 +942,15 @@ msgstr "Suivre {targetUsername}"
msgid "Follow playlist" msgid "Follow playlist"
msgstr "Suivre la collection" msgstr "Suivre la collection"
#: src/components/feed/FollowedFeed.tsx:151 #: src/components/feed/FollowedFeed.tsx:164
msgid "Follow some public playlists to see their dumps here." msgid "Follow some public playlists to see their dumps here."
msgstr "Suivez des collections publiques pour voir leurs recos ici." msgstr "Suivez des collections publiques pour voir leurs recos ici."
#: src/components/feed/FollowedFeed.tsx:143 #: src/components/feed/FollowedFeed.tsx:156
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/feed/FeedTabBar.tsx:44 #: src/components/feed/FeedTabBar.tsx:61
#: src/components/profile/ProfileFollowTab.tsx:140 #: src/components/profile/ProfileFollowTab.tsx:140
#: src/pages/Settings.tsx:57 #: src/pages/Settings.tsx:57
#: src/pages/UserPublicProfile.tsx:163 #: src/pages/UserPublicProfile.tsx:163
@@ -967,11 +967,11 @@ msgstr "Abonné"
msgid "email.digest.forYou" msgid "email.digest.forYou"
msgstr "Pour vous" msgstr "Pour vous"
#: src/pages/UserLogin.tsx:103 #: src/pages/UserLogin.tsx:92
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Mot de passe oublié ?" msgstr "Mot de passe oublié ?"
#: src/components/feed/FollowedFeed.tsx:132 #: src/components/feed/FollowedFeed.tsx:137
msgid "From people" msgid "From people"
msgstr "De personnes" msgstr "De personnes"
@@ -980,7 +980,7 @@ msgstr "De personnes"
msgid "email.digest.network" msgid "email.digest.network"
msgstr "De ceux que vous suivez" msgstr "De ceux que vous suivez"
#: src/components/feed/FollowedFeed.tsx:133 #: src/components/feed/FollowedFeed.tsx:138
msgid "From playlists" msgid "From playlists"
msgstr "De collections" msgstr "De collections"
@@ -992,7 +992,7 @@ msgstr "Plein écran"
msgid "Go home" msgid "Go home"
msgstr "Accueil" msgstr "Accueil"
#: src/pages/ResetPassword.tsx:96 #: src/pages/ResetPassword.tsx:148
msgid "Go to login" msgid "Go to login"
msgstr "Aller à la connexion" msgstr "Aller à la connexion"
@@ -1001,12 +1001,12 @@ msgstr "Aller à la connexion"
msgid "email.reset.hi" msgid "email.reset.hi"
msgstr "Bonjour {username}," msgstr "Bonjour {username},"
#: src/components/feed/FeedTabBar.tsx:40 #: src/components/feed/FeedTabBar.tsx:57
#: src/pages/Settings.tsx:54 #: src/pages/Settings.tsx:54
msgid "Hot" msgid "Hot"
msgstr "Tendances" msgstr "Tendances"
#: src/pages/UserLogin.tsx:144 #: src/pages/ResetPassword.tsx:76
msgid "If that address is registered you'll receive a reset link shortly." msgid "If that address is registered you'll receive a reset link shortly."
msgstr "Si cette adresse est enregistrée, vous recevrez un lien de réinitialisation sous peu." msgstr "Si cette adresse est enregistrée, vous recevrez un lien de réinitialisation sous peu."
@@ -1024,7 +1024,7 @@ msgstr "Si vous n'êtes pas à l'origine de cette demande, ignorez cet e-mail. V
msgid "image" msgid "image"
msgstr "image" msgstr "image"
#: src/pages/Dump.tsx:568 #: src/pages/Dump.tsx:573
msgid "In collections" msgid "In collections"
msgstr "Dans les collections" msgstr "Dans les collections"
@@ -1046,7 +1046,6 @@ msgstr "Instances"
msgid "Invalid invite" msgid "Invalid invite"
msgstr "Invitation invalide" msgstr "Invitation invalide"
#: src/pages/ResetPassword.tsx:64
#: src/pages/Unsubscribe.tsx:66 #: src/pages/Unsubscribe.tsx:66
msgid "Invalid link" msgid "Invalid link"
msgstr "Lien invalide" msgstr "Lien invalide"
@@ -1075,7 +1074,7 @@ msgstr "sur votre invitation"
msgid "Invitees" msgid "Invitees"
msgstr "Invités" msgstr "Invités"
#: src/components/feed/FeedTabBar.tsx:42 #: src/components/feed/FeedTabBar.tsx:59
#: src/pages/Settings.tsx:56 #: src/pages/Settings.tsx:56
msgid "Journal" msgid "Journal"
msgstr "Journal" msgstr "Journal"
@@ -1126,8 +1125,8 @@ msgstr "Charger plus"
msgid "Load older messages" msgid "Load older messages"
msgstr "Charger les messages plus anciens" msgstr "Charger les messages plus anciens"
#: src/pages/Dump.tsx:301 #: src/pages/Dump.tsx:306
#: src/pages/DumpEdit.tsx:209 #: src/pages/DumpEdit.tsx:210
msgid "Loading dump…" msgid "Loading dump…"
msgstr "Chargement de la reco…" msgstr "Chargement de la reco…"
@@ -1139,7 +1138,7 @@ msgstr "Chargement des messages…"
msgid "Loading more…" msgid "Loading more…"
msgstr "Chargement…" msgstr "Chargement…"
#: src/pages/PlaylistDetail.tsx:64 #: src/pages/PlaylistDetail.tsx:82
msgid "Loading playlist…" msgid "Loading playlist…"
msgstr "Chargement de la collection…" msgstr "Chargement de la collection…"
@@ -1161,9 +1160,9 @@ msgstr "Chargement…"
#: src/components/layout/AppHeader.tsx:208 #: src/components/layout/AppHeader.tsx:208
#: src/pages/FederationCallback.tsx:32 #: src/pages/FederationCallback.tsx:32
#: src/pages/UserLogin.tsx:46 #: src/pages/UserLogin.tsx:41
#: src/pages/UserLogin.tsx:73 #: src/pages/UserLogin.tsx:66
#: src/pages/UserLogin.tsx:92 #: src/pages/UserLogin.tsx:85
msgid "Log in" msgid "Log in"
msgstr "Se connecter" msgstr "Se connecter"
@@ -1185,12 +1184,12 @@ msgstr "Se connecter avec {0}"
msgid "Log out" msgid "Log out"
msgstr "Se déconnecter" msgstr "Se déconnecter"
#: src/pages/UserLogin.tsx:91 #: src/pages/UserLogin.tsx:84
msgid "Logging in…" msgid "Logging in…"
msgstr "Connexion…" msgstr "Connexion…"
#: src/pages/FederationCallback.tsx:69 #: src/pages/FederationCallback.tsx:69
#: src/pages/UserLogin.tsx:78 #: src/pages/UserLogin.tsx:71
msgid "Login failed" msgid "Login failed"
msgstr "Connexion échouée" msgstr "Connexion échouée"
@@ -1228,7 +1227,7 @@ msgid "Name"
msgstr "Nom" msgstr "Nom"
#: src/components/chat/ChatModal.tsx:692 #: src/components/chat/ChatModal.tsx:692
#: src/components/feed/FeedTabBar.tsx:41 #: src/components/feed/FeedTabBar.tsx:58
#: src/pages/Settings.tsx:55 #: src/pages/Settings.tsx:55
msgid "New" msgid "New"
msgstr "Nouveau" msgstr "Nouveau"
@@ -1246,7 +1245,7 @@ msgid "digest.newMembers"
msgstr "Nouveaux membres" msgstr "Nouveaux membres"
#: src/components/profile/ChangePasswordModal.tsx:94 #: src/components/profile/ChangePasswordModal.tsx:94
#: src/pages/ResetPassword.tsx:116 #: src/pages/ResetPassword.tsx:168
msgid "New password" msgid "New password"
msgstr "Nouveau mot de passe" msgstr "Nouveau mot de passe"
@@ -1306,7 +1305,7 @@ msgid "No playlists match \"{q}\"."
msgstr "Aucune collection ne correspond à « {q} »." msgstr "Aucune collection ne correspond à « {q} »."
#: src/components/playlist/PlaylistMembershipPanel.tsx:51 #: src/components/playlist/PlaylistMembershipPanel.tsx:51
#: src/components/profile/ProfilePlaylistsTab.tsx:184 #: src/components/profile/ProfilePlaylistsTab.tsx:185
#: src/pages/Playlists.tsx:118 #: src/pages/Playlists.tsx:118
msgid "No playlists yet." msgid "No playlists yet."
msgstr "Pas encore de collections." msgstr "Pas encore de collections."
@@ -1369,7 +1368,7 @@ msgstr "Chaque semaine, vous recevrez un résumé de ce qui s'est passé ici, et
msgid "Only images can be dropped here." msgid "Only images can be dropped here."
msgstr "Seules les images peuvent être déposées ici." msgstr "Seules les images peuvent être déposées ici."
#: src/pages/DumpEdit.tsx:119 #: src/pages/DumpEdit.tsx:120
msgid "Only its poster or a moderator can edit this dump." msgid "Only its poster or a moderator can edit this dump."
msgstr "Seuls son auteur et les modérateurs peuvent modifier ce dump." msgstr "Seuls son auteur et les modérateurs peuvent modifier ce dump."
@@ -1418,7 +1417,7 @@ msgstr "Page introuvable"
#: src/pages/Settings.tsx:96 #: src/pages/Settings.tsx:96
#: src/pages/Settings.tsx:106 #: src/pages/Settings.tsx:106
#: src/pages/UserLogin.tsx:88 #: src/pages/UserLogin.tsx:81
msgid "Password" msgid "Password"
msgstr "Mot de passe" msgstr "Mot de passe"
@@ -1431,12 +1430,12 @@ msgstr "Mot de passe (min. {0} caractères)"
msgid "Password changed successfully." msgid "Password changed successfully."
msgstr "Mot de passe modifié avec succès." msgstr "Mot de passe modifié avec succès."
#: src/pages/ResetPassword.tsx:86 #: src/pages/ResetPassword.tsx:138
msgid "Password updated" msgid "Password updated"
msgstr "Mot de passe mis à jour" msgstr "Mot de passe mis à jour"
#: src/components/profile/ChangePasswordModal.tsx:114 #: src/components/profile/ChangePasswordModal.tsx:114
#: src/pages/ResetPassword.tsx:136 #: src/pages/ResetPassword.tsx:188
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Les mots de passe ne correspondent pas" msgstr "Les mots de passe ne correspondent pas"
@@ -1471,7 +1470,7 @@ msgstr "Catégories de collections"
#: src/components/layout/AppHeader.tsx:172 #: src/components/layout/AppHeader.tsx:172
#: src/components/layout/UserMenu.tsx:92 #: src/components/layout/UserMenu.tsx:92
#: src/components/profile/ProfileFollowTab.tsx:144 #: src/components/profile/ProfileFollowTab.tsx:144
#: src/components/profile/ProfilePlaylistsTab.tsx:155 #: src/components/profile/ProfilePlaylistsTab.tsx:156
#: src/pages/Playlists.tsx:52 #: src/pages/Playlists.tsx:52
#: src/pages/Playlists.tsx:106 #: src/pages/Playlists.tsx:106
#: src/pages/Search.tsx:151 #: src/pages/Search.tsx:151
@@ -1502,7 +1501,7 @@ msgstr "Piste précédente"
#: src/components/playlist/PlaylistCard.tsx:106 #: src/components/playlist/PlaylistCard.tsx:106
#: src/components/playlist/PlaylistHeader.tsx:77 #: src/components/playlist/PlaylistHeader.tsx:77
#: src/components/playlist/PlaylistMembershipPanel.tsx:81 #: src/components/playlist/PlaylistMembershipPanel.tsx:81
#: src/pages/Dump.tsx:507 #: src/pages/Dump.tsx:512
msgid "private" msgid "private"
msgstr "privé" msgstr "privé"
@@ -1519,7 +1518,7 @@ msgstr "public"
msgid "Public" msgid "Public"
msgstr "Public" msgstr "Public"
#: src/pages/DumpEdit.tsx:291 #: src/pages/DumpEdit.tsx:292
msgid "Refresh metadata" msgid "Refresh metadata"
msgstr "Actualiser les métadonnées" msgstr "Actualiser les métadonnées"
@@ -1527,7 +1526,7 @@ msgstr "Actualiser les métadonnées"
msgid "Refreshing stream…" msgid "Refreshing stream…"
msgstr "Actualisation du flux…" msgstr "Actualisation du flux…"
#: src/pages/DumpEdit.tsx:290 #: src/pages/DumpEdit.tsx:291
msgid "Refreshing…" msgid "Refreshing…"
msgstr "Actualisation…" msgstr "Actualisation…"
@@ -1545,7 +1544,7 @@ msgstr "Inscription…"
msgid "Registration failed" msgid "Registration failed"
msgstr "Inscription échouée" msgstr "Inscription échouée"
#: src/pages/Dump.tsx:559 #: src/pages/Dump.tsx:564
msgid "Related" msgid "Related"
msgstr "Connexe" msgstr "Connexe"
@@ -1579,7 +1578,7 @@ msgstr "Renommer"
msgid "Renaming…" msgid "Renaming…"
msgstr "Renommage…" msgstr "Renommage…"
#: src/pages/DumpEdit.tsx:489 #: src/pages/DumpEdit.tsx:490
msgid "Replace file" msgid "Replace file"
msgstr "Remplacer le fichier" msgstr "Remplacer le fichier"
@@ -1593,11 +1592,11 @@ msgstr "Répondre"
msgid "Replying to" msgid "Replying to"
msgstr "En réponse à" msgstr "En réponse à"
#: src/pages/UserLogin.tsx:156 #: src/pages/ResetPassword.tsx:85
msgid "Request failed" msgid "Request failed"
msgstr "Échec de la demande" msgstr "Échec de la demande"
#: src/pages/ResetPassword.tsx:112 #: src/pages/ResetPassword.tsx:164
msgid "Reset failed" msgid "Reset failed"
msgstr "Échec de la réinitialisation" msgstr "Échec de la réinitialisation"
@@ -1606,12 +1605,13 @@ msgstr "Échec de la réinitialisation"
msgid "email.reset.button" msgid "email.reset.button"
msgstr "Choisir un nouveau mot de passe" msgstr "Choisir un nouveau mot de passe"
#: src/pages/ResetPassword.tsx:34 #: src/pages/ResetPassword.tsx:40
#: src/pages/ResetPassword.tsx:71
msgid "Reset password" msgid "Reset password"
msgstr "Réinitialiser le mot de passe" msgstr "Réinitialiser le mot de passe"
#: src/pages/DumpEdit.tsx:465 #: src/pages/DumpEdit.tsx:466
#: src/pages/DumpEdit.tsx:520 #: src/pages/DumpEdit.tsx:521
msgid "Reset to default" msgid "Reset to default"
msgstr "Réinitialiser par défaut" msgstr "Réinitialiser par défaut"
@@ -1631,8 +1631,8 @@ msgid "email.reset.title"
msgstr "Réinitialiser votre mot de passe" msgstr "Réinitialiser votre mot de passe"
#: src/components/chat/PendingItem.tsx:86 #: src/components/chat/PendingItem.tsx:86
#: src/pages/Dump.tsx:318 #: src/pages/Dump.tsx:323
#: src/pages/DumpEdit.tsx:226 #: src/pages/DumpEdit.tsx:227
msgid "Retry" msgid "Retry"
msgstr "Réessayer" msgstr "Réessayer"
@@ -1646,8 +1646,8 @@ msgstr "Rôle"
#: src/components/playlist/PlaylistEditForm.tsx:229 #: src/components/playlist/PlaylistEditForm.tsx:229
#: src/components/profile/DescriptionEditor.tsx:89 #: src/components/profile/DescriptionEditor.tsx:89
#: src/components/profile/EmailEditor.tsx:75 #: src/components/profile/EmailEditor.tsx:75
#: src/pages/Dump.tsx:433 #: src/pages/Dump.tsx:438
#: src/pages/DumpEdit.tsx:563 #: src/pages/DumpEdit.tsx:564
msgid "Save" msgid "Save"
msgstr "Enregistrer" msgstr "Enregistrer"
@@ -1656,8 +1656,8 @@ msgstr "Enregistrer"
#: src/components/profile/ChangePasswordModal.tsx:119 #: src/components/profile/ChangePasswordModal.tsx:119
#: src/components/profile/DescriptionEditor.tsx:86 #: src/components/profile/DescriptionEditor.tsx:86
#: src/components/profile/EmailEditor.tsx:72 #: src/components/profile/EmailEditor.tsx:72
#: src/pages/Dump.tsx:432 #: src/pages/Dump.tsx:437
#: src/pages/ResetPassword.tsx:139 #: src/pages/ResetPassword.tsx:191
msgid "Saving…" msgid "Saving…"
msgstr "Enregistrement…" msgstr "Enregistrement…"
@@ -1700,12 +1700,12 @@ msgstr "Position"
msgid "Send" msgid "Send"
msgstr "Envoyer" msgstr "Envoyer"
#: src/pages/UserLogin.tsx:165 #: src/pages/ResetPassword.tsx:94
msgid "Send reset link" msgid "Send reset link"
msgstr "Envoyer le lien de réinitialisation" msgstr "Envoyer le lien de réinitialisation"
#: src/components/chat/PendingItem.tsx:104 #: src/components/chat/PendingItem.tsx:104
#: src/pages/UserLogin.tsx:164 #: src/pages/ResetPassword.tsx:93
msgid "Sending…" msgid "Sending…"
msgstr "Envoi…" msgstr "Envoi…"
@@ -1713,8 +1713,8 @@ msgstr "Envoi…"
msgid "Server unreachable" msgid "Server unreachable"
msgstr "Serveur inaccessible" msgstr "Serveur inaccessible"
#: src/pages/ResetPassword.tsx:107 #: src/pages/ResetPassword.tsx:159
#: src/pages/ResetPassword.tsx:140 #: src/pages/ResetPassword.tsx:192
msgid "Set new password" msgid "Set new password"
msgstr "Définir un nouveau mot de passe" msgstr "Définir un nouveau mot de passe"
@@ -1766,7 +1766,7 @@ msgstr "Quelqu'un a demandé à réinitialiser le mot de passe de votre compte {
#: src/components/form/FormError.tsx:31 #: src/components/form/FormError.tsx:31
#: src/components/layout/PageError.tsx:29 #: src/components/layout/PageError.tsx:29
#: src/contexts/ws/WSProvider.tsx:732 #: src/contexts/ws/WSProvider.tsx:732
#: src/pages/PlaylistDetail.tsx:124 #: src/pages/PlaylistDetail.tsx:155
msgid "Something went wrong" msgid "Something went wrong"
msgstr "Une erreur est survenue" msgstr "Une erreur est survenue"
@@ -1799,7 +1799,7 @@ msgstr "la tribune"
msgid "the chat" msgid "the chat"
msgstr "la tribune" msgstr "la tribune"
#: src/hooks/playlist/usePlaylist.ts:179 #: src/hooks/playlist/usePlaylist.ts:182
msgid "The dump couldn't be removed. Try again." msgid "The dump couldn't be removed. Try again."
msgstr "Le dump n'a pas pu être retiré. Réessayez." msgstr "Le dump n'a pas pu être retiré. Réessayez."
@@ -1822,7 +1822,7 @@ msgstr "Le mois sur {site}"
msgid "email.digest.monthly.subject" msgid "email.digest.monthly.subject"
msgstr "Le mois sur {site}, {range}" msgstr "Le mois sur {site}, {range}"
#: src/hooks/playlist/usePlaylist.ts:322 #: src/hooks/playlist/usePlaylist.ts:325
msgid "The new order couldn't be saved. Try again." msgid "The new order couldn't be saved. Try again."
msgstr "Le nouvel ordre n'a pas pu être enregistré. Réessayez." msgstr "Le nouvel ordre n'a pas pu être enregistré. Réessayez."
@@ -1834,7 +1834,7 @@ msgstr "Ce commentaire n'a pas pu être supprimé. Réessayez."
msgid "This invite link is missing, expired, or already used." msgid "This invite link is missing, expired, or already used."
msgstr "Ce lien d'invitation est manquant, expiré ou déjà utilisé." msgstr "Ce lien d'invitation est manquant, expiré ou déjà utilisé."
#: src/pages/UserLogin.tsx:112 #: src/pages/UserLogin.tsx:99
msgid "This is a mirage." msgid "This is a mirage."
msgstr "C'est un mirage." msgstr "C'est un mirage."
@@ -1846,10 +1846,6 @@ msgstr "Ce mois-ci"
msgid "This page does not exist." msgid "This page does not exist."
msgstr "Rien à voir, circulez." msgstr "Rien à voir, circulez."
#: src/pages/ResetPassword.tsx:67
msgid "This reset link is missing or malformed."
msgstr "Ce lien de réinitialisation est absent ou malformé."
#: src/pages/FederationCallback.tsx:40 #: src/pages/FederationCallback.tsx:40
msgid "This sign-in link is incomplete." msgid "This sign-in link is incomplete."
msgstr "Ce lien de connexion est incomplet." msgstr "Ce lien de connexion est incomplet."
@@ -1870,14 +1866,14 @@ msgstr "Ce lien de désabonnement est absent ou mal formé."
msgid "This week" msgid "This week"
msgstr "Cette semaine" msgstr "Cette semaine"
#: src/pages/DumpEdit.tsx:449 #: src/pages/DumpEdit.tsx:450
msgid "Thumbnail" msgid "Thumbnail"
msgstr "Miniature" msgstr "Miniature"
#: src/components/dump/create/TitleField.tsx:45 #: src/components/dump/create/TitleField.tsx:45
#: src/components/playlist/PlaylistCreateForm.tsx:100 #: src/components/playlist/PlaylistCreateForm.tsx:100
#: src/components/playlist/PlaylistEditForm.tsx:175 #: src/components/playlist/PlaylistEditForm.tsx:175
#: src/pages/DumpEdit.tsx:510 #: src/pages/DumpEdit.tsx:511
msgid "Title" msgid "Title"
msgstr "Titre" msgstr "Titre"
@@ -2007,7 +2003,7 @@ msgstr "Validé"
msgid "Upvoted ({0})" msgid "Upvoted ({0})"
msgstr "Validé ({0})" msgstr "Validé ({0})"
#: src/pages/DumpEdit.tsx:481 #: src/pages/DumpEdit.tsx:482
msgid "URL" msgid "URL"
msgstr "URL" msgstr "URL"
@@ -2023,7 +2019,7 @@ msgstr "Identifiant d’URL"
msgid "User" msgid "User"
msgstr "Utilisateur" msgstr "Utilisateur"
#: src/components/profile/ProfileManageTab.tsx:54 #: src/components/profile/ProfileManageTab.tsx:56
msgid "User management" msgid "User management"
msgstr "Gestion utilisateur" msgstr "Gestion utilisateur"
@@ -2031,10 +2027,10 @@ msgstr "Gestion utilisateur"
msgid "User menu" msgid "User menu"
msgstr "Menu utilisateur" msgstr "Menu utilisateur"
#: src/components/profile/ProfileManageTab.tsx:61 #: src/components/profile/ProfileManageTab.tsx:63
#: src/components/profile/UsernameField.tsx:51 #: src/components/profile/UsernameField.tsx:51
#: src/pages/Settings.tsx:91 #: src/pages/Settings.tsx:91
#: src/pages/UserLogin.tsx:81 #: src/pages/UserLogin.tsx:74
#: src/pages/UserRegister.tsx:141 #: src/pages/UserRegister.tsx:141
msgid "Username" msgid "Username"
msgstr "Nom d'utilisateur" msgstr "Nom d'utilisateur"
@@ -2079,7 +2075,7 @@ msgid "email.welcome.subject"
msgstr "Bienvenue sur {site}" msgstr "Bienvenue sur {site}"
#: src/components/dump/create/DumpCreateModal.tsx:705 #: src/components/dump/create/DumpCreateModal.tsx:705
#: src/pages/DumpEdit.tsx:538 #: src/pages/DumpEdit.tsx:539
msgid "What makes it worth it?" msgid "What makes it worth it?"
msgstr "Pourquoi on en voudrait ?" msgstr "Pourquoi on en voudrait ?"
@@ -2097,7 +2093,7 @@ msgid "Why & where"
msgstr "Pourquoi & où" msgstr "Pourquoi & où"
#: src/components/dump/create/DumpCreateModal.tsx:704 #: src/components/dump/create/DumpCreateModal.tsx:704
#: src/pages/DumpEdit.tsx:537 #: src/pages/DumpEdit.tsx:538
msgid "Why?" msgid "Why?"
msgstr "Pourquoi ?" msgstr "Pourquoi ?"
@@ -2171,7 +2167,7 @@ msgstr "Votre compte est sur {home} : connectez-vous aux autres instances depuis
msgid "Your account lives on {home}: your password, email address, avatar and username change there." msgid "Your account lives on {home}: your password, email address, avatar and username change there."
msgstr "Votre compte est sur {home} : votre mot de passe, votre adresse e-mail, votre avatar et votre nom d'utilisateur se changent là-bas." msgstr "Votre compte est sur {home} : votre mot de passe, votre adresse e-mail, votre avatar et votre nom d'utilisateur se changent là-bas."
#: src/pages/UserLogin.tsx:160 #: src/pages/ResetPassword.tsx:89
msgid "Your email address" msgid "Your email address"
msgstr "Votre adresse e-mail" msgstr "Votre adresse e-mail"
@@ -2179,7 +2175,7 @@ msgstr "Votre adresse e-mail"
msgid "Your gerbeur's URL" msgid "Your gerbeur's URL"
msgstr "URL de votre gerbeur" msgstr "URL de votre gerbeur"
#: src/pages/ResetPassword.tsx:89 #: src/pages/ResetPassword.tsx:141
msgid "Your password has been changed. You can now log in." msgid "Your password has been changed. You can now log in."
msgstr "Votre mot de passe a été modifié. Vous pouvez maintenant vous connecter." msgstr "Votre mot de passe a été modifié. Vous pouvez maintenant vous connecter."

View File

@@ -3316,34 +3316,6 @@ body.has-player .chat-fab {
margin: 0; margin: 0;
} }
.auth-link-btn {
background: none;
border: none;
padding: 0;
cursor: pointer;
color: var(--color-accent);
font-size: inherit;
font-family: inherit;
text-decoration: underline;
text-underline-offset: 2px;
}
.auth-link-btn:hover {
opacity: 0.8;
}
.auth-reset-panel {
border-top: 1px solid var(--color-border-subtle);
padding-top: 1rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.auth-reset-form {
margin: 0;
}
.auth-reset-sent { .auth-reset-sent {
font-size: var(--text-md); font-size: var(--text-md);
text-align: center; text-align: center;

View File

@@ -14,9 +14,9 @@
* - `/authorize` lets another gerbeur instance sign the user in with their * - `/authorize` lets another gerbeur instance sign the user in with their
* account here (`Authorize`), and `/federation/callback` finishes a sign-in * account here (`Authorize`), and `/federation/callback` finishes a sign-in
* here with an account on another (`FederationCallback`). * here with an account on another (`FederationCallback`).
* - `/reset-password` sets a new password from an emailed link * - `/reset-password` asks for a reset link by email, and sets a new password
* (`ResetPassword`), and `/unsubscribe` turns off a digest from its emailed * from it, `?token=` (`ResetPassword`), and `/unsubscribe` turns off a digest
* link (`Unsubscribe`). * from its emailed link (`Unsubscribe`).
* - `/notifications` (`Notifications`) and `/settings` (`Settings`), signed in * - `/notifications` (`Notifications`) and `/settings` (`Settings`), signed in
* only, and `/manage`, the site's tools, for `category:manage` or * only, and `/manage`, the site's tools, for `category:manage` or
* `federation:manage` (`Manage`). * `federation:manage` (`Manage`).

View File

@@ -1,6 +1,7 @@
/** /**
* Renders the page a password reset email links to, where the user sets a new * Renders the reset of a forgotten password, both its steps on one page: the
* password. * request for a link by email, which "Forgot password?" on the login page
* leads to, and the new password, which the link leads to.
* *
* @module * @module
*/ */
@@ -26,15 +27,88 @@ interface Values {
confirm: string; confirm: string;
} }
interface RequestValues {
email: string;
}
/** /**
* Renders `/reset-password?token=`, for anyone holding a reset link. It doesn't * Renders `/reset-password`, for anyone: the request for a reset link, or,
* sign the user in: a success links to `/login`. * with the link's `?token=`, the new password. It doesn't sign the user in: a
* success links to `/login`.
*/ */
export function ResetPassword(): React.JSX.Element { export function ResetPassword(): React.JSX.Element {
useDocumentTitle(t`Reset password`); useDocumentTitle(t`Reset password`);
const [params] = useSearchParams(); const [params] = useSearchParams();
const navigate = useNavigate();
const token = params.get("token") ?? ""; const token = params.get("token") ?? "";
return token ? <NewPassword token={token} /> : <ResetRequest />;
}
/**
* Renders the form that asks the server to email a reset link, which leads
* back here with its token.
*/
function ResetRequest() {
const [sent, setSent] = useState(false);
const form = useApiForm<RequestValues>({ defaultValues: { email: "" } });
const onSubmit = form.submit(async ({ email }) => {
// The server answers 200 whether or not the address is known, so as not
// to reveal which ones are. An error here means it failed.
await expectOk(
await fetch(`${API_URL}/api/password-resets`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: email.trim() }),
}),
);
setSent(true);
});
return (
<PageShell centered>
<div className="auth-card">
<h1 className="auth-card-title">
<Trans>Reset password</Trans>
</h1>
{sent
? (
<p className="auth-reset-sent">
<Trans>
If that address is registered you'll receive a reset link
shortly.
</Trans>
</p>
)
: (
<FormProvider {...form}>
<form onSubmit={onSubmit} className="form">
<FormError title={t`Request failed`} />
<TextField<RequestValues>
name="email"
type="email"
placeholder={t`Your email address`}
autoFocus
rules={{ required: true }}
/>
<SubmitButton pendingLabel={<Trans>Sending…</Trans>}>
<Trans>Send reset link</Trans>
</SubmitButton>
</form>
</FormProvider>
)}
<p className="auth-card-footer">
<Link to="/login">
<Trans>Back to login</Trans>
</Link>
</p>
</div>
</PageShell>
);
}
/** Renders the form that sets a new password with the link's `token`. */
function NewPassword({ token }: { token: string }) {
const navigate = useNavigate();
const [done, setDone] = useState(false); const [done, setDone] = useState(false);
const form = useApiForm<Values>({ const form = useApiForm<Values>({
@@ -56,28 +130,6 @@ export function ResetPassword(): React.JSX.Element {
setDone(true); setDone(true);
}); });
if (!token) {
return (
<PageShell centered>
<div className="auth-card">
<h1 className="auth-card-title">
<Trans>Invalid link</Trans>
</h1>
<p>
<Trans>This reset link is missing or malformed.</Trans>
</p>
<Link
to="/login"
className="btn-primary"
style={{ marginTop: "1rem", display: "inline-block" }}
>
<Trans>Back to login</Trans>
</Link>
</div>
</PageShell>
);
}
if (done) { if (done) {
return ( return (
<PageShell centered> <PageShell centered>

View File

@@ -1,11 +1,10 @@
/** /**
* Renders the sign-in page, which also requests password reset links. * Renders the sign-in page.
* *
* @module * @module
*/ */
import { useState } from "react"; import { Link, useNavigate, useSearchParams } from "react-router";
import { useNavigate, useSearchParams } from "react-router";
import { t } from "@lingui/core/macro"; import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro"; import { Trans } from "@lingui/react/macro";
@@ -15,7 +14,6 @@ import type { AuthResponse, LoginUserRequest } from "../../shared/user.ts";
import type { Wire } from "../../shared/api.ts"; import type { Wire } from "../../shared/api.ts";
import { useAuth } from "../hooks/auth/useAuth.ts"; import { useAuth } from "../hooks/auth/useAuth.ts";
import { useDocumentTitle } from "../hooks/shared/useDocumentTitle.ts"; import { useDocumentTitle } from "../hooks/shared/useDocumentTitle.ts";
import { useHeightTransition } from "../hooks/shared/useHeightTransition.ts";
import { PageShell } from "../components/layout/PageShell.tsx"; import { PageShell } from "../components/layout/PageShell.tsx";
import { expectOk } from "../utils/shared/api.ts"; import { expectOk } from "../utils/shared/api.ts";
import { FormError } from "../components/form/FormError.tsx"; import { FormError } from "../components/form/FormError.tsx";
@@ -32,13 +30,10 @@ interface LoginValues {
password: string; password: string;
} }
interface ResetValues {
email: string;
}
/** /**
* Renders `/login`, for the signed out. A sign-in lands on the page `?next=` * Renders `/login`, for the signed out. A sign-in lands on the page `?next=`
* names, or `/`, and "Forgot password?" unfolds the reset request form below. * names, or `/`, and "Forgot password?" leads to `/reset-password`, which asks
* for a reset link.
* Users of another instance sign in through {@link FederatedSignIn}, unless * Users of another instance sign in through {@link FederatedSignIn}, unless
* the instance takes none. * the instance takes none.
*/ */
@@ -47,8 +42,6 @@ export function UserLogin(): React.JSX.Element {
const navigate = useNavigate(); const navigate = useNavigate();
const [params] = useSearchParams(); const [params] = useSearchParams();
const { login } = useAuth(); const { login } = useAuth();
const [showReset, setShowReset] = useState(false);
const cardRef = useHeightTransition<HTMLDivElement>(showReset);
const form = useApiForm<LoginValues>({ const form = useApiForm<LoginValues>({
defaultValues: { username: "", password: "" }, defaultValues: { username: "", password: "" },
@@ -68,7 +61,7 @@ export function UserLogin(): React.JSX.Element {
return ( return (
<PageShell centered> <PageShell centered>
<div className="auth-card" ref={cardRef}> <div className="auth-card">
<h1 className="auth-card-title"> <h1 className="auth-card-title">
<Trans>Log in</Trans> <Trans>Log in</Trans>
</h1> </h1>
@@ -95,17 +88,11 @@ export function UserLogin(): React.JSX.Element {
</FormProvider> </FormProvider>
<p className="auth-card-footer"> <p className="auth-card-footer">
<button <Link to="/reset-password">
type="button"
className="auth-link-btn"
onClick={() => setShowReset((v) => !v)}
>
<Trans>Forgot password?</Trans> <Trans>Forgot password?</Trans>
</button> </Link>
</p> </p>
{showReset && <ResetRequestPanel />}
{FEDERATION_INCOMING && <FederatedSignIn />} {FEDERATION_INCOMING && <FederatedSignIn />}
<p className="auth-card-footer"> <p className="auth-card-footer">
@@ -115,57 +102,3 @@ export function UserLogin(): React.JSX.Element {
</PageShell> </PageShell>
); );
} }
/**
* Renders the form that asks the server to email a password reset link, which
* leads to `/reset-password`.
*/
function ResetRequestPanel() {
const [sent, setSent] = useState(false);
const form = useApiForm<ResetValues>({ defaultValues: { email: "" } });
const onSubmit = form.submit(async ({ email }) => {
// The server answers 200 whether or not the address is known, so as not
// to reveal which ones are. An error here means it failed.
await expectOk(
await fetch(`${API_URL}/api/password-resets`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: email.trim() }),
}),
);
setSent(true);
});
if (sent) {
return (
<div className="auth-reset-panel animate-in">
<p className="auth-reset-sent">
<Trans>
If that address is registered you'll receive a reset link shortly.
</Trans>
</p>
</div>
);
}
return (
<div className="auth-reset-panel animate-in">
<FormProvider {...form}>
<form onSubmit={onSubmit} className="form auth-reset-form">
<FormError title={t`Request failed`} />
<TextField<ResetValues>
name="email"
type="email"
placeholder={t`Your email address`}
autoFocus
rules={{ required: true }}
/>
<SubmitButton pendingLabel={<Trans>Sending…</Trans>}>
<Trans>Send reset link</Trans>
</SubmitButton>
</form>
</FormProvider>
</div>
);
}

View File

@@ -17,16 +17,37 @@ describe("ResetPassword", () => {
return user; return user;
} }
test("no token: an invalid link", () => { test("without a token, it asks for a link by email", async () => {
setup(""); route("POST /api/password-resets", () => ({}));
expect(screen.getByRole("heading", { name: "Invalid link" })) const user = setup("");
.toBeInTheDocument(); await user.type(
screen.getByPlaceholderText("Your email address"),
" a@b.c {Enter}",
);
await screen.findByText(/you'll receive a reset link/);
expect(calls("POST /api/password-resets")[0].body).toEqual({
email: "a@b.c",
});
expect(screen.getByRole("link", { name: "Back to login" })).toHaveAttribute( expect(screen.getByRole("link", { name: "Back to login" })).toHaveAttribute(
"href", "href",
"/login", "/login",
); );
}); });
test("a failed request is reported, not claimed as sent", async () => {
route("POST /api/password-resets", () => fail("Internal error", 500));
const user = setup("");
await user.type(
screen.getByPlaceholderText("Your email address"),
"a@b.c{Enter}",
);
expect(await screen.findByRole("alert")).toHaveTextContent(
"Request failed",
);
expect(screen.queryByText(/you'll receive a reset link/)).not
.toBeInTheDocument();
});
test("the two passwords must match", async () => { test("the two passwords must match", async () => {
const user = setup(); const user = setup();
await user.type(screen.getByPlaceholderText("New password"), "longenough"); await user.type(screen.getByPlaceholderText("New password"), "longenough");

View File

@@ -97,36 +97,10 @@ describe("UserLogin", () => {
await screen.findByText("home page"); await screen.findByText("home page");
}); });
test("password reset request", async () => { test("Forgot password? leads to the reset page", () => {
route("POST /api/password-resets", () => ({})); setup();
const user = setup(); expect(screen.getByRole("link", { name: "Forgot password?" }))
await user.click(screen.getByRole("button", { name: "Forgot password?" })); .toHaveAttribute("href", "/reset-password");
await user.type(
screen.getByPlaceholderText("Your email address"),
" a@b.c {Enter}",
);
await screen.findByText(/you'll receive a reset link/);
expect(calls("POST /api/password-resets")[0].body).toEqual({
email: "a@b.c",
});
});
test("a failed reset request is reported, not claimed as sent", async () => {
route(
"POST /api/password-resets",
() => fail("Internal error", 500),
);
const user = setup();
await user.click(screen.getByRole("button", { name: "Forgot password?" }));
await user.type(
screen.getByPlaceholderText("Your email address"),
"a@b.c{Enter}",
);
expect(await screen.findByRole("alert")).toHaveTextContent(
"Request failed",
);
expect(screen.queryByText(/you'll receive a reset link/)).not
.toBeInTheDocument();
}); });
test("a sign-in follows ?next= on this site, and only there", async () => { test("a sign-in follows ?next= on this site, and only there", async () => {

View File

@@ -107,7 +107,6 @@
[data-style="brutalist"] .rich-content-thumbnail-btn, [data-style="brutalist"] .rich-content-thumbnail-btn,
[data-style="brutalist"] .user-menu-trigger, [data-style="brutalist"] .user-menu-trigger,
[data-style="brutalist"] .user-menu-item, [data-style="brutalist"] .user-menu-item,
[data-style="brutalist"] .auth-link-btn,
[data-style="brutalist"] .form-cancel, [data-style="brutalist"] .form-cancel,
[data-style="brutalist"] .form-label-action { [data-style="brutalist"] .form-label-action {
border: none !important; border: none !important;
@@ -118,8 +117,6 @@
[data-style="brutalist"] .user-menu-trigger:active, [data-style="brutalist"] .user-menu-trigger:active,
[data-style="brutalist"] .user-menu-item:hover:not(:disabled), [data-style="brutalist"] .user-menu-item:hover:not(:disabled),
[data-style="brutalist"] .user-menu-item:active, [data-style="brutalist"] .user-menu-item:active,
[data-style="brutalist"] .auth-link-btn:hover:not(:disabled),
[data-style="brutalist"] .auth-link-btn:active,
[data-style="brutalist"] .form-cancel:hover:not(:disabled), [data-style="brutalist"] .form-cancel:hover:not(:disabled),
[data-style="brutalist"] .form-cancel:active, [data-style="brutalist"] .form-cancel:active,
[data-style="brutalist"] .form-label-action:hover:not(:disabled), [data-style="brutalist"] .form-label-action:hover:not(:disabled),

View File

@@ -218,7 +218,6 @@
[data-style="geocities"] .rich-content-thumbnail-btn, [data-style="geocities"] .rich-content-thumbnail-btn,
[data-style="geocities"] .user-menu-trigger, [data-style="geocities"] .user-menu-trigger,
[data-style="geocities"] .user-menu-item, [data-style="geocities"] .user-menu-item,
[data-style="geocities"] .auth-link-btn,
[data-style="geocities"] .form-cancel, [data-style="geocities"] .form-cancel,
[data-style="geocities"] .form-label-action, [data-style="geocities"] .form-label-action,
[data-style="geocities"] .category-editor-btn { [data-style="geocities"] .category-editor-btn {