Compare commits

...

208 Commits

Author SHA1 Message Date
Athou
53f0c33c1d release 5.11.0 2025-08-06 22:51:34 +02:00
Jérémie Panzer
563516901e Merge pull request #1866 from Athou/renovate/major-github-artifact-actions
chore(deps): update actions/download-artifact action to v5
2025-08-06 10:41:11 +02:00
renovate[bot]
73b40fd8b7 chore(deps): update actions/download-artifact action to v5 2025-08-06 03:42:33 +00:00
renovate[bot]
08224a8486 fix(deps): update mantine monorepo to ^8.2.3 2025-08-06 03:42:29 +00:00
Jérémie Panzer
993f3d3aa8 Merge pull request #1865 from Athou/renovate/lock-file-maintenance
chore(deps): lock file maintenance
2025-08-04 23:07:56 +02:00
renovate[bot]
3a975de136 chore(deps): lock file maintenance 2025-08-04 22:12:32 +02:00
renovate[bot]
48b5195798 chore(deps): update docker/login-action digest to 184bdaa 2025-08-04 16:28:14 +00:00
Jérémie Panzer
8eb34c7539 Merge pull request #1864 from Athou/renovate/linguijs-monorepo
fix(deps): update linguijs monorepo to ^5.4.0 (minor)
2025-08-01 17:41:33 +02:00
renovate[bot]
d75d7a9209 fix(deps): update linguijs monorepo to ^5.4.0 2025-08-01 14:49:38 +00:00
Jérémie Panzer
ddf851f1eb Merge pull request #1863 from Athou/renovate/node-22.x
chore(deps): update node.js to v22.18.0
2025-08-01 07:14:50 +02:00
renovate[bot]
889dd00c23 chore(deps): update node.js to v22.18.0 2025-08-01 03:24:10 +00:00
Jérémie Panzer
c5ea2a1aa1 Merge pull request #1862 from Athou/renovate/typescript-5.x
chore(deps): update dependency typescript to ^5.9.2
2025-08-01 00:00:50 +02:00
renovate[bot]
1489aff78e chore(deps): update dependency typescript to ^5.9.2 2025-07-31 21:37:49 +00:00
renovate[bot]
640296d42f chore(deps): update dependency npm to v11.5.2 2025-07-30 21:41:05 +00:00
renovate[bot]
3b12b2a5f6 fix(deps): update mantine monorepo to ^8.2.2 2025-07-30 17:02:31 +00:00
Jérémie Panzer
d5c41a5167 Merge pull request #1861 from Athou/renovate/quarkus.version
fix(deps): update quarkus.version to v3.25.0 (minor)
2025-07-30 14:11:33 +02:00
Athou
58bf86d25d remove warnings 2025-07-30 13:30:27 +02:00
renovate[bot]
d73034d6d9 fix(deps): update quarkus.version to v3.25.0 2025-07-30 10:46:35 +00:00
renovate[bot]
151a613dcc chore(deps): update github/codeql-action digest to 51f7732 2025-07-30 00:24:09 +00:00
renovate[bot]
4bb741a42f chore(deps): update dependency @biomejs/biome to v2.1.3 2025-07-29 14:31:22 +00:00
renovate[bot]
cc9c8d3db3 chore(deps): update dependency vite-plugin-checker to ^0.10.2 2025-07-29 13:47:55 +00:00
Athou
c3d4831550 fix sonar warnings 2025-07-29 15:28:52 +02:00
renovate[bot]
31e385fbfb chore(deps): update react monorepo 2025-07-29 10:41:04 +00:00
Athou
a8c47d717c remove unused google analytics support 2025-07-29 12:35:53 +02:00
Athou
9a25157d3f make sonar scan client files too 2025-07-29 10:28:22 +02:00
renovate[bot]
9176e0f7b7 fix(deps): update react monorepo to ^19.1.1 2025-07-28 20:24:38 +00:00
Athou
ff7aa890a6 fix build warning 2025-07-28 19:55:36 +02:00
Jérémie Panzer
03312c1592 Merge pull request #1855 from Athou/renovate/patch-testing-library-monorepo
chore(deps): update dependency @testing-library/jest-dom to ^6.6.4
2025-07-28 09:38:33 +02:00
renovate[bot]
9d1ec2c636 chore(deps): update dependency @testing-library/jest-dom to ^6.6.4 2025-07-28 06:35:05 +00:00
Athou
c49c31a44e let quarkus generate the documentation 2025-07-28 08:33:34 +02:00
renovate[bot]
947c1f562f chore(deps): lock file maintenance 2025-07-28 03:57:26 +00:00
Jérémie Panzer
2d1dbb6988 Merge pull request #1857 from aniol/patch-2
Update messages.po
2025-07-27 17:45:19 +02:00
Aniol
622e46ff67 Update messages.po 2025-07-27 11:46:17 +02:00
renovate[bot]
4ff45a65c3 chore(deps): update ibm-semeru-runtimes docker tag to open-21.0.8_9-jre 2025-07-25 22:54:42 +00:00
Jérémie Panzer
a62676061b Merge pull request #1853 from Athou/renovate/patch-react-router-monorepo
fix(deps): update dependency react-router-dom to ^7.7.1
2025-07-25 11:49:59 +02:00
renovate[bot]
11d77d2265 fix(deps): update dependency react-router-dom to ^7.7.1 2025-07-25 11:21:11 +02:00
Jérémie Panzer
1e7d44b250 Merge pull request #1852 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.5.1
2025-07-24 23:01:56 +02:00
renovate[bot]
ffd86c6d8c chore(deps): update dependency npm to v11.5.1 2025-07-24 20:10:26 +00:00
renovate[bot]
a566c9460d chore(deps): update dependency vite to ^7.0.6 2025-07-24 06:37:44 +00:00
renovate[bot]
24edae3d58 fix(deps): update quarkus.version to v3.24.5 2025-07-23 22:10:27 +00:00
renovate[bot]
97876344c4 chore(deps): update github/codeql-action digest to 4e828ff 2025-07-23 18:03:54 +00:00
Jérémie Panzer
95dbeb9a47 Merge pull request #1851 from Athou/renovate/axios-1.x
fix(deps): update dependency axios to ^1.11.0
2025-07-23 20:03:14 +02:00
renovate[bot]
3fc64859b1 fix(deps): update dependency axios to ^1.11.0 2025-07-23 12:47:09 +00:00
renovate[bot]
896fe3b5b2 chore(deps): update graalvm/setup-graalvm digest to 7f488cf 2025-07-23 12:46:47 +00:00
Jérémie Panzer
85404781a3 Merge pull request #1850 from Athou/renovate/debian-12.11
chore(deps): update debian:12.11 docker digest to b6507e3
2025-07-22 11:39:19 +02:00
renovate[bot]
efe2abc86e chore(deps): update debian:12.11 docker digest to b6507e3 2025-07-22 07:58:53 +00:00
renovate[bot]
b70b7a0b40 chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v2.46.1 2025-07-21 22:55:20 +00:00
Athou
865c80f87b add more integration tests 2025-07-21 20:21:20 +02:00
Athou
23a91aab12 providers can now return multiple urls 2025-07-21 16:57:10 +02:00
Athou
085a3cbb50 unwanted entries are already filtered at the dao level 2025-07-21 16:57:10 +02:00
Athou
fb9d875c31 extract image proxying from FeedUtils 2025-07-21 16:57:10 +02:00
Athou
5ee15c6f68 we no longer need to generate OPML 1.1 files 2025-07-21 16:57:10 +02:00
Athou
9853205849 remove workaround for #39 as it's not valid rdf 0.9 according to the spec 2025-07-21 16:57:10 +02:00
Athou
2c9ce7e8fc remove workaround for #140 that is no longer needed 2025-07-21 16:57:10 +02:00
Jérémie Panzer
9753ae60e2 Merge pull request #1848 from Athou/renovate/mantine-monorepo
fix(deps): update mantine monorepo to ^8.2.1 (minor)
2025-07-21 16:57:04 +02:00
renovate[bot]
bd66f1e682 fix(deps): update mantine monorepo to ^8.2.1 2025-07-21 14:20:33 +00:00
renovate[bot]
ed6a45c119 chore(deps): update dependency vite-plugin-checker to ^0.10.1 2025-07-21 14:20:25 +00:00
renovate[bot]
8f53ce27fc chore(deps): update github/codeql-action digest to d6bbdef 2025-07-21 12:47:08 +00:00
Athou
f7ae2e6689 add even more integration tests 2025-07-21 11:50:10 +02:00
Jérémie Panzer
c6cc47192c Merge pull request #1847 from Athou/renovate/com.diffplug.spotless-spotless-maven-plugin-2.x
chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v2.46.0
2025-07-21 07:39:56 +02:00
renovate[bot]
1c447fe369 chore(deps): lock file maintenance 2025-07-21 03:02:54 +00:00
renovate[bot]
6b5c92db48 chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v2.46.0 2025-07-20 22:32:09 +00:00
Athou
427e020d27 add category integration tests 2025-07-20 23:20:30 +02:00
Athou
18084995b2 fix integration tests code coverage not taken into account 2025-07-20 09:58:01 +02:00
Athou
f894fdf564 extract url utils from FeedUtils 2025-07-20 07:26:43 +02:00
Athou
0b0a964a90 enums are now supported by openapi, allowableValues is no longer required 2025-07-20 01:08:27 +02:00
Athou
d6df979d0d remove more warnings 2025-07-19 23:47:22 +02:00
renovate[bot]
c366c37afe fix(deps): update dependency tss-react to ^4.9.19 2025-07-19 10:06:55 +00:00
Jérémie Panzer
20cbd239b2 Merge pull request #1846 from Athou/alert-autofix-26
fix for code scanning alert no. 26: Workflow does not contain permissions
2025-07-19 12:06:00 +02:00
Jérémie Panzer
a9c7595ee7 Potential fix for code scanning alert no. 26: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-07-19 11:58:48 +02:00
Athou
3f09e3ca64 remove warnings 2025-07-19 11:33:38 +02:00
Jérémie Panzer
ed42db7a0d Merge pull request #1845 from Athou/renovate/pin-dependencies
chore(deps): pin actions/setup-java action to c5195ef
2025-07-19 11:14:58 +02:00
renovate[bot]
c85daeb46e chore(deps): pin actions/setup-java action to c5195ef 2025-07-19 09:13:57 +00:00
Athou
3f2b93f1f8 run sonarqube as part of the CI 2025-07-19 11:13:10 +02:00
Athou
78d2e66c56 generate code coverage 2025-07-19 09:19:14 +02:00
Athou
0f2de651ff remove more warnings 2025-07-18 23:07:40 +02:00
Jérémie Panzer
2eb7c7237e Merge pull request #1844 from Athou/sonar-warnings
fix sonar warnings
2025-07-18 22:06:04 +02:00
Athou
3b8f62ff11 fix sonar warnings 2025-07-18 21:49:00 +02:00
Athou
f8bf9370de update SECURITY.md 2025-07-18 09:29:31 +02:00
Jérémie Panzer
30cd0ec089 Merge pull request #1843 from Athou/renovate/vitejs-plugin-react-4.x
chore(deps): update dependency @vitejs/plugin-react to ^4.7.0
2025-07-18 09:04:59 +02:00
Athou
e984be9289 update actions 2025-07-18 08:56:35 +02:00
Jérémie Panzer
8069787754 Merge pull request #1842 from Athou/renovate/pin-dependencies
chore(deps): pin github/codeql-action action to 181d5ee
2025-07-18 08:54:15 +02:00
renovate[bot]
343e442dff chore(deps): update dependency @vitejs/plugin-react to ^4.7.0 2025-07-18 06:35:48 +00:00
renovate[bot]
313ccdeae9 chore(deps): pin github/codeql-action action to 181d5ee 2025-07-18 06:35:21 +00:00
Jérémie Panzer
fdec8ebfd3 Create scorecard.yml 2025-07-18 08:34:40 +02:00
renovate[bot]
efddd86263 fix(deps): update quarkus.version to v3.24.4 2025-07-17 19:53:26 +00:00
renovate[bot]
7d18bde40b chore(deps): update dependency @biomejs/biome to v2.1.2 2025-07-17 15:58:18 +00:00
renovate[bot]
7fee410be4 chore(deps): update dependency vite to ^7.0.5 2025-07-17 05:32:53 +00:00
Jérémie Panzer
ebc2516a53 Merge pull request #1841 from Athou/renovate/react-router-monorepo
fix(deps): update dependency react-router-dom to ^7.7.0
2025-07-17 07:31:48 +02:00
renovate[bot]
ade4d1d782 fix(deps): update dependency react-router-dom to ^7.7.0 2025-07-16 20:58:30 +00:00
renovate[bot]
07f7a288d2 chore(deps): update ibm-semeru-runtimes:open-21.0.7_6-jre docker digest to a51c2a5 2025-07-16 16:43:24 +00:00
Jérémie Panzer
380ed16caf Merge pull request #1840 from Athou/renovate/node-22.17.x
chore(deps): update node.js to v22.17.1
2025-07-16 14:05:42 +02:00
renovate[bot]
db654a10d1 chore(deps): update node.js to v22.17.1 2025-07-16 06:49:48 +00:00
renovate[bot]
2cf84d35cd chore(deps): update dependency maven to v3.9.11 2025-07-16 05:03:29 +00:00
renovate[bot]
a4eac86913 chore(deps): update ibm-semeru-runtimes:open-21.0.7_6-jre docker digest to 7b6e1ee 2025-07-16 03:04:56 +00:00
renovate[bot]
5168be45a8 chore(deps): lock file maintenance 2025-07-14 00:41:04 +00:00
Athou
163ab43da3 lingui documentation mentions that their plugin should be declared before the react compiler plugin 2025-07-11 16:56:06 +02:00
Jérémie Panzer
e5fa517270 Merge pull request #1830 from Athou/renovate/vite-7.x
chore(deps): update dependency vite to v7
2025-07-11 16:15:48 +02:00
renovate[bot]
b8b8ea5ce2 chore(deps): update dependency vite to v7 2025-07-11 13:50:21 +00:00
renovate[bot]
991b147af5 fix(deps): update linguijs monorepo to ^5.3.3 2025-07-11 13:25:53 +00:00
renovate[bot]
ecff62d0fa fix(deps): update quarkus.version to v3.24.3 2025-07-09 17:05:02 +00:00
renovate[bot]
cdec4c0879 chore(deps): update dependency @biomejs/biome to v2.1.1 2025-07-08 17:03:55 +00:00
Jérémie Panzer
e8085ac4cf Merge pull request #1838 from Athou/renovate/biomejs-biome-2.x
chore(deps): update dependency @biomejs/biome to v2.1.0
2025-07-08 14:00:21 +02:00
renovate[bot]
327062112b chore(deps): update dependency @biomejs/biome to v2.1.0 2025-07-08 11:36:19 +00:00
Jérémie Panzer
6dfc23c33a Merge pull request #1837 from Athou/renovate/com.diffplug.spotless-spotless-maven-plugin-2.x
chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v2.45.0
2025-07-08 07:16:20 +02:00
renovate[bot]
a601b0ab35 chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v2.45.0 2025-07-08 03:07:19 +00:00
Jérémie Panzer
a48c8ca87a Merge pull request #1836 from Athou/renovate/vite-plugin-checker-0.x
chore(deps): update dependency vite-plugin-checker to ^0.10.0
2025-07-07 22:25:17 +02:00
renovate[bot]
b59e64a3d1 chore(deps): update dependency vite-plugin-checker to ^0.10.0 2025-07-07 16:57:19 +00:00
renovate[bot]
5fc62dd06d fix(deps): update mantine monorepo to ^8.1.3 2025-07-07 16:57:10 +00:00
renovate[bot]
d81a0cae91 chore(deps): lock file maintenance 2025-07-07 01:52:36 +00:00
Jérémie Panzer
50e31c6b69 Merge pull request #1834 from Athou/renovate/patch-quarkus.version
fix(deps): update quarkus.version to v3.24.2 (patch)
2025-07-02 22:36:15 +02:00
Jérémie Panzer
92d3d88127 Merge pull request #1833 from Athou/renovate/ibm-semeru-runtimes-open-21.0.7_6-jre
chore(deps): update ibm-semeru-runtimes:open-21.0.7_6-jre docker digest to 17a67d7
2025-07-02 22:36:09 +02:00
renovate[bot]
517fdb2095 fix(deps): update quarkus.version to v3.24.2 2025-07-02 11:42:24 +00:00
renovate[bot]
d16ebb02b4 chore(deps): update ibm-semeru-runtimes:open-21.0.7_6-jre docker digest to 17a67d7 2025-07-02 11:42:21 +00:00
renovate[bot]
a5c64c8b7b chore(deps): update debian:12.11 docker digest to d42b86d 2025-07-01 08:39:24 +00:00
renovate[bot]
5287a93484 chore(deps): update dependency io.quarkiverse.playwright:quarkus-playwright to v2.1.3 2025-06-30 15:45:38 +00:00
renovate[bot]
06e84d9032 chore(deps): lock file maintenance 2025-06-30 06:50:33 +00:00
Jérémie Panzer
3a63dd032a Merge pull request #1832 from Athou/renovate/io.quarkiverse.playwright-quarkus-playwright-2.1.x
chore(deps): update dependency io.quarkiverse.playwright:quarkus-playwright to v2.1.2
2025-06-30 08:48:51 +02:00
renovate[bot]
889e227523 chore(deps): update dependency io.quarkiverse.playwright:quarkus-playwright to v2.1.2 2025-06-30 06:12:02 +00:00
Athou
57d895daf5 fix playwright tests 2025-06-30 08:08:01 +02:00
renovate[bot]
a744394faa chore(deps): update dependency com.puppycrawl.tools:checkstyle to v10.26.1 2025-06-30 01:01:56 +00:00
renovate[bot]
64e3c25bad chore(deps): update ncipollo/release-action digest to bcfe547 2025-06-29 21:05:27 +00:00
renovate[bot]
75f85e1fb2 chore(deps): update ncipollo/release-action digest to 9128f23 2025-06-29 02:27:25 +00:00
renovate[bot]
00bd4cab37 fix(deps): update dependency react-router-dom to ^7.6.3 2025-06-27 22:26:32 +00:00
Athou
a9527f59a9 add @/ prefix for absolute imports 2025-06-27 16:29:31 +02:00
renovate[bot]
77661930f0 chore(deps): update dependency @biomejs/biome to v2.0.6 2025-06-27 13:14:09 +00:00
Athou
80a09bd9a0 use quarkus-playwright to simplify tests 2025-06-26 21:30:29 +02:00
Jérémie Panzer
6eb7cfbdc2 Merge pull request #1831 from Athou/renovate/react-draggable-4.x
fix(deps): update dependency react-draggable to ^4.5.0
2025-06-26 06:11:08 +02:00
renovate[bot]
fb186530aa fix(deps): update dependency react-draggable to ^4.5.0 2025-06-25 20:36:05 +00:00
renovate[bot]
6c121ccb90 fix(deps): update mantine monorepo to ^8.1.2 2025-06-25 17:40:13 +00:00
renovate[bot]
c08ad3b365 chore(deps): update graalvm/setup-graalvm digest to e1df20a 2025-06-25 17:00:47 +00:00
Jérémie Panzer
1668bc88ad Merge pull request #1828 from Athou/renovate/node-22.x
chore(deps): update node.js to v22.17.0
2025-06-25 19:00:06 +02:00
Jérémie Panzer
3a43f62460 Merge pull request #1829 from Athou/renovate/quarkus.version
fix(deps): update quarkus.version to v3.24.1 (minor)
2025-06-25 18:44:04 +02:00
renovate[bot]
bfba5179d1 fix(deps): update quarkus.version to v3.24.1 2025-06-25 15:15:03 +00:00
renovate[bot]
78bf7856dc chore(deps): update node.js to v22.17.0 2025-06-25 15:14:58 +00:00
Jérémie Panzer
e0c708f677 Merge pull request #1827 from Athou/renovate/com.puppycrawl.tools-checkstyle-10.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v10.26.0
2025-06-25 17:13:36 +02:00
renovate[bot]
794d6824e8 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v10.26.0 2025-06-25 12:44:58 +00:00
renovate[bot]
15573a7bee chore(deps): update dependency @biomejs/biome to v2.0.5 2025-06-23 16:46:39 +00:00
Jérémie Panzer
31c61a79c6 Merge pull request #1824 from Athou/renovate/org.jsoup-jsoup-1.x
fix(deps): update dependency org.jsoup:jsoup to v1.21.1
2025-06-23 08:18:44 +02:00
Jérémie Panzer
87ca427094 Merge pull request #1823 from Athou/renovate/vitejs-plugin-react-4.x
chore(deps): update dependency @vitejs/plugin-react to ^4.6.0
2025-06-23 08:18:30 +02:00
renovate[bot]
99bdc904e0 fix(deps): update dependency org.jsoup:jsoup to v1.21.1 2025-06-23 05:28:26 +00:00
renovate[bot]
2fdee68feb chore(deps): update dependency @vitejs/plugin-react to ^4.6.0 2025-06-23 05:28:23 +00:00
renovate[bot]
7be014f83e chore(deps): lock file maintenance 2025-06-23 02:43:32 +00:00
renovate[bot]
5668fe0a33 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v10.25.1 2025-06-21 16:33:29 +00:00
Jérémie Panzer
32c07efe19 Merge pull request #1821 from Athou/renovate/biomejs-biome-2.0.x
chore(deps): update dependency @biomejs/biome to v2.0.4
2025-06-21 14:03:28 +02:00
renovate[bot]
21b23d0f79 chore(deps): update dependency @biomejs/biome to v2.0.4 2025-06-21 10:03:08 +00:00
Jérémie Panzer
793d0dd13f Merge pull request #1822 from c-cesar/patch-1
Update pt translations
2025-06-21 09:53:12 +02:00
ccesar
14e8ff4c1b Update pt translations
Some fixes and translated the remaining strings.
2025-06-20 23:51:38 -03:00
renovate[bot]
416ab06997 fix(deps): update swagger.version to v2.2.34 2025-06-20 12:09:56 +00:00
renovate[bot]
493cd60dae fix(deps): update dependency io.dropwizard.metrics:metrics-json to v4.2.33 2025-06-20 00:44:19 +00:00
Jérémie Panzer
e0948e1e9e Merge pull request #1819 from Athou/renovate/patch-quarkus.version
fix(deps): update quarkus.version to v3.23.4 (patch)
2025-06-20 00:21:57 +02:00
renovate[bot]
5776b8c044 fix(deps): update quarkus.version to v3.23.4 2025-06-19 16:55:06 +00:00
Athou
38ab4105d8 noUnusedImports and noUnusedVariables are now enabled by default 2025-06-18 20:46:56 +02:00
renovate[bot]
5ed9dadcc2 chore(deps): update docker/setup-buildx-action digest to e468171 2025-06-18 14:10:38 +00:00
renovate[bot]
357d7e2381 chore(deps): update ibm-semeru-runtimes:open-21.0.7_6-jre docker digest to c5b834e 2025-06-17 23:51:09 +00:00
renovate[bot]
8cfaab3e9f chore(deps): update dependency vitest to ^3.2.4 2025-06-17 18:15:47 +00:00
Athou
fef2404357 update biome to v2 2025-06-17 20:14:38 +02:00
renovate[bot]
1aa1bce8c8 fix(deps): update mantine monorepo to ^8.1.1 2025-06-16 22:14:42 +00:00
renovate[bot]
124b2761f6 chore(deps): update docker/setup-buildx-action digest to 18ce135 2025-06-16 17:11:47 +00:00
renovate[bot]
066ca1af7c chore(deps): lock file maintenance 2025-06-16 02:29:40 +00:00
Jérémie Panzer
c20520879b Merge pull request #1817 from Athou/renovate/axios-1.x
fix(deps): update dependency axios to ^1.10.0
2025-06-14 17:35:48 +02:00
renovate[bot]
4fa5b2b856 fix(deps): update dependency axios to ^1.10.0 2025-06-14 14:05:49 +00:00
renovate[bot]
5c1b1fad76 fix(deps): update swagger.version to v2.2.33 2025-06-14 09:46:23 +00:00
renovate[bot]
c18d248c06 chore(deps): update dependency npm to v11.4.2 2025-06-14 07:26:37 +00:00
Athou
d46ee7f673 remove deprecated usage of saveOrUpdate in preparation of hibernate 7 2025-06-13 14:49:22 +02:00
renovate[bot]
f2c0d99bd9 fix(deps): update quarkus.version to v3.23.3 2025-06-12 00:04:22 +00:00
renovate[bot]
60ee0b9185 chore(deps): update dependency @types/react to ^19.1.8 2025-06-11 17:51:58 +00:00
renovate[bot]
4b3e660ae7 chore(deps): update debian:12.11 docker digest to 0d8498a 2025-06-11 03:06:07 +00:00
renovate[bot]
0b42392bfc chore(deps): update dependency vitest to ^3.2.3 2025-06-10 07:21:05 +00:00
renovate[bot]
a94d7ce235 chore(deps): update dependency @vitejs/plugin-react to ^4.5.2 2025-06-10 06:24:42 +00:00
Jérémie Panzer
72aec432ed Merge pull request #1814 from WangLei1993/master
add Chinese translation for new entry
2025-06-10 08:23:18 +02:00
WangLei1993
0e5db8d604 add Chinese translation for new entry 2025-06-10 13:34:56 +08:00
Jérémie Panzer
dc45fb4b84 Merge pull request #1812 from Athou/renovate/mantine-monorepo
fix(deps): update mantine monorepo to ^8.1.0 (minor)
2025-06-10 07:06:50 +02:00
Jérémie Panzer
6503d38fe3 Merge pull request #1813 from canoine/patch-5
Update fr/messages.po
2025-06-10 07:06:35 +02:00
canoine
32c89d9a11 Update fr/messages.po
Translation of the new messages.
2025-06-10 06:04:09 +02:00
renovate[bot]
f279465750 fix(deps): update mantine monorepo to ^8.1.0 2025-06-09 21:31:32 +00:00
renovate[bot]
58ec1b022a chore(deps): update dependency @types/react to ^19.1.7 2025-06-09 21:31:12 +00:00
Athou
612199429e fix custom css code documentation link (#1811) 2025-06-09 22:36:17 +02:00
Jérémie Panzer
e5482f9051 Merge pull request #1810 from Athou/renovate/major-querydsl.version
fix(deps): update querydsl.version to v7 (major)
2025-06-09 18:31:14 +02:00
renovate[bot]
05df14fda2 fix(deps): update querydsl.version to v7 2025-06-09 15:52:21 +00:00
renovate[bot]
29898ba1ba fix(deps): update dependency @fontsource/open-sans to ^5.2.6 2025-06-09 08:55:10 +00:00
renovate[bot]
93d1cec503 chore(deps): update dependency rollup-plugin-visualizer to ^6.0.3 2025-06-07 14:00:43 +00:00
renovate[bot]
9884f44122 fix(deps): update dependency style-to-object to ^1.0.9 2025-06-07 02:54:31 +00:00
renovate[bot]
d400456685 chore(deps): update dependency vitest to ^3.2.2 2025-06-06 23:49:01 +00:00
renovate[bot]
c39069cafd chore(deps): update dependency maven to v3.9.10 2025-06-06 12:01:14 +00:00
Jérémie Panzer
5fb0edc318 Merge pull request #1808 from Athou/renovate/patch-quarkus.version
fix(deps): update quarkus.version to v3.23.2 (patch)
2025-06-05 07:06:42 +02:00
renovate[bot]
21a6b2d780 fix(deps): update quarkus.version to v3.23.2 2025-06-04 22:32:20 +00:00
renovate[bot]
40c9063a54 chore(deps): update dependency @types/react-dom to ^19.1.6 2025-06-04 15:57:40 +00:00
Athou
59b0103ed5 add an option to navigate to the next unread category/feed when marking all as read (#1807) 2025-06-04 09:10:03 +02:00
Athou
f4730e9338 redirect to 'all' if no unread categories or feeds found or if we reached the end of the list (#1807) 2025-06-04 08:40:22 +02:00
Athou
b7b520ca3c faster integration tests execution by truncating tables instead of dropping and recreating tables 2025-06-04 08:35:03 +02:00
renovate[bot]
21d44e6a55 fix(deps): update dependency react-router-dom to ^7.6.2 2025-06-04 00:47:19 +00:00
renovate[bot]
607886f0f0 chore(deps): update dependency vitest to ^3.2.1 2025-06-03 18:19:26 +00:00
Jérémie Panzer
7cd3c68256 Merge pull request #1806 from Athou/renovate/vitejs-plugin-react-4.5.x
chore(deps): update dependency @vitejs/plugin-react to ^4.5.1
2025-06-03 12:02:04 +02:00
renovate[bot]
6e37c1bd86 chore(deps): update dependency @vitejs/plugin-react to ^4.5.1 2025-06-03 06:04:05 +00:00
Jérémie Panzer
5db1a0748f Merge pull request #1805 from Athou/renovate/vitest-monorepo
chore(deps): update dependency vitest to ^3.2.0
2025-06-02 20:20:24 +02:00
renovate[bot]
a7584df4f4 chore(deps): update dependency vitest to ^3.2.0 2025-06-02 13:57:05 +00:00
Athou
4421197403 remove unused variable 2025-06-02 07:42:19 +02:00
renovate[bot]
15b59467fb chore(deps): lock file maintenance 2025-06-02 01:17:56 +00:00
Jérémie Panzer
c95ff0a2ce Merge pull request #1804 from Athou/renovate/com.puppycrawl.tools-checkstyle-10.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v10.25.0
2025-05-31 20:48:46 +02:00
renovate[bot]
7eff9df025 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v10.25.0 2025-05-31 18:22:52 +00:00
Athou
2f05e53e14 remove feed/refresh rest endpoint as it's unused and does not honor the force-refresh-cooldown-duration setting (#1802) 2025-05-30 11:19:26 +02:00
renovate[bot]
6089fe4036 fix(deps): update linguijs monorepo to ^5.3.2 2025-05-28 14:38:20 +00:00
renovate[bot]
10d9af0d86 chore(deps): update dependency rollup-plugin-visualizer to ^6.0.1 2025-05-28 13:12:40 +00:00
Jérémie Panzer
c119d5062a Merge pull request #1800 from Athou/renovate/quarkus.version
fix(deps): update quarkus.version to v3.23.0 (minor)
2025-05-28 15:11:46 +02:00
renovate[bot]
324609ee60 fix(deps): update quarkus.version to v3.23.0 2025-05-28 11:39:17 +00:00
renovate[bot]
a0a65f2b45 chore(deps): update dependency org.codehaus.mojo:exec-maven-plugin to v3.5.1 2025-05-28 07:28:55 +00:00
renovate[bot]
45e5ca704c chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v2.44.5 2025-05-28 05:10:12 +00:00
257 changed files with 5458 additions and 4242 deletions

View File

@@ -29,7 +29,7 @@ jobs:
# Setup
- name: Set up GraalVM
uses: graalvm/setup-graalvm@01ed653ac833fe80569f1ef9f25585ba2811baab # v1
uses: graalvm/setup-graalvm@7f488cf82a3629ee755e4e97342c01d6bed318fa # v1
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: "graalvm"
@@ -107,14 +107,14 @@ jobs:
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
- name: Install required packages
run: sudo apt-get install -y rename unzip
# Prepare artifacts
- name: Download artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
with:
pattern: commafeed-${{ matrix.database }}-*
path: ./artifacts
@@ -135,7 +135,7 @@ jobs:
# Docker
- name: Login to Container Registry
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3
if: ${{ env.DOCKERHUB_USERNAME != '' }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -220,7 +220,7 @@ jobs:
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
with:
pattern: commafeed-*
path: ./artifacts
@@ -236,7 +236,7 @@ jobs:
version: ${{ github.ref_name }}
- name: Create GitHub release
uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1
uses: ncipollo/release-action@bcfe5470707e8832e12347755757cec0eb3c22af # v1
with:
name: CommaFeed ${{ github.ref_name }}
body: ${{ steps.changelog_reader.outputs.changes }}

78
.github/workflows/scorecard.yml vendored Normal file
View File

@@ -0,0 +1,78 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '42 13 * * 4'
push:
branches: [ "master" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
# `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
# file_mode: git
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3
with:
sarif_file: results.sarif

41
.github/workflows/sonar.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: SonarQube
permissions:
contents: read
on:
push:
branches:
- master
pull_request:
types: [ opened, synchronize, reopened ]
env:
JAVA_VERSION: 21
jobs:
build:
runs-on: ubuntu-latest
steps:
# Checkout
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
# Setup
- name: Set up JDK
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: "temurin"
cache: "maven"
- name: Install Playwright dependencies
run: sudo apt-get install -y libgbm1
# Run test coverage and SonarQube analysis
- name: Analyze with SonarQube
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn --batch-mode verify sonar:sonar -Dsonar.projectKey=Athou_commafeed

View File

@@ -15,4 +15,4 @@
# specific language governing permissions and limitations
# under the License.
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip

View File

@@ -1,5 +1,10 @@
# Changelog
## [5.11.0]
- Add an option to navigate to the next unread category/feed when marking all entries as read (#1807)
- Google Analytics support has been removed
## [5.10.0]
- Add an indicator next to each feed's unread count in the tree to show when new entries are discovered while the app is open (#1762)

View File

@@ -2,5 +2,4 @@
## Reporting a Vulnerability
If you found a vulnerability that you deem too sensitive to disclose publicly in a Github issue, please send an email at jeremiepanzer at gmail dot com.
Thanks !
If you found a vulnerability that you deem too sensitive to disclose publicly in a Github issue, please create a private security advisory here: https://github.com/Athou/commafeed/security/advisories

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"$schema": "https://biomejs.dev/schemas/2.1.3/schema.json",
"formatter": {
"indentStyle": "space",
"indentWidth": 4,
@@ -13,15 +13,7 @@
"arrowParentheses": "asNeeded"
}
},
"linter": {
"rules": {
"correctness": {
"noUnusedImports": "error",
"noUnusedVariables": "error"
}
}
},
"files": {
"ignore": ["dist", "node_modules", "target", "target-ide"]
"includes": ["**", "!**/dist", "!**/node_modules", "!**/target", "!**/target-ide"]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,70 +16,68 @@
},
"dependencies": {
"@emotion/react": "^11.14.0",
"@fontsource/open-sans": "^5.2.5",
"@lingui/core": "^5.3.1",
"@lingui/react": "^5.3.1",
"@mantine/core": "^8.0.2",
"@mantine/form": "^8.0.2",
"@mantine/hooks": "^8.0.2",
"@mantine/modals": "^8.0.2",
"@mantine/notifications": "^8.0.2",
"@mantine/spotlight": "^8.0.2",
"@fontsource/open-sans": "^5.2.6",
"@lingui/core": "^5.4.0",
"@lingui/react": "^5.4.0",
"@mantine/core": "^8.2.3",
"@mantine/form": "^8.2.3",
"@mantine/hooks": "^8.2.3",
"@mantine/modals": "^8.2.3",
"@mantine/notifications": "^8.2.3",
"@mantine/spotlight": "^8.2.3",
"@monaco-editor/react": "^4.7.0",
"@reduxjs/toolkit": "^2.8.2",
"axios": "^1.9.0",
"axios": "^1.11.0",
"dayjs": "^1.11.13",
"escape-string-regexp": "^5.0.0",
"interweave": "^13.1.1",
"monaco-editor": "^0.52.2",
"mousetrap": "^1.6.5",
"react": "^19.1.0",
"react": "^19.1.1",
"react-async-hook": "^4.0.0",
"react-contexify": "^6.0.0",
"react-device-detect": "^2.2.3",
"react-dom": "^19.1.0",
"react-draggable": "^4.4.6",
"react-ga4": "^2.1.0",
"react-dom": "^19.1.1",
"react-draggable": "^4.5.0",
"react-icons": "^5.5.0",
"react-infinite-scroller": "^1.2.6",
"react-redux": "^9.2.0",
"react-router-dom": "^7.6.1",
"react-router-dom": "^7.7.1",
"react-swipeable": "^7.0.2",
"redoc": "^2.5.0",
"style-to-object": "^1.0.8",
"style-to-object": "^1.0.9",
"throttle-debounce": "^5.0.2",
"tinycon": "^0.6.8",
"tss-react": "^4.9.18",
"tss-react": "^4.9.19",
"websocket-heartbeat-js": "^1.1.3"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@lingui/babel-plugin-lingui-macro": "^5.3.1",
"@lingui/cli": "^5.3.1",
"@lingui/vite-plugin": "^5.3.1",
"@testing-library/jest-dom": "^6.6.3",
"@biomejs/biome": "^2.1.3",
"@lingui/babel-plugin-lingui-macro": "^5.4.0",
"@lingui/cli": "^5.4.0",
"@lingui/vite-plugin": "^5.4.0",
"@testing-library/jest-dom": "^6.6.4",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/mousetrap": "^1.6.15",
"@types/react": "^19.1.6",
"@types/react-dom": "^19.1.5",
"@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7",
"@types/react-infinite-scroller": "^1.2.5",
"@types/throttle-debounce": "^5.0.2",
"@types/tinycon": "^0.6.7",
"@vitejs/plugin-react": "^4.5.0",
"@vitejs/plugin-react": "^4.7.0",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-react-compiler": "^19.1.0-rc.2",
"jsdom": "^26.1.0",
"rollup-plugin-visualizer": "^6.0.0",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"vite-plugin-checker": "^0.9.3",
"rollup-plugin-visualizer": "^6.0.3",
"typescript": "^5.9.2",
"vite": "^7.0.6",
"vite-plugin-checker": "^0.10.2",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.1.4"
"vitest": "^3.2.4"
},
"overrides": {
"react-infinite-scroller": {
"react": "^19.1.0"
"react": "^19.1.1"
}
}
}

View File

@@ -6,16 +6,19 @@
<parent>
<groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId>
<version>5.10.0</version>
<version>5.11.0</version>
</parent>
<artifactId>commafeed-client</artifactId>
<name>CommaFeed Client</name>
<properties>
<sonar.sources>package.json,src</sonar.sources>
<sonar.coverage.exclusions>**/*</sonar.coverage.exclusions>
<!-- renovate: datasource=node-version depName=node -->
<node.version>v22.16.0</node.version>
<node.version>v22.18.0</node.version>
<!-- renovate: datasource=npm depName=npm -->
<npm.version>11.4.1</npm.version>
<npm.version>11.5.2</npm.version>
</properties>
<build>

View File

@@ -3,40 +3,44 @@ import { I18nProvider } from "@lingui/react"
import { MantineProvider } from "@mantine/core"
import { ModalsProvider } from "@mantine/modals"
import { Notifications } from "@mantine/notifications"
import { Constants } from "app/constants"
import { redirectTo } from "app/redirect/slice"
import { reloadServerInfos } from "app/server/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { categoryUnreadCount } from "app/utils"
import { DisablePullToRefresh } from "components/DisablePullToRefresh"
import { ErrorBoundary } from "components/ErrorBoundary"
import { Header } from "components/header/Header"
import { Tree } from "components/sidebar/Tree"
import { useAppLoading } from "hooks/useAppLoading"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useI18n } from "i18n"
import { WelcomePage } from "pages/WelcomePage"
import { AdminUsersPage } from "pages/admin/AdminUsersPage"
import { MetricsPage } from "pages/admin/MetricsPage"
import { AboutPage } from "pages/app/AboutPage"
import { AddPage } from "pages/app/AddPage"
import { CategoryDetailsPage } from "pages/app/CategoryDetailsPage"
import { DonatePage } from "pages/app/DonatePage"
import { FeedDetailsPage } from "pages/app/FeedDetailsPage"
import { FeedEntriesPage } from "pages/app/FeedEntriesPage"
import Layout from "pages/app/Layout"
import { SettingsPage } from "pages/app/SettingsPage"
import { TagDetailsPage } from "pages/app/TagDetailsPage"
import { LoginPage } from "pages/auth/LoginPage"
import { PasswordRecoveryPage } from "pages/auth/PasswordRecoveryPage"
import { RegistrationPage } from "pages/auth/RegistrationPage"
import React, { useEffect, useState } from "react"
import type React from "react"
import { useEffect, useState } from "react"
import { isSafari } from "react-device-detect"
import ReactGA from "react-ga4"
import { HashRouter, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom"
import { HashRouter, Navigate, Route, Routes, useNavigate } from "react-router-dom"
import Tinycon from "tinycon"
import { Constants } from "@/app/constants"
import { redirectTo } from "@/app/redirect/slice"
import { reloadServerInfos } from "@/app/server/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { categoryUnreadCount } from "@/app/utils"
import { DisablePullToRefresh } from "@/components/DisablePullToRefresh"
import { ErrorBoundary } from "@/components/ErrorBoundary"
import { Header } from "@/components/header/Header"
import { Tree } from "@/components/sidebar/Tree"
import { useAppLoading } from "@/hooks/useAppLoading"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useI18n } from "@/i18n"
import { AdminUsersPage } from "@/pages/admin/AdminUsersPage"
import { MetricsPage } from "@/pages/admin/MetricsPage"
import { AboutPage } from "@/pages/app/AboutPage"
import { AddPage } from "@/pages/app/AddPage"
import { CategoryDetailsPage } from "@/pages/app/CategoryDetailsPage"
import { DonatePage } from "@/pages/app/DonatePage"
import { FeedDetailsPage } from "@/pages/app/FeedDetailsPage"
import { FeedEntriesPage } from "@/pages/app/FeedEntriesPage"
import Layout from "@/pages/app/Layout"
import { SettingsPage } from "@/pages/app/SettingsPage"
import { TagDetailsPage } from "@/pages/app/TagDetailsPage"
import { LoginPage } from "@/pages/auth/LoginPage"
import { PasswordRecoveryPage } from "@/pages/auth/PasswordRecoveryPage"
import { RegistrationPage } from "@/pages/auth/RegistrationPage"
import { WelcomePage } from "@/pages/WelcomePage"
function Providers(props: { children: React.ReactNode }) {
function Providers(
props: Readonly<{
children: React.ReactNode
}>
) {
const primaryColor = useAppSelector(state => state.user.settings?.primaryColor) || Constants.theme.defaultPrimaryColor
return (
<I18nProvider i18n={i18n}>
@@ -72,9 +76,6 @@ function Providers(props: { children: React.ReactNode }) {
)
}
// api documentation page is very large, load only on-demand
const ApiDocumentationPage = React.lazy(async () => await import("pages/app/ApiDocumentationPage"))
function AppRoutes() {
const sidebarVisible = useAppSelector(state => state.tree.sidebarVisible)
@@ -85,7 +86,6 @@ function AppRoutes() {
<Route path="login" element={<LoginPage />} />
<Route path="register" element={<RegistrationPage />} />
<Route path="passwordRecovery" element={<PasswordRecoveryPage />} />
<Route path="api" element={<ApiDocumentationPage />} />
<Route path="app" element={<Layout header={<Header />} sidebar={<Tree />} sidebarVisible={sidebarVisible} />}>
<Route path="category">
<Route path=":id" element={<FeedEntriesPage sourceType="category" />} />
@@ -128,22 +128,11 @@ function RedirectHandler() {
return null
}
function GoogleAnalyticsHandler() {
const location = useLocation()
const googleAnalyticsCode = useAppSelector(state => state.server.serverInfos?.googleAnalyticsCode)
useEffect(() => {
if (googleAnalyticsCode) ReactGA.initialize(googleAnalyticsCode)
}, [googleAnalyticsCode])
useEffect(() => {
if (ReactGA.isInitialized) ReactGA.send({ hitType: "pageview", page: location.pathname })
}, [location])
return null
}
function UnreadCountTitleHandler({ enabled }: { enabled?: boolean }) {
function UnreadCountTitleHandler({
enabled,
}: Readonly<{
enabled?: boolean
}>) {
const root = useAppSelector(state => state.tree.rootCategory)
const unreadCount = categoryUnreadCount(root)
return <title>{enabled && unreadCount > 0 ? `(${unreadCount}) CommaFeed` : "CommaFeed"}</title>
@@ -219,25 +208,22 @@ export function App() {
return (
<Providers>
<>
<UnreadCountTitleHandler enabled={unreadCountTitle} />
<UnreadCountFaviconHandler enabled={unreadCountFavicon} />
<BrowserExtensionBadgeUnreadCountHandler />
<CustomJsHandler />
<CustomCssHandler />
<UnreadCountTitleHandler enabled={unreadCountTitle} />
<UnreadCountFaviconHandler enabled={unreadCountFavicon} />
<BrowserExtensionBadgeUnreadCountHandler />
<CustomJsHandler />
<CustomCssHandler />
{/* disable pull-to-refresh as it messes with vertical scrolling
{/* disable pull-to-refresh as it messes with vertical scrolling
safari behaves weirdly when overscroll-behavior is set to none so we disable it only for other browsers
https://github.com/Athou/commafeed/issues/1168
*/}
{!isSafari && <DisablePullToRefresh />}
{!isSafari && <DisablePullToRefresh />}
<HashRouter>
<GoogleAnalyticsHandler />
<RedirectHandler />
<AppRoutes />
</HashRouter>
</>
<HashRouter>
<RedirectHandler />
<AppRoutes />
</HashRouter>
</Providers>
)
}

View File

@@ -1,5 +1,5 @@
import { createAsyncThunk } from "@reduxjs/toolkit"
import type { AppDispatch, RootState } from "app/store"
import type { AppDispatch, RootState } from "@/app/store"
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState

View File

@@ -105,7 +105,7 @@ export const client = {
},
admin: {
getAllUsers: async () => await axiosInstance.get<UserModel[]>("admin/user/getAll"),
saveUser: async (req: AdminSaveUserRequest) => await axiosInstance.post("admin/user/save", req),
saveUser: async (req: AdminSaveUserRequest) => await axiosInstance.post<number>("admin/user/save", req),
deleteUser: async (req: IDRequest) => await axiosInstance.post("admin/user/delete", req),
getMetrics: async () => await axiosInstance.get<Metrics>("admin/metrics"),
},

View File

@@ -87,17 +87,15 @@ export const Constants = {
headerHeight: 60,
entryMaxWidth: 650,
isTopVisible: (div: HTMLElement) => {
const header = document.getElementById(Constants.dom.headerId)?.getBoundingClientRect()
const header = document.getElementsByTagName("header").item(0)?.getBoundingClientRect()
return div.getBoundingClientRect().top >= (header?.bottom ?? 0)
},
isBottomVisible: (div: HTMLElement) => {
const footer = document.getElementById(Constants.dom.footerId)?.getBoundingClientRect()
const footer = document.getElementsByTagName("footer").item(0)?.getBoundingClientRect()
return div.getBoundingClientRect().bottom <= (footer?.top ?? window.innerHeight)
},
},
dom: {
headerId: "header",
footerId: "footer",
entryId: (entry: Entry) => `entry-id-${entry.id}`,
entryContextMenuId: (entry: Entry) => entry.id,
},

View File

@@ -1,12 +1,12 @@
import { configureStore } from "@reduxjs/toolkit"
import { client } from "app/client"
import { loadEntries, loadMoreEntries, markAllEntries, markEntry } from "app/entries/thunks"
import { type RootState, reducers } from "app/store"
import type { Entries, Entry } from "app/types"
import type { AxiosResponse } from "axios"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { client } from "@/app/client"
import { loadEntries, loadMoreEntries, markAllEntries, markEntry } from "@/app/entries/thunks"
import { type RootState, reducers } from "@/app/store"
import type { Entries, Entry } from "@/app/types"
vi.mock(import("app/client"))
vi.mock(import("@/app/client"))
describe("entries", () => {
beforeEach(() => {
@@ -27,7 +27,12 @@ describe("entries", () => {
} as AxiosResponse<Entries>)
const store = configureStore({ reducer: reducers })
const promise = store.dispatch(loadEntries({ source: { type: "feed", id: "feed-id" }, clearSearch: true }))
const promise = store.dispatch(
loadEntries({
source: { type: "feed", id: "feed-id" },
clearSearch: true,
})
)
expect(store.getState().entries.source.type).toBe("feed")
expect(store.getState().entries.source.id).toBe("feed-id")
@@ -130,11 +135,19 @@ describe("entries", () => {
} as RootState,
})
store.dispatch(markAllEntries({ sourceType: "category", req: { id: "all", read: true } }))
store.dispatch(
markAllEntries({
sourceType: "category",
req: { id: "all", read: true },
})
)
expect(store.getState().entries.entries).toStrictEqual([
{ id: "3", read: true },
{ id: "4", read: true },
])
expect(client.category.markEntries).toHaveBeenCalledWith({ id: "all", read: true })
expect(client.category.markEntries).toHaveBeenCalledWith({
id: "all",
read: true,
})
})
})

View File

@@ -1,7 +1,7 @@
import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
import { Constants } from "app/constants"
import { loadEntries, loadMoreEntries, markAllEntries, markEntry, markMultipleEntries, starEntry, tagEntry } from "app/entries/thunks"
import type { Entry } from "app/types"
import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
import { Constants } from "@/app/constants"
import { loadEntries, loadMoreEntries, markAllEntries, markEntry, markMultipleEntries, starEntry, tagEntry } from "@/app/entries/thunks"
import type { Entry } from "@/app/types"
export type EntrySourceType = "category" | "feed" | "tag"

View File

@@ -1,13 +1,19 @@
import { createAppAsyncThunk } from "app/async-thunk"
import { client } from "app/client"
import { Constants } from "app/constants"
import { type EntrySource, type EntrySourceType, entriesSlice, setMarkAllAsReadConfirmationDialogOpen, setSearch } from "app/entries/slice"
import type { RootState } from "app/store"
import { reloadTree } from "app/tree/thunks"
import type { Entry, MarkRequest, TagRequest } from "app/types"
import { reloadTags } from "app/user/thunks"
import { scrollToWithCallback } from "app/utils"
import { flushSync } from "react-dom"
import { createAppAsyncThunk } from "@/app/async-thunk"
import { client } from "@/app/client"
import { Constants } from "@/app/constants"
import {
type EntrySource,
type EntrySourceType,
entriesSlice,
setMarkAllAsReadConfirmationDialogOpen,
setSearch,
} from "@/app/entries/slice"
import type { RootState } from "@/app/store"
import { reloadTree, selectNextUnreadTreeItem } from "@/app/tree/thunks"
import type { Entry, MarkRequest, TagRequest } from "@/app/types"
import { reloadTags } from "@/app/user/thunks"
import { scrollToWithCallback } from "@/app/utils"
const getEndpoint = (sourceType: EntrySourceType) =>
sourceType === "category" || sourceType === "tag" ? client.category.getEntries : client.feed.getEntries
@@ -130,11 +136,12 @@ export const markAllAsReadWithConfirmationIfRequired = createAppAsyncThunk(
const source = state.entries.source
const entriesTimestamp = state.entries.timestamp ?? Date.now()
const markAllAsReadConfirmation = state.user.settings?.markAllAsReadConfirmation
const markAllAsReadNavigateToNextUnread = state.user.settings?.markAllAsReadNavigateToNextUnread
if (markAllAsReadConfirmation) {
thunkApi.dispatch(setMarkAllAsReadConfirmationDialogOpen(true))
} else {
thunkApi.dispatch(
await thunkApi.dispatch(
markAllEntries({
sourceType: source.type,
req: {
@@ -145,6 +152,9 @@ export const markAllAsReadWithConfirmationIfRequired = createAppAsyncThunk(
},
})
)
const isAllCategorySelected = source.type === "category" && source.id === Constants.categories.all.id
if (markAllAsReadNavigateToNextUnread && !isAllCategorySelected)
await thunkApi.dispatch(selectNextUnreadTreeItem({ direction: "forward" }))
}
}
)
@@ -230,7 +240,7 @@ export const selectEntry = createAppAsyncThunk(
)
const scrollToEntry = (entryElement: HTMLElement, margin: number, scrollSpeed: number | undefined, onScrollEnded: () => void) => {
const header = document.getElementById(Constants.dom.headerId)?.getBoundingClientRect()
const header = document.getElementsByTagName("header").item(0)?.getBoundingClientRect()
const offset = (header?.bottom ?? 0) + margin
scrollToWithCallback({
options: {

View File

@@ -1,6 +1,6 @@
import { redirectToCategory } from "app/redirect/thunks"
import { store } from "app/store"
import { describe, expect, it } from "vitest"
import { redirectToCategory } from "@/app/redirect/thunks"
import { store } from "@/app/store"
describe("redirects", () => {
it("redirects to category", async () => {

View File

@@ -1,4 +1,4 @@
import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
interface RedirectState {
to?: string

View File

@@ -1,12 +1,14 @@
import { createAppAsyncThunk } from "app/async-thunk"
import { Constants } from "app/constants"
import { redirectTo } from "app/redirect/slice"
import { createAppAsyncThunk } from "@/app/async-thunk"
import { Constants } from "@/app/constants"
import { redirectTo } from "@/app/redirect/slice"
export const redirectToLogin = createAppAsyncThunk("redirect/login", (_, thunkApi) => thunkApi.dispatch(redirectTo("/login")))
export const redirectToRegistration = createAppAsyncThunk("redirect/register", (_, thunkApi) => thunkApi.dispatch(redirectTo("/register")))
export const redirectToApiDocumentation = createAppAsyncThunk("redirect/api", (_, thunkApi) => thunkApi.dispatch(redirectTo("/api")))
export const redirectToApiDocumentation = createAppAsyncThunk("redirect/api", () => {
window.location.href = "api-documentation/"
})
export const redirectToSelectedSource = createAppAsyncThunk("redirect/selectedSource", (_, thunkApi) => {
const { source } = thunkApi.getState().entries

View File

@@ -1,6 +1,6 @@
import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
import { reloadServerInfos } from "app/server/thunks"
import type { ServerInfo } from "app/types"
import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
import { reloadServerInfos } from "@/app/server/thunks"
import type { ServerInfo } from "@/app/types"
interface ServerState {
serverInfos?: ServerInfo

View File

@@ -1,4 +1,4 @@
import { createAppAsyncThunk } from "app/async-thunk"
import { client } from "app/client"
import { createAppAsyncThunk } from "@/app/async-thunk"
import { client } from "@/app/client"
export const reloadServerInfos = createAppAsyncThunk("server/infos", async () => await client.server.getServerInfos().then(r => r.data))

View File

@@ -1,11 +1,11 @@
import { configureStore } from "@reduxjs/toolkit"
import { entriesSlice } from "app/entries/slice"
import { redirectSlice } from "app/redirect/slice"
import { serverSlice } from "app/server/slice"
import { treeSlice } from "app/tree/slice"
import type { LocalSettings } from "app/types"
import { initialLocalSettings, userSlice } from "app/user/slice"
import { type TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"
import { entriesSlice } from "@/app/entries/slice"
import { redirectSlice } from "@/app/redirect/slice"
import { serverSlice } from "@/app/server/slice"
import { treeSlice } from "@/app/tree/slice"
import type { LocalSettings } from "@/app/types"
import { initialLocalSettings, userSlice } from "@/app/user/slice"
export const reducers = {
entries: entriesSlice.reducer,

View File

@@ -1,9 +1,9 @@
import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
import { loadEntries, markEntry } from "app/entries/thunks"
import { redirectTo } from "app/redirect/slice"
import { collapseTreeCategory, reloadTree } from "app/tree/thunks"
import type { Category, Subscription } from "app/types"
import { flattenCategoryTree, visitCategoryTree } from "app/utils"
import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
import { loadEntries, markEntry } from "@/app/entries/thunks"
import { redirectTo } from "@/app/redirect/slice"
import { collapseTreeCategory, reloadTree } from "@/app/tree/thunks"
import type { Category, Subscription } from "@/app/types"
import { flattenCategoryTree, visitCategoryTree } from "@/app/utils"
export interface TreeSubscription extends Subscription {
// client-side only flag

View File

@@ -1,9 +1,10 @@
import { createAppAsyncThunk } from "app/async-thunk"
import { client } from "app/client"
import { redirectToCategory, redirectToFeed } from "app/redirect/thunks"
import { incrementUnreadCount } from "app/tree/slice"
import type { CollapseRequest, Subscription } from "app/types"
import { flattenCategoryTree, visitCategoryTree } from "app/utils"
import { createAppAsyncThunk } from "@/app/async-thunk"
import { client } from "@/app/client"
import { Constants } from "@/app/constants"
import { redirectToCategory, redirectToFeed } from "@/app/redirect/thunks"
import { incrementUnreadCount } from "@/app/tree/slice"
import type { CollapseRequest, Subscription } from "@/app/types"
import { flattenCategoryTree, visitCategoryTree } from "@/app/utils"
export const reloadTree = createAppAsyncThunk("tree/reload", async () => await client.category.getRoot().then(r => r.data))
@@ -53,6 +54,9 @@ export const selectNextUnreadTreeItem = createAppAsyncThunk(
}
}
}
// redirect to 'all' if no unread categories or feeds found or if we reached the end of the list
thunkApi.dispatch(redirectToCategory(Constants.categories.all.id))
}
)

View File

@@ -1,13 +1,13 @@
import { configureStore } from "@reduxjs/toolkit"
import { client } from "app/client"
import { loadEntries } from "app/entries/thunks"
import { type RootState, reducers } from "app/store"
import { newFeedEntriesDiscovered, selectNextUnreadTreeItem } from "app/tree/thunks"
import type { Category, Entries, Entry, Subscription } from "app/types"
import type { AxiosResponse } from "axios"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { client } from "@/app/client"
import { loadEntries } from "@/app/entries/thunks"
import { type RootState, reducers } from "@/app/store"
import { newFeedEntriesDiscovered, selectNextUnreadTreeItem } from "@/app/tree/thunks"
import type { Category, Entries, Entry, Subscription } from "@/app/types"
vi.mock(import("app/client"))
vi.mock(import("@/app/client"))
const createCategory = (id: string): Category => ({
id,

View File

@@ -214,7 +214,6 @@ export interface ServerInfo {
version: string
gitCommit: string
allowRegistrations: boolean
googleAnalyticsCode?: string
smtpEnabled: boolean
demoAccountEnabled: boolean
websocketEnabled: boolean
@@ -248,6 +247,7 @@ export interface Settings {
starIconDisplayMode: IconDisplayMode
externalLinkIconDisplayMode: IconDisplayMode
markAllAsReadConfirmation: boolean
markAllAsReadNavigateToNextUnread: boolean
customContextMenu: boolean
mobileFooter: boolean
unreadCountTitle: boolean

View File

@@ -1,13 +1,14 @@
import { t } from "@lingui/core/macro"
import { showNotification } from "@mantine/notifications"
import { type PayloadAction, createSlice, isAnyOf } from "@reduxjs/toolkit"
import type { LocalSettings, Settings, UserModel, ViewMode } from "app/types"
import { createSlice, isAnyOf, type PayloadAction } from "@reduxjs/toolkit"
import type { LocalSettings, Settings, UserModel, ViewMode } from "@/app/types"
import {
changeCustomContextMenu,
changeEntriesToKeepOnTopWhenScrolling,
changeExternalLinkIconDisplayMode,
changeLanguage,
changeMarkAllAsReadConfirmation,
changeMarkAllAsReadNavigateToUnread,
changeMobileFooter,
changePrimaryColor,
changeReadingMode,
@@ -114,6 +115,10 @@ export const userSlice = createSlice({
if (!state.settings) return
state.settings.markAllAsReadConfirmation = action.meta.arg
})
builder.addCase(changeMarkAllAsReadNavigateToUnread.pending, (state, action) => {
if (!state.settings) return
state.settings.markAllAsReadNavigateToNextUnread = action.meta.arg
})
builder.addCase(changeCustomContextMenu.pending, (state, action) => {
if (!state.settings) return
state.settings.customContextMenu = action.meta.arg
@@ -149,6 +154,7 @@ export const userSlice = createSlice({
changeStarIconDisplayMode.fulfilled,
changeExternalLinkIconDisplayMode.fulfilled,
changeMarkAllAsReadConfirmation.fulfilled,
changeMarkAllAsReadNavigateToUnread.fulfilled,
changeCustomContextMenu.fulfilled,
changeMobileFooter.fulfilled,
changeUnreadCountTitle.fulfilled,

View File

@@ -1,7 +1,7 @@
import { createAppAsyncThunk } from "app/async-thunk"
import { client } from "app/client"
import { reloadEntries } from "app/entries/thunks"
import type { IconDisplayMode, ReadingMode, ReadingOrder, ScrollMode, SharingSettings } from "app/types"
import { createAppAsyncThunk } from "@/app/async-thunk"
import { client } from "@/app/client"
import { reloadEntries } from "@/app/entries/thunks"
import type { IconDisplayMode, ReadingMode, ReadingOrder, ScrollMode, SharingSettings } from "@/app/types"
export const reloadSettings = createAppAsyncThunk("settings/reload", async () => await client.user.getSettings().then(r => r.data))
@@ -89,6 +89,15 @@ export const changeMarkAllAsReadConfirmation = createAppAsyncThunk(
}
)
export const changeMarkAllAsReadNavigateToUnread = createAppAsyncThunk(
"settings/markAllAsReadNavigateToUnread",
(markAllAsReadNavigateToNextUnread: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, markAllAsReadNavigateToNextUnread })
}
)
export const changeCustomContextMenu = createAppAsyncThunk("settings/customContextMenu", (customContextMenu: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return

View File

@@ -1,5 +1,5 @@
import type { TreeCategory } from "app/tree/slice"
import { throttle } from "throttle-debounce"
import type { TreeCategory } from "@/app/tree/slice"
import type { Category } from "./types"
export function visitCategoryTree(

View File

@@ -1,8 +1,8 @@
import type { I18nContext } from "@lingui/react"
import { MantineProvider } from "@mantine/core"
import { fireEvent, render, screen, waitFor } from "@testing-library/react"
import { useActionButton } from "hooks/useActionButton"
import { describe, expect, it, vi } from "vitest"
import { useActionButton } from "@/hooks/useActionButton"
import { ActionButton } from "./ActionButton"
vi.mock(import("@lingui/react"), () => ({
@@ -10,7 +10,7 @@ vi.mock(import("@lingui/react"), () => ({
_: msg => msg,
} as I18nContext),
}))
vi.mock(import("hooks/useActionButton"))
vi.mock(import("@/hooks/useActionButton"))
const label = "Test Label"
const icon = "Test Icon"
@@ -18,7 +18,9 @@ describe("ActionButton", () => {
it("renders Button with label on desktop", () => {
vi.mocked(useActionButton).mockReturnValue({ mobile: false, spacing: 0 })
render(<ActionButton label={label} icon={icon} />, { wrapper: MantineProvider })
render(<ActionButton label={label} icon={icon} />, {
wrapper: MantineProvider,
})
expect(screen.getByText(label)).toBeInTheDocument()
expect(screen.getByText(icon)).toBeInTheDocument()
})
@@ -26,7 +28,9 @@ describe("ActionButton", () => {
it("renders ActionIcon with tooltip on mobile", async () => {
vi.mocked(useActionButton).mockReturnValue({ mobile: true, spacing: 0 })
render(<ActionButton label={label} icon={icon} />, { wrapper: MantineProvider })
render(<ActionButton label={label} icon={icon} />, {
wrapper: MantineProvider,
})
expect(screen.queryByText(label)).not.toBeInTheDocument()
expect(screen.getByText(icon)).toBeInTheDocument()
@@ -39,7 +43,9 @@ describe("ActionButton", () => {
vi.mocked(useActionButton).mockReturnValue({ mobile: false, spacing: 0 })
const clickListener = vi.fn()
render(<ActionButton label={label} icon={icon} onClick={clickListener} />, { wrapper: MantineProvider })
render(<ActionButton label={label} icon={icon} onClick={clickListener} />, {
wrapper: MantineProvider,
})
fireEvent.click(screen.getByRole("button"))
expect(clickListener).toHaveBeenCalled()

View File

@@ -2,9 +2,9 @@ import type { MessageDescriptor } from "@lingui/core"
import { useLingui } from "@lingui/react"
import { ActionIcon, Box, Button, type ButtonVariant, Tooltip, useMantineTheme } from "@mantine/core"
import type { ActionIconVariant } from "@mantine/core/lib/components/ActionIcon/ActionIcon"
import { Constants } from "app/constants"
import { useActionButton } from "hooks/useActionButton"
import { type MouseEventHandler, type ReactNode, forwardRef } from "react"
import { forwardRef, type MouseEventHandler, type ReactNode } from "react"
import { Constants } from "@/app/constants"
import { useActionButton } from "@/hooks/useActionButton"
interface ActionButtonProps {
icon: ReactNode

View File

@@ -10,7 +10,7 @@ export interface ErrorsAlertProps {
messages: string[]
}
export function Alert(props: ErrorsAlertProps) {
export function Alert(props: Readonly<ErrorsAlertProps>) {
let title: React.ReactNode
let color: string
let icon: React.ReactNode

View File

@@ -1,9 +1,9 @@
import { Trans } from "@lingui/react/macro"
import { Box, Dialog, Text } from "@mantine/core"
import { useAppDispatch, useAppSelector } from "app/store"
import { setAnnouncementHash } from "app/user/slice"
import { Content } from "components/content/Content"
import { useAsync } from "react-async-hook"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { setAnnouncementHash } from "@/app/user/slice"
import { Content } from "@/components/content/Content"
const sha256Hex = async (input: string | undefined) => {
const data = new TextEncoder().encode(input)

View File

@@ -1,4 +1,4 @@
export const DisablePullToRefresh = () => {
import("./DisablePullToRefresh.css")
return <></>
return null
}

View File

@@ -1,5 +1,5 @@
import { ErrorPage } from "pages/ErrorPage"
import React, { type ReactNode } from "react"
import { ErrorPage } from "@/pages/ErrorPage"
interface ErrorBoundaryProps {
children?: ReactNode

View File

@@ -1,7 +1,7 @@
import { Box, Center } from "@mantine/core"
import { useState } from "react"
import { TbPhoto } from "react-icons/tb"
import { tss } from "tss"
import { tss } from "@/tss"
interface ImageWithPlaceholderWhileLoadingProps {
src: string
@@ -44,7 +44,7 @@ export function ImageWithPlaceholderWhileLoading({
title,
width,
style,
}: ImageWithPlaceholderWhileLoadingProps) {
}: Readonly<ImageWithPlaceholderWhileLoadingProps>) {
const { classes } = useStyles({
placeholderWidth,
placeholderHeight,

View File

@@ -1,7 +1,7 @@
import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Kbd, Stack, Table } from "@mantine/core"
import { useOs } from "@mantine/hooks"
import { Constants } from "app/constants"
import { Constants } from "@/app/constants"
export function KeyboardShortcutsHelp() {
const isMacOS = useOs() === "macos"

View File

@@ -1,10 +1,10 @@
import { Image } from "@mantine/core"
import logo from "assets/logo.svg"
import logo from "@/assets/logo.svg"
export interface LogoProps {
size: number
}
export function Logo(props: LogoProps) {
export function Logo(props: Readonly<LogoProps>) {
return <Image src={logo} w={props.size} />
}

View File

@@ -1,9 +1,11 @@
import { Trans } from "@lingui/react/macro"
import { Button, Code, Group, Modal, Slider, Stack, Text } from "@mantine/core"
import { setMarkAllAsReadConfirmationDialogOpen } from "app/entries/slice"
import { markAllEntries } from "app/entries/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { useState } from "react"
import { Constants } from "@/app/constants"
import { setMarkAllAsReadConfirmationDialogOpen } from "@/app/entries/slice"
import { markAllEntries } from "@/app/entries/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { selectNextUnreadTreeItem } from "@/app/tree/thunks"
export function MarkAllAsReadConfirmationDialog() {
const [threshold, setThreshold] = useState(0)
@@ -11,10 +13,12 @@ export function MarkAllAsReadConfirmationDialog() {
const source = useAppSelector(state => state.entries.source)
const sourceLabel = useAppSelector(state => state.entries.sourceLabel)
const entriesTimestamp = useAppSelector(state => state.entries.timestamp) ?? Date.now()
const markAllAsReadNavigateToNextUnread = useAppSelector(state => state.user.settings?.markAllAsReadNavigateToNextUnread)
const dispatch = useAppDispatch()
const onConfirm = () => {
dispatch(
const onConfirm = async () => {
dispatch(setMarkAllAsReadConfirmationDialogOpen(false))
await dispatch(
markAllEntries({
sourceType: source.type,
req: {
@@ -25,7 +29,9 @@ export function MarkAllAsReadConfirmationDialog() {
},
})
)
dispatch(setMarkAllAsReadConfirmationDialogOpen(false))
const isAllCategorySelected = source.type === "category" && source.id === Constants.categories.all.id
if (markAllAsReadNavigateToNextUnread && !isAllCategorySelected) await dispatch(selectNextUnreadTreeItem({ direction: "forward" }))
}
return (

View File

@@ -1,10 +1,14 @@
import { Trans } from "@lingui/react/macro"
import { Tooltip } from "@mantine/core"
import { Constants } from "app/constants"
import dayjs from "dayjs"
import { useNow } from "hooks/useNow"
import { Constants } from "@/app/constants"
import { useNow } from "@/hooks/useNow"
export function RelativeDate(props: { date: Date | number | undefined }) {
export function RelativeDate(
props: Readonly<{
date: Date | number | undefined
}>
) {
const now = useNow(60 * 1000)
if (!props.date) return <Trans>N/A</Trans>

View File

@@ -1,11 +1,11 @@
import { Trans } from "@lingui/react/macro"
import { Box, Button, Checkbox, Group, PasswordInput, Stack, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import type { AdminSaveUserRequest, UserModel } from "app/types"
import { Alert } from "components/Alert"
import { useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import type { AdminSaveUserRequest, UserModel } from "@/app/types"
import { Alert } from "@/components/Alert"
interface UserEditProps {
user?: UserModel
@@ -13,7 +13,7 @@ interface UserEditProps {
onSave: () => void
}
export function UserEdit(props: UserEditProps) {
export function UserEdit(props: Readonly<UserEditProps>) {
const form = useForm<AdminSaveUserRequest>({
initialValues: props.user ?? {
name: "",

View File

@@ -1,7 +1,7 @@
import { Input, Textarea } from "@mantine/core"
import RichCodeEditor from "components/code/RichCodeEditor"
import { useMobile } from "hooks/useMobile"
import type { ReactNode } from "react"
import RichCodeEditor from "@/components/code/RichCodeEditor"
import { useMobile } from "@/hooks/useMobile"
interface CodeEditorProps {
label?: ReactNode
@@ -11,7 +11,7 @@ interface CodeEditorProps {
onChange: (value: string | undefined) => void
}
export function CodeEditor(props: CodeEditorProps) {
export function CodeEditor(props: Readonly<CodeEditorProps>) {
const mobile = useMobile()
return mobile ? (

View File

@@ -1,6 +1,6 @@
import { Loader } from "components/Loader"
import { useColorScheme } from "hooks/useColorScheme"
import { useAsync } from "react-async-hook"
import { Loader } from "@/components/Loader"
import { useColorScheme } from "@/hooks/useColorScheme"
const init = async () => {
window.MonacoEnvironment = {
@@ -30,7 +30,7 @@ interface RichCodeEditorProps {
onChange: (value: string | undefined) => void
}
function RichCodeEditor(props: RichCodeEditorProps) {
function RichCodeEditor(props: Readonly<RichCodeEditorProps>) {
const colorScheme = useColorScheme()
const editorTheme = colorScheme === "dark" ? "vs-dark" : "light"

View File

@@ -1,6 +1,6 @@
import { TypographyStylesProvider } from "@mantine/core"
import { Typography } from "@mantine/core"
import type { ReactNode } from "react"
import { tss } from "tss"
import { tss } from "@/tss"
/**
* This component is used to provide basic styles to html typography elements.
@@ -20,5 +20,5 @@ const useStyles = tss.create(() => ({
export const BasicHtmlStyles = (props: { children: ReactNode }) => {
const { classes } = useStyles()
return <TypographyStylesProvider className={classes.content}>{props.children}</TypographyStylesProvider>
return <Typography className={classes.content}>{props.children}</Typography>
}

View File

@@ -1,7 +1,7 @@
import { MantineProvider } from "@mantine/core"
import { render } from "@testing-library/react"
import { Content } from "components/content/Content"
import { describe, expect, it } from "vitest"
import { Content } from "@/components/content/Content"
describe("Content component", () => {
it("renders basic content", () => {

View File

@@ -1,13 +1,13 @@
import { Box, Mark } from "@mantine/core"
import { Constants } from "app/constants"
import { calculatePlaceholderSize } from "app/utils"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import escapeStringRegexp from "escape-string-regexp"
import { ALLOWED_TAG_LIST, type ChildrenNode, Interweave, type MatchResponse, Matcher, type Node, type TransformCallback } from "interweave"
import { ALLOWED_TAG_LIST, type ChildrenNode, Interweave, Matcher, type MatchResponse, type Node, type TransformCallback } from "interweave"
import React from "react"
import styleToObject from "style-to-object"
import { tss } from "tss"
import { Constants } from "@/app/constants"
import { calculatePlaceholderSize } from "@/app/utils"
import { BasicHtmlStyles } from "@/components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
import { tss } from "@/tss"
export interface ContentProps {
content: string

View File

@@ -1,10 +1,12 @@
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { BasicHtmlStyles } from "@/components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
export function Enclosure(props: {
enclosureType: string
enclosureUrl: string
}) {
export function Enclosure(
props: Readonly<{
enclosureType: string
enclosureUrl: string
}>
) {
const hasVideo = props.enclosureType.startsWith("video")
const hasAudio = props.enclosureType.startsWith("audio")
const hasImage = props.enclosureType.startsWith("image")

View File

@@ -1,8 +1,12 @@
import { Trans } from "@lingui/react/macro"
import { Box } from "@mantine/core"
import { openModal } from "@mantine/modals"
import { Constants } from "app/constants"
import type { ExpendableEntry } from "app/entries/slice"
import { useEffect } from "react"
import { useContextMenu } from "react-contexify"
import InfiniteScroll from "react-infinite-scroller"
import { throttle } from "throttle-debounce"
import { Constants } from "@/app/constants"
import type { ExpendableEntry } from "@/app/entries/slice"
import {
loadMoreEntries,
markAllAsReadWithConfirmationIfRequired,
@@ -12,19 +16,15 @@ import {
selectNextEntry,
selectPreviousEntry,
starEntry,
} from "app/entries/thunks"
import { redirectToRootCategory } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { toggleSidebar } from "app/tree/slice"
import { selectNextUnreadTreeItem } from "app/tree/thunks"
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp"
import { Loader } from "components/Loader"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useMousetrap } from "hooks/useMousetrap"
import { useEffect } from "react"
import { useContextMenu } from "react-contexify"
import InfiniteScroll from "react-infinite-scroller"
import { throttle } from "throttle-debounce"
} from "@/app/entries/thunks"
import { redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { toggleSidebar } from "@/app/tree/slice"
import { selectNextUnreadTreeItem } from "@/app/tree/thunks"
import { KeyboardShortcutsHelp } from "@/components/KeyboardShortcutsHelp"
import { Loader } from "@/components/Loader"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useMousetrap } from "@/hooks/useMousetrap"
import { FeedEntry } from "./FeedEntry"
export function FeedEntries() {
@@ -287,7 +287,6 @@ export function FeedEntries() {
return (
<InfiniteScroll
id="entries"
className={`cf-entries cf-view-mode-${viewMode}`}
initialLoad={false}
loadMore={async () => await (!loading && dispatch(loadMoreEntries()))}

View File

@@ -1,13 +1,13 @@
import { Box, Divider, type MantineRadius, type MantineSpacing, Paper } from "@mantine/core"
import { Constants } from "app/constants"
import { useAppSelector } from "app/store"
import type { Entry, ViewMode } from "app/types"
import { FeedEntryCompactHeader } from "components/content/header/FeedEntryCompactHeader"
import { FeedEntryHeader } from "components/content/header/FeedEntryHeader"
import { useMobile } from "hooks/useMobile"
import type React from "react"
import { useSwipeable } from "react-swipeable"
import { tss } from "tss"
import { Constants } from "@/app/constants"
import { useAppSelector } from "@/app/store"
import type { Entry, ViewMode } from "@/app/types"
import { FeedEntryCompactHeader } from "@/components/content/header/FeedEntryCompactHeader"
import { FeedEntryHeader } from "@/components/content/header/FeedEntryHeader"
import { useMobile } from "@/hooks/useMobile"
import { tss } from "@/tss"
import { FeedEntryBody } from "./FeedEntryBody"
import { FeedEntryContextMenu } from "./FeedEntryContextMenu"
import { FeedEntryFooter } from "./FeedEntryFooter"
@@ -96,7 +96,7 @@ const useStyles = tss
}
})
export function FeedEntry(props: FeedEntryProps) {
export function FeedEntry(props: Readonly<FeedEntryProps>) {
const viewMode = useAppSelector(state => state.user.localSettings.viewMode)
const fontSizePercentage = useAppSelector(state => state.user.localSettings.fontSizePercentage)
const { classes, cx } = useStyles({

View File

@@ -1,6 +1,6 @@
import { Box } from "@mantine/core"
import { useAppSelector } from "app/store"
import type { Entry } from "app/types"
import { useAppSelector } from "@/app/store"
import type { Entry } from "@/app/types"
import { Content } from "./Content"
import { Enclosure } from "./Enclosure"
import { Media } from "./Media"
@@ -9,7 +9,7 @@ export interface FeedEntryBodyProps {
entry: Entry
}
export function FeedEntryBody(props: FeedEntryBodyProps) {
export function FeedEntryBody(props: Readonly<FeedEntryBodyProps>) {
const search = useAppSelector(state => state.entries.search)
return (
<Box>

View File

@@ -1,16 +1,16 @@
import { Trans } from "@lingui/react/macro"
import { Group } from "@mantine/core"
import { Constants } from "app/constants"
import { markEntriesUpToEntry, markEntry, starEntry } from "app/entries/thunks"
import { redirectToFeed } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import type { Entry } from "app/types"
import { truncate } from "app/utils"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useColorScheme } from "hooks/useColorScheme"
import { Item, Menu, Separator } from "react-contexify"
import { TbArrowBarToDown, TbExternalLink, TbMail, TbMailOpened, TbRss, TbStar, TbStarOff } from "react-icons/tb"
import { tss } from "tss"
import { Constants } from "@/app/constants"
import { markEntriesUpToEntry, markEntry, starEntry } from "@/app/entries/thunks"
import { redirectToFeed } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { Entry } from "@/app/types"
import { truncate } from "@/app/utils"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useColorScheme } from "@/hooks/useColorScheme"
import { tss } from "@/tss"
interface FeedEntryContextMenuProps {
entry: Entry
@@ -27,7 +27,7 @@ const useStyles = tss.create(({ theme, colorScheme }) => ({
},
}))
export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
export function FeedEntryContextMenu(props: Readonly<FeedEntryContextMenuProps>) {
const colorScheme = useColorScheme()
const { classes } = useStyles()
const sourceType = useAppSelector(state => state.entries.source.type)

View File

@@ -1,20 +1,20 @@
import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react"
import { Group, Indicator, Popover, TagsInput } from "@mantine/core"
import { markEntriesUpToEntry, markEntry, starEntry, tagEntry } from "app/entries/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import type { Entry } from "app/types"
import { ActionButton } from "components/ActionButton"
import { useActionButton } from "hooks/useActionButton"
import { useMobile } from "hooks/useMobile"
import { TbArrowBarToDown, TbExternalLink, TbMail, TbMailOpened, TbShare, TbStar, TbStarOff, TbTag } from "react-icons/tb"
import { markEntriesUpToEntry, markEntry, starEntry, tagEntry } from "@/app/entries/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { Entry } from "@/app/types"
import { ActionButton } from "@/components/ActionButton"
import { useActionButton } from "@/hooks/useActionButton"
import { useMobile } from "@/hooks/useMobile"
import { ShareButtons } from "./ShareButtons"
interface FeedEntryFooterProps {
entry: Entry
}
export function FeedEntryFooter(props: FeedEntryFooterProps) {
export function FeedEntryFooter(props: Readonly<FeedEntryFooterProps>) {
const tags = useAppSelector(state => state.user.tags)
const mobile = useMobile()
const { spacing } = useActionButton()

View File

@@ -1,11 +1,11 @@
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
export interface FeedFaviconProps {
url: string
size?: number
}
export function FeedFavicon({ url, size = 18 }: FeedFaviconProps) {
export function FeedFavicon({ url, size = 18 }: Readonly<FeedFaviconProps>) {
return (
<ImageWithPlaceholderWhileLoading
src={url}

View File

@@ -1,8 +1,8 @@
import { Box } from "@mantine/core"
import { Constants } from "app/constants"
import { calculatePlaceholderSize } from "app/utils"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { Constants } from "@/app/constants"
import { calculatePlaceholderSize } from "@/app/utils"
import { BasicHtmlStyles } from "@/components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
import { Content } from "./Content"
export interface MediaProps {
@@ -12,7 +12,7 @@ export interface MediaProps {
description?: string
}
export function Media(props: MediaProps) {
export function Media(props: Readonly<MediaProps>) {
const width = props.thumbnailWidth
const height = props.thumbnailHeight
const placeholderSize = calculatePlaceholderSize({

View File

@@ -1,13 +1,13 @@
import { Trans } from "@lingui/react/macro"
import { ActionIcon, Box, CopyButton, Divider, SimpleGrid } from "@mantine/core"
import { Constants } from "app/constants"
import { useAppSelector } from "app/store"
import type { SharingSettings } from "app/types"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useMobile } from "hooks/useMobile"
import type { IconType } from "react-icons"
import { TbCheck, TbCopy, TbDeviceDesktopShare, TbDeviceMobileShare } from "react-icons/tb"
import { tss } from "tss"
import { Constants } from "@/app/constants"
import { useAppSelector } from "@/app/store"
import type { SharingSettings } from "@/app/types"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useMobile } from "@/hooks/useMobile"
import { tss } from "@/tss"
type Color = `#${string}`
@@ -22,7 +22,15 @@ const useStyles = tss
},
}))
function ShareButton({ icon, color, onClick }: { icon: IconType; color: Color; onClick: () => void }) {
function ShareButton({
icon,
color,
onClick,
}: Readonly<{
icon: IconType
color: Color
onClick: () => void
}>) {
const { classes } = useStyles({
color,
})
@@ -36,7 +44,15 @@ function ShareButton({ icon, color, onClick }: { icon: IconType; color: Color; o
)
}
function SiteShareButton({ url, icon, color }: { icon: IconType; color: Color; url: string }) {
function SiteShareButton({
url,
icon,
color,
}: Readonly<{
icon: IconType
color: Color
url: string
}>) {
const onClick = () => {
window.open(url, "", "menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=800,height=600")
}
@@ -44,7 +60,11 @@ function SiteShareButton({ url, icon, color }: { icon: IconType; color: Color; u
return <ShareButton icon={icon} color={color} onClick={onClick} />
}
function CopyUrlButton({ url }: { url: string }) {
function CopyUrlButton({
url,
}: Readonly<{
url: string
}>) {
return (
<CopyButton value={url}>
{({ copied, copy }) => <ShareButton icon={copied ? TbCheck : TbCopy} color="#000" onClick={copy} />}
@@ -52,7 +72,13 @@ function CopyUrlButton({ url }: { url: string }) {
)
}
function BrowserNativeShareButton({ url, description }: { url: string; description: string }) {
function BrowserNativeShareButton({
url,
description,
}: Readonly<{
url: string
description: string
}>) {
const mobile = useMobile()
const { isBrowserExtensionPopup } = useBrowserExtension()
const onClick = () => {
@@ -71,7 +97,12 @@ function BrowserNativeShareButton({ url, description }: { url: string; descripti
)
}
export function ShareButtons(props: { url: string; description: string }) {
export function ShareButtons(
props: Readonly<{
url: string
description: string
}>
) {
const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings)
const enabledSharingSites = (Object.keys(Constants.sharing) as Array<keyof SharingSettings>).filter(site => sharingSettings?.[site])
const url = encodeURIComponent(props.url)

View File

@@ -3,14 +3,14 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro"
import { Box, Button, Group, Stack, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import { redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch } from "app/store"
import { reloadTree } from "app/tree/thunks"
import type { AddCategoryRequest } from "app/types"
import { Alert } from "components/Alert"
import { useAsyncCallback } from "react-async-hook"
import { TbFolderPlus } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import { redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch } from "@/app/store"
import { reloadTree } from "@/app/tree/thunks"
import type { AddCategoryRequest } from "@/app/types"
import { Alert } from "@/components/Alert"
import { CategorySelect } from "./CategorySelect"
export function AddCategory() {

View File

@@ -2,10 +2,10 @@ import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react"
import { Select, type SelectProps } from "@mantine/core"
import type { ComboboxItem } from "@mantine/core/lib/components/Combobox/Combobox.types"
import { Constants } from "app/constants"
import { useAppSelector } from "app/store"
import type { Category } from "app/types"
import { flattenCategoryTree } from "app/utils"
import { Constants } from "@/app/constants"
import { useAppSelector } from "@/app/store"
import type { Category } from "@/app/types"
import { flattenCategoryTree } from "@/app/utils"
type CategorySelectProps = Partial<SelectProps> & {
withAll?: boolean

View File

@@ -3,13 +3,13 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro"
import { Box, Button, FileInput, Group, Stack } from "@mantine/core"
import { isNotEmpty, useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import { redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch } from "app/store"
import { reloadTree } from "app/tree/thunks"
import { Alert } from "components/Alert"
import { useAsyncCallback } from "react-async-hook"
import { TbFileImport } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import { redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch } from "@/app/store"
import { reloadTree } from "@/app/tree/thunks"
import { Alert } from "@/components/Alert"
export function ImportOpml() {
const dispatch = useAppDispatch()

View File

@@ -1,16 +1,16 @@
import { Trans } from "@lingui/react/macro"
import { Box, Button, Group, Stack, Stepper, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import { Constants } from "app/constants"
import { redirectToFeed, redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch } from "app/store"
import { reloadTree } from "app/tree/thunks"
import type { FeedInfoRequest, SubscribeRequest } from "app/types"
import { Alert } from "components/Alert"
import { useState } from "react"
import { useAsyncCallback } from "react-async-hook"
import { TbRss } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import { Constants } from "@/app/constants"
import { redirectToFeed, redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch } from "@/app/store"
import { reloadTree } from "@/app/tree/thunks"
import type { FeedInfoRequest, SubscribeRequest } from "@/app/types"
import { Alert } from "@/components/Alert"
import { CategorySelect } from "./CategorySelect"
export function Subscribe() {
@@ -39,9 +39,8 @@ export function Subscribe() {
},
})
const subscribe = useAsyncCallback(client.feed.subscribe, {
onSuccess: async sub => {
await dispatch(reloadTree())
dispatch(redirectToFeed(sub.data))
onSuccess: sub => {
dispatch(reloadTree()).then(() => dispatch(redirectToFeed(sub.data)))
},
})

View File

@@ -1,11 +1,11 @@
import { Box } from "@mantine/core"
import type { Entry } from "app/types"
import { RelativeDate } from "components/RelativeDate"
import { FeedFavicon } from "components/content/FeedFavicon"
import { OpenExternalLink } from "components/content/header/OpenExternalLink"
import { Star } from "components/content/header/Star"
import { OnDesktop } from "components/responsive/OnDesktop"
import { tss } from "tss"
import type { Entry } from "@/app/types"
import { FeedFavicon } from "@/components/content/FeedFavicon"
import { OpenExternalLink } from "@/components/content/header/OpenExternalLink"
import { Star } from "@/components/content/header/Star"
import { RelativeDate } from "@/components/RelativeDate"
import { OnDesktop } from "@/components/responsive/OnDesktop"
import { tss } from "@/tss"
import { FeedEntryTitle } from "./FeedEntryTitle"
export interface FeedEntryHeaderProps {
@@ -43,7 +43,7 @@ const useStyles = tss
},
}))
export function FeedEntryCompactHeader(props: FeedEntryHeaderProps) {
export function FeedEntryCompactHeader(props: Readonly<FeedEntryHeaderProps>) {
const { classes } = useStyles({
read: props.entry.read,
})

View File

@@ -1,10 +1,10 @@
import { Box, Flex, Space } from "@mantine/core"
import type { Entry } from "app/types"
import { RelativeDate } from "components/RelativeDate"
import { FeedFavicon } from "components/content/FeedFavicon"
import { OpenExternalLink } from "components/content/header/OpenExternalLink"
import { Star } from "components/content/header/Star"
import { tss } from "tss"
import type { Entry } from "@/app/types"
import { FeedFavicon } from "@/components/content/FeedFavicon"
import { OpenExternalLink } from "@/components/content/header/OpenExternalLink"
import { Star } from "@/components/content/header/Star"
import { RelativeDate } from "@/components/RelativeDate"
import { tss } from "@/tss"
import { FeedEntryTitle } from "./FeedEntryTitle"
export interface FeedEntryHeaderProps {
@@ -24,7 +24,7 @@ const useStyles = tss
},
}))
export function FeedEntryHeader(props: FeedEntryHeaderProps) {
export function FeedEntryHeader(props: Readonly<FeedEntryHeaderProps>) {
const { classes } = useStyles({
read: props.entry.read,
})

View File

@@ -1,12 +1,12 @@
import { Highlight } from "@mantine/core"
import { useAppSelector } from "app/store"
import type { Entry } from "app/types"
import { useAppSelector } from "@/app/store"
import type { Entry } from "@/app/types"
export interface FeedEntryTitleProps {
entry: Entry
}
export function FeedEntryTitle(props: FeedEntryTitleProps) {
export function FeedEntryTitle(props: Readonly<FeedEntryTitleProps>) {
const search = useAppSelector(state => state.entries.search)
const keywords = search?.split(" ")
return (

View File

@@ -1,12 +1,16 @@
import { Trans } from "@lingui/react/macro"
import { ActionIcon, Anchor, Tooltip } from "@mantine/core"
import { Constants } from "app/constants"
import { markEntry } from "app/entries/thunks"
import { useAppDispatch } from "app/store"
import type { Entry } from "app/types"
import { TbExternalLink } from "react-icons/tb"
import { Constants } from "@/app/constants"
import { markEntry } from "@/app/entries/thunks"
import { useAppDispatch } from "@/app/store"
import type { Entry } from "@/app/types"
export function OpenExternalLink(props: { entry: Entry }) {
export function OpenExternalLink(
props: Readonly<{
entry: Entry
}>
) {
const dispatch = useAppDispatch()
const onClick = (e: React.MouseEvent) => {
e.stopPropagation()

View File

@@ -1,12 +1,16 @@
import { Trans } from "@lingui/react/macro"
import { ActionIcon, Tooltip } from "@mantine/core"
import { Constants } from "app/constants"
import { starEntry } from "app/entries/thunks"
import { useAppDispatch } from "app/store"
import type { Entry } from "app/types"
import { TbStar, TbStarFilled } from "react-icons/tb"
import { Constants } from "@/app/constants"
import { starEntry } from "@/app/entries/thunks"
import { useAppDispatch } from "@/app/store"
import type { Entry } from "@/app/types"
export function Star(props: { entry: Entry }) {
export function Star(
props: Readonly<{
entry: Entry
}>
) {
const dispatch = useAppDispatch()
const onClick = (e: React.MouseEvent) => {
e.stopPropagation()

View File

@@ -2,14 +2,6 @@ import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react"
import { Box, Center, CloseButton, Divider, Group, Indicator, Popover, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form"
import { markAllAsReadWithConfirmationIfRequired, reloadEntries, search, selectNextEntry, selectPreviousEntry } from "app/entries/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { changeReadingMode, changeReadingOrder } from "app/user/thunks"
import { ActionButton } from "components/ActionButton"
import { Loader } from "components/Loader"
import { useActionButton } from "hooks/useActionButton"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useMobile } from "hooks/useMobile"
import { useEffect } from "react"
import {
TbArrowDown,
@@ -25,6 +17,14 @@ import {
TbSortDescending,
TbUser,
} from "react-icons/tb"
import { markAllAsReadWithConfirmationIfRequired, reloadEntries, search, selectNextEntry, selectPreviousEntry } from "@/app/entries/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { changeReadingMode, changeReadingOrder } from "@/app/user/thunks"
import { ActionButton } from "@/components/ActionButton"
import { Loader } from "@/components/Loader"
import { useActionButton } from "@/hooks/useActionButton"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useMobile } from "@/hooks/useMobile"
import { ProfileMenu } from "./ProfileMenu"
function HeaderDivider() {

View File

@@ -11,14 +11,7 @@ import {
useMantineColorScheme,
} from "@mantine/core"
import { showNotification } from "@mantine/notifications"
import { client } from "app/client"
import { redirectToAbout, redirectToAdminUsers, redirectToDonate, redirectToMetrics, redirectToSettings } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import type { ViewMode } from "app/types"
import { setFontSizePercentage, setViewMode } from "app/user/slice"
import { reloadProfile } from "app/user/thunks"
import dayjs from "dayjs"
import { useNow } from "hooks/useNow"
import { type ReactNode, useState } from "react"
import {
TbChartLine,
@@ -36,6 +29,13 @@ import {
TbUsers,
TbWorldDownload,
} from "react-icons/tb"
import { client } from "@/app/client"
import { redirectToAbout, redirectToAdminUsers, redirectToDonate, redirectToMetrics, redirectToSettings } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { ViewMode } from "@/app/types"
import { setFontSizePercentage, setViewMode } from "@/app/user/slice"
import { reloadProfile } from "@/app/user/thunks"
import { useNow } from "@/hooks/useNow"
interface ProfileMenuProps {
control: React.ReactElement
@@ -94,7 +94,7 @@ const viewModeData: ViewModeControlItem[] = [
},
]
export function ProfileMenu(props: ProfileMenuProps) {
export function ProfileMenu(props: Readonly<ProfileMenuProps>) {
const [opened, setOpened] = useState(false)
const now = useNow()
const profile = useAppSelector(state => state.user.profile)
@@ -145,7 +145,7 @@ export function ProfileMenu(props: ProfileMenuProps) {
color: "green",
autoClose: 1000,
})
} catch (_) {
} catch {
showNotification({
message: <Trans>Force fetching feeds is not yet available.</Trans>,
color: "red",

View File

@@ -1,10 +1,10 @@
import { NumberFormatter } from "@mantine/core"
import type { MetricGauge } from "app/types"
import type { MetricGauge } from "@/app/types"
interface MeterProps {
interface GaugeProps {
gauge: MetricGauge
}
export function Gauge(props: MeterProps) {
export function Gauge(props: Readonly<GaugeProps>) {
return <NumberFormatter value={props.gauge.value} thousandSeparator />
}

View File

@@ -1,11 +1,11 @@
import { Box } from "@mantine/core"
import type { MetricMeter } from "app/types"
import type { MetricMeter } from "@/app/types"
interface MeterProps {
meter: MetricMeter
}
export function Meter(props: MeterProps) {
export function Meter(props: Readonly<MeterProps>) {
return (
<Box>
<Box>Mean: {props.meter.mean_rate.toFixed(2)}</Box>

View File

@@ -7,7 +7,7 @@ interface MetricAccordionItemProps {
children: React.ReactNode
}
export function MetricAccordionItem({ metricKey, name, headerValue, children }: MetricAccordionItemProps) {
export function MetricAccordionItem({ metricKey, name, headerValue, children }: Readonly<MetricAccordionItemProps>) {
return (
<Accordion.Item value={metricKey} key={metricKey}>
<Accordion.Control>

View File

@@ -1,11 +1,11 @@
import { Box } from "@mantine/core"
import type { MetricTimer } from "app/types"
import type { MetricTimer } from "@/app/types"
interface MetricTimerProps {
timer: MetricTimer
}
export function Timer(props: MetricTimerProps) {
export function Timer(props: Readonly<MetricTimerProps>) {
return (
<Box>
<Box>Mean: {props.timer.mean_rate.toFixed(2)}</Box>

View File

@@ -1,8 +1,12 @@
import { Box } from "@mantine/core"
import { useMobile } from "hooks/useMobile"
import type React from "react"
import { useMobile } from "@/hooks/useMobile"
export function OnDesktop(props: { children: React.ReactNode }) {
export function OnDesktop(
props: Readonly<{
children: React.ReactNode
}>
) {
const mobile = useMobile()
return <Box>{!mobile && props.children}</Box>
}

View File

@@ -1,8 +1,12 @@
import { Box } from "@mantine/core"
import { useMobile } from "hooks/useMobile"
import type React from "react"
import { useMobile } from "@/hooks/useMobile"
export function OnMobile(props: { children: React.ReactNode }) {
export function OnMobile(
props: Readonly<{
children: React.ReactNode
}>
) {
const mobile = useMobile()
return <Box>{mobile && props.children}</Box>
}

View File

@@ -1,15 +1,15 @@
import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Button, Group, Stack } from "@mantine/core"
import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import { Constants } from "app/constants"
import { redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { Alert } from "components/Alert"
import { CodeEditor } from "components/code/CodeEditor"
import { useEffect } from "react"
import { useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import { Constants } from "@/app/constants"
import { redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { Alert } from "@/components/Alert"
import { CodeEditor } from "@/components/code/CodeEditor"
interface FormData {
customCss: string
@@ -60,18 +60,14 @@ export function CustomCodeSettings() {
<CodeEditor
label={<Trans>Custom CSS rules that will be applied</Trans>}
description={
<Trans>
<span>See </span>
<Anchor
href={Constants.customCssDocumentationUrl}
target="_blank"
rel="noreferrer"
style={{ fontSize: "inherit" }}
>
here
</Anchor>
<span> for more information.</span>
</Trans>
<Anchor
href={Constants.customCssDocumentationUrl}
target="_blank"
rel="noreferrer"
style={{ fontSize: "inherit" }}
>
<Trans>Link to the documentation</Trans>
</Anchor>
}
language="css"
{...form.getInputProps("customCss")}

View File

@@ -3,15 +3,17 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro"
import { Box, Divider, Group, NumberInput, Radio, Select, type SelectProps, SimpleGrid, Stack, Switch } from "@mantine/core"
import type { ComboboxData } from "@mantine/core/lib/components/Combobox/Combobox.types"
import { Constants } from "app/constants"
import { useAppDispatch, useAppSelector } from "app/store"
import type { IconDisplayMode, ScrollMode, SharingSettings } from "app/types"
import type { ReactNode } from "react"
import { Constants } from "@/app/constants"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { IconDisplayMode, ScrollMode, SharingSettings } from "@/app/types"
import {
changeCustomContextMenu,
changeEntriesToKeepOnTopWhenScrolling,
changeExternalLinkIconDisplayMode,
changeLanguage,
changeMarkAllAsReadConfirmation,
changeMarkAllAsReadNavigateToUnread,
changeMobileFooter,
changePrimaryColor,
changeScrollMarks,
@@ -22,9 +24,8 @@ import {
changeStarIconDisplayMode,
changeUnreadCountFavicon,
changeUnreadCountTitle,
} from "app/user/thunks"
import { locales } from "i18n"
import type { ReactNode } from "react"
} from "@/app/user/thunks"
import { locales } from "@/i18n"
export function DisplaySettings() {
const language = useAppSelector(state => state.user.settings?.language)
@@ -36,6 +37,7 @@ export function DisplaySettings() {
const starIconDisplayMode = useAppSelector(state => state.user.settings?.starIconDisplayMode)
const externalLinkIconDisplayMode = useAppSelector(state => state.user.settings?.externalLinkIconDisplayMode)
const markAllAsReadConfirmation = useAppSelector(state => state.user.settings?.markAllAsReadConfirmation)
const markAllAsReadNavigateToNextUnread = useAppSelector(state => state.user.settings?.markAllAsReadNavigateToNextUnread)
const customContextMenu = useAppSelector(state => state.user.settings?.customContextMenu)
const mobileFooter = useAppSelector(state => state.user.settings?.mobileFooter)
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
@@ -127,6 +129,12 @@ export function DisplaySettings() {
onChange={async e => await dispatch(changeMarkAllAsReadConfirmation(e.currentTarget.checked))}
/>
<Switch
label={<Trans>Navigate to the next category/feed with unread entries when marking all entries as read</Trans>}
checked={markAllAsReadNavigateToNextUnread}
onChange={async e => await dispatch(changeMarkAllAsReadNavigateToUnread(e.currentTarget.checked))}
/>
<Switch
label={<Trans>On mobile, show action buttons at the bottom of the screen</Trans>}
checked={mobileFooter}

View File

@@ -4,15 +4,15 @@ import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Button, Checkbox, Divider, Group, Input, PasswordInput, Stack, Text, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form"
import { openConfirmModal } from "@mantine/modals"
import { client, errorToStrings } from "app/client"
import { redirectToLogin, redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import type { ProfileModificationRequest } from "app/types"
import { reloadProfile } from "app/user/thunks"
import { Alert } from "components/Alert"
import { useEffect } from "react"
import { useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy, TbTrash } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import { redirectToLogin, redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { ProfileModificationRequest } from "@/app/types"
import { reloadProfile } from "@/app/user/thunks"
import { Alert } from "@/components/Alert"
interface FormData extends ProfileModificationRequest {
newPasswordConfirmation?: string
@@ -52,7 +52,9 @@ export function ProfileSettings() {
),
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
confirmProps: { color: "red" },
onConfirm: async () => await deleteProfile.execute(),
onConfirm: () => {
deleteProfile.execute()
},
})
useEffect(() => {

View File

@@ -1,6 +1,8 @@
import { Trans } from "@lingui/react/macro"
import { Box, Stack } from "@mantine/core"
import { Constants } from "app/constants"
import React from "react"
import { TbChevronDown, TbChevronRight, TbInbox, TbStar, TbTag } from "react-icons/tb"
import { Constants } from "@/app/constants"
import {
redirectToCategory,
redirectToCategoryDetails,
@@ -8,16 +10,14 @@ import {
redirectToFeedDetails,
redirectToTag,
redirectToTagDetails,
} from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import type { TreeSubscription } from "app/tree/slice"
import { collapseTreeCategory } from "app/tree/thunks"
import type { Category, Subscription } from "app/types"
import { categoryHasNewEntries, categoryUnreadCount, flattenCategoryTree } from "app/utils"
import { Loader } from "components/Loader"
import { OnDesktop } from "components/responsive/OnDesktop"
import React from "react"
import { TbChevronDown, TbChevronRight, TbInbox, TbStar, TbTag } from "react-icons/tb"
} from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { TreeSubscription } from "@/app/tree/slice"
import { collapseTreeCategory } from "@/app/tree/thunks"
import type { Category, Subscription } from "@/app/types"
import { categoryHasNewEntries, categoryUnreadCount, flattenCategoryTree } from "@/app/utils"
import { Loader } from "@/components/Loader"
import { OnDesktop } from "@/components/responsive/OnDesktop"
import { TreeNode } from "./TreeNode"
import { TreeSearch } from "./TreeSearch"

View File

@@ -1,8 +1,8 @@
import { Box, Center } from "@mantine/core"
import type { EntrySourceType } from "app/entries/slice"
import { FeedFavicon } from "components/content/FeedFavicon"
import type React from "react"
import { tss } from "tss"
import type { EntrySourceType } from "@/app/entries/slice"
import { FeedFavicon } from "@/components/content/FeedFavicon"
import { tss } from "@/tss"
import { UnreadCount } from "./UnreadCount"
interface TreeNodeProps {
@@ -59,7 +59,7 @@ const useStyles = tss
}
})
export function TreeNode(props: TreeNodeProps) {
export function TreeNode(props: Readonly<TreeNodeProps>) {
const { classes } = useStyles({
selected: props.selected,
hasError: props.hasError,

View File

@@ -3,18 +3,18 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro"
import { Box, TextInput } from "@mantine/core"
import { Spotlight, type SpotlightActionData, spotlight } from "@mantine/spotlight"
import { redirectToFeed } from "app/redirect/thunks"
import { useAppDispatch } from "app/store"
import type { Subscription } from "app/types"
import { FeedFavicon } from "components/content/FeedFavicon"
import { useMousetrap } from "hooks/useMousetrap"
import { TbSearch } from "react-icons/tb"
import { redirectToFeed } from "@/app/redirect/thunks"
import { useAppDispatch } from "@/app/store"
import type { Subscription } from "@/app/types"
import { FeedFavicon } from "@/components/content/FeedFavicon"
import { useMousetrap } from "@/hooks/useMousetrap"
export interface TreeSearchProps {
feeds: Subscription[]
}
export function TreeSearch(props: TreeSearchProps) {
export function TreeSearch(props: Readonly<TreeSearchProps>) {
const dispatch = useAppDispatch()
const { _ } = useLingui()

View File

@@ -1,6 +1,6 @@
import { Badge, Indicator, Tooltip } from "@mantine/core"
import { Constants } from "app/constants"
import { tss } from "tss"
import { Constants } from "@/app/constants"
import { tss } from "@/tss"
const useStyles = tss.create(() => ({
badge: {
@@ -10,7 +10,12 @@ const useStyles = tss.create(() => ({
},
}))
export function UnreadCount(props: { unreadCount: number; showIndicator: boolean }) {
export function UnreadCount(
props: Readonly<{
unreadCount: number
showIndicator: boolean
}>
) {
const { classes } = useStyles()
if (props.unreadCount <= 0) return null

View File

@@ -1,5 +1,5 @@
import { useMantineTheme } from "@mantine/core"
import { useMobile } from "hooks/useMobile"
import { useMobile } from "@/hooks/useMobile"
export const useActionButton = () => {
const theme = useMantineTheme()

View File

@@ -1,6 +1,6 @@
import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react"
import { useAppSelector } from "app/store"
import { useAppSelector } from "@/app/store"
interface Step {
label: string

View File

@@ -1,5 +1,5 @@
import { useMediaQuery } from "@mantine/hooks"
import { Constants } from "app/constants"
import { Constants } from "@/app/constants"
export const useMobile = (breakpoint: string | number = Constants.layout.mobileBreakpoint) => {
const bp = typeof breakpoint === "number" ? `${breakpoint}px` : breakpoint

View File

@@ -1,8 +1,8 @@
import { setWebSocketConnected } from "app/server/slice"
import { type AppDispatch, useAppDispatch, useAppSelector } from "app/store"
import { newFeedEntriesDiscovered } from "app/tree/thunks"
import { useEffect } from "react"
import WebsocketHeartbeatJs from "websocket-heartbeat-js"
import { setWebSocketConnected } from "@/app/server/slice"
import { type AppDispatch, useAppDispatch, useAppSelector } from "@/app/store"
import { newFeedEntriesDiscovered } from "@/app/tree/thunks"
const handleMessage = (dispatch: AppDispatch, message: string) => {
const parts = message.split(":")

View File

@@ -1,7 +1,7 @@
import { type Messages, i18n } from "@lingui/core"
import { useAppSelector } from "app/store"
import { i18n, type Messages } from "@lingui/core"
import dayjs from "dayjs"
import { useEffect } from "react"
import { useAppSelector } from "@/app/store"
interface Locale {
key: string
@@ -12,34 +12,146 @@ interface Locale {
// add an object to the array to add a new locale
// don't forget to also add it to the 'locales' array in lingui.config.ts
export const locales: Locale[] = [
{ key: "ar", label: "العربية", dayjsImportFn: async () => await import("dayjs/locale/ar") },
{ key: "ca", label: "Català", dayjsImportFn: async () => await import("dayjs/locale/ca") },
{ key: "cs", label: "Čeština", dayjsImportFn: async () => await import("dayjs/locale/cs") },
{ key: "cy", label: "Cymraeg", dayjsImportFn: async () => await import("dayjs/locale/cy") },
{ key: "da", label: "Danish", dayjsImportFn: async () => await import("dayjs/locale/da") },
{ key: "de", label: "Deutsch", dayjsImportFn: async () => await import("dayjs/locale/de") },
{ key: "en", label: "English", dayjsImportFn: async () => await import("dayjs/locale/en") },
{ key: "es", label: "Español", dayjsImportFn: async () => await import("dayjs/locale/es") },
{ key: "fa", label: "فارسی", dayjsImportFn: async () => await import("dayjs/locale/fa") },
{ key: "fi", label: "Suomi", dayjsImportFn: async () => await import("dayjs/locale/fi") },
{ key: "fr", label: "Français", dayjsImportFn: async () => await import("dayjs/locale/fr") },
{ key: "gl", label: "Galician", dayjsImportFn: async () => await import("dayjs/locale/gl") },
{ key: "hu", label: "Magyar", dayjsImportFn: async () => await import("dayjs/locale/hu") },
{ key: "id", label: "Indonesian", dayjsImportFn: async () => await import("dayjs/locale/id") },
{ key: "it", label: "Italiano", dayjsImportFn: async () => await import("dayjs/locale/it") },
{ key: "ja", label: "日本語", dayjsImportFn: async () => await import("dayjs/locale/ja") },
{ key: "ko", label: "한국어", dayjsImportFn: async () => await import("dayjs/locale/ko") },
{ key: "ms", label: "Bahasa Malaysian", dayjsImportFn: async () => await import("dayjs/locale/ms") },
{ key: "nb", label: "Norsk (bokmål)", dayjsImportFn: async () => await import("dayjs/locale/nb") },
{ key: "nl", label: "Nederlands", dayjsImportFn: async () => await import("dayjs/locale/nl") },
{ key: "nn", label: "Norsk (nynorsk)", dayjsImportFn: async () => await import("dayjs/locale/nn") },
{ key: "pl", label: "Polski", dayjsImportFn: async () => await import("dayjs/locale/pl") },
{ key: "pt", label: "Português", dayjsImportFn: async () => await import("dayjs/locale/pt") },
{ key: "ru", label: "Русский", dayjsImportFn: async () => await import("dayjs/locale/ru") },
{ key: "sk", label: "Slovenčina", dayjsImportFn: async () => await import("dayjs/locale/sk") },
{ key: "sv", label: "Svenska", dayjsImportFn: async () => await import("dayjs/locale/sv") },
{ key: "tr", label: "Türkçe", dayjsImportFn: async () => await import("dayjs/locale/tr") },
{ key: "zh", label: "简体中文", dayjsImportFn: async () => await import("dayjs/locale/zh") },
{
key: "ar",
label: "العربية",
dayjsImportFn: async () => await import("dayjs/locale/ar"),
},
{
key: "ca",
label: "Català",
dayjsImportFn: async () => await import("dayjs/locale/ca"),
},
{
key: "cs",
label: "Čeština",
dayjsImportFn: async () => await import("dayjs/locale/cs"),
},
{
key: "cy",
label: "Cymraeg",
dayjsImportFn: async () => await import("dayjs/locale/cy"),
},
{
key: "da",
label: "Danish",
dayjsImportFn: async () => await import("dayjs/locale/da"),
},
{
key: "de",
label: "Deutsch",
dayjsImportFn: async () => await import("dayjs/locale/de"),
},
{
key: "en",
label: "English",
dayjsImportFn: async () => await import("dayjs/locale/en"),
},
{
key: "es",
label: "Español",
dayjsImportFn: async () => await import("dayjs/locale/es"),
},
{
key: "fa",
label: "فارسی",
dayjsImportFn: async () => await import("dayjs/locale/fa"),
},
{
key: "fi",
label: "Suomi",
dayjsImportFn: async () => await import("dayjs/locale/fi"),
},
{
key: "fr",
label: "Français",
dayjsImportFn: async () => await import("dayjs/locale/fr"),
},
{
key: "gl",
label: "Galician",
dayjsImportFn: async () => await import("dayjs/locale/gl"),
},
{
key: "hu",
label: "Magyar",
dayjsImportFn: async () => await import("dayjs/locale/hu"),
},
{
key: "id",
label: "Indonesian",
dayjsImportFn: async () => await import("dayjs/locale/id"),
},
{
key: "it",
label: "Italiano",
dayjsImportFn: async () => await import("dayjs/locale/it"),
},
{
key: "ja",
label: "日本語",
dayjsImportFn: async () => await import("dayjs/locale/ja"),
},
{
key: "ko",
label: "한국어",
dayjsImportFn: async () => await import("dayjs/locale/ko"),
},
{
key: "ms",
label: "Bahasa Malaysian",
dayjsImportFn: async () => await import("dayjs/locale/ms"),
},
{
key: "nb",
label: "Norsk (bokmål)",
dayjsImportFn: async () => await import("dayjs/locale/nb"),
},
{
key: "nl",
label: "Nederlands",
dayjsImportFn: async () => await import("dayjs/locale/nl"),
},
{
key: "nn",
label: "Norsk (nynorsk)",
dayjsImportFn: async () => await import("dayjs/locale/nn"),
},
{
key: "pl",
label: "Polski",
dayjsImportFn: async () => await import("dayjs/locale/pl"),
},
{
key: "pt",
label: "Português",
dayjsImportFn: async () => await import("dayjs/locale/pt"),
},
{
key: "ru",
label: "Русский",
dayjsImportFn: async () => await import("dayjs/locale/ru"),
},
{
key: "sk",
label: "Slovenčina",
dayjsImportFn: async () => await import("dayjs/locale/sk"),
},
{
key: "sv",
label: "Svenska",
dayjsImportFn: async () => await import("dayjs/locale/sv"),
},
{
key: "tr",
label: "Türkçe",
dayjsImportFn: async () => await import("dayjs/locale/tr"),
},
{
key: "zh",
label: "简体中文",
dayjsImportFn: async () => await import("dayjs/locale/zh"),
},
]
function activateLocale(locale: string) {

View File

@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx
@@ -33,10 +33,6 @@ msgstr ""
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0> هل تحتاج إلى حساب؟ </0> <1> اشترك! </ 1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr "رابط"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "تحميل ملف التعريف ..."
@@ -602,6 +602,10 @@ msgstr "الاسم"
msgid "Navigate to a subscription by entering its name"
msgstr "انتقل إلى اشتراك بإدخال اسمه"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,8 +18,8 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr "<0>CommaFeed és un projecte de codi obert. El codi font està allotjat a </0><1>GitHub</1>."
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgstr "<0>La sintaxi completa està disponible </0><1>aquí</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr "<0>La sintaxi completa està disponible </0><1>aquí</1><2>.</2>"
#: src/pages/auth/RegistrationPage.tsx
msgid "<0>Have an account?</0><1>Log in!</1>"
@@ -33,10 +33,6 @@ msgstr "<0>Ei,</0><1> sóc la Jérémie de Bèlgica i fa més de 10 anys que tre
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Necessites un compte?</0><1>Registreu-vos!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -139,7 +135,7 @@ msgstr "Tornar a iniciar sessió"
#: src/components/settings/DisplaySettings.tsx
msgid "Blue"
msgstr ""
msgstr "Blau"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
@@ -151,7 +147,7 @@ msgstr "Extensió del navegador"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr ""
msgstr "Pestanya del navegador"
#: src/pages/app/TagDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
@@ -191,7 +187,7 @@ msgstr "Tanca el menu"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Cmd"
msgstr ""
msgstr "Cmd"
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed browser extension version {browserExtensionVersion}."
@@ -251,7 +247,7 @@ msgstr "Codi JS personalitzat que s'executarà en carregar la pàgina"
#: src/components/settings/DisplaySettings.tsx
msgid "Cyan"
msgstr ""
msgstr "Cian"
#: src/components/settings/DisplaySettings.tsx
#: src/components/header/ProfileMenu.tsx
@@ -338,11 +334,11 @@ msgstr "introduïu la vostra contrasenya actual per canviar la configuració del
#: src/components/settings/DisplaySettings.tsx
msgid "Entries to keep above the selected entry when scrolling"
msgstr ""
msgstr "Entrades que es mantindran a sobre de l'entrada seleccionada en desplaçar-se"
#: src/components/settings/DisplaySettings.tsx
msgid "Entry headers"
msgstr ""
msgstr "Encapçalaments d'entrada"
#: src/components/Alert.tsx
msgid "Error"
@@ -381,11 +377,11 @@ msgstr "Carrega tots els meus feeds ara"
#: src/components/settings/ProfileSettings.tsx
msgid "Fever API"
msgstr ""
msgstr "Fever API"
#: src/components/settings/ProfileSettings.tsx
msgid "Fever API URL"
msgstr ""
msgstr "URL de Fever API"
#: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression"
@@ -393,11 +389,11 @@ msgstr "Expressió de filtratge"
#: src/components/header/ProfileMenu.tsx
msgid "Font size"
msgstr ""
msgstr "Mida de la lletra"
#: src/components/header/ProfileMenu.tsx
msgid "Force fetching feeds is not yet available."
msgstr ""
msgstr "La recuperació forçada de feeds encara no està disponible."
#: src/pages/auth/LoginPage.tsx
msgid "Forgot password?"
@@ -438,15 +434,15 @@ msgstr "Bones"
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr ""
msgstr "Raïm"
#: src/components/settings/DisplaySettings.tsx
msgid "Gray"
msgstr ""
msgstr "Gris"
#: src/components/settings/DisplaySettings.tsx
msgid "Green"
msgstr ""
msgstr "Verd"
#: src/pages/admin/AdminUsersPage.tsx
msgid "Id"
@@ -470,11 +466,11 @@ msgstr "Importació"
#: src/components/settings/DisplaySettings.tsx
msgid "In expanded view, scrolling through entries mark them as read"
msgstr "a la vista ampliada, desplaçant-se per les entrades les marqueu com a llegides"
msgstr "En la vista ampliada, en desplaçar-se per les entrades, es marquen com a llegides"
#: src/components/settings/DisplaySettings.tsx
msgid "Indigo"
msgstr ""
msgstr "Indi"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryContextMenu.tsx
@@ -508,7 +504,7 @@ msgstr "Clar"
#: src/components/settings/DisplaySettings.tsx
msgid "Lime"
msgstr ""
msgstr "Llima"
#: src/pages/app/TagDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr "Enllaç"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr "Enllaç a la documentació"
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Carregant el perfil..."
@@ -544,7 +544,7 @@ msgstr "Tanca sessió"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
msgstr "Prem llargament la tecla"
#: src/pages/admin/AdminUsersPage.tsx
#: src/components/header/ProfileMenu.tsx
@@ -589,7 +589,7 @@ msgstr "Mou la pàgina cap amunt"
#: src/pages/app/FeedDetailsPage.tsx
#: src/components/RelativeDate.tsx
msgid "N/A"
msgstr ""
msgstr "No es coneix"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
@@ -602,6 +602,10 @@ msgstr "Nom"
msgid "Navigate to a subscription by entering its name"
msgstr "Navegueu a una subscripció introduint-ne el nom"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navega a la següent categoria/canal amb entrades no llegides quan es marquen totes les entrades com a llegides"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
@@ -634,7 +638,7 @@ msgstr "No hi ha més entrades"
#: src/components/content/ShareButtons.tsx
msgid "No sharing options available."
msgstr ""
msgstr "No hi ha opcions de compartició disponibles."
#: src/components/sidebar/TreeSearch.tsx
msgid "Nothing found"
@@ -646,11 +650,11 @@ msgstr "el més vell primer"
#: src/components/settings/DisplaySettings.tsx
msgid "On desktop"
msgstr ""
msgstr "A l'scriptori"
#: src/components/settings/DisplaySettings.tsx
msgid "On mobile"
msgstr ""
msgstr "Al mòbil"
#: src/components/settings/DisplaySettings.tsx
msgid "On mobile, show action buttons at the bottom of the screen"
@@ -658,7 +662,7 @@ msgstr "Al mòbil, mostra els botons d'acció a la part inferior de la pantalla"
#: src/components/settings/DisplaySettings.tsx
msgid "Only applies to compact, cozy and detailed modes"
msgstr ""
msgstr "Només s'aplica als modes compacte, acollidor i detallat"
#: src/pages/ErrorPage.tsx
msgid "Oops!"
@@ -720,11 +724,11 @@ msgstr "Fitxer OPML"
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file is required"
msgstr ""
msgstr "Cal un fitxer OPML"
#: src/components/settings/DisplaySettings.tsx
msgid "Orange"
msgstr ""
msgstr "Taronja"
#: src/pages/app/AboutPage.tsx
msgid "Order"
@@ -756,7 +760,7 @@ msgstr "Les contrasenyes no coincideixen"
#: src/components/settings/DisplaySettings.tsx
msgid "Pink"
msgstr ""
msgstr "Rosa"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
@@ -769,7 +773,7 @@ msgstr "Anterior"
#: src/components/settings/DisplaySettings.tsx
msgid "Primary color"
msgstr ""
msgstr "Color primari"
#: src/pages/app/SettingsPage.tsx
msgid "Profile"
@@ -781,7 +785,7 @@ msgstr "Recuperar la contrasenya"
#: src/components/settings/DisplaySettings.tsx
msgid "Red"
msgstr ""
msgstr "Vermell"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/header/Header.tsx
@@ -830,19 +834,19 @@ msgstr "Cerca"
#: src/components/header/Header.tsx
msgid "Search requires at least 3 characters"
msgstr "la cerca requereix almenys 3 caràcters"
msgstr "La cerca requereix almenys 3 caràcters"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Select next unread feed/category"
msgstr ""
msgstr "Selecciona el següent canal/categoria no llegit"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Select previous unread feed/category"
msgstr ""
msgstr "Selecciona el canal/categoria anterior sense llegir"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Set focus on next entry without opening it"
msgstr "posa el focus a la següent entrada sense obrir-la"
msgstr "Posa el focus a la següent entrada sense obrir-la"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Set focus on previous entry without opening it"
@@ -862,7 +866,7 @@ msgstr "Comparteix"
#: src/components/settings/DisplaySettings.tsx
msgid "Sharing sites"
msgstr "Compartir llocs"
msgstr "Compartir a altres llocs web"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
@@ -888,7 +892,7 @@ msgstr "Mostra el menú d'entrada (mòbil)"
#: src/components/settings/DisplaySettings.tsx
msgid "Show external link icon"
msgstr ""
msgstr "Mostra la icona d'enllaç extern"
#: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries"
@@ -904,15 +908,15 @@ msgstr "Mostra el menú natiu (escriptori)"
#: src/components/settings/DisplaySettings.tsx
msgid "Show star icon"
msgstr ""
msgstr "Mostra la icona d'estrella"
#: src/components/settings/DisplaySettings.tsx
msgid "Show unread count in tab favicon"
msgstr ""
msgstr "Mostra el recompte de no llegits a la icona de favorits de la pestanya"
#: src/components/settings/DisplaySettings.tsx
msgid "Show unread count in tab title"
msgstr ""
msgstr "Mostra el recompte de no llegits al títol de la pestanya"
#: src/pages/WelcomePage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -982,7 +986,7 @@ msgstr "Etiquetes"
#: src/components/settings/DisplaySettings.tsx
msgid "Teal"
msgstr ""
msgstr "Blau verdós"
#: src/components/content/add/Subscribe.tsx
msgid "The URL for the feed you want to subscribe to. You can also use the website's url directly and CommaFeed will try to find the feed in the page."
@@ -1042,7 +1046,7 @@ msgstr "Nom d'usuari o correu electrònic"
#: src/components/settings/DisplaySettings.tsx
msgid "Violet"
msgstr ""
msgstr "Violeta"
#: src/components/Alert.tsx
msgid "Warning"
@@ -1054,7 +1058,7 @@ msgstr "Lloc web"
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""
msgstr "Groc"
#: src/pages/app/FeedEntriesPage.tsx
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"

View File

@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx
@@ -33,10 +33,6 @@ msgstr ""
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Potřebujete účet?</0><1>Zaregistrujte se!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr "Odkaz"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Načítání profilu..."
@@ -602,6 +602,10 @@ msgstr "Jméno"
msgid "Navigate to a subscription by entering its name"
msgstr "Přejděte na předplatné zadáním jeho názvu"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx
@@ -33,10 +33,6 @@ msgstr ""
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Angen cyfrif?</0><1>Ymunwch!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr "Cyswllt"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Wrthi'n llwytho proffil..."
@@ -602,6 +602,10 @@ msgstr "Enw"
msgid "Navigate to a subscription by entering its name"
msgstr "Llywiwch i danysgrifiad trwy nodi ei enw"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx
@@ -33,10 +33,6 @@ msgstr ""
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Har du brug for en konto?</0><1>Tilmeld dig!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr ""
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Indlæser profil..."
@@ -602,6 +602,10 @@ msgstr "Navn"
msgid "Navigate to a subscription by entering its name"
msgstr "Naviger til et abonnement ved at indtaste dets navn"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,8 +18,8 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr "<0>CommaFeed ist ein Open Source Projekt. Der Quellcode wird auf auf </0><1>GitHub</1> gehostet."
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgstr "<0>Die vollständige Syntax ist </0><1>hier</1> verfügbar."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr "<0>Die vollständige Syntax ist </0><1>hier</1> verfügbar<2>.</2>"
#: src/pages/auth/RegistrationPage.tsx
msgid "<0>Have an account?</0><1>Log in!</1>"
@@ -33,10 +33,6 @@ msgstr "<0>Hey,</0><1>Ich bin Jérémie aus Belgien und arbeite seit über 10 Ja
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Benötigen Sie ein Konto?</0><1>Hier geht's zur Registrierung!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr "Verbindung"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Lade Profil..."
@@ -602,6 +602,10 @@ msgstr ""
msgid "Navigate to a subscription by entering its name"
msgstr "Navigieren Sie zu einem Abonnement, indem Sie seinen Namen eingeben"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,8 +18,8 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgstr "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr "<0>Complete syntax is available </0><1>here</1><2>.</2>"
#: src/pages/auth/RegistrationPage.tsx
msgid "<0>Have an account?</0><1>Log in!</1>"
@@ -33,10 +33,6 @@ msgstr "<0>Hey,</0><1>I'm Jérémie from Belgium and I've been working on CommaF
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Need an account?</0><1>Sign up!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr "<0>See </0><1>here</1><2> for more information.</2>"
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr "Lime"
msgid "Link"
msgstr "Link"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr "Link to the documentation"
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Loading profile..."
@@ -602,6 +602,10 @@ msgstr "Name"
msgid "Navigate to a subscription by entering its name"
msgstr "Navigate to a subscription by entering its name"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navigate to the next category/feed with unread entries when marking all entries as read"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -19,8 +19,8 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr "<0>CommaFeed es un proyecto de código abierto. El código fuente está hospedado en </0><1>GitHub</1>."
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgstr "<0>La sintaxis completa está disponible </0><1>aquí</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr "<0>La sintaxis completa está disponible </0><1>aquí</1><2>.</2>"
#: src/pages/auth/RegistrationPage.tsx
msgid "<0>Have an account?</0><1>Log in!</1>"
@@ -34,10 +34,6 @@ msgstr "<0>Hola,</0><1>Soy Jérémie de Bélgica y he estado trabajando en Comma
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>¿Necesitas una cuenta?</0><1>¡Regístrate!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -517,6 +513,10 @@ msgstr ""
msgid "Link"
msgstr "Enlace"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Cargando perfil..."
@@ -603,6 +603,10 @@ msgstr "Nombre"
msgid "Navigate to a subscription by entering its name"
msgstr "Navegar a una suscripción introduciendo su nombre"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx
@@ -33,10 +33,6 @@ msgstr ""
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>به یک حساب نیاز دارید؟</0><1>ثبت نام کنید!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr "پیوند"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "بارگیری نمایه..."
@@ -602,6 +602,10 @@ msgstr "نام"
msgid "Navigate to a subscription by entering its name"
msgstr "با وارد کردن نام اشتراک، به آن بروید"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx
@@ -33,10 +33,6 @@ msgstr ""
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Tarvitsetko tilin?</0><1>Rekisteröidy!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr "Linkki"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Ladataan profiilia..."
@@ -602,6 +602,10 @@ msgstr "Nimi"
msgid "Navigate to a subscription by entering its name"
msgstr "Siirry tilaukseen kirjoittamalla sen nimi"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,8 +18,8 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr "<0>CommaFeed est un projet open-source. Les sources sont hébergées sur </0><1>GitHub</1>."
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgstr "<0>La syntaxe complète est disponible </0><1>ici</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr "<0>La syntaxe complète est disponible </0><1>ici</1><2>.</2>"
#: src/pages/auth/RegistrationPage.tsx
msgid "<0>Have an account?</0><1>Log in!</1>"
@@ -33,10 +33,6 @@ msgstr "<0>Salut,</0><1>Je m'appelle Jérémie, je suis belge, et je développe
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Besoin d'un compte ?</0><1>Enregistrez-vous !</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr "Jaune-vert"
msgid "Link"
msgstr "Lien"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr "Lien vers la documentation"
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Chargement du profil..."
@@ -602,6 +602,10 @@ msgstr "Nom"
msgid "Navigate to a subscription by entering its name"
msgstr "Naviguer vers un abonnement en entrant son nom"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Aller vers la catégorie/le flux comportant des entrées non lues suivant après avoir marqué toutes les entrées lues"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx
@@ -33,10 +33,6 @@ msgstr ""
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Necesitas unha conta?</0><1>Rexístrate!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr "Ligazón"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Cargando perfil..."
@@ -602,6 +602,10 @@ msgstr "Nome"
msgid "Navigate to a subscription by entering its name"
msgstr "Navega a unha subscrición introducindo o seu nome"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx
@@ -33,10 +33,6 @@ msgstr ""
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Fiókra van szüksége?</0><1>Regisztráljon!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr ""
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Profil betöltése..."
@@ -602,6 +602,10 @@ msgstr "Név"
msgid "Navigate to a subscription by entering its name"
msgstr "Navigáljon egy előfizetéshez a nevének megadásával"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx
@@ -33,10 +33,6 @@ msgstr ""
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Butuh akun?</0><1>Daftar!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr "Tautan"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Memuat profil..."
@@ -602,6 +602,10 @@ msgstr "Nama"
msgid "Navigate to a subscription by entering its name"
msgstr "Navigasikan ke langganan dengan memasukkan namanya"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

View File

@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>."
msgid "<0>Complete syntax is available </0><1>here</1><2>.</2>"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx
@@ -33,10 +33,6 @@ msgstr ""
msgid "<0>Need an account?</0><1>Sign up!</1>"
msgstr "<0>Hai bisogno di un account?</0><1>Registrati!</1>"
#: src/components/settings/CustomCodeSettings.tsx
msgid "<0>See </0><1>here</1><2> for more information.</2>"
msgstr ""
#: src/pages/app/AboutPage.tsx
#: src/components/header/ProfileMenu.tsx
msgid "About"
@@ -516,6 +512,10 @@ msgstr ""
msgid "Link"
msgstr "Collegamento"
#: src/components/settings/CustomCodeSettings.tsx
msgid "Link to the documentation"
msgstr ""
#: src/hooks/useAppLoading.ts
msgid "Loading profile..."
msgstr "Caricamento profilo..."
@@ -602,6 +602,10 @@ msgstr "Nome"
msgid "Navigate to a subscription by entering its name"
msgstr "Navigare verso un abbonamento inserendo il suo nome"
#: src/components/settings/DisplaySettings.tsx
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"

Some files were not shown because too many files have changed in this diff Show More