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:
5
API.md
5
API.md
@@ -23,7 +23,7 @@ has one word, the same as in pages.
|
||||
## Pages
|
||||
|
||||
| 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` |
|
||||
| `/<category>` | `/<category>/hot`, `/new`, `/journal`, `/follow`, `/follow/playlists` | anyone, `/follow` signed in | `Index` |
|
||||
| `/@<name>` | the overview: dumps beside upvoted | anyone | `UserPublicProfile` |
|
||||
@@ -42,7 +42,8 @@ has one word, the same as in pages.
|
||||
| `/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` |
|
||||
| `/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,
|
||||
`/?chat=open` opens the chat over any page, and `#comment-<id>` scrolls a dump's
|
||||
|
||||
186
locales/en.po
186
locales/en.po
@@ -206,14 +206,14 @@ msgstr "# Welcome to {site}!\n"
|
||||
"Your account has been created successfully. Welcome aboard!"
|
||||
|
||||
#: src/components/dump/create/DumpCreateModal.tsx:736
|
||||
#: src/pages/PlaylistDetail.tsx:80
|
||||
#: src/pages/PlaylistDetail.tsx:98
|
||||
#: src/pages/UserPublicProfile.tsx:125
|
||||
msgid "← Back"
|
||||
msgstr "← Back"
|
||||
|
||||
#: src/pages/Dump.tsx:325
|
||||
#: src/pages/Dump.tsx:552
|
||||
#: src/pages/DumpEdit.tsx:233
|
||||
#: src/pages/Dump.tsx:330
|
||||
#: src/pages/Dump.tsx:557
|
||||
#: src/pages/DumpEdit.tsx:234
|
||||
msgid "← Back to all dumps"
|
||||
msgstr "← Back to all dumps"
|
||||
|
||||
@@ -225,7 +225,7 @@ msgstr "+ Invite someone"
|
||||
msgid "+ New playlist"
|
||||
msgstr "+ New playlist"
|
||||
|
||||
#: src/pages/Dump.tsx:399
|
||||
#: src/pages/Dump.tsx:404
|
||||
msgid "+ Playlist"
|
||||
msgstr "+ Playlist"
|
||||
|
||||
@@ -377,7 +377,7 @@ msgstr "Appearance"
|
||||
|
||||
#. placeholder {0}: VALIDATION.PASSWORD_MIN
|
||||
#: src/components/profile/ChangePasswordModal.tsx:101
|
||||
#: src/pages/ResetPassword.tsx:124
|
||||
#: src/pages/ResetPassword.tsx:176
|
||||
#: src/pages/UserRegister.tsx:168
|
||||
msgid "At least {0} characters"
|
||||
msgstr "At least {0} characters"
|
||||
@@ -390,8 +390,8 @@ msgstr "At most {maxLength} characters."
|
||||
msgid "Auto"
|
||||
msgstr "Auto"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:74
|
||||
#: src/pages/ResetPassword.tsx:147
|
||||
#: src/pages/ResetPassword.tsx:101
|
||||
#: src/pages/ResetPassword.tsx:199
|
||||
msgid "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/shared/ConfirmModal.tsx:63
|
||||
#: src/pages/Authorize.tsx:158
|
||||
#: src/pages/Dump.tsx:441
|
||||
#: src/pages/DumpEdit.tsx:560
|
||||
#: src/pages/Dump.tsx:446
|
||||
#: src/pages/DumpEdit.tsx:561
|
||||
msgid "Cancel"
|
||||
msgstr "Cancel"
|
||||
|
||||
@@ -546,7 +546,7 @@ msgid "digest.community"
|
||||
msgstr "Community"
|
||||
|
||||
#: src/components/profile/ChangePasswordModal.tsx:108
|
||||
#: src/pages/ResetPassword.tsx:131
|
||||
#: src/pages/ResetPassword.tsx:183
|
||||
msgid "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."
|
||||
msgstr "Could not connect to the server. Check your connection and try again."
|
||||
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:177
|
||||
#: src/pages/DumpEdit.tsx:554
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:178
|
||||
#: src/pages/DumpEdit.tsx:555
|
||||
msgid "Could not delete"
|
||||
msgstr "Could not delete"
|
||||
|
||||
@@ -591,7 +591,7 @@ msgstr "Could not delete"
|
||||
msgid "Could not rename"
|
||||
msgstr "Could not rename"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:506
|
||||
#: src/pages/DumpEdit.tsx:507
|
||||
msgid "Could not save"
|
||||
msgstr "Could not save"
|
||||
|
||||
@@ -668,20 +668,20 @@ msgstr "Delete category"
|
||||
msgid "Delete category \"{0}\"? This cannot be undone."
|
||||
msgstr "Delete category \"{0}\"? This cannot be undone."
|
||||
|
||||
#: src/pages/DumpEdit.tsx:315
|
||||
#: src/pages/DumpEdit.tsx:557
|
||||
#: src/pages/DumpEdit.tsx:316
|
||||
#: src/pages/DumpEdit.tsx:558
|
||||
msgid "Delete dump"
|
||||
msgstr "Delete dump"
|
||||
|
||||
#. placeholder {0}: res.status
|
||||
#: src/pages/DumpEdit.tsx:194
|
||||
#: src/pages/DumpEdit.tsx:195
|
||||
msgid "Delete failed ({0})"
|
||||
msgstr "Delete failed ({0})"
|
||||
|
||||
#: src/components/playlist/PlaylistCard.tsx:149
|
||||
#: src/components/playlist/PlaylistEditForm.tsx:213
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:207
|
||||
#: src/pages/PlaylistDetail.tsx:141
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:208
|
||||
#: src/pages/PlaylistDetail.tsx:172
|
||||
msgid "Delete playlist"
|
||||
msgstr "Delete playlist"
|
||||
|
||||
@@ -689,7 +689,7 @@ msgstr "Delete playlist"
|
||||
msgid "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."
|
||||
msgstr "Delete this dump? This cannot be undone."
|
||||
|
||||
@@ -697,8 +697,8 @@ msgstr "Delete this dump? This cannot be undone."
|
||||
msgid "Delete this message?"
|
||||
msgstr "Delete this message?"
|
||||
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:206
|
||||
#: src/pages/PlaylistDetail.tsx:140
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:207
|
||||
#: src/pages/PlaylistDetail.tsx:171
|
||||
msgid "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"
|
||||
msgstr "Drop a file here, or paste one"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:497
|
||||
#: src/pages/DumpEdit.tsx:498
|
||||
msgid "Drop a replacement here"
|
||||
msgstr "Drop a replacement here"
|
||||
|
||||
@@ -788,7 +788,7 @@ msgstr "Earlier"
|
||||
#: src/components/chat/PendingItem.tsx:93
|
||||
#: src/components/comment/CommentNode.tsx:256
|
||||
#: src/components/playlist/PlaylistHeader.tsx:60
|
||||
#: src/pages/Dump.tsx:548
|
||||
#: src/pages/Dump.tsx:553
|
||||
msgid "Edit"
|
||||
msgstr "Edit"
|
||||
|
||||
@@ -806,7 +806,7 @@ msgid "Edit email"
|
||||
msgstr "Edit email"
|
||||
|
||||
#: src/components/dump/create/TitleField.tsx:69
|
||||
#: src/pages/Dump.tsx:458
|
||||
#: src/pages/Dump.tsx:463
|
||||
msgid "Edit title"
|
||||
msgstr "Edit title"
|
||||
|
||||
@@ -820,7 +820,7 @@ msgstr "edited"
|
||||
#: src/components/chat/ChatMessageItem.tsx:132
|
||||
#: src/components/comment/CommentNode.tsx:205
|
||||
#: src/components/playlist/PlaylistHeader.tsx:95
|
||||
#: src/pages/Dump.tsx:501
|
||||
#: src/pages/Dump.tsx:506
|
||||
msgid "edited {0}"
|
||||
msgstr "edited {0}"
|
||||
|
||||
@@ -830,16 +830,16 @@ msgstr "edited {0}"
|
||||
#: src/components/chat/ChatMessageItem.tsx:128
|
||||
#: src/components/comment/CommentNode.tsx:203
|
||||
#: src/components/playlist/PlaylistHeader.tsx:93
|
||||
#: src/pages/Dump.tsx:499
|
||||
#: src/pages/Dump.tsx:504
|
||||
msgid "Edited {0}"
|
||||
msgstr "Edited {0}"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:258
|
||||
#: src/pages/DumpEdit.tsx:259
|
||||
msgid "Editing"
|
||||
msgstr "Editing"
|
||||
|
||||
#. placeholder {0}: state.dump.title
|
||||
#: src/pages/DumpEdit.tsx:81
|
||||
#: src/pages/DumpEdit.tsx:82
|
||||
msgid "Editing {0}"
|
||||
msgstr "Editing {0}"
|
||||
|
||||
@@ -942,15 +942,15 @@ msgstr "Follow {targetUsername}"
|
||||
msgid "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."
|
||||
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."
|
||||
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/pages/Settings.tsx:57
|
||||
#: src/pages/UserPublicProfile.tsx:163
|
||||
@@ -967,11 +967,11 @@ msgstr "Following"
|
||||
msgid "email.digest.forYou"
|
||||
msgstr "For you"
|
||||
|
||||
#: src/pages/UserLogin.tsx:103
|
||||
#: src/pages/UserLogin.tsx:92
|
||||
msgid "Forgot password?"
|
||||
msgstr "Forgot password?"
|
||||
|
||||
#: src/components/feed/FollowedFeed.tsx:132
|
||||
#: src/components/feed/FollowedFeed.tsx:137
|
||||
msgid "From people"
|
||||
msgstr "From people"
|
||||
|
||||
@@ -980,7 +980,7 @@ msgstr "From people"
|
||||
msgid "email.digest.network"
|
||||
msgstr "From people you follow"
|
||||
|
||||
#: src/components/feed/FollowedFeed.tsx:133
|
||||
#: src/components/feed/FollowedFeed.tsx:138
|
||||
msgid "From playlists"
|
||||
msgstr "From playlists"
|
||||
|
||||
@@ -992,7 +992,7 @@ msgstr "Fullscreen"
|
||||
msgid "Go home"
|
||||
msgstr "Go home"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:96
|
||||
#: src/pages/ResetPassword.tsx:148
|
||||
msgid "Go to login"
|
||||
msgstr "Go to login"
|
||||
|
||||
@@ -1001,12 +1001,12 @@ msgstr "Go to login"
|
||||
msgid "email.reset.hi"
|
||||
msgstr "Hi {username},"
|
||||
|
||||
#: src/components/feed/FeedTabBar.tsx:40
|
||||
#: src/components/feed/FeedTabBar.tsx:57
|
||||
#: src/pages/Settings.tsx:54
|
||||
msgid "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."
|
||||
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"
|
||||
msgstr "image"
|
||||
|
||||
#: src/pages/Dump.tsx:568
|
||||
#: src/pages/Dump.tsx:573
|
||||
msgid "In collections"
|
||||
msgstr "In collections"
|
||||
|
||||
@@ -1046,7 +1046,6 @@ msgstr "Instances"
|
||||
msgid "Invalid invite"
|
||||
msgstr "Invalid invite"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:64
|
||||
#: src/pages/Unsubscribe.tsx:66
|
||||
msgid "Invalid link"
|
||||
msgstr "Invalid link"
|
||||
@@ -1075,7 +1074,7 @@ msgstr "invited by you"
|
||||
msgid "Invitees"
|
||||
msgstr "Invitees"
|
||||
|
||||
#: src/components/feed/FeedTabBar.tsx:42
|
||||
#: src/components/feed/FeedTabBar.tsx:59
|
||||
#: src/pages/Settings.tsx:56
|
||||
msgid "Journal"
|
||||
msgstr "Journal"
|
||||
@@ -1126,8 +1125,8 @@ msgstr "Load more"
|
||||
msgid "Load older messages"
|
||||
msgstr "Load older messages"
|
||||
|
||||
#: src/pages/Dump.tsx:301
|
||||
#: src/pages/DumpEdit.tsx:209
|
||||
#: src/pages/Dump.tsx:306
|
||||
#: src/pages/DumpEdit.tsx:210
|
||||
msgid "Loading dump…"
|
||||
msgstr "Loading dump…"
|
||||
|
||||
@@ -1139,7 +1138,7 @@ msgstr "Loading messages…"
|
||||
msgid "Loading more…"
|
||||
msgstr "Loading more…"
|
||||
|
||||
#: src/pages/PlaylistDetail.tsx:64
|
||||
#: src/pages/PlaylistDetail.tsx:82
|
||||
msgid "Loading playlist…"
|
||||
msgstr "Loading playlist…"
|
||||
|
||||
@@ -1161,9 +1160,9 @@ msgstr "Loading…"
|
||||
|
||||
#: src/components/layout/AppHeader.tsx:208
|
||||
#: src/pages/FederationCallback.tsx:32
|
||||
#: src/pages/UserLogin.tsx:46
|
||||
#: src/pages/UserLogin.tsx:73
|
||||
#: src/pages/UserLogin.tsx:92
|
||||
#: src/pages/UserLogin.tsx:41
|
||||
#: src/pages/UserLogin.tsx:66
|
||||
#: src/pages/UserLogin.tsx:85
|
||||
msgid "Log in"
|
||||
msgstr "Log in"
|
||||
|
||||
@@ -1185,12 +1184,12 @@ msgstr "Log in with {0}"
|
||||
msgid "Log out"
|
||||
msgstr "Log out"
|
||||
|
||||
#: src/pages/UserLogin.tsx:91
|
||||
#: src/pages/UserLogin.tsx:84
|
||||
msgid "Logging in…"
|
||||
msgstr "Logging in…"
|
||||
|
||||
#: src/pages/FederationCallback.tsx:69
|
||||
#: src/pages/UserLogin.tsx:78
|
||||
#: src/pages/UserLogin.tsx:71
|
||||
msgid "Login failed"
|
||||
msgstr "Login failed"
|
||||
|
||||
@@ -1228,7 +1227,7 @@ msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: src/components/chat/ChatModal.tsx:692
|
||||
#: src/components/feed/FeedTabBar.tsx:41
|
||||
#: src/components/feed/FeedTabBar.tsx:58
|
||||
#: src/pages/Settings.tsx:55
|
||||
msgid "New"
|
||||
msgstr "New"
|
||||
@@ -1246,7 +1245,7 @@ msgid "digest.newMembers"
|
||||
msgstr "New members"
|
||||
|
||||
#: src/components/profile/ChangePasswordModal.tsx:94
|
||||
#: src/pages/ResetPassword.tsx:116
|
||||
#: src/pages/ResetPassword.tsx:168
|
||||
msgid "New password"
|
||||
msgstr "New password"
|
||||
|
||||
@@ -1306,7 +1305,7 @@ msgid "No playlists match \"{q}\"."
|
||||
msgstr "No playlists match \"{q}\"."
|
||||
|
||||
#: src/components/playlist/PlaylistMembershipPanel.tsx:51
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:184
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:185
|
||||
#: src/pages/Playlists.tsx:118
|
||||
msgid "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."
|
||||
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."
|
||||
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:106
|
||||
#: src/pages/UserLogin.tsx:88
|
||||
#: src/pages/UserLogin.tsx:81
|
||||
msgid "Password"
|
||||
msgstr "Password"
|
||||
|
||||
@@ -1431,12 +1430,12 @@ msgstr "Password (min. {0} characters)"
|
||||
msgid "Password changed successfully."
|
||||
msgstr "Password changed successfully."
|
||||
|
||||
#: src/pages/ResetPassword.tsx:86
|
||||
#: src/pages/ResetPassword.tsx:138
|
||||
msgid "Password updated"
|
||||
msgstr "Password updated"
|
||||
|
||||
#: src/components/profile/ChangePasswordModal.tsx:114
|
||||
#: src/pages/ResetPassword.tsx:136
|
||||
#: src/pages/ResetPassword.tsx:188
|
||||
msgid "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/UserMenu.tsx:92
|
||||
#: 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:106
|
||||
#: src/pages/Search.tsx:151
|
||||
@@ -1502,7 +1501,7 @@ msgstr "Previous track"
|
||||
#: src/components/playlist/PlaylistCard.tsx:106
|
||||
#: src/components/playlist/PlaylistHeader.tsx:77
|
||||
#: src/components/playlist/PlaylistMembershipPanel.tsx:81
|
||||
#: src/pages/Dump.tsx:507
|
||||
#: src/pages/Dump.tsx:512
|
||||
msgid "private"
|
||||
msgstr "private"
|
||||
|
||||
@@ -1519,7 +1518,7 @@ msgstr "public"
|
||||
msgid "Public"
|
||||
msgstr "Public"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:291
|
||||
#: src/pages/DumpEdit.tsx:292
|
||||
msgid "Refresh metadata"
|
||||
msgstr "Refresh metadata"
|
||||
|
||||
@@ -1527,7 +1526,7 @@ msgstr "Refresh metadata"
|
||||
msgid "Refreshing stream…"
|
||||
msgstr "Refreshing stream…"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:290
|
||||
#: src/pages/DumpEdit.tsx:291
|
||||
msgid "Refreshing…"
|
||||
msgstr "Refreshing…"
|
||||
|
||||
@@ -1545,7 +1544,7 @@ msgstr "Registering…"
|
||||
msgid "Registration failed"
|
||||
msgstr "Registration failed"
|
||||
|
||||
#: src/pages/Dump.tsx:559
|
||||
#: src/pages/Dump.tsx:564
|
||||
msgid "Related"
|
||||
msgstr "Related"
|
||||
|
||||
@@ -1579,7 +1578,7 @@ msgstr "Rename"
|
||||
msgid "Renaming…"
|
||||
msgstr "Renaming…"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:489
|
||||
#: src/pages/DumpEdit.tsx:490
|
||||
msgid "Replace file"
|
||||
msgstr "Replace file"
|
||||
|
||||
@@ -1593,11 +1592,11 @@ msgstr "Reply"
|
||||
msgid "Replying to"
|
||||
msgstr "Replying to"
|
||||
|
||||
#: src/pages/UserLogin.tsx:156
|
||||
#: src/pages/ResetPassword.tsx:85
|
||||
msgid "Request failed"
|
||||
msgstr "Request failed"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:112
|
||||
#: src/pages/ResetPassword.tsx:164
|
||||
msgid "Reset failed"
|
||||
msgstr "Reset failed"
|
||||
|
||||
@@ -1606,12 +1605,13 @@ msgstr "Reset failed"
|
||||
msgid "email.reset.button"
|
||||
msgstr "Reset password"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:34
|
||||
#: src/pages/ResetPassword.tsx:40
|
||||
#: src/pages/ResetPassword.tsx:71
|
||||
msgid "Reset password"
|
||||
msgstr "Reset password"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:465
|
||||
#: src/pages/DumpEdit.tsx:520
|
||||
#: src/pages/DumpEdit.tsx:466
|
||||
#: src/pages/DumpEdit.tsx:521
|
||||
msgid "Reset to default"
|
||||
msgstr "Reset to default"
|
||||
|
||||
@@ -1631,8 +1631,8 @@ msgid "email.reset.title"
|
||||
msgstr "Reset your password"
|
||||
|
||||
#: src/components/chat/PendingItem.tsx:86
|
||||
#: src/pages/Dump.tsx:318
|
||||
#: src/pages/DumpEdit.tsx:226
|
||||
#: src/pages/Dump.tsx:323
|
||||
#: src/pages/DumpEdit.tsx:227
|
||||
msgid "Retry"
|
||||
msgstr "Retry"
|
||||
|
||||
@@ -1646,8 +1646,8 @@ msgstr "Role"
|
||||
#: src/components/playlist/PlaylistEditForm.tsx:229
|
||||
#: src/components/profile/DescriptionEditor.tsx:89
|
||||
#: src/components/profile/EmailEditor.tsx:75
|
||||
#: src/pages/Dump.tsx:433
|
||||
#: src/pages/DumpEdit.tsx:563
|
||||
#: src/pages/Dump.tsx:438
|
||||
#: src/pages/DumpEdit.tsx:564
|
||||
msgid "Save"
|
||||
msgstr "Save"
|
||||
|
||||
@@ -1656,8 +1656,8 @@ msgstr "Save"
|
||||
#: src/components/profile/ChangePasswordModal.tsx:119
|
||||
#: src/components/profile/DescriptionEditor.tsx:86
|
||||
#: src/components/profile/EmailEditor.tsx:72
|
||||
#: src/pages/Dump.tsx:432
|
||||
#: src/pages/ResetPassword.tsx:139
|
||||
#: src/pages/Dump.tsx:437
|
||||
#: src/pages/ResetPassword.tsx:191
|
||||
msgid "Saving…"
|
||||
msgstr "Saving…"
|
||||
|
||||
@@ -1700,12 +1700,12 @@ msgstr "Seek"
|
||||
msgid "Send"
|
||||
msgstr "Send"
|
||||
|
||||
#: src/pages/UserLogin.tsx:165
|
||||
#: src/pages/ResetPassword.tsx:94
|
||||
msgid "Send reset link"
|
||||
msgstr "Send reset link"
|
||||
|
||||
#: src/components/chat/PendingItem.tsx:104
|
||||
#: src/pages/UserLogin.tsx:164
|
||||
#: src/pages/ResetPassword.tsx:93
|
||||
msgid "Sending…"
|
||||
msgstr "Sending…"
|
||||
|
||||
@@ -1713,8 +1713,8 @@ msgstr "Sending…"
|
||||
msgid "Server unreachable"
|
||||
msgstr "Server unreachable"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:107
|
||||
#: src/pages/ResetPassword.tsx:140
|
||||
#: src/pages/ResetPassword.tsx:159
|
||||
#: src/pages/ResetPassword.tsx:192
|
||||
msgid "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/layout/PageError.tsx:29
|
||||
#: src/contexts/ws/WSProvider.tsx:732
|
||||
#: src/pages/PlaylistDetail.tsx:124
|
||||
#: src/pages/PlaylistDetail.tsx:155
|
||||
msgid "Something went wrong"
|
||||
msgstr "Something went wrong"
|
||||
|
||||
@@ -1799,7 +1799,7 @@ msgstr "the chat"
|
||||
msgid "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."
|
||||
msgstr "The dump couldn't be removed. Try again."
|
||||
|
||||
@@ -1822,7 +1822,7 @@ msgstr "The month on {site}"
|
||||
msgid "email.digest.monthly.subject"
|
||||
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."
|
||||
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."
|
||||
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."
|
||||
msgstr "This is a mirage."
|
||||
|
||||
@@ -1846,10 +1846,6 @@ msgstr "This month"
|
||||
msgid "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
|
||||
msgid "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"
|
||||
msgstr "This week"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:449
|
||||
#: src/pages/DumpEdit.tsx:450
|
||||
msgid "Thumbnail"
|
||||
msgstr "Thumbnail"
|
||||
|
||||
#: src/components/dump/create/TitleField.tsx:45
|
||||
#: src/components/playlist/PlaylistCreateForm.tsx:100
|
||||
#: src/components/playlist/PlaylistEditForm.tsx:175
|
||||
#: src/pages/DumpEdit.tsx:510
|
||||
#: src/pages/DumpEdit.tsx:511
|
||||
msgid "Title"
|
||||
msgstr "Title"
|
||||
|
||||
@@ -2007,7 +2003,7 @@ msgstr "Upvoted"
|
||||
msgid "Upvoted ({0})"
|
||||
msgstr "Upvoted ({0})"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:481
|
||||
#: src/pages/DumpEdit.tsx:482
|
||||
msgid "URL"
|
||||
msgstr "URL"
|
||||
|
||||
@@ -2023,7 +2019,7 @@ msgstr "URL slug"
|
||||
msgid "User"
|
||||
msgstr "User"
|
||||
|
||||
#: src/components/profile/ProfileManageTab.tsx:54
|
||||
#: src/components/profile/ProfileManageTab.tsx:56
|
||||
msgid "User management"
|
||||
msgstr "User management"
|
||||
|
||||
@@ -2031,10 +2027,10 @@ msgstr "User management"
|
||||
msgid "User menu"
|
||||
msgstr "User menu"
|
||||
|
||||
#: src/components/profile/ProfileManageTab.tsx:61
|
||||
#: src/components/profile/ProfileManageTab.tsx:63
|
||||
#: src/components/profile/UsernameField.tsx:51
|
||||
#: src/pages/Settings.tsx:91
|
||||
#: src/pages/UserLogin.tsx:81
|
||||
#: src/pages/UserLogin.tsx:74
|
||||
#: src/pages/UserRegister.tsx:141
|
||||
msgid "Username"
|
||||
msgstr "Username"
|
||||
@@ -2079,7 +2075,7 @@ msgid "email.welcome.subject"
|
||||
msgstr "Welcome to {site}"
|
||||
|
||||
#: src/components/dump/create/DumpCreateModal.tsx:705
|
||||
#: src/pages/DumpEdit.tsx:538
|
||||
#: src/pages/DumpEdit.tsx:539
|
||||
msgid "What makes it worth it?"
|
||||
msgstr "What makes it worth it?"
|
||||
|
||||
@@ -2097,7 +2093,7 @@ msgid "Why & where"
|
||||
msgstr "Why & where"
|
||||
|
||||
#: src/components/dump/create/DumpCreateModal.tsx:704
|
||||
#: src/pages/DumpEdit.tsx:537
|
||||
#: src/pages/DumpEdit.tsx:538
|
||||
msgid "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."
|
||||
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"
|
||||
msgstr "Your email address"
|
||||
|
||||
@@ -2179,7 +2175,7 @@ msgstr "Your email address"
|
||||
msgid "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."
|
||||
msgstr "Your password has been changed. You can now log in."
|
||||
|
||||
|
||||
186
locales/fr.po
186
locales/fr.po
@@ -206,14 +206,14 @@ msgstr "# Bienvenue sur {site} !\n"
|
||||
"Votre compte a bien été créé. Bienvenue à bord !"
|
||||
|
||||
#: src/components/dump/create/DumpCreateModal.tsx:736
|
||||
#: src/pages/PlaylistDetail.tsx:80
|
||||
#: src/pages/PlaylistDetail.tsx:98
|
||||
#: src/pages/UserPublicProfile.tsx:125
|
||||
msgid "← Back"
|
||||
msgstr "← Retour"
|
||||
|
||||
#: src/pages/Dump.tsx:325
|
||||
#: src/pages/Dump.tsx:552
|
||||
#: src/pages/DumpEdit.tsx:233
|
||||
#: src/pages/Dump.tsx:330
|
||||
#: src/pages/Dump.tsx:557
|
||||
#: src/pages/DumpEdit.tsx:234
|
||||
msgid "← Back to all dumps"
|
||||
msgstr "← Retour à toutes les recos"
|
||||
|
||||
@@ -225,7 +225,7 @@ msgstr "+ Inviter quelqu'un"
|
||||
msgid "+ New playlist"
|
||||
msgstr "+ Nouvelle collection"
|
||||
|
||||
#: src/pages/Dump.tsx:399
|
||||
#: src/pages/Dump.tsx:404
|
||||
msgid "+ Playlist"
|
||||
msgstr "+ Collection"
|
||||
|
||||
@@ -377,7 +377,7 @@ msgstr "Apparence"
|
||||
|
||||
#. placeholder {0}: VALIDATION.PASSWORD_MIN
|
||||
#: src/components/profile/ChangePasswordModal.tsx:101
|
||||
#: src/pages/ResetPassword.tsx:124
|
||||
#: src/pages/ResetPassword.tsx:176
|
||||
#: src/pages/UserRegister.tsx:168
|
||||
msgid "At least {0} characters"
|
||||
msgstr "Au moins {0} caractères"
|
||||
@@ -390,8 +390,8 @@ msgstr "{maxLength} caractères au maximum."
|
||||
msgid "Auto"
|
||||
msgstr "Auto"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:74
|
||||
#: src/pages/ResetPassword.tsx:147
|
||||
#: src/pages/ResetPassword.tsx:101
|
||||
#: src/pages/ResetPassword.tsx:199
|
||||
msgid "Back to login"
|
||||
msgstr "Retour à la connexion"
|
||||
|
||||
@@ -439,8 +439,8 @@ msgstr "Connexion impossible"
|
||||
#: src/components/profile/EmailEditor.tsx:83
|
||||
#: src/components/shared/ConfirmModal.tsx:63
|
||||
#: src/pages/Authorize.tsx:158
|
||||
#: src/pages/Dump.tsx:441
|
||||
#: src/pages/DumpEdit.tsx:560
|
||||
#: src/pages/Dump.tsx:446
|
||||
#: src/pages/DumpEdit.tsx:561
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
@@ -546,7 +546,7 @@ msgid "digest.community"
|
||||
msgstr "Communauté"
|
||||
|
||||
#: src/components/profile/ChangePasswordModal.tsx:108
|
||||
#: src/pages/ResetPassword.tsx:131
|
||||
#: src/pages/ResetPassword.tsx:183
|
||||
msgid "Confirm new password"
|
||||
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."
|
||||
msgstr "Impossible de joindre le serveur. Vérifiez votre connexion et réessayez."
|
||||
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:177
|
||||
#: src/pages/DumpEdit.tsx:554
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:178
|
||||
#: src/pages/DumpEdit.tsx:555
|
||||
msgid "Could not delete"
|
||||
msgstr "Suppression impossible"
|
||||
|
||||
@@ -591,7 +591,7 @@ msgstr "Suppression impossible"
|
||||
msgid "Could not rename"
|
||||
msgstr "Renommage impossible"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:506
|
||||
#: src/pages/DumpEdit.tsx:507
|
||||
msgid "Could not save"
|
||||
msgstr "Sauvegarde impossible"
|
||||
|
||||
@@ -668,20 +668,20 @@ msgstr "Supprimer la catégorie"
|
||||
msgid "Delete category \"{0}\"? This cannot be undone."
|
||||
msgstr "Supprimer la catégorie \"{0}\" ? Cette action est irréversible."
|
||||
|
||||
#: src/pages/DumpEdit.tsx:315
|
||||
#: src/pages/DumpEdit.tsx:557
|
||||
#: src/pages/DumpEdit.tsx:316
|
||||
#: src/pages/DumpEdit.tsx:558
|
||||
msgid "Delete dump"
|
||||
msgstr "Supprimer la reco"
|
||||
|
||||
#. placeholder {0}: res.status
|
||||
#: src/pages/DumpEdit.tsx:194
|
||||
#: src/pages/DumpEdit.tsx:195
|
||||
msgid "Delete failed ({0})"
|
||||
msgstr "Échec de la suppression ({0})"
|
||||
|
||||
#: src/components/playlist/PlaylistCard.tsx:149
|
||||
#: src/components/playlist/PlaylistEditForm.tsx:213
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:207
|
||||
#: src/pages/PlaylistDetail.tsx:141
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:208
|
||||
#: src/pages/PlaylistDetail.tsx:172
|
||||
msgid "Delete playlist"
|
||||
msgstr "Supprimer la collection"
|
||||
|
||||
@@ -689,7 +689,7 @@ msgstr "Supprimer la collection"
|
||||
msgid "Delete this comment?"
|
||||
msgstr "Supprimer ce commentaire ?"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:314
|
||||
#: src/pages/DumpEdit.tsx:315
|
||||
msgid "Delete this dump? This cannot be undone."
|
||||
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?"
|
||||
msgstr "Supprimer ce message ?"
|
||||
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:206
|
||||
#: src/pages/PlaylistDetail.tsx:140
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:207
|
||||
#: src/pages/PlaylistDetail.tsx:171
|
||||
msgid "Delete this playlist? This cannot be undone."
|
||||
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"
|
||||
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"
|
||||
msgstr "Déposez un fichier de remplacement ici"
|
||||
|
||||
@@ -788,7 +788,7 @@ msgstr "Plus tôt"
|
||||
#: src/components/chat/PendingItem.tsx:93
|
||||
#: src/components/comment/CommentNode.tsx:256
|
||||
#: src/components/playlist/PlaylistHeader.tsx:60
|
||||
#: src/pages/Dump.tsx:548
|
||||
#: src/pages/Dump.tsx:553
|
||||
msgid "Edit"
|
||||
msgstr "Modifier"
|
||||
|
||||
@@ -806,7 +806,7 @@ msgid "Edit email"
|
||||
msgstr "Modifier l'e-mail"
|
||||
|
||||
#: src/components/dump/create/TitleField.tsx:69
|
||||
#: src/pages/Dump.tsx:458
|
||||
#: src/pages/Dump.tsx:463
|
||||
msgid "Edit title"
|
||||
msgstr "Modifier le titre"
|
||||
|
||||
@@ -820,7 +820,7 @@ msgstr "modifié"
|
||||
#: src/components/chat/ChatMessageItem.tsx:132
|
||||
#: src/components/comment/CommentNode.tsx:205
|
||||
#: src/components/playlist/PlaylistHeader.tsx:95
|
||||
#: src/pages/Dump.tsx:501
|
||||
#: src/pages/Dump.tsx:506
|
||||
msgid "edited {0}"
|
||||
msgstr "modifié {0}"
|
||||
|
||||
@@ -830,16 +830,16 @@ msgstr "modifié {0}"
|
||||
#: src/components/chat/ChatMessageItem.tsx:128
|
||||
#: src/components/comment/CommentNode.tsx:203
|
||||
#: src/components/playlist/PlaylistHeader.tsx:93
|
||||
#: src/pages/Dump.tsx:499
|
||||
#: src/pages/Dump.tsx:504
|
||||
msgid "Edited {0}"
|
||||
msgstr "Modifié le {0}"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:258
|
||||
#: src/pages/DumpEdit.tsx:259
|
||||
msgid "Editing"
|
||||
msgstr "Modification"
|
||||
|
||||
#. placeholder {0}: state.dump.title
|
||||
#: src/pages/DumpEdit.tsx:81
|
||||
#: src/pages/DumpEdit.tsx:82
|
||||
msgid "Editing {0}"
|
||||
msgstr "Édition de {0}"
|
||||
|
||||
@@ -942,15 +942,15 @@ msgstr "Suivre {targetUsername}"
|
||||
msgid "Follow playlist"
|
||||
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."
|
||||
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."
|
||||
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/pages/Settings.tsx:57
|
||||
#: src/pages/UserPublicProfile.tsx:163
|
||||
@@ -967,11 +967,11 @@ msgstr "Abonné"
|
||||
msgid "email.digest.forYou"
|
||||
msgstr "Pour vous"
|
||||
|
||||
#: src/pages/UserLogin.tsx:103
|
||||
#: src/pages/UserLogin.tsx:92
|
||||
msgid "Forgot password?"
|
||||
msgstr "Mot de passe oublié ?"
|
||||
|
||||
#: src/components/feed/FollowedFeed.tsx:132
|
||||
#: src/components/feed/FollowedFeed.tsx:137
|
||||
msgid "From people"
|
||||
msgstr "De personnes"
|
||||
|
||||
@@ -980,7 +980,7 @@ msgstr "De personnes"
|
||||
msgid "email.digest.network"
|
||||
msgstr "De ceux que vous suivez"
|
||||
|
||||
#: src/components/feed/FollowedFeed.tsx:133
|
||||
#: src/components/feed/FollowedFeed.tsx:138
|
||||
msgid "From playlists"
|
||||
msgstr "De collections"
|
||||
|
||||
@@ -992,7 +992,7 @@ msgstr "Plein écran"
|
||||
msgid "Go home"
|
||||
msgstr "Accueil"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:96
|
||||
#: src/pages/ResetPassword.tsx:148
|
||||
msgid "Go to login"
|
||||
msgstr "Aller à la connexion"
|
||||
|
||||
@@ -1001,12 +1001,12 @@ msgstr "Aller à la connexion"
|
||||
msgid "email.reset.hi"
|
||||
msgstr "Bonjour {username},"
|
||||
|
||||
#: src/components/feed/FeedTabBar.tsx:40
|
||||
#: src/components/feed/FeedTabBar.tsx:57
|
||||
#: src/pages/Settings.tsx:54
|
||||
msgid "Hot"
|
||||
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."
|
||||
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"
|
||||
msgstr "image"
|
||||
|
||||
#: src/pages/Dump.tsx:568
|
||||
#: src/pages/Dump.tsx:573
|
||||
msgid "In collections"
|
||||
msgstr "Dans les collections"
|
||||
|
||||
@@ -1046,7 +1046,6 @@ msgstr "Instances"
|
||||
msgid "Invalid invite"
|
||||
msgstr "Invitation invalide"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:64
|
||||
#: src/pages/Unsubscribe.tsx:66
|
||||
msgid "Invalid link"
|
||||
msgstr "Lien invalide"
|
||||
@@ -1075,7 +1074,7 @@ msgstr "sur votre invitation"
|
||||
msgid "Invitees"
|
||||
msgstr "Invités"
|
||||
|
||||
#: src/components/feed/FeedTabBar.tsx:42
|
||||
#: src/components/feed/FeedTabBar.tsx:59
|
||||
#: src/pages/Settings.tsx:56
|
||||
msgid "Journal"
|
||||
msgstr "Journal"
|
||||
@@ -1126,8 +1125,8 @@ msgstr "Charger plus"
|
||||
msgid "Load older messages"
|
||||
msgstr "Charger les messages plus anciens"
|
||||
|
||||
#: src/pages/Dump.tsx:301
|
||||
#: src/pages/DumpEdit.tsx:209
|
||||
#: src/pages/Dump.tsx:306
|
||||
#: src/pages/DumpEdit.tsx:210
|
||||
msgid "Loading dump…"
|
||||
msgstr "Chargement de la reco…"
|
||||
|
||||
@@ -1139,7 +1138,7 @@ msgstr "Chargement des messages…"
|
||||
msgid "Loading more…"
|
||||
msgstr "Chargement…"
|
||||
|
||||
#: src/pages/PlaylistDetail.tsx:64
|
||||
#: src/pages/PlaylistDetail.tsx:82
|
||||
msgid "Loading playlist…"
|
||||
msgstr "Chargement de la collection…"
|
||||
|
||||
@@ -1161,9 +1160,9 @@ msgstr "Chargement…"
|
||||
|
||||
#: src/components/layout/AppHeader.tsx:208
|
||||
#: src/pages/FederationCallback.tsx:32
|
||||
#: src/pages/UserLogin.tsx:46
|
||||
#: src/pages/UserLogin.tsx:73
|
||||
#: src/pages/UserLogin.tsx:92
|
||||
#: src/pages/UserLogin.tsx:41
|
||||
#: src/pages/UserLogin.tsx:66
|
||||
#: src/pages/UserLogin.tsx:85
|
||||
msgid "Log in"
|
||||
msgstr "Se connecter"
|
||||
|
||||
@@ -1185,12 +1184,12 @@ msgstr "Se connecter avec {0}"
|
||||
msgid "Log out"
|
||||
msgstr "Se déconnecter"
|
||||
|
||||
#: src/pages/UserLogin.tsx:91
|
||||
#: src/pages/UserLogin.tsx:84
|
||||
msgid "Logging in…"
|
||||
msgstr "Connexion…"
|
||||
|
||||
#: src/pages/FederationCallback.tsx:69
|
||||
#: src/pages/UserLogin.tsx:78
|
||||
#: src/pages/UserLogin.tsx:71
|
||||
msgid "Login failed"
|
||||
msgstr "Connexion échouée"
|
||||
|
||||
@@ -1228,7 +1227,7 @@ msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: src/components/chat/ChatModal.tsx:692
|
||||
#: src/components/feed/FeedTabBar.tsx:41
|
||||
#: src/components/feed/FeedTabBar.tsx:58
|
||||
#: src/pages/Settings.tsx:55
|
||||
msgid "New"
|
||||
msgstr "Nouveau"
|
||||
@@ -1246,7 +1245,7 @@ msgid "digest.newMembers"
|
||||
msgstr "Nouveaux membres"
|
||||
|
||||
#: src/components/profile/ChangePasswordModal.tsx:94
|
||||
#: src/pages/ResetPassword.tsx:116
|
||||
#: src/pages/ResetPassword.tsx:168
|
||||
msgid "New password"
|
||||
msgstr "Nouveau mot de passe"
|
||||
|
||||
@@ -1306,7 +1305,7 @@ msgid "No playlists match \"{q}\"."
|
||||
msgstr "Aucune collection ne correspond à « {q} »."
|
||||
|
||||
#: src/components/playlist/PlaylistMembershipPanel.tsx:51
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:184
|
||||
#: src/components/profile/ProfilePlaylistsTab.tsx:185
|
||||
#: src/pages/Playlists.tsx:118
|
||||
msgid "No playlists yet."
|
||||
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."
|
||||
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."
|
||||
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:106
|
||||
#: src/pages/UserLogin.tsx:88
|
||||
#: src/pages/UserLogin.tsx:81
|
||||
msgid "Password"
|
||||
msgstr "Mot de passe"
|
||||
|
||||
@@ -1431,12 +1430,12 @@ msgstr "Mot de passe (min. {0} caractères)"
|
||||
msgid "Password changed successfully."
|
||||
msgstr "Mot de passe modifié avec succès."
|
||||
|
||||
#: src/pages/ResetPassword.tsx:86
|
||||
#: src/pages/ResetPassword.tsx:138
|
||||
msgid "Password updated"
|
||||
msgstr "Mot de passe mis à jour"
|
||||
|
||||
#: src/components/profile/ChangePasswordModal.tsx:114
|
||||
#: src/pages/ResetPassword.tsx:136
|
||||
#: src/pages/ResetPassword.tsx:188
|
||||
msgid "Passwords do not match"
|
||||
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/UserMenu.tsx:92
|
||||
#: 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:106
|
||||
#: src/pages/Search.tsx:151
|
||||
@@ -1502,7 +1501,7 @@ msgstr "Piste précédente"
|
||||
#: src/components/playlist/PlaylistCard.tsx:106
|
||||
#: src/components/playlist/PlaylistHeader.tsx:77
|
||||
#: src/components/playlist/PlaylistMembershipPanel.tsx:81
|
||||
#: src/pages/Dump.tsx:507
|
||||
#: src/pages/Dump.tsx:512
|
||||
msgid "private"
|
||||
msgstr "privé"
|
||||
|
||||
@@ -1519,7 +1518,7 @@ msgstr "public"
|
||||
msgid "Public"
|
||||
msgstr "Public"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:291
|
||||
#: src/pages/DumpEdit.tsx:292
|
||||
msgid "Refresh metadata"
|
||||
msgstr "Actualiser les métadonnées"
|
||||
|
||||
@@ -1527,7 +1526,7 @@ msgstr "Actualiser les métadonnées"
|
||||
msgid "Refreshing stream…"
|
||||
msgstr "Actualisation du flux…"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:290
|
||||
#: src/pages/DumpEdit.tsx:291
|
||||
msgid "Refreshing…"
|
||||
msgstr "Actualisation…"
|
||||
|
||||
@@ -1545,7 +1544,7 @@ msgstr "Inscription…"
|
||||
msgid "Registration failed"
|
||||
msgstr "Inscription échouée"
|
||||
|
||||
#: src/pages/Dump.tsx:559
|
||||
#: src/pages/Dump.tsx:564
|
||||
msgid "Related"
|
||||
msgstr "Connexe"
|
||||
|
||||
@@ -1579,7 +1578,7 @@ msgstr "Renommer"
|
||||
msgid "Renaming…"
|
||||
msgstr "Renommage…"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:489
|
||||
#: src/pages/DumpEdit.tsx:490
|
||||
msgid "Replace file"
|
||||
msgstr "Remplacer le fichier"
|
||||
|
||||
@@ -1593,11 +1592,11 @@ msgstr "Répondre"
|
||||
msgid "Replying to"
|
||||
msgstr "En réponse à"
|
||||
|
||||
#: src/pages/UserLogin.tsx:156
|
||||
#: src/pages/ResetPassword.tsx:85
|
||||
msgid "Request failed"
|
||||
msgstr "Échec de la demande"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:112
|
||||
#: src/pages/ResetPassword.tsx:164
|
||||
msgid "Reset failed"
|
||||
msgstr "Échec de la réinitialisation"
|
||||
|
||||
@@ -1606,12 +1605,13 @@ msgstr "Échec de la réinitialisation"
|
||||
msgid "email.reset.button"
|
||||
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"
|
||||
msgstr "Réinitialiser le mot de passe"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:465
|
||||
#: src/pages/DumpEdit.tsx:520
|
||||
#: src/pages/DumpEdit.tsx:466
|
||||
#: src/pages/DumpEdit.tsx:521
|
||||
msgid "Reset to default"
|
||||
msgstr "Réinitialiser par défaut"
|
||||
|
||||
@@ -1631,8 +1631,8 @@ msgid "email.reset.title"
|
||||
msgstr "Réinitialiser votre mot de passe"
|
||||
|
||||
#: src/components/chat/PendingItem.tsx:86
|
||||
#: src/pages/Dump.tsx:318
|
||||
#: src/pages/DumpEdit.tsx:226
|
||||
#: src/pages/Dump.tsx:323
|
||||
#: src/pages/DumpEdit.tsx:227
|
||||
msgid "Retry"
|
||||
msgstr "Réessayer"
|
||||
|
||||
@@ -1646,8 +1646,8 @@ msgstr "Rôle"
|
||||
#: src/components/playlist/PlaylistEditForm.tsx:229
|
||||
#: src/components/profile/DescriptionEditor.tsx:89
|
||||
#: src/components/profile/EmailEditor.tsx:75
|
||||
#: src/pages/Dump.tsx:433
|
||||
#: src/pages/DumpEdit.tsx:563
|
||||
#: src/pages/Dump.tsx:438
|
||||
#: src/pages/DumpEdit.tsx:564
|
||||
msgid "Save"
|
||||
msgstr "Enregistrer"
|
||||
|
||||
@@ -1656,8 +1656,8 @@ msgstr "Enregistrer"
|
||||
#: src/components/profile/ChangePasswordModal.tsx:119
|
||||
#: src/components/profile/DescriptionEditor.tsx:86
|
||||
#: src/components/profile/EmailEditor.tsx:72
|
||||
#: src/pages/Dump.tsx:432
|
||||
#: src/pages/ResetPassword.tsx:139
|
||||
#: src/pages/Dump.tsx:437
|
||||
#: src/pages/ResetPassword.tsx:191
|
||||
msgid "Saving…"
|
||||
msgstr "Enregistrement…"
|
||||
|
||||
@@ -1700,12 +1700,12 @@ msgstr "Position"
|
||||
msgid "Send"
|
||||
msgstr "Envoyer"
|
||||
|
||||
#: src/pages/UserLogin.tsx:165
|
||||
#: src/pages/ResetPassword.tsx:94
|
||||
msgid "Send reset link"
|
||||
msgstr "Envoyer le lien de réinitialisation"
|
||||
|
||||
#: src/components/chat/PendingItem.tsx:104
|
||||
#: src/pages/UserLogin.tsx:164
|
||||
#: src/pages/ResetPassword.tsx:93
|
||||
msgid "Sending…"
|
||||
msgstr "Envoi…"
|
||||
|
||||
@@ -1713,8 +1713,8 @@ msgstr "Envoi…"
|
||||
msgid "Server unreachable"
|
||||
msgstr "Serveur inaccessible"
|
||||
|
||||
#: src/pages/ResetPassword.tsx:107
|
||||
#: src/pages/ResetPassword.tsx:140
|
||||
#: src/pages/ResetPassword.tsx:159
|
||||
#: src/pages/ResetPassword.tsx:192
|
||||
msgid "Set new password"
|
||||
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/layout/PageError.tsx:29
|
||||
#: src/contexts/ws/WSProvider.tsx:732
|
||||
#: src/pages/PlaylistDetail.tsx:124
|
||||
#: src/pages/PlaylistDetail.tsx:155
|
||||
msgid "Something went wrong"
|
||||
msgstr "Une erreur est survenue"
|
||||
|
||||
@@ -1799,7 +1799,7 @@ msgstr "la tribune"
|
||||
msgid "the chat"
|
||||
msgstr "la tribune"
|
||||
|
||||
#: src/hooks/playlist/usePlaylist.ts:179
|
||||
#: src/hooks/playlist/usePlaylist.ts:182
|
||||
msgid "The dump couldn't be removed. Try again."
|
||||
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"
|
||||
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."
|
||||
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."
|
||||
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."
|
||||
msgstr "C'est un mirage."
|
||||
|
||||
@@ -1846,10 +1846,6 @@ msgstr "Ce mois-ci"
|
||||
msgid "This page does not exist."
|
||||
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
|
||||
msgid "This sign-in link is incomplete."
|
||||
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"
|
||||
msgstr "Cette semaine"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:449
|
||||
#: src/pages/DumpEdit.tsx:450
|
||||
msgid "Thumbnail"
|
||||
msgstr "Miniature"
|
||||
|
||||
#: src/components/dump/create/TitleField.tsx:45
|
||||
#: src/components/playlist/PlaylistCreateForm.tsx:100
|
||||
#: src/components/playlist/PlaylistEditForm.tsx:175
|
||||
#: src/pages/DumpEdit.tsx:510
|
||||
#: src/pages/DumpEdit.tsx:511
|
||||
msgid "Title"
|
||||
msgstr "Titre"
|
||||
|
||||
@@ -2007,7 +2003,7 @@ msgstr "Validé"
|
||||
msgid "Upvoted ({0})"
|
||||
msgstr "Validé ({0})"
|
||||
|
||||
#: src/pages/DumpEdit.tsx:481
|
||||
#: src/pages/DumpEdit.tsx:482
|
||||
msgid "URL"
|
||||
msgstr "URL"
|
||||
|
||||
@@ -2023,7 +2019,7 @@ msgstr "Identifiant d’URL"
|
||||
msgid "User"
|
||||
msgstr "Utilisateur"
|
||||
|
||||
#: src/components/profile/ProfileManageTab.tsx:54
|
||||
#: src/components/profile/ProfileManageTab.tsx:56
|
||||
msgid "User management"
|
||||
msgstr "Gestion utilisateur"
|
||||
|
||||
@@ -2031,10 +2027,10 @@ msgstr "Gestion utilisateur"
|
||||
msgid "User menu"
|
||||
msgstr "Menu utilisateur"
|
||||
|
||||
#: src/components/profile/ProfileManageTab.tsx:61
|
||||
#: src/components/profile/ProfileManageTab.tsx:63
|
||||
#: src/components/profile/UsernameField.tsx:51
|
||||
#: src/pages/Settings.tsx:91
|
||||
#: src/pages/UserLogin.tsx:81
|
||||
#: src/pages/UserLogin.tsx:74
|
||||
#: src/pages/UserRegister.tsx:141
|
||||
msgid "Username"
|
||||
msgstr "Nom d'utilisateur"
|
||||
@@ -2079,7 +2075,7 @@ msgid "email.welcome.subject"
|
||||
msgstr "Bienvenue sur {site}"
|
||||
|
||||
#: src/components/dump/create/DumpCreateModal.tsx:705
|
||||
#: src/pages/DumpEdit.tsx:538
|
||||
#: src/pages/DumpEdit.tsx:539
|
||||
msgid "What makes it worth it?"
|
||||
msgstr "Pourquoi on en voudrait ?"
|
||||
|
||||
@@ -2097,7 +2093,7 @@ msgid "Why & where"
|
||||
msgstr "Pourquoi & où"
|
||||
|
||||
#: src/components/dump/create/DumpCreateModal.tsx:704
|
||||
#: src/pages/DumpEdit.tsx:537
|
||||
#: src/pages/DumpEdit.tsx:538
|
||||
msgid "Why?"
|
||||
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."
|
||||
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"
|
||||
msgstr "Votre adresse e-mail"
|
||||
|
||||
@@ -2179,7 +2175,7 @@ msgstr "Votre adresse e-mail"
|
||||
msgid "Your gerbeur's URL"
|
||||
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."
|
||||
msgstr "Votre mot de passe a été modifié. Vous pouvez maintenant vous connecter."
|
||||
|
||||
|
||||
28
src/App.css
28
src/App.css
@@ -3316,34 +3316,6 @@ body.has-player .chat-fab {
|
||||
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 {
|
||||
font-size: var(--text-md);
|
||||
text-align: center;
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
* - `/authorize` lets another gerbeur instance sign the user in with their
|
||||
* account here (`Authorize`), and `/federation/callback` finishes a sign-in
|
||||
* here with an account on another (`FederationCallback`).
|
||||
* - `/reset-password` sets a new password from an emailed link
|
||||
* (`ResetPassword`), and `/unsubscribe` turns off a digest from its emailed
|
||||
* link (`Unsubscribe`).
|
||||
* - `/reset-password` asks for a reset link by email, and sets a new password
|
||||
* from it, `?token=` (`ResetPassword`), and `/unsubscribe` turns off a digest
|
||||
* from its emailed link (`Unsubscribe`).
|
||||
* - `/notifications` (`Notifications`) and `/settings` (`Settings`), signed in
|
||||
* only, and `/manage`, the site's tools, for `category:manage` or
|
||||
* `federation:manage` (`Manage`).
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* Renders the page a password reset email links to, where the user sets a new
|
||||
* password.
|
||||
* Renders the reset of a forgotten password, both its steps on one page: the
|
||||
* 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
|
||||
*/
|
||||
@@ -26,15 +27,88 @@ interface Values {
|
||||
confirm: string;
|
||||
}
|
||||
|
||||
interface RequestValues {
|
||||
email: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders `/reset-password?token=`, for anyone holding a reset link. It doesn't
|
||||
* sign the user in: a success links to `/login`.
|
||||
* Renders `/reset-password`, for anyone: the request for a reset link, or,
|
||||
* 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 {
|
||||
useDocumentTitle(t`Reset password`);
|
||||
const [params] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
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 form = useApiForm<Values>({
|
||||
@@ -56,28 +130,6 @@ export function ResetPassword(): React.JSX.Element {
|
||||
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) {
|
||||
return (
|
||||
<PageShell centered>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/**
|
||||
* Renders the sign-in page, which also requests password reset links.
|
||||
* Renders the sign-in page.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
import { useState } from "react";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { Link, useNavigate, useSearchParams } from "react-router";
|
||||
import { t } from "@lingui/core/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 { useAuth } from "../hooks/auth/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/shared/useDocumentTitle.ts";
|
||||
import { useHeightTransition } from "../hooks/shared/useHeightTransition.ts";
|
||||
import { PageShell } from "../components/layout/PageShell.tsx";
|
||||
import { expectOk } from "../utils/shared/api.ts";
|
||||
import { FormError } from "../components/form/FormError.tsx";
|
||||
@@ -32,13 +30,10 @@ interface LoginValues {
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface ResetValues {
|
||||
email: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the instance takes none.
|
||||
*/
|
||||
@@ -47,8 +42,6 @@ export function UserLogin(): React.JSX.Element {
|
||||
const navigate = useNavigate();
|
||||
const [params] = useSearchParams();
|
||||
const { login } = useAuth();
|
||||
const [showReset, setShowReset] = useState(false);
|
||||
const cardRef = useHeightTransition<HTMLDivElement>(showReset);
|
||||
|
||||
const form = useApiForm<LoginValues>({
|
||||
defaultValues: { username: "", password: "" },
|
||||
@@ -68,7 +61,7 @@ export function UserLogin(): React.JSX.Element {
|
||||
|
||||
return (
|
||||
<PageShell centered>
|
||||
<div className="auth-card" ref={cardRef}>
|
||||
<div className="auth-card">
|
||||
<h1 className="auth-card-title">
|
||||
<Trans>Log in</Trans>
|
||||
</h1>
|
||||
@@ -95,17 +88,11 @@ export function UserLogin(): React.JSX.Element {
|
||||
</FormProvider>
|
||||
|
||||
<p className="auth-card-footer">
|
||||
<button
|
||||
type="button"
|
||||
className="auth-link-btn"
|
||||
onClick={() => setShowReset((v) => !v)}
|
||||
>
|
||||
<Link to="/reset-password">
|
||||
<Trans>Forgot password?</Trans>
|
||||
</button>
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
{showReset && <ResetRequestPanel />}
|
||||
|
||||
{FEDERATION_INCOMING && <FederatedSignIn />}
|
||||
|
||||
<p className="auth-card-footer">
|
||||
@@ -115,57 +102,3 @@ export function UserLogin(): React.JSX.Element {
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,16 +17,37 @@ describe("ResetPassword", () => {
|
||||
return user;
|
||||
}
|
||||
|
||||
test("no token: an invalid link", () => {
|
||||
setup("");
|
||||
expect(screen.getByRole("heading", { name: "Invalid link" }))
|
||||
.toBeInTheDocument();
|
||||
test("without a token, it asks for a link by email", async () => {
|
||||
route("POST /api/password-resets", () => ({}));
|
||||
const user = setup("");
|
||||
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(
|
||||
"href",
|
||||
"/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 () => {
|
||||
const user = setup();
|
||||
await user.type(screen.getByPlaceholderText("New password"), "longenough");
|
||||
|
||||
@@ -97,36 +97,10 @@ describe("UserLogin", () => {
|
||||
await screen.findByText("home page");
|
||||
});
|
||||
|
||||
test("password reset request", async () => {
|
||||
route("POST /api/password-resets", () => ({}));
|
||||
const user = setup();
|
||||
await user.click(screen.getByRole("button", { name: "Forgot 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("Forgot password? leads to the reset page", () => {
|
||||
setup();
|
||||
expect(screen.getByRole("link", { name: "Forgot password?" }))
|
||||
.toHaveAttribute("href", "/reset-password");
|
||||
});
|
||||
|
||||
test("a sign-in follows ?next= on this site, and only there", async () => {
|
||||
|
||||
@@ -107,7 +107,6 @@
|
||||
[data-style="brutalist"] .rich-content-thumbnail-btn,
|
||||
[data-style="brutalist"] .user-menu-trigger,
|
||||
[data-style="brutalist"] .user-menu-item,
|
||||
[data-style="brutalist"] .auth-link-btn,
|
||||
[data-style="brutalist"] .form-cancel,
|
||||
[data-style="brutalist"] .form-label-action {
|
||||
border: none !important;
|
||||
@@ -118,8 +117,6 @@
|
||||
[data-style="brutalist"] .user-menu-trigger:active,
|
||||
[data-style="brutalist"] .user-menu-item:hover:not(:disabled),
|
||||
[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:active,
|
||||
[data-style="brutalist"] .form-label-action:hover:not(:disabled),
|
||||
|
||||
@@ -218,7 +218,6 @@
|
||||
[data-style="geocities"] .rich-content-thumbnail-btn,
|
||||
[data-style="geocities"] .user-menu-trigger,
|
||||
[data-style="geocities"] .user-menu-item,
|
||||
[data-style="geocities"] .auth-link-btn,
|
||||
[data-style="geocities"] .form-cancel,
|
||||
[data-style="geocities"] .form-label-action,
|
||||
[data-style="geocities"] .category-editor-btn {
|
||||
|
||||
Reference in New Issue
Block a user