Compare commits

..

90 Commits
6.0.0 ... 6.2.0

Author SHA1 Message Date
Athou
141a863079 release 6.2.0 2026-02-09 19:32:22 +01:00
Athou
6fa8d4be34 keep starred entries (#1581) 2026-02-09 06:59:41 +01:00
renovate[bot]
984e8a44d5 chore(deps): lock file maintenance 2026-02-09 01:33:44 +00:00
renovate[bot]
bdb296bce2 chore(deps): update dependency @types/react to ^19.2.13 2026-02-08 14:02:01 +00:00
Jérémie Panzer
955a9084c3 Merge pull request #2042 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.9.0
2026-02-08 05:23:05 +01:00
renovate[bot]
70f486b0eb chore(deps): update dependency npm to v11.9.0 2026-02-07 21:45:56 +00:00
renovate[bot]
0bc383c6a8 chore(deps): update dependency @types/react to ^19.2.11 2026-02-07 13:12:20 +00:00
renovate[bot]
0bb2b36585 chore(deps): update dependency @biomejs/biome to v2.3.14 2026-02-06 16:48:35 +00:00
Jérémie Panzer
9e3a24753a Merge pull request #2040 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.2.0
2026-02-06 02:20:48 +01:00
renovate[bot]
f2c400799e chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.2.0 2026-02-05 21:38:36 +00:00
renovate[bot]
25a8c8a7e3 chore(deps): update dependency @vitejs/plugin-react to ^5.1.3 2026-02-05 10:32:18 +00:00
Jérémie Panzer
8f95d89fc6 Merge pull request #2039 from Athou/renovate/jsdom-28.x
chore(deps): update dependency jsdom to v28
2026-02-05 11:30:07 +01:00
renovate[bot]
39b0cdb9d5 chore(deps): update dependency jsdom to v28 2026-02-05 09:42:39 +00:00
renovate[bot]
42e06b848e fix(deps): update quarkus.version to v3.31.2 2026-02-04 17:56:35 +00:00
renovate[bot]
7c3a13b1c4 fix(deps): update mantine monorepo to ^8.3.14 2026-02-04 12:45:49 +00:00
Jérémie Panzer
151248fce2 Merge pull request #2038 from xmgz/master
Update gl messages.po
2026-02-04 07:13:01 +01:00
ghose
6e8d6fe063 Update gl messages.po
up to date gl translation
2026-02-04 04:09:30 +00:00
renovate[bot]
ca2da5e631 chore(deps): update actions/checkout digest to de0fac2 2026-02-03 16:36:24 +00:00
renovate[bot]
6cd3b70201 chore(deps): update debian:13.3 docker digest to 2c91e48 2026-02-03 14:08:51 +00:00
renovate[bot]
2dcfba75b5 chore(deps): update jaywcjlove/markdown-to-html-cli action to v5.0.4 2026-02-03 09:32:58 +00:00
Jérémie Panzer
44a51b03d3 Merge pull request #2037 from Athou/renovate/org.apache.maven.plugins-maven-compiler-plugin-3.x
chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.15.0
2026-02-02 06:14:39 +01:00
renovate[bot]
6ee9e9831e chore(deps): lock file maintenance 2026-02-02 01:56:05 +00:00
renovate[bot]
68c717cee8 chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.15.0 2026-02-01 21:36:26 +00:00
Jérémie Panzer
b15fc02c34 Merge pull request #2035 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.1.0
2026-02-01 07:38:58 +01:00
renovate[bot]
033ebfb497 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.1.0 2026-01-31 20:23:47 +00:00
renovate[bot]
4cceaa7650 fix(deps): update dependency axios to ^1.13.4 2026-01-30 21:51:52 +00:00
renovate[bot]
5df47f1396 chore(deps): update dependency @types/react to ^19.2.10 2026-01-30 12:33:18 +00:00
renovate[bot]
903f35c01b fix(deps): update react monorepo to ^19.2.4 2026-01-29 20:53:17 +00:00
renovate[bot]
6a34f94277 chore(deps): update dependency @biomejs/biome to v2.3.13 2026-01-29 17:09:33 +00:00
Athou
dcc143eb7d upgrade to quarkus 3.31 2026-01-28 18:04:16 +01:00
renovate[bot]
fb47bf27e8 fix(deps): update dependency axios to ^1.13.3 2026-01-28 16:39:42 +00:00
renovate[bot]
dcf969ff2e chore(deps): update docker/login-action digest to c94ce9f 2026-01-28 13:35:35 +00:00
renovate[bot]
32c1318355 chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.2.1 2026-01-27 22:36:42 +00:00
Jérémie Panzer
8ca6b89da4 Merge pull request #2033 from Athou/renovate/patch-react-router-monorepo
fix(deps): update dependency react-router-dom to ^7.13.0
2026-01-27 07:21:39 +01:00
renovate[bot]
b46c3a15f3 fix(deps): update dependency react-router-dom to ^7.13.0 2026-01-27 04:49:15 +00:00
renovate[bot]
cbc5e014f7 chore(deps): update dependency vite-tsconfig-paths to ^6.0.5 (#2032)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-27 04:48:45 +00:00
renovate[bot]
8925b248e4 fix(deps): update linguijs monorepo to ^5.9.0 2026-01-26 22:16:14 +00:00
renovate[bot]
cc6aa2bbc5 chore(deps): update dependency @biomejs/biome to v2.3.12 2026-01-26 17:45:44 +00:00
Athou
1989aaf8b4 release 6.1.1 2026-01-26 15:47:39 +01:00
Athou
c90c91b748 fix old starred entries not loading if they were marked as read (#2031) 2026-01-26 15:45:50 +01:00
Athou
bca23db213 remvoe unused jacoco plugin 2026-01-26 13:39:50 +01:00
renovate[bot]
c9a92d2043 chore(deps): lock file maintenance 2026-01-26 05:00:06 +00:00
renovate[bot]
c48e06fa46 fix(deps): update dependency io.dropwizard.metrics:metrics-json to v4.2.38 2026-01-26 02:10:23 +00:00
renovate[bot]
5529eced91 chore(deps): update dependency vitest to ^4.0.18 2026-01-25 20:53:59 +00:00
Athou
2a0d935471 release 6.1.0 2026-01-25 10:58:32 +01:00
Athou
6c68fda572 make "disable pull to refresh" false by default (#2030) 2026-01-25 10:49:11 +01:00
Jérémie Panzer
861c1fc3dc Merge pull request #2029 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.8.0
2026-01-25 05:04:32 +01:00
renovate[bot]
5971bb4255 chore(deps): update dependency npm to v11.8.0 2026-01-25 01:59:43 +00:00
renovate[bot]
76ba360631 chore(deps): update dependency @types/react to ^19.2.9 2026-01-23 19:59:17 +00:00
renovate[bot]
89d3ff3c90 fix(deps): update quarkus.version to v3.30.8 2026-01-23 12:23:43 +00:00
renovate[bot]
34024a913d fix(deps): update mantine monorepo to ^8.3.13 2026-01-23 10:50:58 +00:00
Athou
a858380121 cleanup 2026-01-23 08:34:10 +01:00
Jérémie Panzer
e1dc870005 Merge pull request #2028 from Athou/renovate/com.diffplug.spotless-spotless-maven-plugin-3.x
chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.2.0
2026-01-23 02:04:25 +01:00
renovate[bot]
2fc1cac869 chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.2.0 2026-01-23 00:39:57 +00:00
renovate[bot]
f627ff4958 chore(deps): update dependency @testing-library/react to ^16.3.2 2026-01-22 13:07:35 +00:00
renovate[bot]
5ff8e51948 chore(deps): update dependency io.quarkiverse.playwright:quarkus-playwright to v2.3.2 2026-01-21 22:06:21 +00:00
renovate[bot]
538f25c6bb fix(deps): update quarkus.version to v3.30.7 2026-01-21 15:56:51 +00:00
Jérémie Panzer
65100ba279 Merge pull request #2026 from WangLei1993/master
add Chinese translation for new entry
2026-01-20 20:55:40 +01:00
WangLei1993
79fd470bbf add Chinese translation for new entry 2026-01-21 01:32:16 +08:00
Jérémie Panzer
866d74665b Merge pull request #2025 from canoine/patch-8
Update fr/messages.po
2026-01-20 14:13:04 +01:00
canoine
29da74f038 Update fr/messages.po
New etries translated
2026-01-20 13:56:59 +01:00
Athou
3c8ac35a46 make password match rule reusable 2026-01-20 08:54:48 +01:00
Athou
afe957ba59 create a dedicated password reset page (#2023) 2026-01-19 20:55:26 +01:00
renovate[bot]
7e50e99351 fix(deps): update linguijs monorepo to ^5.8.0 2026-01-19 10:48:25 +00:00
renovate[bot]
62ce462cc8 chore(deps): lock file maintenance 2026-01-19 03:48:36 +00:00
Jérémie Panzer
108cb06f43 Merge pull request #2024 from Athou/renovate/org.codehaus.mojo-properties-maven-plugin-1.x
chore(deps): update dependency org.codehaus.mojo:properties-maven-plugin to v1.3.0
2026-01-19 04:47:18 +01:00
renovate[bot]
95a38675bc chore(deps): update dependency org.codehaus.mojo:properties-maven-plugin to v1.3.0 2026-01-19 00:35:24 +00:00
Athou
714681bc50 don't set "en" as the default language, use the preferred language of the browser (#2018) 2026-01-17 20:09:23 +01:00
Athou
0f8d91d997 close profile menu on scroll (#2019) 2026-01-16 15:53:40 +01:00
Jérémie Panzer
562297a82f Merge pull request #2022 from WangLei1993/master
add Chinese translation for new entry
2026-01-16 15:40:53 +01:00
WangLei1993
b108bf06e5 add Chinese translation for new entry 2026-01-16 22:21:00 +08:00
renovate[bot]
3c819066fd chore(deps): update ibm-semeru-runtimes:open-jdk-25.0.1_8-jre docker digest to e12d5f2 2026-01-16 02:15:00 +00:00
Athou
5f30cb7e2e reuse validation rule 2026-01-16 01:08:25 +01:00
Athou
5a95b95801 validate password in the frontend too (#2017) 2026-01-15 21:36:00 +01:00
renovate[bot]
eb573fdc8b chore(deps): update dependency vitest to ^4.0.17 2026-01-15 14:58:31 +00:00
Jérémie Panzer
238ea54e98 Merge pull request #2020 from canoine/patch-7
Update fr/messages.po
2026-01-15 08:56:05 +01:00
canoine
e4dfc47fb8 Update fr/messages.po
Translation of the new entries.
2026-01-15 07:28:50 +01:00
Jérémie Panzer
a1491c779a Merge pull request #2015 from WangLei1993/master
add Chinese translation for new entry
2026-01-14 20:08:02 +01:00
WangLei1993
dabd7552be add Chinese translation for new entry 2026-01-15 01:06:19 +08:00
Jérémie Panzer
0a4c56af1f Merge pull request #2014 from Athou/renovate/node-24.x
chore(deps): update node.js to v24.13.0
2026-01-13 19:44:55 +01:00
renovate[bot]
c3dae5b92c chore(deps): update node.js to v24.13.0 2026-01-13 14:11:32 +00:00
renovate[bot]
2c3105b526 chore(deps): update debian:13.3 docker digest to 5cf544f 2026-01-13 14:11:28 +00:00
renovate[bot]
20f5081ac8 chore(deps): update dependency @types/react to ^19.2.8 2026-01-13 10:49:24 +00:00
Jérémie Panzer
3091eb9d14 Merge pull request #2013 from Athou/renovate/debian-13.x
chore(deps): update debian docker tag to v13.3
2026-01-13 06:52:42 +01:00
renovate[bot]
5bdda42239 chore(deps): update debian docker tag to v13.3 2026-01-13 04:51:00 +00:00
renovate[bot]
7eda8b7662 chore(deps): update dependency vite-tsconfig-paths to ^6.0.4 2026-01-13 02:08:08 +00:00
renovate[bot]
fc94ce5d2b fix(deps): update mantine monorepo to ^8.3.12 2026-01-12 14:23:32 +00:00
renovate[bot]
e5d7161ab7 chore(deps): lock file maintenance 2026-01-12 01:10:34 +00:00
Jérémie Panzer
f725cb7fa4 Merge pull request #2012 from Athou/renovate/react-router-monorepo
fix(deps): update dependency react-router-dom to ^7.12.0
2026-01-10 21:35:47 +01:00
renovate[bot]
830e689fe8 fix(deps): update dependency react-router-dom to ^7.12.0 2026-01-10 20:02:47 +00:00
65 changed files with 2149 additions and 1272 deletions

View File

@@ -23,7 +23,7 @@ jobs:
steps: steps:
# Checkout # Checkout
- name: Checkout - name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -48,19 +48,19 @@ jobs:
run: mkdir -p target/pages/documentation/custom-css run: mkdir -p target/pages/documentation/custom-css
- name: Convert readme file to html - name: Convert readme file to html
uses: jaywcjlove/markdown-to-html-cli@d2c8ffd676de1801e2586904bc540a938e4bc480 # v5.0.3 uses: jaywcjlove/markdown-to-html-cli@cff9330af4ca8048b197a76d9eb1db189c2a7cee # v5.0.4
with: with:
source: README.md source: README.md
output: target/pages/index.html output: target/pages/index.html
- name: Convert config documentation to html - name: Convert config documentation to html
uses: jaywcjlove/markdown-to-html-cli@d2c8ffd676de1801e2586904bc540a938e4bc480 # v5.0.3 uses: jaywcjlove/markdown-to-html-cli@cff9330af4ca8048b197a76d9eb1db189c2a7cee # v5.0.4
with: with:
source: commafeed-server/target/quarkus-generated-doc/config/commafeed-server.md source: commafeed-server/target/quarkus-generated-doc/config/commafeed-server.md
output: target/pages/documentation/index.html output: target/pages/documentation/index.html
- name: Convert custom css documentation to html - name: Convert custom css documentation to html
uses: jaywcjlove/markdown-to-html-cli@d2c8ffd676de1801e2586904bc540a938e4bc480 # v5.0.3 uses: jaywcjlove/markdown-to-html-cli@cff9330af4ca8048b197a76d9eb1db189c2a7cee # v5.0.4
with: with:
source: documentation/CUSTOMCSS.md source: documentation/CUSTOMCSS.md
output: target/pages/documentation/custom-css/index.html output: target/pages/documentation/custom-css/index.html
@@ -98,7 +98,7 @@ jobs:
steps: steps:
# Checkout # Checkout
- name: Checkout - name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -135,7 +135,7 @@ jobs:
# Docker # Docker
- name: Login to Container Registry - name: Login to Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
if: ${{ env.DOCKERHUB_USERNAME != '' }} if: ${{ env.DOCKERHUB_USERNAME != '' }}
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -215,7 +215,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -249,7 +249,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
fetch-depth: 0 fetch-depth: 0

View File

@@ -1,5 +1,20 @@
# Changelog # Changelog
## [6.2.0]
- Starred entries are no longer deleted after a certain amount of time, they are now kept indefinitely. The new `commafeed.database.cleanup.keep-starred-entries` setting can be disabled to restore the previous behavior if you want to keep deleting starred entries during normal entries cleanup (#1581)
## [6.1.1]
- Fix old starred entries not loading if they were marked as read (#2031)
## [6.1.0]
- When clicking on the password reset link, a random password is no longer generated automatically. The user is now redirected to a page where they can set their own password (#2023)
- Use browser preferred language instead of English when using CommaFeed for the first time (#2018)
- The profile menu is now closed when scrolling the page (#2019)
- The "disable pull to refresh" feature is now disabled by default (#2030)
## [6.0.0] ## [6.0.0]
- When booting CommaFeed for the first time, the default "admin" account is no longer created automatically. A setup wizard will guide you through the creation of an admin account - When booting CommaFeed for the first time, the default "admin" account is no longer created automatically. A setup wizard will guide you through the creation of an admin account

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json", "$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
"formatter": { "formatter": {
"indentStyle": "space", "indentStyle": "space",
"indentWidth": 4, "indentWidth": 4,

File diff suppressed because it is too large Load Diff

View File

@@ -17,31 +17,31 @@
"dependencies": { "dependencies": {
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@fontsource/open-sans": "^5.2.7", "@fontsource/open-sans": "^5.2.7",
"@lingui/core": "^5.7.0", "@lingui/core": "^5.9.0",
"@lingui/react": "^5.7.0", "@lingui/react": "^5.9.0",
"@mantine/core": "^8.3.11", "@mantine/core": "^8.3.14",
"@mantine/form": "^8.3.11", "@mantine/form": "^8.3.14",
"@mantine/hooks": "^8.3.11", "@mantine/hooks": "^8.3.14",
"@mantine/modals": "^8.3.11", "@mantine/modals": "^8.3.14",
"@mantine/notifications": "^8.3.11", "@mantine/notifications": "^8.3.14",
"@mantine/spotlight": "^8.3.11", "@mantine/spotlight": "^8.3.14",
"@monaco-editor/react": "^4.7.0", "@monaco-editor/react": "^4.7.0",
"@reduxjs/toolkit": "^2.11.2", "@reduxjs/toolkit": "^2.11.2",
"axios": "^1.13.2", "axios": "^1.13.4",
"dayjs": "^1.11.19", "dayjs": "^1.11.19",
"escape-string-regexp": "^5.0.0", "escape-string-regexp": "^5.0.0",
"interweave": "^13.1.1", "interweave": "^13.1.1",
"monaco-editor": "^0.55.1", "monaco-editor": "^0.55.1",
"mousetrap": "^1.6.5", "mousetrap": "^1.6.5",
"react": "^19.2.3", "react": "^19.2.4",
"react-async-hook": "^4.0.0", "react-async-hook": "^4.0.0",
"react-contexify": "^6.0.0", "react-contexify": "^6.0.0",
"react-dom": "^19.2.3", "react-dom": "^19.2.4",
"react-draggable": "^4.5.0", "react-draggable": "^4.5.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-infinite-scroller": "^1.2.6", "react-infinite-scroller": "^1.2.6",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-router-dom": "^7.11.0", "react-router-dom": "^7.13.0",
"react-swipeable": "^7.0.2", "react-swipeable": "^7.0.2",
"style-to-object": "^1.0.14", "style-to-object": "^1.0.14",
"throttle-debounce": "^5.0.2", "throttle-debounce": "^5.0.2",
@@ -50,32 +50,32 @@
"websocket-heartbeat-js": "^1.1.3" "websocket-heartbeat-js": "^1.1.3"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.3.11", "@biomejs/biome": "^2.3.14",
"@lingui/babel-plugin-lingui-macro": "^5.7.0", "@lingui/babel-plugin-lingui-macro": "^5.9.0",
"@lingui/cli": "^5.7.0", "@lingui/cli": "^5.9.0",
"@lingui/vite-plugin": "^5.7.0", "@lingui/vite-plugin": "^5.9.0",
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.1", "@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@types/mousetrap": "^1.6.15", "@types/mousetrap": "^1.6.15",
"@types/react": "^19.2.7", "@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@types/react-infinite-scroller": "^1.2.5", "@types/react-infinite-scroller": "^1.2.5",
"@types/throttle-debounce": "^5.0.2", "@types/throttle-debounce": "^5.0.2",
"@types/tinycon": "^0.6.7", "@types/tinycon": "^0.6.7",
"@vitejs/plugin-react": "^5.1.2", "@vitejs/plugin-react": "^5.1.3",
"babel-plugin-react-compiler": "1.0.0", "babel-plugin-react-compiler": "1.0.0",
"jsdom": "^27.4.0", "jsdom": "^28.0.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^7.3.1", "vite": "^7.3.1",
"vite-plugin-checker": "^0.12.0", "vite-plugin-checker": "^0.12.0",
"vite-tsconfig-paths": "^6.0.3", "vite-tsconfig-paths": "^6.0.5",
"vitest": "^4.0.16", "vitest": "^4.0.18",
"yaml": "^2.8.2" "yaml": "^2.8.2"
}, },
"overrides": { "overrides": {
"react-infinite-scroller": { "react-infinite-scroller": {
"react": "^19.2.3" "react": "^19.2.4"
} }
} }
} }

View File

@@ -6,16 +6,16 @@
<parent> <parent>
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId> <artifactId>commafeed</artifactId>
<version>6.0.0</version> <version>6.2.0</version>
</parent> </parent>
<artifactId>commafeed-client</artifactId> <artifactId>commafeed-client</artifactId>
<name>CommaFeed Client</name> <name>CommaFeed Client</name>
<properties> <properties>
<!-- renovate: datasource=node-version depName=node --> <!-- renovate: datasource=node-version depName=node -->
<node.version>v24.12.0</node.version> <node.version>v24.13.0</node.version>
<!-- renovate: datasource=npm depName=npm --> <!-- renovate: datasource=npm depName=npm -->
<npm.version>11.7.0</npm.version> <npm.version>11.9.0</npm.version>
</properties> </properties>
<build> <build>

View File

@@ -34,6 +34,7 @@ import { TagDetailsPage } from "@/pages/app/TagDetailsPage"
import { InitialSetupPage } from "@/pages/auth/InitialSetupPage" import { InitialSetupPage } from "@/pages/auth/InitialSetupPage"
import { LoginPage } from "@/pages/auth/LoginPage" import { LoginPage } from "@/pages/auth/LoginPage"
import { PasswordRecoveryPage } from "@/pages/auth/PasswordRecoveryPage" import { PasswordRecoveryPage } from "@/pages/auth/PasswordRecoveryPage"
import { PasswordResetPage } from "@/pages/auth/PasswordResetPage"
import { RegistrationPage } from "@/pages/auth/RegistrationPage" import { RegistrationPage } from "@/pages/auth/RegistrationPage"
import { WelcomePage } from "@/pages/WelcomePage" import { WelcomePage } from "@/pages/WelcomePage"
@@ -88,6 +89,7 @@ function AppRoutes() {
<Route path="login" element={<LoginPage />} /> <Route path="login" element={<LoginPage />} />
<Route path="register" element={<RegistrationPage />} /> <Route path="register" element={<RegistrationPage />} />
<Route path="passwordRecovery" element={<PasswordRecoveryPage />} /> <Route path="passwordRecovery" element={<PasswordRecoveryPage />} />
<Route path="passwordReset" element={<PasswordResetPage />} />
<Route path="app" element={<Layout header={<Header />} sidebar={<Tree />} sidebarVisible={sidebarVisible} />}> <Route path="app" element={<Layout header={<Header />} sidebar={<Tree />} sidebarVisible={sidebarVisible} />}>
<Route path="category"> <Route path="category">
<Route path=":id" element={<FeedEntriesPage sourceType="category" />} /> <Route path=":id" element={<FeedEntriesPage sourceType="category" />} />
@@ -193,6 +195,8 @@ function CustomJsHandler() {
document.body.appendChild(script) document.body.appendChild(script)
setScriptLoaded(true) setScriptLoaded(true)
return () => script.remove()
}, [scriptLoaded, loading]) }, [scriptLoaded, loading])
return null return null
@@ -205,6 +209,8 @@ function CustomCssHandler() {
link.type = "text/css" link.type = "text/css"
link.href = "custom_css.css" link.href = "custom_css.css"
document.head.appendChild(link) document.head.appendChild(link)
return () => link.remove()
}, []) }, [])
return null return null

View File

@@ -17,6 +17,7 @@ import type {
MarkRequest, MarkRequest,
Metrics, Metrics,
MultipleMarkRequest, MultipleMarkRequest,
PasswordResetConfirmationRequest,
PasswordResetRequest, PasswordResetRequest,
ProfileModificationRequest, ProfileModificationRequest,
RegistrationRequest, RegistrationRequest,
@@ -97,6 +98,7 @@ export const client = {
register: async (req: RegistrationRequest) => await axiosInstance.post("user/register", req), register: async (req: RegistrationRequest) => await axiosInstance.post("user/register", req),
initialSetup: async (req: InitialSetupRequest) => await axiosInstance.post("user/initialSetup", req), initialSetup: async (req: InitialSetupRequest) => await axiosInstance.post("user/initialSetup", req),
passwordReset: async (req: PasswordResetRequest) => await axiosInstance.post("user/passwordReset", req), passwordReset: async (req: PasswordResetRequest) => await axiosInstance.post("user/passwordReset", req),
passwordResetCallback: async (req: PasswordResetConfirmationRequest) => await axiosInstance.post("user/passwordResetCallback", req),
getSettings: async () => await axiosInstance.get<Settings>("user/settings"), getSettings: async () => await axiosInstance.get<Settings>("user/settings"),
saveSettings: async (settings: Settings) => await axiosInstance.post("user/settings", settings), saveSettings: async (settings: Settings) => await axiosInstance.post("user/settings", settings),
getProfile: async () => await axiosInstance.get<UserModel>("user/profile"), getProfile: async () => await axiosInstance.get<UserModel>("user/profile"),

View File

@@ -40,7 +40,9 @@ export const loadMoreEntries = createAppAsyncThunk("entries/loadMore", async (_,
const state = thunkApi.getState() const state = thunkApi.getState()
const { source } = state.entries const { source } = state.entries
const offset = const offset =
state.user.settings?.readingMode === "all" ? state.entries.entries.length : state.entries.entries.filter(e => !e.read).length state.user.settings?.readingMode === "all" || (source.type === "category" && source.id === "starred")
? state.entries.entries.length
: state.entries.entries.filter(e => !e.read).length
const endpoint = getEndpoint(state.entries.source.type) const endpoint = getEndpoint(state.entries.source.type)
const result = await endpoint(buildGetEntriesPaginatedRequest(state, source, offset)) const result = await endpoint(buildGetEntriesPaginatedRequest(state, source, offset))
return result.data return result.data

View File

@@ -196,6 +196,12 @@ export interface PasswordResetRequest {
email: string email: string
} }
export interface PasswordResetConfirmationRequest {
email: string
token: string
password: string
}
export interface ProfileModificationRequest { export interface ProfileModificationRequest {
currentPassword: string currentPassword: string
email: string email: string
@@ -228,6 +234,7 @@ export interface ServerInfo {
treeReloadInterval: number treeReloadInterval: number
forceRefreshCooldownDuration: number forceRefreshCooldownDuration: number
initialSetupRequired: boolean initialSetupRequired: boolean
minimumPasswordLength: number
} }
export interface SharingSettings { export interface SharingSettings {
@@ -242,7 +249,7 @@ export interface SharingSettings {
} }
export interface Settings { export interface Settings {
language: string language?: string
readingMode: ReadingMode readingMode: ReadingMode
readingOrder: ReadingOrder readingOrder: ReadingOrder
showRead: boolean showRead: boolean

View File

@@ -12,7 +12,7 @@ import {
} from "@mantine/core" } from "@mantine/core"
import { showNotification } from "@mantine/notifications" import { showNotification } from "@mantine/notifications"
import dayjs from "dayjs" import dayjs from "dayjs"
import { type ReactNode, useState } from "react" import { type ReactNode, useEffect, useState } from "react"
import { import {
TbChartLine, TbChartLine,
TbHeartFilled, TbHeartFilled,
@@ -29,6 +29,7 @@ import {
TbUsers, TbUsers,
TbWorldDownload, TbWorldDownload,
} from "react-icons/tb" } from "react-icons/tb"
import { throttle } from "throttle-debounce"
import { client } from "@/app/client" import { client } from "@/app/client"
import { redirectToAbout, redirectToAdminUsers, redirectToDonate, redirectToMetrics, redirectToSettings } from "@/app/redirect/thunks" import { redirectToAbout, redirectToAdminUsers, redirectToDonate, redirectToMetrics, redirectToSettings } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store" import { useAppDispatch, useAppSelector } from "@/app/store"
@@ -96,6 +97,14 @@ const viewModeData: ViewModeControlItem[] = [
export function ProfileMenu(props: Readonly<ProfileMenuProps>) { export function ProfileMenu(props: Readonly<ProfileMenuProps>) {
const [opened, setOpened] = useState(false) const [opened, setOpened] = useState(false)
// close profile menu on scroll
useEffect(() => {
const listener = throttle(100, () => setOpened(false))
window.addEventListener("scroll", listener)
return () => window.removeEventListener("scroll", listener)
}, [])
const now = useNow() const now = useNow()
const profile = useAppSelector(state => state.user.profile) const profile = useAppSelector(state => state.user.profile)
const admin = useAppSelector(state => state.user.profile?.admin) const admin = useAppSelector(state => state.user.profile?.admin)

View File

@@ -143,6 +143,47 @@ export function DisplaySettings() {
onChange={async e => await dispatch(changeMobileFooter(e.currentTarget.checked))} onChange={async e => await dispatch(changeMobileFooter(e.currentTarget.checked))}
/> />
<Divider label={<Trans>Scrolling</Trans>} labelPosition="center" />
<Switch
label={<Trans>Disable "Pull to refresh" browser behavior</Trans>}
description={<Trans>This setting can cause scrolling issues on some browsers (e.g. Safari)</Trans>}
checked={disablePullToRefresh}
onChange={async e => await dispatch(changeDisablePullToRefresh(e.currentTarget.checked))}
/>
<Radio.Group
label={<Trans>Scroll selected entry to the top of the page</Trans>}
value={scrollMode}
onChange={async value => await dispatch(changeScrollMode(value as ScrollMode))}
>
<Group mt="xs">
{Object.entries(scrollModeOptions).map(e => (
<Radio key={e[0]} value={e[0]} label={e[1]} />
))}
</Group>
</Radio.Group>
<NumberInput
label={<Trans>Entries to keep above the selected entry when scrolling</Trans>}
description={<Trans>Only applies to compact, cozy and detailed modes</Trans>}
min={0}
value={entriesToKeepOnTop}
onChange={async value => await dispatch(changeEntriesToKeepOnTopWhenScrolling(+value))}
/>
<Switch
label={<Trans>Scroll smoothly when navigating between entries</Trans>}
checked={scrollSpeed ? scrollSpeed > 0 : false}
onChange={async e => await dispatch(changeScrollSpeed(e.currentTarget.checked))}
/>
<Switch
label={<Trans>In expanded view, scrolling through entries mark them as read</Trans>}
checked={scrollMarks}
onChange={async e => await dispatch(changeScrollMarks(e.currentTarget.checked))}
/>
<Divider label={<Trans>Browser tab</Trans>} labelPosition="center" /> <Divider label={<Trans>Browser tab</Trans>} labelPosition="center" />
<Switch <Switch
@@ -179,47 +220,6 @@ export function DisplaySettings() {
onChange={async e => await dispatch(changeCustomContextMenu(e.currentTarget.checked))} onChange={async e => await dispatch(changeCustomContextMenu(e.currentTarget.checked))}
/> />
<Divider label={<Trans>Scrolling</Trans>} labelPosition="center" />
<Radio.Group
label={<Trans>Scroll selected entry to the top of the page</Trans>}
value={scrollMode}
onChange={async value => await dispatch(changeScrollMode(value as ScrollMode))}
>
<Group mt="xs">
{Object.entries(scrollModeOptions).map(e => (
<Radio key={e[0]} value={e[0]} label={e[1]} />
))}
</Group>
</Radio.Group>
<NumberInput
label={<Trans>Entries to keep above the selected entry when scrolling</Trans>}
description={<Trans>Only applies to compact, cozy and detailed modes</Trans>}
min={0}
value={entriesToKeepOnTop}
onChange={async value => await dispatch(changeEntriesToKeepOnTopWhenScrolling(+value))}
/>
<Switch
label={<Trans>Scroll smoothly when navigating between entries</Trans>}
checked={scrollSpeed ? scrollSpeed > 0 : false}
onChange={async e => await dispatch(changeScrollSpeed(e.currentTarget.checked))}
/>
<Switch
label={<Trans>In expanded view, scrolling through entries mark them as read</Trans>}
checked={scrollMarks}
onChange={async e => await dispatch(changeScrollMarks(e.currentTarget.checked))}
/>
<Switch
label={<Trans>Disable "Pull to refresh" browser behavior</Trans>}
description={<Trans>This setting can cause scrolling issues on some browsers (e.g. Safari)</Trans>}
checked={disablePullToRefresh}
onChange={async e => await dispatch(changeDisablePullToRefresh(e.currentTarget.checked))}
/>
<Divider label={<Trans>Sharing sites</Trans>} labelPosition="center" /> <Divider label={<Trans>Sharing sites</Trans>} labelPosition="center" />
<SimpleGrid cols={2}> <SimpleGrid cols={2}>

View File

@@ -1,4 +1,3 @@
import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react" import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Button, Checkbox, Divider, Group, Input, PasswordInput, Stack, Text, TextInput } from "@mantine/core" import { Anchor, Box, Button, Checkbox, Divider, Group, Input, PasswordInput, Stack, Text, TextInput } from "@mantine/core"
@@ -13,6 +12,7 @@ import { useAppDispatch, useAppSelector } from "@/app/store"
import type { ProfileModificationRequest } from "@/app/types" import type { ProfileModificationRequest } from "@/app/types"
import { reloadProfile } from "@/app/user/thunks" import { reloadProfile } from "@/app/user/thunks"
import { Alert } from "@/components/Alert" import { Alert } from "@/components/Alert"
import { useValidationRules } from "@/hooks/useValidationRules"
interface FormData extends ProfileModificationRequest { interface FormData extends ProfileModificationRequest {
newPasswordConfirmation?: string newPasswordConfirmation?: string
@@ -23,11 +23,14 @@ export function ProfileSettings() {
const serverInfos = useAppSelector(state => state.server.serverInfos) const serverInfos = useAppSelector(state => state.server.serverInfos)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { _ } = useLingui() const { _ } = useLingui()
const validationRules = useValidationRules()
const form = useForm<FormData>({ const form = useForm<FormData>({
validate: { validate: {
newPasswordConfirmation: (value, values) => (value !== values.newPassword ? _(msg`Passwords do not match`) : null), newPassword: validationRules.password,
newPasswordConfirmation: (value, values) => validationRules.passwordConfirmation(value, values.newPassword),
}, },
validateInputOnChange: true,
}) })
const { setValues } = form const { setValues } = form

View File

@@ -0,0 +1,17 @@
import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react"
import { useAppSelector } from "@/app/store"
export function useValidationRules() {
const minimumPasswordLength = useAppSelector(state => state.server.serverInfos?.minimumPasswordLength)
const { _ } = useLingui()
return {
password: (value: string | undefined) =>
value && minimumPasswordLength && value.length < minimumPasswordLength
? _(msg`Password must be at least ${minimumPasswordLength} characters`)
: null,
passwordConfirmation: (newPasswordConfirmation: string | undefined, newPassword: string | undefined) =>
newPasswordConfirmation && newPasswordConfirmation !== newPassword ? _(msg`Passwords do not match`) : null,
}
}

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "العودة" msgstr "العودة"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "العودة لتسجيل الدخول" msgstr "العودة لتسجيل الدخول"
@@ -226,6 +227,11 @@ msgstr "تأكيد"
msgid "Confirm password" msgid "Confirm password"
msgstr "تأكيد كلمة المرور" msgstr "تأكيد كلمة المرور"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "دافئ" msgstr "دافئ"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "كلمة مرور جديدة" msgstr "كلمة مرور جديدة"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "الأحدث أولاً" msgstr "الأحدث أولاً"
@@ -771,11 +786,15 @@ msgstr "الفئة الأصل"
msgid "Password" msgid "Password"
msgstr "كلمة المرور" msgstr "كلمة المرور"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "استعادة كلمة المرور" msgstr "استعادة كلمة المرور"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "كلمات المرور غير متطابقة" msgstr "كلمات المرور غير متطابقة"
@@ -817,6 +836,11 @@ msgstr "تحديث"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "تم إغلاق التسجيلات في مثيل CommaFeed هذا" msgstr "تم إغلاق التسجيلات في مثيل CommaFeed هذا"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr "إلغاء النجم"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "إلغاء الاشتراك" msgstr "إلغاء الاشتراك"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "اسم المستخدم" msgstr "اسم المستخدم"
@@ -1096,3 +1116,7 @@ msgstr "ليس لديك أي اشتراكات حتى الآن. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Enrere" msgstr "Enrere"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Torna a iniciar sessió" msgstr "Torna a iniciar sessió"
@@ -226,6 +227,11 @@ msgstr "Confirma"
msgid "Confirm password" msgid "Confirm password"
msgstr "Confirmeu la contrasenya" msgstr "Confirmeu la contrasenya"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Acollidor" msgstr "Acollidor"
@@ -491,6 +497,10 @@ msgstr "Indi"
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr "Mai"
msgid "New password" msgid "New password"
msgstr "Contrasenya nova" msgstr "Contrasenya nova"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "El més nou primer" msgstr "El més nou primer"
@@ -771,11 +786,15 @@ msgstr "Categoria pare"
msgid "Password" msgid "Password"
msgstr "Contrasenya" msgstr "Contrasenya"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Recuperació de contrasenya" msgstr "Recuperació de contrasenya"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Les contrasenyes no coincideixen" msgstr "Les contrasenyes no coincideixen"
@@ -817,6 +836,11 @@ msgstr "Actualitzar"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Els registres estan tancats en aquesta instància de CommaFeed" msgstr "Els registres estan tancats en aquesta instància de CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
@@ -1056,10 +1080,6 @@ msgstr "Desestrellar"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Donar-se de baixa" msgstr "Donar-se de baixa"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Nom d'usuari" msgstr "Nom d'usuari"
@@ -1096,3 +1116,7 @@ msgstr "Encara no teniu cap subscripció. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "Els vostres feeds s'han posat a la cua per actualitzar-los." msgstr "Els vostres feeds s'han posat a la cua per actualitzar-los."
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Zpět" msgstr "Zpět"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Zpět k přihlášení" msgstr "Zpět k přihlášení"
@@ -226,6 +227,11 @@ msgstr "Potvrdit"
msgid "Confirm password" msgid "Confirm password"
msgstr "Potvrďte heslo" msgstr "Potvrďte heslo"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Útulný" msgstr "Útulný"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Nové heslo" msgstr "Nové heslo"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Nejnovější jako první" msgstr "Nejnovější jako první"
@@ -771,11 +786,15 @@ msgstr "Rodičovská kategorie"
msgid "Password" msgid "Password"
msgstr "Heslo" msgstr "Heslo"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Obnovení hesla" msgstr "Obnovení hesla"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Hesla se neshodují" msgstr "Hesla se neshodují"
@@ -817,6 +836,11 @@ msgstr "Obnovit"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "V této instanci CommaFeed jsou registrace uzavřeny" msgstr "V této instanci CommaFeed jsou registrace uzavřeny"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr "Odstranit hvězdu"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Odhlásit odběr" msgstr "Odhlásit odběr"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Uživatelské jméno" msgstr "Uživatelské jméno"
@@ -1096,3 +1116,7 @@ msgstr "Zatím nemáte žádné předplatné. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Yn ôl" msgstr "Yn ôl"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Yn ôl i fewngofnodi" msgstr "Yn ôl i fewngofnodi"
@@ -226,6 +227,11 @@ msgstr "Cadarnhau"
msgid "Confirm password" msgid "Confirm password"
msgstr "Cadarnhau'r cyfrinair" msgstr "Cadarnhau'r cyfrinair"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "clyd" msgstr "clyd"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Cyfrinair newydd" msgstr "Cyfrinair newydd"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Y diweddaraf yn gyntaf" msgstr "Y diweddaraf yn gyntaf"
@@ -771,11 +786,15 @@ msgstr "Categori Rhiant"
msgid "Password" msgid "Password"
msgstr "cyfrinair" msgstr "cyfrinair"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Adfer Cyfrinair" msgstr "Adfer Cyfrinair"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Nid yw cyfrineiriau yn cyfateb" msgstr "Nid yw cyfrineiriau yn cyfateb"
@@ -817,6 +836,11 @@ msgstr "Adnewyddu"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Mae cofrestriadau ar gau ar yr achos CommaFeed hwn" msgstr "Mae cofrestriadau ar gau ar yr achos CommaFeed hwn"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr "dad-seren"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Dad-danysgrifio" msgstr "Dad-danysgrifio"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Enw defnyddiwr" msgstr "Enw defnyddiwr"
@@ -1096,3 +1116,7 @@ msgstr "Nid oes gennych unrhyw danysgrifiadau eto. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Tilbage" msgstr "Tilbage"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Tilbage for at logge ind" msgstr "Tilbage for at logge ind"
@@ -226,6 +227,11 @@ msgstr "Bekræft"
msgid "Confirm password" msgid "Confirm password"
msgstr "Bekræft adgangskode" msgstr "Bekræft adgangskode"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Hyggeligt" msgstr "Hyggeligt"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Ny adgangskode" msgstr "Ny adgangskode"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Nyeste først" msgstr "Nyeste først"
@@ -771,11 +786,15 @@ msgstr "Forældrekategori"
msgid "Password" msgid "Password"
msgstr "Adgangskode" msgstr "Adgangskode"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Gendannelse af adgangskode" msgstr "Gendannelse af adgangskode"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Adgangskoder stemmer ikke overens" msgstr "Adgangskoder stemmer ikke overens"
@@ -817,6 +836,11 @@ msgstr "Opdater"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringer er lukket på denne CommaFeed-instans" msgstr "Registreringer er lukket på denne CommaFeed-instans"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr ""
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Afmeld" msgstr "Afmeld"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Brugernavn" msgstr "Brugernavn"
@@ -1096,3 +1116,7 @@ msgstr "Du har ingen abonnementer endnu. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Zurück" msgstr "Zurück"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Zurück zum Login" msgstr "Zurück zum Login"
@@ -226,6 +227,11 @@ msgstr "Bestätigen"
msgid "Confirm password" msgid "Confirm password"
msgstr "Passwort bestätigen" msgstr "Passwort bestätigen"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Gemütlich" msgstr "Gemütlich"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr "Niemals"
msgid "New password" msgid "New password"
msgstr "Neues Passwort" msgstr "Neues Passwort"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Neueste zuerst" msgstr "Neueste zuerst"
@@ -771,11 +786,15 @@ msgstr "Übergeordnete Kategorie"
msgid "Password" msgid "Password"
msgstr "Passwort" msgstr "Passwort"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Passwortwiederherstellung" msgstr "Passwortwiederherstellung"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Passwörter stimmen nicht überein" msgstr "Passwörter stimmen nicht überein"
@@ -817,6 +836,11 @@ msgstr "Aktualisieren"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registrierungen sind für diese CommaFeed-Instanz geschlossen" msgstr "Registrierungen sind für diese CommaFeed-Instanz geschlossen"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "REST-API" msgstr "REST-API"
@@ -1056,10 +1080,6 @@ msgstr "Stern entfernen"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Abbestellen" msgstr "Abbestellen"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Benutzername" msgstr "Benutzername"
@@ -1096,3 +1116,7 @@ msgstr "Sie haben noch keine Abonnements."
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "Ihr Feed wurde für die Aktualisierung eingereiht." msgstr "Ihr Feed wurde für die Aktualisierung eingereiht."
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Back" msgstr "Back"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Back to log in" msgstr "Back to log in"
@@ -226,6 +227,11 @@ msgstr "Confirm"
msgid "Confirm password" msgid "Confirm password"
msgstr "Confirm password" msgstr "Confirm password"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Confirm Password"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Cozy" msgstr "Cozy"
@@ -491,6 +497,10 @@ msgstr "Indigo"
msgid "Initial Setup" msgid "Initial Setup"
msgstr "Initial Setup" msgstr "Initial Setup"
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr "Invalid password reset link. Please request a new one."
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr "Never"
msgid "New password" msgid "New password"
msgstr "New password" msgstr "New password"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "New Password"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Newest first" msgstr "Newest first"
@@ -771,11 +786,15 @@ msgstr "Parent Category"
msgid "Password" msgid "Password"
msgstr "Password" msgstr "Password"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr "Password must be at least {minimumPasswordLength} characters"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Password Recovery" msgstr "Password Recovery"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Passwords do not match" msgstr "Passwords do not match"
@@ -817,6 +836,11 @@ msgstr "Refresh"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registrations are closed on this CommaFeed instance" msgstr "Registrations are closed on this CommaFeed instance"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Reset Password"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "REST API" msgstr "REST API"
@@ -1056,10 +1080,6 @@ msgstr "Unstar"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Unsubscribe" msgstr "Unsubscribe"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr "User created."
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "User name" msgstr "User name"
@@ -1096,3 +1116,7 @@ msgstr "You don't have any subscriptions yet. Why not try adding one by clicking
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "Your feeds have been queued for refresh." msgstr "Your feeds have been queued for refresh."
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr "Your password has been changed. You can now log in with your new password."

View File

@@ -136,6 +136,7 @@ msgid "Back"
msgstr "Atrás" msgstr "Atrás"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Volver a iniciar sesión" msgstr "Volver a iniciar sesión"
@@ -227,6 +228,11 @@ msgstr "Confirmar"
msgid "Confirm password" msgid "Confirm password"
msgstr "Confirmar contraseña" msgstr "Confirmar contraseña"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Acogedor" msgstr "Acogedor"
@@ -492,6 +498,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -635,6 +645,11 @@ msgstr "Nunca"
msgid "New password" msgid "New password"
msgstr "Nueva contraseña" msgstr "Nueva contraseña"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Las más recientes primero" msgstr "Las más recientes primero"
@@ -772,11 +787,15 @@ msgstr "Categoría principal"
msgid "Password" msgid "Password"
msgstr "Contraseña" msgstr "Contraseña"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Recuperación de contraseña" msgstr "Recuperación de contraseña"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Las contraseñas no coinciden" msgstr "Las contraseñas no coinciden"
@@ -818,6 +837,11 @@ msgstr "Actualizar"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Los registros están cerrados en esta instancia de CommaFeed" msgstr "Los registros están cerrados en esta instancia de CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
@@ -1057,10 +1081,6 @@ msgstr "Desmarcar"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Cancelar suscripción" msgstr "Cancelar suscripción"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Nombre de usuario" msgstr "Nombre de usuario"
@@ -1097,3 +1117,7 @@ msgstr "Aún no tienes ninguna suscripción. ¿Por qué no intentas agregar una
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "Tus feeds se han puesto en cola para actualizarse." msgstr "Tus feeds se han puesto en cola para actualizarse."
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "برگشت" msgstr "برگشت"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "بازگشت برای ورود به سیستم" msgstr "بازگشت برای ورود به سیستم"
@@ -226,6 +227,11 @@ msgstr "تأیید کنید"
msgid "Confirm password" msgid "Confirm password"
msgstr "رمز عبور را تأیید کنید" msgstr "رمز عبور را تأیید کنید"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "دنج" msgstr "دنج"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "رمز عبور جدید" msgstr "رمز عبور جدید"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "ابتدا جدیدترین" msgstr "ابتدا جدیدترین"
@@ -771,11 +786,15 @@ msgstr "دسته والد"
msgid "Password" msgid "Password"
msgstr "رمز عبور" msgstr "رمز عبور"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "بازیابی رمز عبور" msgstr "بازیابی رمز عبور"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "گذرواژه ها مطابقت ندارند" msgstr "گذرواژه ها مطابقت ندارند"
@@ -817,6 +836,11 @@ msgstr "تازه کردن"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "ثبت نام در این نمونه CommaFeed بسته شده است" msgstr "ثبت نام در این نمونه CommaFeed بسته شده است"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr ""
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "لغو اشتراک" msgstr "لغو اشتراک"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "نام کاربری" msgstr "نام کاربری"
@@ -1096,3 +1116,7 @@ msgstr "شما هنوز هیچ اشتراکی ندارید. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Takaisin" msgstr "Takaisin"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Takaisin sisäänkirjautumiseen" msgstr "Takaisin sisäänkirjautumiseen"
@@ -226,6 +227,11 @@ msgstr "Vahvista"
msgid "Confirm password" msgid "Confirm password"
msgstr "Vahvista salasana" msgstr "Vahvista salasana"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Viihtyisä" msgstr "Viihtyisä"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Uusi salasana" msgstr "Uusi salasana"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Uusin ensin" msgstr "Uusin ensin"
@@ -771,11 +786,15 @@ msgstr "Pääluokka"
msgid "Password" msgid "Password"
msgstr "Salasana" msgstr "Salasana"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Salasanan palautus" msgstr "Salasanan palautus"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Salasanat eivät täsmää" msgstr "Salasanat eivät täsmää"
@@ -817,6 +836,11 @@ msgstr "Päivitä"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Tämän CommaFeed-esiintymän rekisteröinnit on suljettu" msgstr "Tämän CommaFeed-esiintymän rekisteröinnit on suljettu"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr "Poista tähti"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Peruuta tilaus" msgstr "Peruuta tilaus"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Käyttäjänimi" msgstr "Käyttäjänimi"
@@ -1096,3 +1116,7 @@ msgstr "Sinulla ei ole vielä tilauksia. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -63,7 +63,7 @@ msgstr "Administrateur"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name" msgid "Admin user name"
msgstr "" msgstr "Nom de l'administrateur"
#: src/components/content/add/CategorySelect.tsx #: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx #: src/components/header/Header.tsx
@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Retour" msgstr "Retour"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Retour à la connexion" msgstr "Retour à la connexion"
@@ -226,13 +227,18 @@ msgstr "Confirmer"
msgid "Confirm password" msgid "Confirm password"
msgstr "Confirmer le mot de passe" msgstr "Confirmer le mot de passe"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Confirmer le mot de passe"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Cozy" msgstr "Cozy"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account" msgid "Create Admin Account"
msgstr "" msgstr "Créer un compte adminstrateur"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl" msgid "Ctrl"
@@ -489,7 +495,11 @@ msgstr "Indigo"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr "Configuration initiale"
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr "Lien de réinitialisation de mot de passse invalide. Recommencez la procédure."
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
@@ -634,6 +644,11 @@ msgstr "Jamais"
msgid "New password" msgid "New password"
msgstr "Nouveau mot de passe" msgstr "Nouveau mot de passe"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nouveau mot de passe"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Plus récent en premier" msgstr "Plus récent en premier"
@@ -771,11 +786,15 @@ msgstr "Catégorie parente"
msgid "Password" msgid "Password"
msgstr "Mot de passe" msgstr "Mot de passe"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr "Le mot de passe doit mesurer au moins {minimumPasswordLength} caractères"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Récupération de mot de passe" msgstr "Récupération de mot de passe"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
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"
@@ -817,6 +836,11 @@ msgstr "Rafraîchir"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Les inscriptions sont fermées sur cette instance de CommaFeed" msgstr "Les inscriptions sont fermées sur cette instance de CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Réinitialiser le mot de passe"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
@@ -1056,10 +1080,6 @@ msgstr "Retirer des favoris"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Se désabonner" msgstr "Se désabonner"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Nom" msgstr "Nom"
@@ -1083,7 +1103,7 @@ msgstr "Site web"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started." msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "" msgstr "Bienvenue ! Il semble que ce soit le premier démarrage de Commafeed. Avant tout, vous devez créer un compte administrateur."
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Yellow" msgid "Yellow"
@@ -1096,3 +1116,7 @@ msgstr "Vous n'avez pas encore d'abonnements. Pourquoi ne pas essayer d'en ajout
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "Vos flux sont en cours de rafraîchissement" msgstr "Vos flux sont en cours de rafraîchissement"
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr "Votre mot de passe a été modifié. Vous pouvez vous connecter avec votre nouveau mot de passe."

View File

@@ -9,7 +9,7 @@ msgstr ""
"Language: gl\n" "Language: gl\n"
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-12-27 15:30+0200\n" "PO-Revision-Date: 2026-02-04 05:30+0200\n"
"Last-Translator: José M. <correoxm@disroot.org>\n" "Last-Translator: José M. <correoxm@disroot.org>\n"
"Language-Team: gl\n" "Language-Team: gl\n"
"Plural-Forms: \n" "Plural-Forms: \n"
@@ -64,7 +64,7 @@ msgstr "Administración"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name" msgid "Admin user name"
msgstr "" msgstr "Identificador de admin"
#: src/components/content/add/CategorySelect.tsx #: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx #: src/components/header/Header.tsx
@@ -136,6 +136,7 @@ msgid "Back"
msgstr "Volver" msgstr "Volver"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Volver para iniciar sesión" msgstr "Volver para iniciar sesión"
@@ -227,13 +228,18 @@ msgstr "Confirmar"
msgid "Confirm password" msgid "Confirm password"
msgstr "Confirmar contrasinal" msgstr "Confirmar contrasinal"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Confirmar contrasinal"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Acolledor" msgstr "Acolledor"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account" msgid "Create Admin Account"
msgstr "" msgstr "Crear conta Admin"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl" msgid "Ctrl"
@@ -490,7 +496,11 @@ msgstr "Índigo"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr "Configuración inicial"
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr "A ligazón de restablecemento non é válida. Solicita unha nova."
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
@@ -635,6 +645,11 @@ msgstr "Nunca"
msgid "New password" msgid "New password"
msgstr "Novo contrasinal" msgstr "Novo contrasinal"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Novo contrasinal"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Primeiro o máis recente" msgstr "Primeiro o máis recente"
@@ -772,11 +787,15 @@ msgstr "Categoría superior"
msgid "Password" msgid "Password"
msgstr "Contrasinal" msgstr "Contrasinal"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr "O contrasinal ten que ter {minimumPasswordLength} caracteres polo menos"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Recuperación do contrasinal" msgstr "Recuperación do contrasinal"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Os contrasinais non coinciden" msgstr "Os contrasinais non coinciden"
@@ -818,6 +837,11 @@ msgstr "Actualizar"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Non se admiten novas contas nesta instancia de CommaFeed" msgstr "Non se admiten novas contas nesta instancia de CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Restablecer contrasinal"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
@@ -1057,10 +1081,6 @@ msgstr "Retirar estrela"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Cancelar a subscrición" msgstr "Cancelar a subscrición"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Identificador" msgstr "Identificador"
@@ -1084,7 +1104,7 @@ msgstr "Páxina web"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started." msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "" msgstr "Benvida! Semella que é a primeira vez que executas CommaFeed. Para comezar, crea unha conta de administración."
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Yellow" msgid "Yellow"
@@ -1097,3 +1117,7 @@ msgstr "Aínda non tes ningunha subscrición. Por que non engades unha premendo
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "As túas canles están na cola para ser actualizadas." msgstr "As túas canles están na cola para ser actualizadas."
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr "O teu contrasinal cambiou. Xa podes acceder coas novas credenciais."

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Vissza" msgstr "Vissza"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Vissza a bejelentkezéshez" msgstr "Vissza a bejelentkezéshez"
@@ -226,6 +227,11 @@ msgstr "Erősítse meg"
msgid "Confirm password" msgid "Confirm password"
msgstr "Erősítse meg a jelszót" msgstr "Erősítse meg a jelszót"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Hangulatos" msgstr "Hangulatos"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Új jelszó" msgstr "Új jelszó"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "A legújabbak először" msgstr "A legújabbak először"
@@ -771,11 +786,15 @@ msgstr "Szülő kategória"
msgid "Password" msgid "Password"
msgstr "Jelszó" msgstr "Jelszó"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Jelszó helyreállítás" msgstr "Jelszó helyreállítás"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "A jelszavak nem egyeznek" msgstr "A jelszavak nem egyeznek"
@@ -817,6 +836,11 @@ msgstr "Frissítés"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "A regisztrációk le vannak zárva ezen a CommaFeed példányon" msgstr "A regisztrációk le vannak zárva ezen a CommaFeed példányon"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr ""
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Leiratkozás" msgstr "Leiratkozás"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Felhasználónév" msgstr "Felhasználónév"
@@ -1096,3 +1116,7 @@ msgstr "Még nincs előfizetése. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Kembali" msgstr "Kembali"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Kembali untuk masuk" msgstr "Kembali untuk masuk"
@@ -226,6 +227,11 @@ msgstr "Konfirmasi"
msgid "Confirm password" msgid "Confirm password"
msgstr "Konfirmasi kata sandi" msgstr "Konfirmasi kata sandi"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Nyaman" msgstr "Nyaman"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Kata sandi baru" msgstr "Kata sandi baru"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Terbaru dulu" msgstr "Terbaru dulu"
@@ -771,11 +786,15 @@ msgstr "Kategori Induk"
msgid "Password" msgid "Password"
msgstr "Kata Sandi" msgstr "Kata Sandi"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Pemulihan Kata Sandi" msgstr "Pemulihan Kata Sandi"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Kata sandi tidak cocok" msgstr "Kata sandi tidak cocok"
@@ -817,6 +836,11 @@ msgstr "Segarkan"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Pendaftaran ditutup pada instans CommaFeed ini" msgstr "Pendaftaran ditutup pada instans CommaFeed ini"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr "Hapus bintang"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Berhenti berlangganan" msgstr "Berhenti berlangganan"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Nama pengguna" msgstr "Nama pengguna"
@@ -1096,3 +1116,7 @@ msgstr "Anda belum memiliki langganan. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Indietro" msgstr "Indietro"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Torna per accedere" msgstr "Torna per accedere"
@@ -226,6 +227,11 @@ msgstr "Conferma"
msgid "Confirm password" msgid "Confirm password"
msgstr "Conferma password" msgstr "Conferma password"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Accogliente" msgstr "Accogliente"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Nuova password" msgstr "Nuova password"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Il più recente prima" msgstr "Il più recente prima"
@@ -771,11 +786,15 @@ msgstr "Categoria padre"
msgid "Password" msgid "Password"
msgstr "" msgstr ""
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Recupero password" msgstr "Recupero password"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Le password non corrispondono" msgstr "Le password non corrispondono"
@@ -817,6 +836,11 @@ msgstr "Aggiorna"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Le registrazioni sono chiuse su questa istanza CommaFeed" msgstr "Le registrazioni sono chiuse su questa istanza CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
@@ -1056,10 +1080,6 @@ msgstr "Elimina le stelle"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Annulla iscrizione" msgstr "Annulla iscrizione"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Nome utente" msgstr "Nome utente"
@@ -1096,3 +1116,7 @@ msgstr "Non hai ancora abbonamenti. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "戻る" msgstr "戻る"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "ログインに戻る" msgstr "ログインに戻る"
@@ -226,6 +227,11 @@ msgstr "確認"
msgid "Confirm password" msgid "Confirm password"
msgstr "パスワード確認" msgstr "パスワード確認"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Cozy" msgstr "Cozy"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr "しない"
msgid "New password" msgid "New password"
msgstr "新しいパスワード" msgstr "新しいパスワード"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "最新順" msgstr "最新順"
@@ -771,11 +786,15 @@ msgstr "親カテゴリ"
msgid "Password" msgid "Password"
msgstr "パスワード" msgstr "パスワード"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "パスワード回復" msgstr "パスワード回復"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "パスワードが一致しません" msgstr "パスワードが一致しません"
@@ -817,6 +836,11 @@ msgstr "リフレッシュ"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "このCommaFeedインスタンスの登録は終了しています" msgstr "このCommaFeedインスタンスの登録は終了しています"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "REST API" msgstr "REST API"
@@ -1056,10 +1080,6 @@ msgstr "スターを外す"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "退会" msgstr "退会"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "ユーザー名" msgstr "ユーザー名"
@@ -1096,3 +1116,7 @@ msgstr "まだサブスクリプションがありません。上部の + 記号
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "フィードの更新がキューに登録されました。" msgstr "フィードの更新がキューに登録されました。"
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "뒤로" msgstr "뒤로"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "로그인으로 돌아가기" msgstr "로그인으로 돌아가기"
@@ -226,6 +227,11 @@ msgstr "확인"
msgid "Confirm password" msgid "Confirm password"
msgstr "비밀번호 확인" msgstr "비밀번호 확인"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "코지" msgstr "코지"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "새 비밀번호" msgstr "새 비밀번호"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "최신순" msgstr "최신순"
@@ -771,11 +786,15 @@ msgstr "부모 카테고리"
msgid "Password" msgid "Password"
msgstr "비밀번호" msgstr "비밀번호"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "비밀번호 복구" msgstr "비밀번호 복구"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "비밀번호가 일치하지 않습니다" msgstr "비밀번호가 일치하지 않습니다"
@@ -817,6 +836,11 @@ msgstr "새로 고침"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "이 CommaFeed 인스턴스에 대한 등록이 마감되었습니다." msgstr "이 CommaFeed 인스턴스에 대한 등록이 마감되었습니다."
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr "별표 제거"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "구독 취소" msgstr "구독 취소"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "사용자 이름" msgstr "사용자 이름"
@@ -1096,3 +1116,7 @@ msgstr "아직 구독이 없습니다. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Kembali" msgstr "Kembali"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Kembali untuk log masuk" msgstr "Kembali untuk log masuk"
@@ -226,6 +227,11 @@ msgstr "Sahkan"
msgid "Confirm password" msgid "Confirm password"
msgstr "Sahkan kata laluan" msgstr "Sahkan kata laluan"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Nyaman" msgstr "Nyaman"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Kata laluan baharu" msgstr "Kata laluan baharu"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Terbaharu dahulu" msgstr "Terbaharu dahulu"
@@ -771,11 +786,15 @@ msgstr "Kategori Induk"
msgid "Password" msgid "Password"
msgstr "Kata Laluan" msgstr "Kata Laluan"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Pemulihan Kata Laluan" msgstr "Pemulihan Kata Laluan"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Kata laluan tidak sepadan" msgstr "Kata laluan tidak sepadan"
@@ -817,6 +836,11 @@ msgstr "Muat semula"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Pendaftaran ditutup pada contoh CommaFeed ini" msgstr "Pendaftaran ditutup pada contoh CommaFeed ini"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "REHAT API" msgstr "REHAT API"
@@ -1056,10 +1080,6 @@ msgstr "Nyahbintang"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Nyahlanggan" msgstr "Nyahlanggan"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Nama pengguna" msgstr "Nama pengguna"
@@ -1096,3 +1116,7 @@ msgstr "Anda belum mempunyai sebarang langganan lagi. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Tilbake" msgstr "Tilbake"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Tilbake for å logge inn" msgstr "Tilbake for å logge inn"
@@ -226,6 +227,11 @@ msgstr "Bekreft"
msgid "Confirm password" msgid "Confirm password"
msgstr "Bekreft passord" msgstr "Bekreft passord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Koselig" msgstr "Koselig"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Nytt passord" msgstr "Nytt passord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Nyeste først" msgstr "Nyeste først"
@@ -771,11 +786,15 @@ msgstr "Overordnet kategori"
msgid "Password" msgid "Password"
msgstr "Passord" msgstr "Passord"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Passordgjenoppretting" msgstr "Passordgjenoppretting"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Passordene samsvarer ikke" msgstr "Passordene samsvarer ikke"
@@ -817,6 +836,11 @@ msgstr "Oppdater"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringer er stengt på denne CommaFeed-forekomsten" msgstr "Registreringer er stengt på denne CommaFeed-forekomsten"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr "Fjern stjerne"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Avslutt abonnementet" msgstr "Avslutt abonnementet"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Brukernavn" msgstr "Brukernavn"
@@ -1096,3 +1116,7 @@ msgstr "Du har ingen abonnementer ennå. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Terug" msgstr "Terug"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Terug naar inloggen" msgstr "Terug naar inloggen"
@@ -226,6 +227,11 @@ msgstr "Bevestigen"
msgid "Confirm password" msgid "Confirm password"
msgstr "Bevestig wachtwoord" msgstr "Bevestig wachtwoord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Gezellig" msgstr "Gezellig"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Nieuw wachtwoord" msgstr "Nieuw wachtwoord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Nieuwste eerst" msgstr "Nieuwste eerst"
@@ -771,11 +786,15 @@ msgstr "Oudercategorie"
msgid "Password" msgid "Password"
msgstr "Wachtwoord" msgstr "Wachtwoord"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Wachtwoordherstel" msgstr "Wachtwoordherstel"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Wachtwoorden komen niet overeen" msgstr "Wachtwoorden komen niet overeen"
@@ -817,6 +836,11 @@ msgstr "Vernieuwen"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registraties zijn gesloten op deze CommaFeed-instantie" msgstr "Registraties zijn gesloten op deze CommaFeed-instantie"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "REST-API" msgstr "REST-API"
@@ -1056,10 +1080,6 @@ msgstr "Sterren uit"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Afmelden" msgstr "Afmelden"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Gebruikersnaam" msgstr "Gebruikersnaam"
@@ -1096,3 +1116,7 @@ msgstr "Je hebt nog geen abonnementen. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Tilbake" msgstr "Tilbake"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Tilbake for å logge inn" msgstr "Tilbake for å logge inn"
@@ -226,6 +227,11 @@ msgstr "Bekreft"
msgid "Confirm password" msgid "Confirm password"
msgstr "Bekreft passord" msgstr "Bekreft passord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Koselig" msgstr "Koselig"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Nytt passord" msgstr "Nytt passord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Nyeste først" msgstr "Nyeste først"
@@ -771,11 +786,15 @@ msgstr "Overordnet kategori"
msgid "Password" msgid "Password"
msgstr "Passord" msgstr "Passord"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Passordgjenoppretting" msgstr "Passordgjenoppretting"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Passordene samsvarer ikke" msgstr "Passordene samsvarer ikke"
@@ -817,6 +836,11 @@ msgstr "Oppdater"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringer er stengt på denne CommaFeed-forekomsten" msgstr "Registreringer er stengt på denne CommaFeed-forekomsten"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr "Fjern stjerne"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Avslutt abonnementet" msgstr "Avslutt abonnementet"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Brukernavn" msgstr "Brukernavn"
@@ -1096,3 +1116,7 @@ msgstr "Du har ingen abonnementer ennå. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Powrót" msgstr "Powrót"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Powrót do logowania" msgstr "Powrót do logowania"
@@ -226,6 +227,11 @@ msgstr "Potwierdź"
msgid "Confirm password" msgid "Confirm password"
msgstr "Potwierdź hasło" msgstr "Potwierdź hasło"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Przytulny" msgstr "Przytulny"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Nowe hasło" msgstr "Nowe hasło"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Najnowsze jako pierwsze" msgstr "Najnowsze jako pierwsze"
@@ -771,11 +786,15 @@ msgstr "Kategoria nadrzędna"
msgid "Password" msgid "Password"
msgstr "Hasło" msgstr "Hasło"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Odzyskiwanie hasła" msgstr "Odzyskiwanie hasła"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Hasła nie pasują" msgstr "Hasła nie pasują"
@@ -817,6 +836,11 @@ msgstr "Odśwież"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Rejestracje są zamknięte w tej instancji CommaFeed" msgstr "Rejestracje są zamknięte w tej instancji CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr ""
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Anuluj subskrypcję" msgstr "Anuluj subskrypcję"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Nazwa użytkownika" msgstr "Nazwa użytkownika"
@@ -1096,3 +1116,7 @@ msgstr "Nie masz jeszcze żadnych subskrypcji. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Voltar" msgstr "Voltar"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Voltar para logar" msgstr "Voltar para logar"
@@ -226,6 +227,11 @@ msgstr "Confirmar"
msgid "Confirm password" msgid "Confirm password"
msgstr "Confirmar senha" msgstr "Confirmar senha"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Aconchegante" msgstr "Aconchegante"
@@ -491,6 +497,10 @@ msgstr "Índigo"
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr "Nunca"
msgid "New password" msgid "New password"
msgstr "Nova senha" msgstr "Nova senha"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Mais novo primeiro" msgstr "Mais novo primeiro"
@@ -771,11 +786,15 @@ msgstr "Categoria Pai"
msgid "Password" msgid "Password"
msgstr "Senha" msgstr "Senha"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Recuperação de Senha" msgstr "Recuperação de Senha"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Senhas não coincidem" msgstr "Senhas não coincidem"
@@ -817,6 +836,11 @@ msgstr "Atualizar"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Os registros estão fechados nesta instância do CommaFeed" msgstr "Os registros estão fechados nesta instância do CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
@@ -1056,10 +1080,6 @@ msgstr "Desestrelar"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Cancelar inscrição" msgstr "Cancelar inscrição"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Nome de usuário" msgstr "Nome de usuário"
@@ -1096,3 +1116,7 @@ msgstr "Você ainda não tem nenhuma assinatura. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "Seus feed foram enfileirados para atualização" msgstr "Seus feed foram enfileirados para atualização"
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Назад" msgstr "Назад"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Вернуться к входу" msgstr "Вернуться к входу"
@@ -226,6 +227,11 @@ msgstr "Подтвердить"
msgid "Confirm password" msgid "Confirm password"
msgstr "Подтвердить пароль" msgstr "Подтвердить пароль"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Уютно" msgstr "Уютно"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr "Никогда"
msgid "New password" msgid "New password"
msgstr "Новый пароль" msgstr "Новый пароль"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Сначала новые" msgstr "Сначала новые"
@@ -771,11 +786,15 @@ msgstr "Родительская категория"
msgid "Password" msgid "Password"
msgstr "Пароль" msgstr "Пароль"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Восстановление пароля" msgstr "Восстановление пароля"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Пароли не совпадают" msgstr "Пароли не совпадают"
@@ -817,6 +836,11 @@ msgstr "Обновить"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Регистрация закрыта для этого экземпляра CommaFeed." msgstr "Регистрация закрыта для этого экземпляра CommaFeed."
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "REST API" msgstr "REST API"
@@ -1056,10 +1080,6 @@ msgstr "Удалить из избранного"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Отписаться" msgstr "Отписаться"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Имя пользователя" msgstr "Имя пользователя"
@@ -1096,3 +1116,7 @@ msgstr "У вас еще нет подписок. Почему бы не поп
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "Ваши каналы были поставлены в очередь на обновление." msgstr "Ваши каналы были поставлены в очередь на обновление."
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Späť" msgstr "Späť"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Späť na prihlásenie" msgstr "Späť na prihlásenie"
@@ -226,6 +227,11 @@ msgstr "Potvrdiť"
msgid "Confirm password" msgid "Confirm password"
msgstr "Potvrďte heslo" msgstr "Potvrďte heslo"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Útulný" msgstr "Útulný"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Nové heslo" msgstr "Nové heslo"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Najnovšie ako prvé" msgstr "Najnovšie ako prvé"
@@ -771,11 +786,15 @@ msgstr "Rodičovská kategória"
msgid "Password" msgid "Password"
msgstr "Heslo" msgstr "Heslo"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Obnovenie hesla" msgstr "Obnovenie hesla"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Heslá sa nezhodujú" msgstr "Heslá sa nezhodujú"
@@ -817,6 +836,11 @@ msgstr "Obnoviť"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "V tejto inštancii CommaFeed sú registrácie uzavreté" msgstr "V tejto inštancii CommaFeed sú registrácie uzavreté"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr "Odobrať hviezdičku"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Zrušte odber" msgstr "Zrušte odber"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Meno používateľa" msgstr "Meno používateľa"
@@ -1096,3 +1116,7 @@ msgstr "Zatiaľ nemáte žiadne odbery. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Tillbaka" msgstr "Tillbaka"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Tillbaka för att logga in" msgstr "Tillbaka för att logga in"
@@ -226,6 +227,11 @@ msgstr "Bekräfta"
msgid "Confirm password" msgid "Confirm password"
msgstr "Bekräfta lösenord" msgstr "Bekräfta lösenord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Mysigt" msgstr "Mysigt"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Nytt lösenord" msgstr "Nytt lösenord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Nyast först" msgstr "Nyast först"
@@ -771,11 +786,15 @@ msgstr "Föräldrakategori"
msgid "Password" msgid "Password"
msgstr "Lösenord" msgstr "Lösenord"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Lösenordsåterställning" msgstr "Lösenordsåterställning"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Lösenorden matchar inte" msgstr "Lösenorden matchar inte"
@@ -817,6 +836,11 @@ msgstr "Uppdatera"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringar är stängda på denna CommaFeed-instans" msgstr "Registreringar är stängda på denna CommaFeed-instans"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
@@ -1056,10 +1080,6 @@ msgstr ""
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Avregistrera" msgstr "Avregistrera"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Användarnamn" msgstr "Användarnamn"
@@ -1096,3 +1116,7 @@ msgstr "Du har inga prenumerationer än. "
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -135,6 +135,7 @@ msgid "Back"
msgstr "Geri" msgstr "Geri"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "Giriş yapmak için geri dön" msgstr "Giriş yapmak için geri dön"
@@ -226,6 +227,11 @@ msgstr "Onayla"
msgid "Confirm password" msgid "Confirm password"
msgstr "Şifreyi onayla" msgstr "Şifreyi onayla"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "Rahat" msgstr "Rahat"
@@ -491,6 +497,10 @@ msgstr ""
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr ""
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
@@ -634,6 +644,11 @@ msgstr ""
msgid "New password" msgid "New password"
msgstr "Yeni şifre" msgstr "Yeni şifre"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "Önce en yenisi" msgstr "Önce en yenisi"
@@ -771,11 +786,15 @@ msgstr "Üst Kategori"
msgid "Password" msgid "Password"
msgstr "Şifre" msgstr "Şifre"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr ""
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "Parola Kurtarma" msgstr "Parola Kurtarma"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "Parolalar eşleşmiyor" msgstr "Parolalar eşleşmiyor"
@@ -817,6 +836,11 @@ msgstr "Yenile"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Bu CommaFeed örneğinde kayıtlar kapalı" msgstr "Bu CommaFeed örneğinde kayıtlar kapalı"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "REST API" msgstr "REST API"
@@ -1056,10 +1080,6 @@ msgstr "Yıldızı kaldır"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "Aboneliği iptal et" msgstr "Aboneliği iptal et"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "Kullanıcı adı" msgstr "Kullanıcı adı"
@@ -1096,3 +1116,7 @@ msgstr "Henüz aboneliğiniz yok. Sayfanın üstündeki + işaretiyle feed ekley
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "Feed'leriniz yenileme için sıraya alındı." msgstr "Feed'leriniz yenileme için sıraya alındı."
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr ""

View File

@@ -63,7 +63,7 @@ msgstr "管理员"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name" msgid "Admin user name"
msgstr "" msgstr "管理员用户名"
#: src/components/content/add/CategorySelect.tsx #: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx #: src/components/header/Header.tsx
@@ -135,6 +135,7 @@ msgid "Back"
msgstr "返回" msgstr "返回"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Back to log in" msgid "Back to log in"
msgstr "返回登录" msgstr "返回登录"
@@ -226,13 +227,18 @@ msgstr "确认"
msgid "Confirm password" msgid "Confirm password"
msgstr "确认密码" msgstr "确认密码"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "确认密码"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Cozy" msgid "Cozy"
msgstr "宽松" msgstr "宽松"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account" msgid "Create Admin Account"
msgstr "" msgstr "创建管理员帐号"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl" msgid "Ctrl"
@@ -489,7 +495,11 @@ msgstr "靛蓝"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup" msgid "Initial Setup"
msgstr "" msgstr "初始化设置"
#: src/pages/auth/PasswordResetPage.tsx
msgid "Invalid password reset link. Please request a new one."
msgstr "密码重置链接无效,请重新申请。"
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
@@ -634,6 +644,11 @@ msgstr "从不"
msgid "New password" msgid "New password"
msgstr "新密码" msgstr "新密码"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "新密码"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Newest first" msgid "Newest first"
msgstr "最新的优先" msgstr "最新的优先"
@@ -771,11 +786,15 @@ msgstr "父类别"
msgid "Password" msgid "Password"
msgstr "密码" msgstr "密码"
#: src/hooks/useValidationRules.ts
msgid "Password must be at least {minimumPasswordLength} characters"
msgstr "密码最少需要 {minimumPasswordLength} 个字符"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "Password Recovery" msgid "Password Recovery"
msgstr "密码恢复" msgstr "密码恢复"
#: src/components/settings/ProfileSettings.tsx #: src/hooks/useValidationRules.ts
msgid "Passwords do not match" msgid "Passwords do not match"
msgstr "密码不匹配" msgstr "密码不匹配"
@@ -817,6 +836,11 @@ msgstr "刷新"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "此 CommaFeed 实例上的注册已关闭" msgstr "此 CommaFeed 实例上的注册已关闭"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "重置密码"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "REST API" msgstr "REST API"
@@ -1056,10 +1080,6 @@ msgstr "取消星标"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "取消订阅" msgstr "取消订阅"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "User name" msgid "User name"
msgstr "用户名" msgstr "用户名"
@@ -1083,7 +1103,7 @@ msgstr "网站"
#: src/pages/auth/InitialSetupPage.tsx #: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started." msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "" msgstr "欢迎当前页仅当您第一次使用CommaFeed时出现请创建一个管理员帐号以开始使用。"
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Yellow" msgid "Yellow"
@@ -1096,3 +1116,7 @@ msgstr "您还没有任何订阅。"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "您的订阅已经进入刷新队列。" msgstr "您的订阅已经进入刷新队列。"
#: src/pages/auth/PasswordResetPage.tsx
msgid "Your password has been changed. You can now log in with your new password."
msgstr "您的密码已更改。您现在可以使用新密码登录。"

View File

@@ -9,11 +9,13 @@ import { redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch } from "@/app/store" import { useAppDispatch } from "@/app/store"
import type { InitialSetupRequest } from "@/app/types" import type { InitialSetupRequest } from "@/app/types"
import { Alert } from "@/components/Alert" import { Alert } from "@/components/Alert"
import { useValidationRules } from "@/hooks/useValidationRules"
import { PageTitle } from "@/pages/PageTitle" import { PageTitle } from "@/pages/PageTitle"
export function InitialSetupPage() { export function InitialSetupPage() {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { _ } = useLingui() const { _ } = useLingui()
const validationRules = useValidationRules()
const form = useForm<InitialSetupRequest>({ const form = useForm<InitialSetupRequest>({
initialValues: { initialValues: {
@@ -21,6 +23,10 @@ export function InitialSetupPage() {
password: "", password: "",
email: "", email: "",
}, },
validate: {
password: validationRules.password,
},
validateInputOnChange: true,
}) })
const login = useAsyncCallback(client.user.login, { const login = useAsyncCallback(client.user.login, {

View File

@@ -0,0 +1,119 @@
import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, Stack, Title } from "@mantine/core"
import { useForm } from "@mantine/form"
import { useState } from "react"
import { useAsyncCallback } from "react-async-hook"
import { Link, useSearchParams } from "react-router-dom"
import { client, errorToStrings } from "@/app/client"
import { Alert } from "@/components/Alert"
import { useValidationRules } from "@/hooks/useValidationRules"
import { PageTitle } from "@/pages/PageTitle"
interface PasswordResetFormValues {
password: string
passwordConfirmation: string
}
export function PasswordResetPage() {
const [message, setMessage] = useState("")
const [searchParams] = useSearchParams()
const { _ } = useLingui()
const validationRules = useValidationRules()
const email = searchParams.get("email") ?? ""
const token = searchParams.get("token") ?? ""
const form = useForm<PasswordResetFormValues>({
initialValues: {
password: "",
passwordConfirmation: "",
},
validate: {
password: validationRules.password,
passwordConfirmation: (value, values) => validationRules.passwordConfirmation(value, values.password),
},
validateInputOnChange: true,
})
const resetPassword = useAsyncCallback(client.user.passwordResetCallback, {
onSuccess: () => {
setMessage(_(msg`Your password has been changed. You can now log in with your new password.`))
form.reset()
},
})
const isMissingParams = !email || !token
return (
<Container size="xs">
<PageTitle />
<Paper>
<Title order={2} mb="md">
<Trans>Reset Password</Trans>
</Title>
{resetPassword.error && (
<Box mb="md">
<Alert messages={errorToStrings(resetPassword.error)} />
</Box>
)}
{isMissingParams && (
<Box mb="md">
<Alert messages={[_(msg`Invalid password reset link. Please request a new one.`)]} />
</Box>
)}
{message && (
<Box mb="md">
<Alert level="success" messages={[message]} />
</Box>
)}
{!isMissingParams && !message && (
<form
onSubmit={form.onSubmit(values => {
resetPassword.execute({
email,
token,
password: values.password,
})
})}
>
<Stack>
<PasswordInput
label={<Trans>New Password</Trans>}
placeholder={_(msg`New Password`)}
{...form.getInputProps("password")}
size="md"
required
/>
<PasswordInput
label={<Trans>Confirm Password</Trans>}
placeholder={_(msg`Confirm Password`)}
{...form.getInputProps("passwordConfirmation")}
size="md"
required
/>
<Button type="submit" loading={resetPassword.loading}>
<Trans>Reset Password</Trans>
</Button>
</Stack>
</form>
)}
<Center mt="md">
<Group>
<Anchor component={Link} to="/login">
<Trans>Back to log in</Trans>
</Anchor>
</Group>
</Center>
</Paper>
</Container>
)
}

View File

@@ -10,12 +10,14 @@ import { redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store" import { useAppDispatch, useAppSelector } from "@/app/store"
import type { RegistrationRequest } from "@/app/types" import type { RegistrationRequest } from "@/app/types"
import { Alert } from "@/components/Alert" import { Alert } from "@/components/Alert"
import { useValidationRules } from "@/hooks/useValidationRules"
import { PageTitle } from "@/pages/PageTitle" import { PageTitle } from "@/pages/PageTitle"
export function RegistrationPage() { export function RegistrationPage() {
const serverInfos = useAppSelector(state => state.server.serverInfos) const serverInfos = useAppSelector(state => state.server.serverInfos)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { _ } = useLingui() const { _ } = useLingui()
const validationRules = useValidationRules()
const form = useForm<RegistrationRequest>({ const form = useForm<RegistrationRequest>({
initialValues: { initialValues: {
@@ -23,6 +25,10 @@ export function RegistrationPage() {
password: "", password: "",
email: "", email: "",
}, },
validate: {
password: validationRules.password,
},
validateInputOnChange: true,
}) })
const login = useAsyncCallback(client.user.login, { const login = useAsyncCallback(client.user.login, {

View File

@@ -6,13 +6,14 @@
<parent> <parent>
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId> <artifactId>commafeed</artifactId>
<version>6.0.0</version> <version>6.2.0</version>
</parent> </parent>
<artifactId>commafeed-server</artifactId> <artifactId>commafeed-server</artifactId>
<name>CommaFeed Server</name> <name>CommaFeed Server</name>
<packaging>quarkus</packaging>
<properties> <properties>
<quarkus.version>3.30.6</quarkus.version> <quarkus.version>3.31.2</quarkus.version>
<querydsl.version>7.1</querydsl.version> <querydsl.version>7.1</querydsl.version>
<rome.version>2.1.0</rome.version> <rome.version>2.1.0</rome.version>
@@ -55,7 +56,7 @@
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId> <artifactId>properties-maven-plugin</artifactId>
<version>1.2.1</version> <version>1.3.0</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@@ -77,24 +78,14 @@
<artifactId>quarkus-maven-plugin</artifactId> <artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.version}</version> <version>${quarkus.version}</version>
<extensions>true</extensions> <extensions>true</extensions>
<executions> <configuration>
<execution> <properties>
<goals> <quarkus.package.output-name>commafeed-${project.version}</quarkus.package.output-name>
<goal>build</goal> <quarkus.package.runner-suffix>
<goal>generate-code</goal> -${build.database}-${os.detected.name}-${os.detected.arch}-runner
<goal>generate-code-tests</goal> </quarkus.package.runner-suffix>
<goal>native-image-agent</goal> </properties>
</goals> </configuration>
<configuration>
<properties>
<quarkus.package.output-name>commafeed-${project.version}</quarkus.package.output-name>
<quarkus.package.runner-suffix>
-${build.database}-${os.detected.name}-${os.detected.arch}-runner
</quarkus.package.runner-suffix>
</properties>
</configuration>
</execution>
</executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
@@ -167,6 +158,7 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.5.4</version> <version>3.5.4</version>
<configuration> <configuration>
<argLine>@{argLine}</argLine>
<systemPropertyVariables> <systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<quarkus.datasource.db-kind>${build.database}</quarkus.datasource.db-kind> <quarkus.datasource.db-kind>${build.database}</quarkus.datasource.db-kind>
@@ -186,14 +178,13 @@
</execution> </execution>
</executions> </executions>
<configuration> <configuration>
<argLine>@{argLine}</argLine>
<systemPropertyVariables> <systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner <native.image.path>${project.build.directory}/${project.build.finalName}-runner
</native.image.path> </native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<quarkus.datasource.db-kind>${build.database}</quarkus.datasource.db-kind> <quarkus.datasource.db-kind>${build.database}</quarkus.datasource.db-kind>
</systemPropertyVariables> </systemPropertyVariables>
<!-- fix for java.lang.NoClassDefFoundError: Could not initialize class org.jboss.threads.JDKSpecific$ThreadAccess (#1938) -->
<argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
</configuration> </configuration>
<!-- failsafe plugin does not seem to be able to pick up dependencies declared in profiles --> <!-- failsafe plugin does not seem to be able to pick up dependencies declared in profiles -->
<dependencies> <dependencies>
@@ -219,61 +210,6 @@
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.14</version>
<configuration>
<!-- excluding SACParserCSS21TokenManager because it causes a "Method too large" exception -->
<excludes>
<exclude>com/steadystate/css/parser/SACParserCSS21TokenManager</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>unit-tests-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>${project.build.directory}/jacoco-output/jacoco-unit-tests.exec</destFile>
</configuration>
</execution>
<execution>
<id>integration-tests-agent</id>
<goals>
<goal>prepare-agent-integration</goal>
</goals>
<configuration>
<destFile>${project.build.directory}/jacoco-output/jacoco-integration-tests.exec</destFile>
</configuration>
</execution>
<execution>
<id>merge</id>
<phase>post-integration-test</phase>
<goals>
<goal>merge</goal>
</goals>
<configuration>
<fileSets>
<fileSet>
<directory>${project.build.directory}/jacoco-output</directory>
<includes>
<include>*.exec</include>
</includes>
</fileSet>
</fileSets>
</configuration>
</execution>
<execution>
<id>generate-code-coverage-report</id>
<phase>post-integration-test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>io.github.git-commit-id</groupId> <groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId> <artifactId>git-commit-id-maven-plugin</artifactId>
@@ -301,7 +237,7 @@
<dependency> <dependency>
<groupId>com.puppycrawl.tools</groupId> <groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId> <artifactId>checkstyle</artifactId>
<version>13.0.0</version> <version>13.2.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
<executions> <executions>
@@ -330,7 +266,7 @@
<plugin> <plugin>
<groupId>com.diffplug.spotless</groupId> <groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId> <artifactId>spotless-maven-plugin</artifactId>
<version>3.1.0</version> <version>3.2.1</version>
<?m2e ignore?> <?m2e ignore?>
<executions> <executions>
<execution> <execution>
@@ -359,7 +295,7 @@
<dependency> <dependency>
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed-client</artifactId> <artifactId>commafeed-client</artifactId>
<version>6.0.0</version> <version>6.2.0</version>
</dependency> </dependency>
<!-- compile-time processors --> <!-- compile-time processors -->
@@ -427,7 +363,7 @@
<dependency> <dependency>
<groupId>io.dropwizard.metrics</groupId> <groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-json</artifactId> <artifactId>metrics-json</artifactId>
<version>4.2.37</version> <version>4.2.38</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.github.openfeign.querydsl</groupId> <groupId>io.github.openfeign.querydsl</groupId>
@@ -530,18 +466,13 @@
<!-- test dependencies --> <!-- test dependencies -->
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId> <artifactId>quarkus-junit-mockito</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jacoco</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.quarkiverse.playwright</groupId> <groupId>io.quarkiverse.playwright</groupId>
<artifactId>quarkus-playwright</artifactId> <artifactId>quarkus-playwright</artifactId>
<version>2.3.1</version> <version>2.3.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@@ -1,4 +1,4 @@
FROM ibm-semeru-runtimes:open-jdk-25.0.1_8-jre@sha256:d88c854ca5506a04dd2cdaedb98dcf23d6b1b077aebaf738d5c3c5d8c94fff20 FROM ibm-semeru-runtimes:open-jdk-25.0.1_8-jre@sha256:e12d5f2461606d625e4d1e22dd0db89e4ae18f58a7f96332811554209ef9028a
EXPOSE 8082 EXPOSE 8082
RUN mkdir -p /commafeed/data RUN mkdir -p /commafeed/data

View File

@@ -1,4 +1,4 @@
FROM debian:13.2@sha256:c71b05eac0b20adb4cdcc9f7b052227efd7da381ad10bb92f972e8eae7c6cdc9 FROM debian:13.3@sha256:2c91e484d93f0830a7e05a2b9d92a7b102be7cab562198b984a84fdbc7806d91
ARG TARGETARCH ARG TARGETARCH
EXPOSE 8082 EXPOSE 8082

View File

@@ -312,6 +312,12 @@ public interface CommaFeedConfiguration {
@WithDefault("100") @WithDefault("100")
int batchSize(); int batchSize();
/**
* Whether to keep starred entries when cleaning up old entries.
*/
@WithDefault("true")
boolean keepStarredEntries();
default Instant statusesInstantThreshold() { default Instant statusesInstantThreshold() {
return statusesMaxAge().toMillis() > 0 ? Instant.now().minus(statusesMaxAge()) : null; return statusesMaxAge().toMillis() > 0 ? Instant.now().minus(statusesMaxAge()) : null;
} }

View File

@@ -11,6 +11,7 @@ import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.QFeedEntry; import com.commafeed.backend.model.QFeedEntry;
import com.querydsl.core.Tuple; import com.querydsl.core.Tuple;
import com.querydsl.core.types.dsl.NumberExpression; import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQuery;
@Singleton @Singleton
public class FeedEntryDAO extends GenericDAO<FeedEntry> { public class FeedEntryDAO extends GenericDAO<FeedEntry> {
@@ -25,15 +26,21 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
return query().select(ENTRY).from(ENTRY).where(ENTRY.guidHash.eq(guidHash), ENTRY.feed.eq(feed)).limit(1).fetchOne(); return query().select(ENTRY).from(ENTRY).where(ENTRY.guidHash.eq(guidHash), ENTRY.feed.eq(feed)).limit(1).fetchOne();
} }
public List<FeedCapacity> findFeedsExceedingCapacity(long maxCapacity, long max) { public List<FeedCapacity> findFeedsExceedingCapacity(long maxCapacity, long max, boolean keepStarredEntries) {
NumberExpression<Long> count = ENTRY.id.count(); NumberExpression<Long> count = ENTRY.id.count();
List<Tuple> tuples = query().select(ENTRY.feed.id, count) JPAQuery<Tuple> query = query().select(ENTRY.feed.id, count).from(ENTRY);
.from(ENTRY)
.groupBy(ENTRY.feed) if (keepStarredEntries) {
query.where(Predicates.isNotStarred(ENTRY));
}
return query.groupBy(ENTRY.feed)
.having(count.gt(maxCapacity)) .having(count.gt(maxCapacity))
.limit(max) .limit(max)
.fetch(); .fetch()
return tuples.stream().map(t -> new FeedCapacity(t.get(ENTRY.feed.id), t.get(count))).toList(); .stream()
.map(t -> new FeedCapacity(t.get(ENTRY.feed.id), t.get(count)))
.toList();
} }
public int delete(Long feedId, long max) { public int delete(Long feedId, long max) {
@@ -44,21 +51,30 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
/** /**
* Delete entries older than a certain date * Delete entries older than a certain date
*/ */
public int deleteEntriesOlderThan(Instant olderThan, long max) { public int deleteEntriesOlderThan(Instant olderThan, long max, boolean keepStarredEntries) {
List<FeedEntry> list = query().selectFrom(ENTRY) JPAQuery<FeedEntry> query = query().selectFrom(ENTRY)
.where(ENTRY.published.lt(olderThan)) .where(ENTRY.published.lt(olderThan))
.orderBy(ENTRY.published.asc()) .orderBy(ENTRY.published.asc())
.limit(max) .limit(max);
.fetch();
return delete(list); if (keepStarredEntries) {
query.where(Predicates.isNotStarred(ENTRY));
}
return delete(query.fetch());
} }
/** /**
* Delete the oldest entries of a feed * Delete the oldest entries of a feed
*/ */
public int deleteOldEntries(Long feedId, long max) { public int deleteOldEntries(Long feedId, long max, boolean keepStarredEntries) {
List<FeedEntry> list = query().selectFrom(ENTRY).where(ENTRY.feed.id.eq(feedId)).orderBy(ENTRY.published.asc()).limit(max).fetch(); JPAQuery<FeedEntry> query = query().selectFrom(ENTRY).where(ENTRY.feed.id.eq(feedId)).orderBy(ENTRY.published.asc()).limit(max);
return delete(list);
if (keepStarredEntries) {
query.where(Predicates.isNotStarred(ENTRY));
}
return delete(query.fetch());
} }
public record FeedCapacity(Long id, Long capacity) { public record FeedCapacity(Long id, Long capacity) {

View File

@@ -0,0 +1,18 @@
package com.commafeed.backend.dao;
import com.commafeed.backend.model.QFeedEntry;
import com.commafeed.backend.model.QFeedEntryStatus;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.JPAExpressions;
import lombok.experimental.UtilityClass;
@UtilityClass
public class Predicates {
private static final QFeedEntryStatus STATUS = QFeedEntryStatus.feedEntryStatus;
public static BooleanExpression isNotStarred(QFeedEntry entry) {
return JPAExpressions.selectOne().from(STATUS).where(STATUS.entry.eq(entry).and(STATUS.starred.isTrue())).notExists();
}
}

View File

@@ -1,6 +1,5 @@
package com.commafeed.backend.service; package com.commafeed.backend.service;
import java.io.Serializable;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
@@ -18,11 +17,10 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
// taken from http://www.javacodegeeks.com/2012/05/secure-password-storage-donts-dos-and.html // taken from http://www.javacodegeeks.com/2012/05/secure-password-storage-donts-dos-and.html
@SuppressWarnings("serial")
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Singleton @Singleton
public class PasswordEncryptionService implements Serializable { public class PasswordEncryptionService {
public boolean authenticate(String attemptedPassword, byte[] encryptedPassword, byte[] salt) { public boolean authenticate(String attemptedPassword, byte[] encryptedPassword, byte[] salt) {
if (StringUtils.isBlank(attemptedPassword)) { if (StringUtils.isBlank(attemptedPassword)) {

View File

@@ -27,13 +27,13 @@ import lombok.extern.slf4j.Slf4j;
@Singleton @Singleton
public class DatabaseCleaningService { public class DatabaseCleaningService {
private final int batchSize;
private final UnitOfWork unitOfWork; private final UnitOfWork unitOfWork;
private final FeedDAO feedDAO; private final FeedDAO feedDAO;
private final FeedEntryDAO feedEntryDAO; private final FeedEntryDAO feedEntryDAO;
private final FeedEntryContentDAO feedEntryContentDAO; private final FeedEntryContentDAO feedEntryContentDAO;
private final FeedEntryStatusDAO feedEntryStatusDAO; private final FeedEntryStatusDAO feedEntryStatusDAO;
private final int batchSize;
private final boolean keepStarredEntries;
private final Meter entriesDeletedMeter; private final Meter entriesDeletedMeter;
public DatabaseCleaningService(CommaFeedConfiguration config, UnitOfWork unitOfWork, FeedDAO feedDAO, FeedEntryDAO feedEntryDAO, public DatabaseCleaningService(CommaFeedConfiguration config, UnitOfWork unitOfWork, FeedDAO feedDAO, FeedEntryDAO feedEntryDAO,
@@ -44,6 +44,7 @@ public class DatabaseCleaningService {
this.feedEntryContentDAO = feedEntryContentDAO; this.feedEntryContentDAO = feedEntryContentDAO;
this.feedEntryStatusDAO = feedEntryStatusDAO; this.feedEntryStatusDAO = feedEntryStatusDAO;
this.batchSize = config.database().cleanup().batchSize(); this.batchSize = config.database().cleanup().batchSize();
this.keepStarredEntries = config.database().cleanup().keepStarredEntries();
this.entriesDeletedMeter = metrics.meter(MetricRegistry.name(getClass(), "entriesDeleted")); this.entriesDeletedMeter = metrics.meter(MetricRegistry.name(getClass(), "entriesDeleted"));
} }
@@ -86,21 +87,23 @@ public class DatabaseCleaningService {
log.info("cleaning entries exceeding feed capacity"); log.info("cleaning entries exceeding feed capacity");
long total = 0; long total = 0;
while (true) { while (true) {
List<FeedCapacity> feeds = unitOfWork.call(() -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, batchSize)); List<FeedCapacity> feeds = unitOfWork
.call(() -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, batchSize, keepStarredEntries));
if (feeds.isEmpty()) { if (feeds.isEmpty()) {
break; break;
} }
for (final FeedCapacity feed : feeds) { for (final FeedCapacity feed : feeds) {
long remaining = feed.capacity() - maxFeedCapacity; long remaining = feed.capacity() - maxFeedCapacity;
int deleted;
do { do {
final long rem = remaining; final long rem = remaining;
int deleted = unitOfWork.call(() -> feedEntryDAO.deleteOldEntries(feed.id(), Math.min(batchSize, rem))); deleted = unitOfWork.call(() -> feedEntryDAO.deleteOldEntries(feed.id(), Math.min(batchSize, rem), keepStarredEntries));
entriesDeletedMeter.mark(deleted); entriesDeletedMeter.mark(deleted);
total += deleted; total += deleted;
remaining -= deleted; remaining -= deleted;
log.debug("removed {} entries for feeds exceeding capacity", total); log.debug("removed {} entries for feeds exceeding capacity", total);
} while (remaining > 0); } while (deleted > 0 && remaining > 0);
} }
} }
log.info("cleanup done: {} entries for feeds exceeding capacity deleted", total); log.info("cleanup done: {} entries for feeds exceeding capacity deleted", total);
@@ -111,7 +114,7 @@ public class DatabaseCleaningService {
long total = 0; long total = 0;
long deleted; long deleted;
do { do {
deleted = unitOfWork.call(() -> feedEntryDAO.deleteEntriesOlderThan(olderThan, batchSize)); deleted = unitOfWork.call(() -> feedEntryDAO.deleteEntriesOlderThan(olderThan, batchSize, keepStarredEntries));
entriesDeletedMeter.mark(deleted); entriesDeletedMeter.mark(deleted);
total += deleted; total += deleted;
log.debug("removed {} old entries", total); log.debug("removed {} old entries", total);

View File

@@ -49,4 +49,7 @@ public class ServerInfo implements Serializable {
@Schema(required = true) @Schema(required = true)
private boolean initialSetupRequired; private boolean initialSetupRequired;
@Schema(required = true)
private int minimumPasswordLength;
} }

View File

@@ -18,7 +18,7 @@ import lombok.Data;
@RegisterForReflection @RegisterForReflection
public class Settings implements Serializable { public class Settings implements Serializable {
@Schema(description = "user's preferred language, english if none", required = true) @Schema(description = "user's preferred language, english if none")
private String language; private String language;
@Schema(description = "user reads all entries or unread entries only", required = true) @Schema(description = "user reads all entries or unread entries only", required = true)

View File

@@ -0,0 +1,34 @@
package com.commafeed.frontend.model.request;
import java.io.Serializable;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import com.commafeed.security.password.ValidPassword;
import lombok.Data;
@SuppressWarnings("serial")
@Data
@Schema
public class PasswordResetConfirmationRequest implements Serializable {
@Schema(description = "email address for password recovery", required = true)
@Email
@NotEmpty
@Size(max = 255)
private String email;
@Schema(description = "password recovery token", required = true)
@NotEmpty
private String token;
@Schema(description = "new password", required = true)
@NotEmpty
@ValidPassword
private String password;
}

View File

@@ -61,6 +61,7 @@ public class ServerREST {
infos.setTreeReloadInterval(config.websocket().treeReloadInterval().toMillis()); infos.setTreeReloadInterval(config.websocket().treeReloadInterval().toMillis());
infos.setForceRefreshCooldownDuration(config.feedRefresh().forceRefreshCooldownDuration().toMillis()); infos.setForceRefreshCooldownDuration(config.feedRefresh().forceRefreshCooldownDuration().toMillis());
infos.setInitialSetupRequired(databaseStartupService.isInitialSetupRequired()); infos.setInitialSetupRequired(databaseStartupService.isInitialSetupRequired());
infos.setMinimumPasswordLength(config.users().minimumPasswordLength());
return infos; return infos;
} }

View File

@@ -1,6 +1,5 @@
package com.commafeed.frontend.resource; package com.commafeed.frontend.resource;
import java.net.MalformedURLException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
@@ -21,13 +20,11 @@ import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST; import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.core.UriInfo;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.net.URIBuilder;
import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.Operation;
@@ -38,7 +35,6 @@ import com.commafeed.CommaFeedConfiguration;
import com.commafeed.CommaFeedConstants; import com.commafeed.CommaFeedConstants;
import com.commafeed.backend.Digests; import com.commafeed.backend.Digests;
import com.commafeed.backend.Urls; import com.commafeed.backend.Urls;
import com.commafeed.backend.dao.UnitOfWork;
import com.commafeed.backend.dao.UserDAO; import com.commafeed.backend.dao.UserDAO;
import com.commafeed.backend.dao.UserRoleDAO; import com.commafeed.backend.dao.UserRoleDAO;
import com.commafeed.backend.dao.UserSettingsDAO; import com.commafeed.backend.dao.UserSettingsDAO;
@@ -57,6 +53,7 @@ import com.commafeed.backend.service.db.DatabaseStartupService;
import com.commafeed.frontend.model.Settings; import com.commafeed.frontend.model.Settings;
import com.commafeed.frontend.model.UserModel; import com.commafeed.frontend.model.UserModel;
import com.commafeed.frontend.model.request.InitialSetupRequest; import com.commafeed.frontend.model.request.InitialSetupRequest;
import com.commafeed.frontend.model.request.PasswordResetConfirmationRequest;
import com.commafeed.frontend.model.request.PasswordResetRequest; import com.commafeed.frontend.model.request.PasswordResetRequest;
import com.commafeed.frontend.model.request.ProfileModificationRequest; import com.commafeed.frontend.model.request.ProfileModificationRequest;
import com.commafeed.frontend.model.request.RegistrationRequest; import com.commafeed.frontend.model.request.RegistrationRequest;
@@ -87,7 +84,6 @@ public class UserREST {
private final MailService mailService; private final MailService mailService;
private final CommaFeedConfiguration config; private final CommaFeedConfiguration config;
private final UriInfo uri; private final UriInfo uri;
private final UnitOfWork unitOfWork;
@Path("/settings") @Path("/settings")
@GET @GET
@@ -144,7 +140,6 @@ public class UserREST {
s.getSharingSettings().setBuffer(true); s.getSharingSettings().setBuffer(true);
s.setScrollMarks(true); s.setScrollMarks(true);
s.setLanguage("en");
s.setScrollSpeed(400); s.setScrollSpeed(400);
s.setScrollMode(ScrollMode.IF_NEEDED); s.setScrollMode(ScrollMode.IF_NEEDED);
s.setEntriesToKeepOnTopWhenScrolling(1); s.setEntriesToKeepOnTopWhenScrolling(1);
@@ -156,7 +151,7 @@ public class UserREST {
s.setMobileFooter(false); s.setMobileFooter(false);
s.setUnreadCountTitle(false); s.setUnreadCountTitle(false);
s.setUnreadCountFavicon(true); s.setUnreadCountFavicon(true);
s.setDisablePullToRefresh(true); s.setDisablePullToRefresh(false);
} }
return s; return s;
} }
@@ -335,45 +330,44 @@ public class UserREST {
} }
} }
private String buildEmailContent(User user) throws URISyntaxException, MalformedURLException { private String buildEmailContent(User user) throws URISyntaxException {
String publicUrl = Urls.removeTrailingSlash(uri.getBaseUri().toString()); String publicUrl = Urls.removeTrailingSlash(uri.getBaseUri().toString());
publicUrl += "/rest/user/passwordResetCallback";
return String.format( return String.format(
"You asked for password recovery for account '%s', <a href='%s'>follow this link</a> to change your password. Ignore this if you didn't request a password recovery.", "You asked for password recovery for account '%s', <a href='%s'>follow this link</a> to change your password. Ignore this if you didn't request a password recovery.",
user.getName(), callbackUrl(user, publicUrl)); user.getName(), callbackUrl(user, publicUrl));
} }
private String callbackUrl(User user, String publicUrl) throws URISyntaxException, MalformedURLException { private String callbackUrl(User user, String publicUrl) throws URISyntaxException {
return new URIBuilder(publicUrl).addParameter("email", user.getEmail()) URIBuilder queryBuilder = new URIBuilder();
.addParameter("token", user.getRecoverPasswordToken()) queryBuilder.addParameter("email", user.getEmail());
.build() queryBuilder.addParameter("token", user.getRecoverPasswordToken());
.toURL() String queryString = queryBuilder.build().getRawQuery();
.toString(); return publicUrl + "/#/passwordReset?" + queryString;
} }
@Path("/passwordResetCallback") @Path("/passwordResetCallback")
@PermitAll @PermitAll
@GET @POST
@Transactional @Transactional
@Produces(MediaType.TEXT_HTML) @Operation(summary = "confirm password reset with new password")
public Response passwordRecoveryCallback(@Parameter(required = true) @QueryParam("email") String email, public Response passwordRecoveryCallback(@Valid @Parameter(required = true) PasswordResetConfirmationRequest req) {
@Parameter(required = true) @QueryParam("token") String token) { String email = req.getEmail();
String token = req.getToken();
String password = req.getPassword();
Preconditions.checkNotNull(email); Preconditions.checkNotNull(email);
Preconditions.checkNotNull(token); Preconditions.checkNotNull(token);
Preconditions.checkNotNull(password);
User user = userDAO.findByEmail(email); User user = userDAO.findByEmail(email);
if (user == null) { if (user == null || user.getRecoverPasswordToken() == null || !user.getRecoverPasswordToken().equals(token)) {
return Response.status(Status.UNAUTHORIZED).entity("Email not found.").build(); return Response.status(Status.UNAUTHORIZED).entity("Email not found or invalid token.").build();
} }
if (user.getRecoverPasswordToken() == null || !user.getRecoverPasswordToken().equals(token)) { if (ChronoUnit.MINUTES.between(user.getRecoverPasswordTokenDate(), Instant.now()) >= 30) {
return Response.status(Status.UNAUTHORIZED).entity("Invalid token.").build(); return Response.status(Status.UNAUTHORIZED).entity("Token expired.").build();
}
if (ChronoUnit.DAYS.between(user.getRecoverPasswordTokenDate(), Instant.now()) >= 2) {
return Response.status(Status.UNAUTHORIZED).entity("token expired.").build();
} }
String passwd = RandomStringUtils.secure().nextAlphanumeric(10); byte[] encryptedPassword = encryptionService.getEncryptedPassword(password, user.getSalt());
byte[] encryptedPassword = encryptionService.getEncryptedPassword(passwd, user.getSalt());
user.setPassword(encryptedPassword); user.setPassword(encryptedPassword);
if (StringUtils.isNotBlank(user.getApiKey())) { if (StringUtils.isNotBlank(user.getApiKey())) {
user.setApiKey(userService.generateApiKey(user)); user.setApiKey(userService.generateApiKey(user));
@@ -381,10 +375,7 @@ public class UserREST {
user.setRecoverPasswordToken(null); user.setRecoverPasswordToken(null);
user.setRecoverPasswordTokenDate(null); user.setRecoverPasswordTokenDate(null);
String message = "Your new password is: " + passwd; return Response.ok().build();
message += "<br />";
message += String.format("<a href=\"%s\">Back to Homepage</a>", uri.getBaseUri());
return Response.ok(message).build();
} }
@Path("/profile/deleteAccount") @Path("/profile/deleteAccount")

View File

@@ -115,13 +115,13 @@ class DatabaseCleaningServiceTest {
Mockito.when(feed2.id()).thenReturn(2L); Mockito.when(feed2.id()).thenReturn(2L);
Mockito.when(feed2.capacity()).thenReturn(120L); Mockito.when(feed2.capacity()).thenReturn(120L);
Mockito.when(feedEntryDAO.findFeedsExceedingCapacity(50, BATCH_SIZE)) Mockito.when(feedEntryDAO.findFeedsExceedingCapacity(50, BATCH_SIZE, false))
.thenReturn(Arrays.asList(feed1, feed2)) .thenReturn(Arrays.asList(feed1, feed2))
.thenReturn(Collections.emptyList()); .thenReturn(Collections.emptyList());
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 100)).thenReturn(80); Mockito.when(feedEntryDAO.deleteOldEntries(1L, 100, false)).thenReturn(80);
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 50)).thenReturn(50); Mockito.when(feedEntryDAO.deleteOldEntries(1L, 50, false)).thenReturn(50);
Mockito.when(feedEntryDAO.deleteOldEntries(2L, 70)).thenReturn(70); Mockito.when(feedEntryDAO.deleteOldEntries(2L, 70, false)).thenReturn(70);
service.cleanEntriesForFeedsExceedingCapacity(50); service.cleanEntriesForFeedsExceedingCapacity(50);
@@ -132,11 +132,11 @@ class DatabaseCleaningServiceTest {
void cleanEntriesOlderThanDeletesOldEntries() { void cleanEntriesOlderThanDeletesOldEntries() {
Instant cutoff = LocalDate.now().minusDays(30).atStartOfDay().toInstant(ZoneOffset.UTC); Instant cutoff = LocalDate.now().minusDays(30).atStartOfDay().toInstant(ZoneOffset.UTC);
Mockito.when(feedEntryDAO.deleteEntriesOlderThan(cutoff, BATCH_SIZE)).thenReturn(100, 50, 0); Mockito.when(feedEntryDAO.deleteEntriesOlderThan(cutoff, BATCH_SIZE, false)).thenReturn(100, 50, 0);
service.cleanEntriesOlderThan(cutoff); service.cleanEntriesOlderThan(cutoff);
Mockito.verify(feedEntryDAO, Mockito.times(3)).deleteEntriesOlderThan(cutoff, BATCH_SIZE); Mockito.verify(feedEntryDAO, Mockito.times(3)).deleteEntriesOlderThan(cutoff, BATCH_SIZE, false);
Mockito.verify(entriesDeletedMeter, Mockito.times(3)).mark(Mockito.anyLong()); Mockito.verify(entriesDeletedMeter, Mockito.times(3)).mark(Mockito.anyLong());
} }

View File

@@ -6,7 +6,6 @@ import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants; import com.commafeed.TestConstants;
import com.microsoft.playwright.BrowserContext; import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page; import com.microsoft.playwright.Page;
import com.microsoft.playwright.assertions.PlaywrightAssertions; import com.microsoft.playwright.assertions.PlaywrightAssertions;
import com.microsoft.playwright.options.AriaRole; import com.microsoft.playwright.options.AriaRole;
@@ -48,17 +47,6 @@ class AuthentificationIT {
PlaywrightAssertions.assertThat(page).hasURL("http://localhost:8085/#/app/category/all"); PlaywrightAssertions.assertThat(page).hasURL("http://localhost:8085/#/app/category/all");
} }
@Test
void registerFailPasswordTooSimple() {
Page page = context.newPage();
page.navigate(getLoginPageUrl());
page.getByText("Sign up!").click();
PlaywrightTestUtils.register(page, "user", "user@domain.com", "p");
Locator alert = page.getByRole(AriaRole.ALERT);
PlaywrightAssertions.assertThat(alert).containsText("Password must be 4 or more characters in length.");
}
@Test @Test
void registerSuccess() { void registerSuccess() {
Page page = context.newPage(); Page page = context.newPage();

View File

@@ -0,0 +1,185 @@
package com.commafeed.integration.cleanup;
import java.time.Duration;
import java.time.Instant;
import jakarta.inject.Inject;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants;
import com.commafeed.backend.service.db.DatabaseCleaningService;
import com.commafeed.frontend.model.Entries;
import com.commafeed.frontend.model.Entry;
import com.commafeed.frontend.model.request.StarRequest;
import com.commafeed.frontend.resource.CategoryREST;
import com.commafeed.integration.BaseIT;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
@QuarkusTest
class DatabaseCleaningIT extends BaseIT {
@Inject
DatabaseCleaningService databaseCleaningService;
@BeforeEach
void setup() {
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
@AfterEach
void cleanup() {
RestAssured.reset();
}
private void starEntry(String entryId, Long subscriptionId) {
StarRequest starRequest = new StarRequest();
starRequest.setId(entryId);
starRequest.setFeedId(subscriptionId);
starRequest.setStarred(true);
RestAssured.given().body(starRequest).contentType(ContentType.JSON).post("rest/entry/star").then().statusCode(200);
}
private void unstarEntry(String entryId, Long subscriptionId) {
StarRequest starRequest = new StarRequest();
starRequest.setId(entryId);
starRequest.setFeedId(subscriptionId);
starRequest.setStarred(false);
RestAssured.given().body(starRequest).contentType(ContentType.JSON).post("rest/entry/star").then().statusCode(200);
}
@Nested
class KeepStarredEntries {
@Test
void starredEntriesAreKeptWhenCleaningFeedsExceedingCapacity() {
// Subscribe to feed and wait for entries
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
// Verify we have 2 entries
Entries entriesBefore = getFeedEntries(subscriptionId);
Assertions.assertEquals(2, entriesBefore.getEntries().size());
// Star the first entry
Entry entryToStar = entriesBefore.getEntries().getFirst();
starEntry(entryToStar.getId(), subscriptionId);
// Verify the entry is starred
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
Assertions.assertEquals(1, starredEntries.getEntries().size());
Assertions.assertEquals(entryToStar.getId(), starredEntries.getEntries().getFirst().getId());
// Run cleanup with capacity of 0 (should delete all non-starred entries)
// With keepStarredEntries=true (default), only non-starred entries are counted for capacity.
// We have 2 entries, 1 starred and 1 non-starred. With capacity=0, the 1 non-starred entry exceeds capacity.
databaseCleaningService.cleanEntriesForFeedsExceedingCapacity(0);
// Verify starred entry is still present
Entries starredEntriesAfter = getCategoryEntries(CategoryREST.STARRED);
Assertions.assertEquals(1, starredEntriesAfter.getEntries().size());
Assertions.assertEquals(entryToStar.getId(), starredEntriesAfter.getEntries().getFirst().getId());
// Verify the non-starred entry was deleted (only starred entry should remain)
Entries entriesAfter = getFeedEntries(subscriptionId);
Assertions.assertEquals(1, entriesAfter.getEntries().size());
Assertions.assertEquals(entryToStar.getId(), entriesAfter.getEntries().getFirst().getId());
}
@Test
void starredEntriesAreKeptWhenCleaningOldEntries() {
// Subscribe to feed and wait for entries
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
// Verify we have 2 entries
Entries entriesBefore = getFeedEntries(subscriptionId);
Assertions.assertEquals(2, entriesBefore.getEntries().size());
// Star the first entry (oldest one based on published date in rss.xml)
Entry entryToStar = entriesBefore.getEntries().getFirst();
starEntry(entryToStar.getId(), subscriptionId);
// Verify the entry is starred
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
Assertions.assertEquals(1, starredEntries.getEntries().size());
// Run cleanup for entries older than now (should try to delete all entries)
// With keepStarredEntries=true (default), the starred entry should be preserved
Instant olderThan = Instant.now().plus(Duration.ofDays(1));
databaseCleaningService.cleanEntriesOlderThan(olderThan);
// Verify starred entry is still present
Entries starredEntriesAfter = getCategoryEntries(CategoryREST.STARRED);
Assertions.assertEquals(1, starredEntriesAfter.getEntries().size());
Assertions.assertEquals(entryToStar.getId(), starredEntriesAfter.getEntries().getFirst().getId());
// Verify the non-starred entry was deleted
Entries entriesAfter = getFeedEntries(subscriptionId);
Assertions.assertEquals(1, entriesAfter.getEntries().size());
Assertions.assertEquals(entryToStar.getId(), entriesAfter.getEntries().getFirst().getId());
}
@Test
void multipleStarredEntriesAreAllKept() {
// Subscribe to feed and wait for entries
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
// Verify we have 2 entries
Entries entriesBefore = getFeedEntries(subscriptionId);
Assertions.assertEquals(2, entriesBefore.getEntries().size());
// Star both entries
entriesBefore.getEntries().forEach(entry -> starEntry(entry.getId(), subscriptionId));
// Verify both entries are starred
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
Assertions.assertEquals(2, starredEntries.getEntries().size());
// Run cleanup with capacity of 0 (should delete all non-starred entries)
databaseCleaningService.cleanEntriesForFeedsExceedingCapacity(0);
// Verify both starred entries are still present
Entries starredEntriesAfter = getCategoryEntries(CategoryREST.STARRED);
Assertions.assertEquals(2, starredEntriesAfter.getEntries().size());
// Verify all entries are preserved (since all are starred)
Entries entriesAfter = getFeedEntries(subscriptionId);
Assertions.assertEquals(2, entriesAfter.getEntries().size());
}
@Test
void unstarringEntryMakesItEligibleForCleanup() {
// Subscribe to feed and wait for entries
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
// Star the first entry
Entries entriesBefore = getFeedEntries(subscriptionId);
Entry entry = entriesBefore.getEntries().getFirst();
starEntry(entry.getId(), subscriptionId);
// Verify entry is starred
Assertions.assertEquals(1, getCategoryEntries(CategoryREST.STARRED).getEntries().size());
// Unstar the entry
unstarEntry(entry.getId(), subscriptionId);
// Verify entry is no longer starred
Assertions.assertEquals(0, getCategoryEntries(CategoryREST.STARRED).getEntries().size());
// Run cleanup for entries older than now
Instant olderThan = Instant.now().plus(Duration.ofDays(1));
databaseCleaningService.cleanEntriesOlderThan(olderThan);
// Verify both entries were deleted (neither is starred)
Entries entriesAfter = getFeedEntries(subscriptionId);
Assertions.assertEquals(0, entriesAfter.getEntries().size());
}
}
}

View File

@@ -22,6 +22,7 @@ class ServerIT extends BaseIT {
Assertions.assertEquals(900000, serverInfos.getWebsocketPingInterval()); Assertions.assertEquals(900000, serverInfos.getWebsocketPingInterval());
Assertions.assertEquals(30000, serverInfos.getTreeReloadInterval()); Assertions.assertEquals(30000, serverInfos.getTreeReloadInterval());
Assertions.assertEquals(60000, serverInfos.getForceRefreshCooldownDuration()); Assertions.assertEquals(60000, serverInfos.getForceRefreshCooldownDuration());
Assertions.assertEquals(4, serverInfos.getMinimumPasswordLength());
} }
} }

View File

@@ -1,5 +1,7 @@
package com.commafeed.integration.rest; package com.commafeed.integration.rest;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import jakarta.inject.Inject; import jakarta.inject.Inject;
@@ -13,6 +15,7 @@ import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants; import com.commafeed.TestConstants;
import com.commafeed.frontend.model.Settings; import com.commafeed.frontend.model.Settings;
import com.commafeed.frontend.model.request.PasswordResetConfirmationRequest;
import com.commafeed.frontend.model.request.PasswordResetRequest; import com.commafeed.frontend.model.request.PasswordResetRequest;
import com.commafeed.integration.BaseIT; import com.commafeed.integration.BaseIT;
@@ -57,8 +60,32 @@ class UserIT extends BaseIT {
Element a = Jsoup.parse(message.getHtml()).select("a").getFirst(); Element a = Jsoup.parse(message.getHtml()).select("a").getFirst();
String link = a.attr("href"); String link = a.attr("href");
String newPasswordResponse = RestAssured.given().urlEncodingEnabled(false).get(link).then().statusCode(200).extract().asString();
Assertions.assertTrue(newPasswordResponse.contains("Your new password is:")); String email = null;
String token = null;
String queryString = link.substring(link.indexOf('?') + 1);
for (String param : queryString.split("&")) {
String[] keyValue = param.split("=");
if ("email".equals(keyValue[0])) {
email = URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8);
} else if ("token".equals(keyValue[0])) {
token = URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8);
}
}
Assertions.assertNotNull(email);
Assertions.assertNotNull(token);
Assertions.assertTrue(link.contains("#/passwordReset?"));
String newPassword = "MyNewPassword123!";
PasswordResetConfirmationRequest confirmReq = new PasswordResetConfirmationRequest();
confirmReq.setEmail(email);
confirmReq.setToken(token);
confirmReq.setPassword(newPassword);
RestAssured.given().body(confirmReq).contentType(ContentType.JSON).post("rest/user/passwordResetCallback").then().statusCode(200);
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, newPassword);
RestAssured.given().get("rest/user/settings").then().statusCode(200);
} }
@Test @Test

View File

@@ -1,2 +0,0 @@
# we generate the jacoco report ourselves by aggregating the unit tests and integration tests jacoco.exec files
quarkus.jacoco.report=false

View File

@@ -5,7 +5,7 @@
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId> <artifactId>commafeed</artifactId>
<version>6.0.0</version> <version>6.2.0</version>
<name>CommaFeed</name> <name>CommaFeed</name>
<packaging>pom</packaging> <packaging>pom</packaging>
@@ -19,7 +19,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version> <version>3.15.0</version>
<configuration> <configuration>
<parameters>true</parameters> <parameters>true</parameters>