forked from Archives/Athou_commafeed
Compare commits
580 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53f0c33c1d | ||
|
|
563516901e | ||
|
|
73b40fd8b7 | ||
|
|
08224a8486 | ||
|
|
993f3d3aa8 | ||
|
|
3a975de136 | ||
|
|
48b5195798 | ||
|
|
8eb34c7539 | ||
|
|
d75d7a9209 | ||
|
|
ddf851f1eb | ||
|
|
889dd00c23 | ||
|
|
c5ea2a1aa1 | ||
|
|
1489aff78e | ||
|
|
640296d42f | ||
|
|
3b12b2a5f6 | ||
|
|
d5c41a5167 | ||
|
|
58bf86d25d | ||
|
|
d73034d6d9 | ||
|
|
151a613dcc | ||
|
|
4bb741a42f | ||
|
|
cc9c8d3db3 | ||
|
|
c3d4831550 | ||
|
|
31e385fbfb | ||
|
|
a8c47d717c | ||
|
|
9a25157d3f | ||
|
|
9176e0f7b7 | ||
|
|
ff7aa890a6 | ||
|
|
03312c1592 | ||
|
|
9d1ec2c636 | ||
|
|
c49c31a44e | ||
|
|
947c1f562f | ||
|
|
2d1dbb6988 | ||
|
|
622e46ff67 | ||
|
|
4ff45a65c3 | ||
|
|
a62676061b | ||
|
|
11d77d2265 | ||
|
|
1e7d44b250 | ||
|
|
ffd86c6d8c | ||
|
|
a566c9460d | ||
|
|
24edae3d58 | ||
|
|
97876344c4 | ||
|
|
95dbeb9a47 | ||
|
|
3fc64859b1 | ||
|
|
896fe3b5b2 | ||
|
|
85404781a3 | ||
|
|
efe2abc86e | ||
|
|
b70b7a0b40 | ||
|
|
865c80f87b | ||
|
|
23a91aab12 | ||
|
|
085a3cbb50 | ||
|
|
fb9d875c31 | ||
|
|
5ee15c6f68 | ||
|
|
9853205849 | ||
|
|
2c9ce7e8fc | ||
|
|
9753ae60e2 | ||
|
|
bd66f1e682 | ||
|
|
ed6a45c119 | ||
|
|
8f53ce27fc | ||
|
|
f7ae2e6689 | ||
|
|
c6cc47192c | ||
|
|
1c447fe369 | ||
|
|
6b5c92db48 | ||
|
|
427e020d27 | ||
|
|
18084995b2 | ||
|
|
f894fdf564 | ||
|
|
0b0a964a90 | ||
|
|
d6df979d0d | ||
|
|
c366c37afe | ||
|
|
20cbd239b2 | ||
|
|
a9c7595ee7 | ||
|
|
3f09e3ca64 | ||
|
|
ed42db7a0d | ||
|
|
c85daeb46e | ||
|
|
3f2b93f1f8 | ||
|
|
78d2e66c56 | ||
|
|
0f2de651ff | ||
|
|
2eb7c7237e | ||
|
|
3b8f62ff11 | ||
|
|
f8bf9370de | ||
|
|
30cd0ec089 | ||
|
|
e984be9289 | ||
|
|
8069787754 | ||
|
|
343e442dff | ||
|
|
313ccdeae9 | ||
|
|
fdec8ebfd3 | ||
|
|
efddd86263 | ||
|
|
7d18bde40b | ||
|
|
7fee410be4 | ||
|
|
ebc2516a53 | ||
|
|
ade4d1d782 | ||
|
|
07f7a288d2 | ||
|
|
380ed16caf | ||
|
|
db654a10d1 | ||
|
|
2cf84d35cd | ||
|
|
a4eac86913 | ||
|
|
5168be45a8 | ||
|
|
163ab43da3 | ||
|
|
e5fa517270 | ||
|
|
b8b8ea5ce2 | ||
|
|
991b147af5 | ||
|
|
ecff62d0fa | ||
|
|
cdec4c0879 | ||
|
|
e8085ac4cf | ||
|
|
327062112b | ||
|
|
6dfc23c33a | ||
|
|
a601b0ab35 | ||
|
|
a48c8ca87a | ||
|
|
b59e64a3d1 | ||
|
|
5fc62dd06d | ||
|
|
d81a0cae91 | ||
|
|
50e31c6b69 | ||
|
|
92d3d88127 | ||
|
|
517fdb2095 | ||
|
|
d16ebb02b4 | ||
|
|
a5c64c8b7b | ||
|
|
5287a93484 | ||
|
|
06e84d9032 | ||
|
|
3a63dd032a | ||
|
|
889e227523 | ||
|
|
57d895daf5 | ||
|
|
a744394faa | ||
|
|
64e3c25bad | ||
|
|
75f85e1fb2 | ||
|
|
00bd4cab37 | ||
|
|
a9527f59a9 | ||
|
|
77661930f0 | ||
|
|
80a09bd9a0 | ||
|
|
6eb7cfbdc2 | ||
|
|
fb186530aa | ||
|
|
6c121ccb90 | ||
|
|
c08ad3b365 | ||
|
|
1668bc88ad | ||
|
|
3a43f62460 | ||
|
|
bfba5179d1 | ||
|
|
78bf7856dc | ||
|
|
e0c708f677 | ||
|
|
794d6824e8 | ||
|
|
15573a7bee | ||
|
|
31c61a79c6 | ||
|
|
87ca427094 | ||
|
|
99bdc904e0 | ||
|
|
2fdee68feb | ||
|
|
7be014f83e | ||
|
|
5668fe0a33 | ||
|
|
32c07efe19 | ||
|
|
21b23d0f79 | ||
|
|
793d0dd13f | ||
|
|
14e8ff4c1b | ||
|
|
416ab06997 | ||
|
|
493cd60dae | ||
|
|
e0948e1e9e | ||
|
|
5776b8c044 | ||
|
|
38ab4105d8 | ||
|
|
5ed9dadcc2 | ||
|
|
357d7e2381 | ||
|
|
8cfaab3e9f | ||
|
|
fef2404357 | ||
|
|
1aa1bce8c8 | ||
|
|
124b2761f6 | ||
|
|
066ca1af7c | ||
|
|
c20520879b | ||
|
|
4fa5b2b856 | ||
|
|
5c1b1fad76 | ||
|
|
c18d248c06 | ||
|
|
d46ee7f673 | ||
|
|
f2c0d99bd9 | ||
|
|
60ee0b9185 | ||
|
|
4b3e660ae7 | ||
|
|
0b42392bfc | ||
|
|
a94d7ce235 | ||
|
|
72aec432ed | ||
|
|
0e5db8d604 | ||
|
|
dc45fb4b84 | ||
|
|
6503d38fe3 | ||
|
|
32c89d9a11 | ||
|
|
f279465750 | ||
|
|
58ec1b022a | ||
|
|
612199429e | ||
|
|
e5482f9051 | ||
|
|
05df14fda2 | ||
|
|
29898ba1ba | ||
|
|
93d1cec503 | ||
|
|
9884f44122 | ||
|
|
d400456685 | ||
|
|
c39069cafd | ||
|
|
5fb0edc318 | ||
|
|
21a6b2d780 | ||
|
|
40c9063a54 | ||
|
|
59b0103ed5 | ||
|
|
f4730e9338 | ||
|
|
b7b520ca3c | ||
|
|
21d44e6a55 | ||
|
|
607886f0f0 | ||
|
|
7cd3c68256 | ||
|
|
6e37c1bd86 | ||
|
|
5db1a0748f | ||
|
|
a7584df4f4 | ||
|
|
4421197403 | ||
|
|
15b59467fb | ||
|
|
c95ff0a2ce | ||
|
|
7eff9df025 | ||
|
|
2f05e53e14 | ||
|
|
6089fe4036 | ||
|
|
10d9af0d86 | ||
|
|
c119d5062a | ||
|
|
324609ee60 | ||
|
|
a0a65f2b45 | ||
|
|
45e5ca704c | ||
|
|
f361be0c72 | ||
|
|
1611dc5703 | ||
|
|
04faad84a4 | ||
|
|
19c42e5838 | ||
|
|
4918b69d0a | ||
|
|
c7cec464aa | ||
|
|
91857c4d73 | ||
|
|
fc6f9f4258 | ||
|
|
34f9f9374a | ||
|
|
0ae4c1621f | ||
|
|
c393f5c045 | ||
|
|
1624290dc1 | ||
|
|
c6491990ac | ||
|
|
15dea17923 | ||
|
|
689d5ac7b2 | ||
|
|
2142e20e7d | ||
|
|
dc23126570 | ||
|
|
55856f9060 | ||
|
|
c756ce5fc8 | ||
|
|
0546f25d55 | ||
|
|
7b33717333 | ||
|
|
6ea6d16e58 | ||
|
|
a9b65c83aa | ||
|
|
a497802b50 | ||
|
|
42b0428b9a | ||
|
|
931c553e1d | ||
|
|
f3c0b92a3c | ||
|
|
970cabf241 | ||
|
|
e321ecde5d | ||
|
|
32ac326a77 | ||
|
|
134dcd4466 | ||
|
|
26a44353d4 | ||
|
|
55acb3ef28 | ||
|
|
0e96307726 | ||
|
|
0199a36238 | ||
|
|
3f2f6e83fa | ||
|
|
4fa780cac2 | ||
|
|
edb0f655b0 | ||
|
|
651ada7073 | ||
|
|
efb5d49d04 | ||
|
|
f78cc18b06 | ||
|
|
8acffa11e5 | ||
|
|
f4246807ff | ||
|
|
abf6e7131b | ||
|
|
b2688520cc | ||
|
|
fad0aea108 | ||
|
|
0b63773c83 | ||
|
|
3ef28009ac | ||
|
|
8979e2b191 | ||
|
|
d6910aa1e8 | ||
|
|
afc56c6053 | ||
|
|
1bd504cbfb | ||
|
|
2c089ddb5e | ||
|
|
0b5245643a | ||
|
|
ae35d43f7f | ||
|
|
fe55682c9f | ||
|
|
0d3e6f17e2 | ||
|
|
d5659c4278 | ||
|
|
69b87b9026 | ||
|
|
168bcd3a37 | ||
|
|
e3b6be0cd0 | ||
|
|
eeceda0ca8 | ||
|
|
aa903039c8 | ||
|
|
73d81d0cdb | ||
|
|
01fe539af6 | ||
|
|
c08063ca57 | ||
|
|
60d4af2890 | ||
|
|
6378f074a8 | ||
|
|
5082ec86fd | ||
|
|
6cff5bb099 | ||
|
|
d54562d56f | ||
|
|
2b45a8fae5 | ||
|
|
8654df8994 | ||
|
|
4d5145c17e | ||
|
|
850921bca9 | ||
|
|
1dc6470419 | ||
|
|
b5c197f499 | ||
|
|
d417655a86 | ||
|
|
ebca9f8290 | ||
|
|
53b1f89b30 | ||
|
|
6885191877 | ||
|
|
e69d9fe8b8 | ||
|
|
d6a1f1ae15 | ||
|
|
a7813f4442 | ||
|
|
1e4664987a | ||
|
|
7a819f5d58 | ||
|
|
45ef56e9da | ||
|
|
f43c7aa5d0 | ||
|
|
8d88711e59 | ||
|
|
280c0b60e9 | ||
|
|
605f8f6615 | ||
|
|
0e88b4de1b | ||
|
|
b02aa923d7 | ||
|
|
680c927e1d | ||
|
|
4b2e65abdc | ||
|
|
e501bf6b05 | ||
|
|
80bf2582bd | ||
|
|
586da4424d | ||
|
|
8e0e8c2407 | ||
|
|
40461ac883 | ||
|
|
9288a7e66e | ||
|
|
275db4ec72 | ||
|
|
67b2f8968d | ||
|
|
45ce35dfdb | ||
|
|
52aa9ab2fe | ||
|
|
d24725bd55 | ||
|
|
f368a67dec | ||
|
|
564d1744e1 | ||
|
|
d091ecfa5f | ||
|
|
7d23165e14 | ||
|
|
a9ca3278c6 | ||
|
|
e8734710ca | ||
|
|
f8a0e20df9 | ||
|
|
de90e4de54 | ||
|
|
03cb27f69a | ||
|
|
f86f1dd770 | ||
|
|
ec21ffc571 | ||
|
|
c73c9c74ba | ||
|
|
fd3c264d0c | ||
|
|
3983ba6cd0 | ||
|
|
dc0b5bdd11 | ||
|
|
5b3728186e | ||
|
|
8b0936b678 | ||
|
|
b99e81a389 | ||
|
|
0b8fb0f9a7 | ||
|
|
7d5bbe0130 | ||
|
|
c6637c6814 | ||
|
|
e302a011bb | ||
|
|
25f8bdaa28 | ||
|
|
e7d1018cbc | ||
|
|
e81fa69a03 | ||
|
|
176e76ad2d | ||
|
|
0925a91089 | ||
|
|
c04d0b147c | ||
|
|
a349eff1a3 | ||
|
|
2ddeda9a27 | ||
|
|
00b0fe921f | ||
|
|
bcc6cdf4b1 | ||
|
|
ba46cc3cd6 | ||
|
|
ed1bf609b8 | ||
|
|
13262b678d | ||
|
|
cf9321de23 | ||
|
|
ac7a78bdc0 | ||
|
|
640d3f1f6e | ||
|
|
d716a8081c | ||
|
|
1cec4e68b1 | ||
|
|
9d16299c5b | ||
|
|
cb92b1969c | ||
|
|
55a62d393d | ||
|
|
027b2252db | ||
|
|
ccfb88ddcc | ||
|
|
dde0736fcf | ||
|
|
6498bb5ee6 | ||
|
|
d7ca2db330 | ||
|
|
99b32795a5 | ||
|
|
840f670d5d | ||
|
|
e3c0a4c665 | ||
|
|
915506527a | ||
|
|
7541251344 | ||
|
|
40c9b42b24 | ||
|
|
dfab678070 | ||
|
|
3d371d5942 | ||
|
|
9bfbaa8ded | ||
|
|
be0fc95c45 | ||
|
|
dcb6113eb7 | ||
|
|
55491651f6 | ||
|
|
c1d471ebdc | ||
|
|
069e675f19 | ||
|
|
d34f719a4f | ||
|
|
4eb932a3f0 | ||
|
|
1ab27f2626 | ||
|
|
7d3ce7e602 | ||
|
|
cf530b2c60 | ||
|
|
d9bcd7f592 | ||
|
|
8b854b5cda | ||
|
|
f7b6677bb1 | ||
|
|
0cc5e8f5b8 | ||
|
|
8d153e3b2b | ||
|
|
d15428971c | ||
|
|
f34c2aa437 | ||
|
|
9b3ff5f81f | ||
|
|
e1f6937802 | ||
|
|
0c0834b30f | ||
|
|
5ad4b97205 | ||
|
|
c4ec249bc4 | ||
|
|
cf8d3965d5 | ||
|
|
3903fd9374 | ||
|
|
77d59dabe8 | ||
|
|
56ca737297 | ||
|
|
9edb539be3 | ||
|
|
31a773d200 | ||
|
|
61355eabf7 | ||
|
|
569874e51f | ||
|
|
00d47901fc | ||
|
|
d8b4ef55ce | ||
|
|
da41a4cab9 | ||
|
|
8a90ef0471 | ||
|
|
b4ab32a578 | ||
|
|
03aa53abc8 | ||
|
|
2ae5c0cd8e | ||
|
|
cacc632443 | ||
|
|
28f865ccfa | ||
|
|
a4c949e8b3 | ||
|
|
6098994397 | ||
|
|
5763ca30d6 | ||
|
|
7d039d1001 | ||
|
|
7fe74af906 | ||
|
|
80b72aa30b | ||
|
|
3ba0d241f9 | ||
|
|
67428aa0c7 | ||
|
|
b9a0256031 | ||
|
|
f3c2296636 | ||
|
|
b6e8f21975 | ||
|
|
284f80045f | ||
|
|
f589477aa8 | ||
|
|
29cb296d09 | ||
|
|
86caa1450a | ||
|
|
9dd4b9e67f | ||
|
|
e2e654f05b | ||
|
|
72dbc62b41 | ||
|
|
0a21014668 | ||
|
|
b6d9d2a26c | ||
|
|
25c3a7748c | ||
|
|
b2bcfdd6eb | ||
|
|
2a978db406 | ||
|
|
9e40d0d066 | ||
|
|
c912650d59 | ||
|
|
464ebcb471 | ||
|
|
463e0e59d7 | ||
|
|
b4e5d8ef20 | ||
|
|
126905aeb3 | ||
|
|
1af10d3364 | ||
|
|
6ad854c019 | ||
|
|
b30117aa4d | ||
|
|
5a66482d1e | ||
|
|
2628ec49bb | ||
|
|
f3d15cf173 | ||
|
|
bbcf55ce57 | ||
|
|
72fc3716e7 | ||
|
|
81a6cfaa88 | ||
|
|
aed5165ef3 | ||
|
|
eaf2933726 | ||
|
|
39da4d9d36 | ||
|
|
e5ebd7ff39 | ||
|
|
b6ae3e4e1e | ||
|
|
32d1488352 | ||
|
|
b08d0a388f | ||
|
|
7fe004a696 | ||
|
|
f620d033b0 | ||
|
|
ba071ba71f | ||
|
|
6f3197302d | ||
|
|
131a8ebf68 | ||
|
|
8b24c125c2 | ||
|
|
52293376ec | ||
|
|
f8ac59af6a | ||
|
|
5c791e2305 | ||
|
|
6641bc0631 | ||
|
|
da690aa750 | ||
|
|
fb7f041454 | ||
|
|
ec4554c76e | ||
|
|
068e85fe6e | ||
|
|
ba926c674e | ||
|
|
836f8f14c0 | ||
|
|
eeecac96e1 | ||
|
|
ecc62f222a | ||
|
|
9022f93811 | ||
|
|
e7225d35b2 | ||
|
|
454fc03038 | ||
|
|
9c0674fd83 | ||
|
|
7a20482ddf | ||
|
|
32ad47ba16 | ||
|
|
fc562cce0f | ||
|
|
b029b251db | ||
|
|
e3e28e727f | ||
|
|
50cb728db7 | ||
|
|
c654ba4d1b | ||
|
|
846e29b15e | ||
|
|
f2b4062d73 | ||
|
|
9051e6a6db | ||
|
|
b733129043 | ||
|
|
d46b571444 | ||
|
|
7d744b4ce0 | ||
|
|
801dda912c | ||
|
|
a20005409a | ||
|
|
6f1411d075 | ||
|
|
1aa263a6c0 | ||
|
|
9d511ac7dd | ||
|
|
122e98cc76 | ||
|
|
e445e5ea39 | ||
|
|
5b9212015b | ||
|
|
293292f341 | ||
|
|
57d8a4dbb1 | ||
|
|
e104f531f9 | ||
|
|
bf1361926f | ||
|
|
cc4f4d9eb4 | ||
|
|
706bad26f1 | ||
|
|
4ecefe6491 | ||
|
|
937e7353ce | ||
|
|
1dcf76fc0a | ||
|
|
9d794dcad7 | ||
|
|
d11b666755 | ||
|
|
7a444e4861 | ||
|
|
5992795579 | ||
|
|
4441d76a7f | ||
|
|
c1305b56e3 | ||
|
|
cc0440c029 | ||
|
|
f65591c170 | ||
|
|
9a32dce9d1 | ||
|
|
789bd3edae | ||
|
|
256cd426d9 | ||
|
|
58af2da105 | ||
|
|
e0de397273 | ||
|
|
75cc3cf29c | ||
|
|
af60758e2a | ||
|
|
01180e95a2 | ||
|
|
fa683ef7e1 | ||
|
|
462d17a429 | ||
|
|
17f71a40d4 | ||
|
|
de91a3a05a | ||
|
|
ead587ee88 | ||
|
|
62b3e6fb3a | ||
|
|
037ff15045 | ||
|
|
ed35b06934 | ||
|
|
3cfb1a13a7 | ||
|
|
d04745d859 | ||
|
|
58b18f36c5 | ||
|
|
7282d18d8f | ||
|
|
8e58fa22b4 | ||
|
|
58d6eb2c5a | ||
|
|
2f7c7498e2 | ||
|
|
bcf8dcd551 | ||
|
|
511f0a60bb | ||
|
|
72db0d815f | ||
|
|
280d0b7fdd | ||
|
|
42e4575cb7 | ||
|
|
28a4bb403a | ||
|
|
cca3c907db | ||
|
|
1a5b932742 | ||
|
|
a1d3f3008a | ||
|
|
902f2efbd2 | ||
|
|
2e534af146 | ||
|
|
23ca30c3c2 | ||
|
|
517eedad00 | ||
|
|
216ea1fb42 | ||
|
|
640d1a0ce3 | ||
|
|
bba7425b5f | ||
|
|
7a1a49bfb4 | ||
|
|
e451e6698c | ||
|
|
9af3f21404 | ||
|
|
7b14a9c0c2 | ||
|
|
0b65cc9510 | ||
|
|
7879ab9b61 | ||
|
|
e6bebcafb3 | ||
|
|
3b465cebb7 | ||
|
|
aeb211be06 | ||
|
|
ad992aea7b | ||
|
|
d848f72a0b | ||
|
|
0db087908d | ||
|
|
42138d04d6 | ||
|
|
4522a9d0d5 | ||
|
|
7440fcad0e | ||
|
|
fc51c1882f | ||
|
|
e24498b31f | ||
|
|
60fdc79563 | ||
|
|
6729ebc6ea | ||
|
|
c8ff216ce5 | ||
|
|
98c4150cfe | ||
|
|
128332d710 | ||
|
|
eabcb519a4 | ||
|
|
5e14cead3d | ||
|
|
b601f938ff | ||
|
|
4acfda32d0 |
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
* text eol=lf
|
||||||
|
*.cmd text eol=crlf
|
||||||
|
*.png binary
|
||||||
36
.github/stale.yml
vendored
36
.github/stale.yml
vendored
@@ -1,19 +1,19 @@
|
|||||||
# Number of days of inactivity before an issue becomes stale
|
# Number of days of inactivity before an issue becomes stale
|
||||||
daysUntilStale: 60
|
daysUntilStale: 60
|
||||||
# Number of days of inactivity before a stale issue is closed
|
# Number of days of inactivity before a stale issue is closed
|
||||||
daysUntilClose: 7
|
daysUntilClose: 7
|
||||||
# Issues with these labels will never be considered stale
|
# Issues with these labels will never be considered stale
|
||||||
exemptLabels:
|
exemptLabels:
|
||||||
- pinned
|
- pinned
|
||||||
- security
|
- security
|
||||||
- enhancement
|
- enhancement
|
||||||
- bug
|
- bug
|
||||||
# Label to use when marking an issue as stale
|
# Label to use when marking an issue as stale
|
||||||
staleLabel: wontfix
|
staleLabel: wontfix
|
||||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
markComment: >
|
markComment: >
|
||||||
This issue has been automatically marked as stale because it has not had
|
This issue has been automatically marked as stale because it has not had
|
||||||
recent activity. It will be closed if no further activity occurs. Thank you
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
for your contributions.
|
for your contributions.
|
||||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
closeComment: false
|
closeComment: false
|
||||||
505
.github/workflows/ci.yml
vendored
505
.github/workflows/ci.yml
vendored
@@ -1,227 +1,278 @@
|
|||||||
name: ci
|
name: ci
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 21
|
JAVA_VERSION: 21
|
||||||
DOCKER_BUILD_SUMMARY: false
|
DOCKER_BUILD_SUMMARY: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.event_name != 'pull_request' || github.actor != 'renovate[bot]' # renovate already triggers the build on pushes
|
if: github.event_name != 'pull_request' || github.actor != 'renovate[bot]' # renovate already triggers the build on pushes
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ "ubuntu-latest", "ubuntu-22.04-arm", "windows-latest" ]
|
os: [ "ubuntu-latest", "ubuntu-22.04-arm", "windows-latest" ]
|
||||||
database: [ "h2", "postgresql", "mysql", "mariadb" ]
|
database: [ "h2", "postgresql", "mysql", "mariadb" ]
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
# Checkout
|
# Checkout
|
||||||
- name: Configure git to checkout as-is
|
- name: Checkout
|
||||||
run: git config --global core.autocrlf false
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
with:
|
||||||
- name: Checkout
|
fetch-depth: 0
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
with:
|
# Setup
|
||||||
fetch-depth: 0
|
- name: Set up GraalVM
|
||||||
|
uses: graalvm/setup-graalvm@7f488cf82a3629ee755e4e97342c01d6bed318fa # v1
|
||||||
# Setup
|
with:
|
||||||
- name: Set up GraalVM
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
uses: graalvm/setup-graalvm@b0cb26a8da53cb3e97cdc0c827d8e3071240e730 # v1
|
distribution: "graalvm"
|
||||||
with:
|
cache: "maven"
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
|
||||||
distribution: "graalvm"
|
- name: Install Playwright dependencies
|
||||||
cache: "maven"
|
run: sudo apt-get install -y libgbm1
|
||||||
|
if: matrix.os != 'windows-latest'
|
||||||
- name: Install Playwright dependencies
|
|
||||||
run: sudo apt-get install -y libgbm1
|
# Build & Test
|
||||||
if: matrix.os != 'windows-latest'
|
- name: Build with Maven
|
||||||
|
run: mvn --batch-mode --no-transfer-progress install -Pnative -P${{ matrix.database }} -DskipTests=${{ matrix.os == 'windows-latest' && matrix.database != 'h2' }}
|
||||||
# Build & Test
|
|
||||||
- name: Build with Maven
|
# Build pages
|
||||||
run: mvn --batch-mode --no-transfer-progress install -Pnative -P${{ matrix.database }} -DskipTests=${{ matrix.os == 'windows-latest' && matrix.database != 'h2' }}
|
- name: Create pages directory structure
|
||||||
|
run: mkdir -p target/pages/documentation/custom-css
|
||||||
# Upload artifacts
|
|
||||||
- name: Upload cross-platform app
|
- name: Convert readme file to html
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
|
uses: jaywcjlove/markdown-to-html-cli@d2c8ffd676de1801e2586904bc540a938e4bc480 # v5.0.3
|
||||||
if: matrix.os == 'ubuntu-latest' # we only need to upload the cross-platform artifact once per database
|
with:
|
||||||
with:
|
source: README.md
|
||||||
name: commafeed-${{ matrix.database }}-jvm
|
output: target/pages/index.html
|
||||||
path: commafeed-server/target/commafeed-*.zip
|
|
||||||
|
- name: Convert config documentation to html
|
||||||
- name: Upload native executable
|
uses: jaywcjlove/markdown-to-html-cli@d2c8ffd676de1801e2586904bc540a938e4bc480 # v5.0.3
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
|
with:
|
||||||
with:
|
source: commafeed-server/target/quarkus-generated-doc/config/commafeed-server.md
|
||||||
name: commafeed-${{ matrix.database }}-${{ runner.os }}-${{ runner.arch }}
|
output: target/pages/documentation/index.html
|
||||||
path: commafeed-server/target/commafeed-*-runner*
|
|
||||||
|
- name: Convert custom css documentation to html
|
||||||
docker:
|
uses: jaywcjlove/markdown-to-html-cli@d2c8ffd676de1801e2586904bc540a938e4bc480 # v5.0.3
|
||||||
runs-on: ubuntu-latest
|
with:
|
||||||
needs: build
|
source: documentation/CUSTOMCSS.md
|
||||||
env:
|
output: target/pages/documentation/custom-css/index.html
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
|
# Upload artifacts
|
||||||
strategy:
|
- name: Upload cross-platform app
|
||||||
matrix:
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
database: [ "h2", "postgresql", "mysql", "mariadb" ]
|
if: matrix.os == 'ubuntu-latest' # we only need to upload the cross-platform artifact once per database
|
||||||
|
with:
|
||||||
steps:
|
name: commafeed-${{ matrix.database }}-jvm
|
||||||
# Checkout
|
path: commafeed-server/target/commafeed-*.zip
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- name: Upload native executable
|
||||||
with:
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
fetch-depth: 0
|
with:
|
||||||
|
name: commafeed-${{ matrix.database }}-${{ runner.os }}-${{ runner.arch }}
|
||||||
# Setup
|
path: commafeed-server/target/commafeed-*-runner*
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@4574d27a4764455b42196d70a065bc6853246a25 # v3
|
- name: Upload pages
|
||||||
|
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
|
||||||
- name: Set up Docker Buildx
|
if: matrix.os == 'ubuntu-latest' && matrix.database == 'h2' # we only need to upload the pages once
|
||||||
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3
|
with:
|
||||||
|
path: target/pages
|
||||||
- name: Install required packages
|
|
||||||
run: sudo apt-get install -y rename unzip
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
# Prepare artifacts
|
needs: build
|
||||||
- name: Download artifacts
|
env:
|
||||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
with:
|
|
||||||
pattern: commafeed-${{ matrix.database }}-*
|
strategy:
|
||||||
path: ./artifacts
|
matrix:
|
||||||
merge-multiple: true
|
database: [ "h2", "postgresql", "mysql", "mariadb" ]
|
||||||
|
|
||||||
- name: Set the exec flag on the native executables
|
steps:
|
||||||
run: chmod +x artifacts/*-runner
|
# Checkout
|
||||||
|
- name: Checkout
|
||||||
- name: Rename native executables to match buildx TARGETARCH
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
run: |
|
with:
|
||||||
rename 's/x86_64/amd64/g' artifacts/*
|
fetch-depth: 0
|
||||||
rename 's/aarch_64/arm64/g' artifacts/*
|
|
||||||
|
# Setup
|
||||||
- name: Unzip jvm package
|
- name: Set up QEMU
|
||||||
run: |
|
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3
|
||||||
unzip artifacts/*-jvm.zip -d artifacts/extracted-jvm-package
|
|
||||||
rename 's/commafeed-.*/quarkus-app/g' artifacts/extracted-jvm-package/*
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
|
||||||
# Docker
|
|
||||||
- name: Login to Container Registry
|
- name: Install required packages
|
||||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
run: sudo apt-get install -y rename unzip
|
||||||
if: ${{ env.DOCKERHUB_USERNAME != '' }}
|
|
||||||
with:
|
# Prepare artifacts
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
- name: Download artifacts
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||||
|
with:
|
||||||
## build but don't push for PRs and renovate
|
pattern: commafeed-${{ matrix.database }}-*
|
||||||
- name: Docker build - native
|
path: ./artifacts
|
||||||
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6
|
merge-multiple: true
|
||||||
with:
|
|
||||||
context: .
|
- name: Set the exec flag on the native executables
|
||||||
file: commafeed-server/src/main/docker/Dockerfile.native
|
run: chmod +x artifacts/*-runner
|
||||||
push: false
|
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
- name: Rename native executables to match buildx TARGETARCH
|
||||||
|
run: |
|
||||||
- name: Docker build - jvm
|
rename 's/x86_64/amd64/g' artifacts/*
|
||||||
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6
|
rename 's/aarch_64/arm64/g' artifacts/*
|
||||||
with:
|
|
||||||
context: .
|
- name: Unzip jvm package
|
||||||
file: commafeed-server/src/main/docker/Dockerfile.jvm
|
run: |
|
||||||
push: false
|
unzip artifacts/*-jvm.zip -d artifacts/extracted-jvm-package
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
rename 's/commafeed-.*/quarkus-app/g' artifacts/extracted-jvm-package/*
|
||||||
|
|
||||||
## build and push tag
|
# Docker
|
||||||
- name: Docker build and push tag - native
|
- name: Login to Container Registry
|
||||||
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
if: ${{ env.DOCKERHUB_USERNAME != '' }}
|
||||||
with:
|
with:
|
||||||
context: .
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
file: commafeed-server/src/main/docker/Dockerfile.native
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
push: ${{ env.DOCKERHUB_USERNAME != '' }}
|
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
## build but don't push for PRs and renovate
|
||||||
tags: |
|
- name: Docker build - native
|
||||||
athou/commafeed:latest-${{ matrix.database }}
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||||
athou/commafeed:${{ github.ref_name }}-${{ matrix.database }}
|
with:
|
||||||
|
context: .
|
||||||
- name: Docker build and push tag - jvm
|
file: commafeed-server/src/main/docker/Dockerfile.native
|
||||||
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6
|
push: false
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
with:
|
|
||||||
context: .
|
- name: Docker build - jvm
|
||||||
file: commafeed-server/src/main/docker/Dockerfile.jvm
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||||
push: ${{ env.DOCKERHUB_USERNAME != '' }}
|
with:
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
context: .
|
||||||
tags: |
|
file: commafeed-server/src/main/docker/Dockerfile.jvm
|
||||||
athou/commafeed:latest-${{ matrix.database }}-jvm
|
push: false
|
||||||
athou/commafeed:${{ github.ref_name }}-${{ matrix.database }}-jvm
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
|
||||||
## build and push master
|
## build and push tag
|
||||||
- name: Docker build and push master - native
|
- name: Docker build and push tag - native
|
||||||
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||||
if: ${{ github.ref_name == 'master' }}
|
if: ${{ github.ref_type == 'tag' }}
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: commafeed-server/src/main/docker/Dockerfile.native
|
file: commafeed-server/src/main/docker/Dockerfile.native
|
||||||
push: ${{ env.DOCKERHUB_USERNAME != '' }}
|
push: ${{ env.DOCKERHUB_USERNAME != '' }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
tags: athou/commafeed:master-${{ matrix.database }}
|
tags: |
|
||||||
|
athou/commafeed:latest-${{ matrix.database }}
|
||||||
- name: Docker build and push master - jvm
|
athou/commafeed:${{ github.ref_name }}-${{ matrix.database }}
|
||||||
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6
|
|
||||||
if: ${{ github.ref_name == 'master' }}
|
- name: Docker build and push tag - jvm
|
||||||
with:
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||||
context: .
|
if: ${{ github.ref_type == 'tag' }}
|
||||||
file: commafeed-server/src/main/docker/Dockerfile.jvm
|
with:
|
||||||
push: ${{ env.DOCKERHUB_USERNAME != '' }}
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
file: commafeed-server/src/main/docker/Dockerfile.jvm
|
||||||
tags: athou/commafeed:master-${{ matrix.database }}-jvm
|
push: ${{ env.DOCKERHUB_USERNAME != '' }}
|
||||||
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
release:
|
tags: |
|
||||||
runs-on: ubuntu-latest
|
athou/commafeed:latest-${{ matrix.database }}-jvm
|
||||||
needs:
|
athou/commafeed:${{ github.ref_name }}-${{ matrix.database }}-jvm
|
||||||
- build
|
|
||||||
- docker
|
## build and push master
|
||||||
permissions:
|
- name: Docker build and push master - native
|
||||||
contents: write
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||||
if: github.ref_type == 'tag'
|
if: ${{ github.ref_name == 'master' }}
|
||||||
|
with:
|
||||||
steps:
|
context: .
|
||||||
- name: Checkout
|
file: commafeed-server/src/main/docker/Dockerfile.native
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
push: ${{ env.DOCKERHUB_USERNAME != '' }}
|
||||||
with:
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
fetch-depth: 0
|
tags: athou/commafeed:master-${{ matrix.database }}
|
||||||
|
|
||||||
- name: Download artifacts
|
- name: Docker build and push master - jvm
|
||||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||||
with:
|
if: ${{ github.ref_name == 'master' }}
|
||||||
pattern: commafeed-*
|
with:
|
||||||
path: ./artifacts
|
context: .
|
||||||
merge-multiple: true
|
file: commafeed-server/src/main/docker/Dockerfile.jvm
|
||||||
|
push: ${{ env.DOCKERHUB_USERNAME != '' }}
|
||||||
- name: Set the exec flag on the native executables
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
run: chmod +x artifacts/*-runner
|
tags: athou/commafeed:master-${{ matrix.database }}-jvm
|
||||||
|
|
||||||
- name: Extract Changelog Entry
|
release:
|
||||||
uses: mindsers/changelog-reader-action@32aa5b4c155d76c94e4ec883a223c947b2f02656 # v2
|
runs-on: ubuntu-latest
|
||||||
id: changelog_reader
|
needs:
|
||||||
with:
|
- build
|
||||||
version: ${{ github.ref_name }}
|
- docker
|
||||||
|
permissions:
|
||||||
- name: Create GitHub release
|
contents: write
|
||||||
uses: ncipollo/release-action@cdcc88a9acf3ca41c16c37bb7d21b9ad48560d87 # v1
|
if: github.ref_type == 'tag'
|
||||||
with:
|
|
||||||
name: CommaFeed ${{ github.ref_name }}
|
steps:
|
||||||
body: ${{ steps.changelog_reader.outputs.changes }}
|
- name: Checkout
|
||||||
artifacts: ./artifacts/*
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
with:
|
||||||
- name: Update Docker Hub Description
|
fetch-depth: 0
|
||||||
uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae # v4
|
|
||||||
with:
|
- name: Download artifacts
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
with:
|
||||||
repository: athou/commafeed
|
pattern: commafeed-*
|
||||||
short-description: ${{ github.event.repository.description }}
|
path: ./artifacts
|
||||||
readme-filepath: commafeed-server/src/main/docker/README.md
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Set the exec flag on the native executables
|
||||||
|
run: chmod +x artifacts/*-runner
|
||||||
|
|
||||||
|
- name: Extract Changelog Entry
|
||||||
|
uses: mindsers/changelog-reader-action@32aa5b4c155d76c94e4ec883a223c947b2f02656 # v2
|
||||||
|
id: changelog_reader
|
||||||
|
with:
|
||||||
|
version: ${{ github.ref_name }}
|
||||||
|
|
||||||
|
- name: Create GitHub release
|
||||||
|
uses: ncipollo/release-action@bcfe5470707e8832e12347755757cec0eb3c22af # v1
|
||||||
|
with:
|
||||||
|
name: CommaFeed ${{ github.ref_name }}
|
||||||
|
body: ${{ steps.changelog_reader.outputs.changes }}
|
||||||
|
artifacts: ./artifacts/*
|
||||||
|
|
||||||
|
|
||||||
|
update-dockerhub-description:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Update Docker Hub Description
|
||||||
|
uses: peter-evans/dockerhub-description@432a30c9e07499fd01da9f8a49f0faf9e0ca5b77 # v4
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
repository: athou/commafeed
|
||||||
|
short-description: ${{ github.event.repository.description }}
|
||||||
|
readme-filepath: commafeed-server/src/main/docker/README.md
|
||||||
|
|
||||||
|
|
||||||
|
deploy-pages:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: release
|
||||||
|
permissions:
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
|
||||||
|
id: deployment
|
||||||
|
|||||||
78
.github/workflows/scorecard.yml
vendored
Normal file
78
.github/workflows/scorecard.yml
vendored
Normal 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
41
.github/workflows/sonar.yml
vendored
Normal 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
|
||||||
36
.mvn/wrapper/maven-wrapper.properties
vendored
36
.mvn/wrapper/maven-wrapper.properties
vendored
@@ -1,18 +1,18 @@
|
|||||||
# Licensed to the Apache Software Foundation (ASF) under one
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
# or more contributor license agreements. See the NOTICE file
|
# or more contributor license agreements. See the NOTICE file
|
||||||
# distributed with this work for additional information
|
# distributed with this work for additional information
|
||||||
# regarding copyright ownership. The ASF licenses this file
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
# to you under the Apache License, Version 2.0 (the
|
# to you under the Apache License, Version 2.0 (the
|
||||||
# "License"); you may not use this file except in compliance
|
# "License"); you may not use this file except in compliance
|
||||||
# with the License. You may obtain a copy of the License at
|
# with the License. You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing,
|
# Unless required by applicable law or agreed to in writing,
|
||||||
# software distributed under the License is distributed on an
|
# software distributed under the License is distributed on an
|
||||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
# KIND, either express or implied. See the License for the
|
# KIND, either express or implied. See the License for the
|
||||||
# specific language governing permissions and limitations
|
# specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
distributionType=only-script
|
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
|
||||||
|
|||||||
963
CHANGELOG.md
963
CHANGELOG.md
@@ -1,461 +1,502 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [5.6.0]
|
## [5.11.0]
|
||||||
|
|
||||||
- To better respect the bandwidth of feed owners, the default value of `commafeed.feed-refresh.interval-empirical` is now true. This means feeds no longer refresh exactly every 5 minutes (the default value of `commafeed.feed-refresh.interval`) but between 5 minutes and 4 hours (the default value of the new `commafeed.feed-refresh.max-interval` setting). The interval is calculated based on feed activity, so highly active feeds refresh more often (#1677)
|
- Add an option to navigate to the next unread category/feed when marking all entries as read (#1807)
|
||||||
- Many previously hardcoded values used in feed refresh interval calculation are now exposed as settings (#1677)
|
- Google Analytics support has been removed
|
||||||
- Access to local addresses is now blocked to mitigate server-side request forgery (SSRF) attacks, which could potentially expose internal resources. You might want to disable the new `commafeed.http-client.block-local-addresses` setting if you subscribe to feeds only available on your local network and you trust all your users
|
|
||||||
- If a feed responds with a "429 - Too many requests" response, a backoff mechanism is triggered when the response does not contain a "Retry-After" header
|
## [5.10.0]
|
||||||
|
|
||||||
## [5.5.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)
|
||||||
|
- Feeds with uppercase HTTP:// or HTTPS:// URLs are now correctly handled again
|
||||||
- CommaFeed now honors the Retry-After response header and will not try to refresh a feed sooner than the value of this header
|
- The aarch64 native executable now also works on the Raspberry Pi 5 (#1795)
|
||||||
- Audio enclosures (e.g. podcasts) now fill available entry width
|
- Improve general performance of the UI by reducing the number of re-renders, especially when a lot of entries are displayed (#1087)
|
||||||
- Fix an issue with some labels not correctly internationalized
|
|
||||||
|
## [5.9.0]
|
||||||
## [5.4.0]
|
|
||||||
|
- A lot of CSS classes have been added to the elements of the application to ease custom CSS rules (#1757)
|
||||||
- An arm64 native executable is now available for download on the releases page
|
- Added a link in the README to the [documentation](https://athou.github.io/commafeed/documentation/custom-css/) of the new CSS classes
|
||||||
- The native executable Docker image now supports arm64
|
- Static resources are now cached for much longer (#1782)
|
||||||
- Fixed an issue with feeds that declared an invalid DOCTYPE (#1260)
|
|
||||||
|
## [5.8.0]
|
||||||
## [5.3.6]
|
|
||||||
|
- A color picker is now available on the settings page to change the orange accent of the application (#1598)
|
||||||
- Ignore invalid Cache-Control header values (#1619)
|
- A font size slider is now available to change the size of the text of feed entries (#1462)
|
||||||
|
- The "mark all as read" confirmation setting now also applies to the "shift+a" keyboard shortcut (#1744)
|
||||||
## [5.3.5]
|
- CommaFeed wil try to match the language of the browser before defaulting to english (#1767)
|
||||||
|
- The default value for the number of entries to keep above the selected entry when scrolling is now 1 instead of 0 to match what other feed readers do
|
||||||
- Fixed an issue with the aspect ratio of images of some feeds (#1595)
|
|
||||||
- CommaFeed now honors the Cache-Control response header and will not try to refresh a feed sooner than its max-age property (#1615)
|
## [5.7.0]
|
||||||
- Added support for compilation with JDK 23+. If you're building CommaFeed from sources with a JDK 17 or 21, you may need to update it to the most recent patch version to support `-proc:full` (#1618)
|
|
||||||
|
- Add Shift+J/Shift+K keyboard shortcuts to navigate to the next/previous feed or category with unread entries (#1558)
|
||||||
## [5.3.4]
|
- Add the referrer "no-referrer" meta to index.html (#1724)
|
||||||
|
- Load custom JS code when the app is done loading (#1724)
|
||||||
- Added support for Internationalized Domain Names (#1588)
|
- Correctly handle feeds that return an unmodified Last-Modified header but a different ETag header (#1746)
|
||||||
|
- Restore gzip compression of responses that was accidentaly disabled since 5.0.0
|
||||||
## [5.3.3]
|
- Fix tooltips not showing up in mobile view
|
||||||
|
- Fix the bookmarklet generator on the About page
|
||||||
- Removed image bottom margins (#1587)
|
|
||||||
|
## [5.6.1]
|
||||||
## [5.3.2]
|
|
||||||
|
- Restore support for iframes in feed entries (#1688)
|
||||||
- Fixed an issue that could cause some images from not being rendered correctly (#1587)
|
- There is now a package available for Arch Linux thanks to @dcelasun (#1691)
|
||||||
|
|
||||||
## [5.3.1]
|
## [5.6.0]
|
||||||
|
|
||||||
- Fixed an issue that could cause some HTTP feeds to return a 400 error (#1572)
|
- To better respect the bandwidth of feed owners, the default value of `commafeed.feed-refresh.interval-empirical` is now true. This means feeds no longer refresh exactly every 5 minutes (the default value of `commafeed.feed-refresh.interval`) but between 5 minutes and 4 hours (the default value of the new `commafeed.feed-refresh.max-interval` setting). The interval is calculated based on feed activity, so highly active feeds refresh more often (#1677)
|
||||||
|
- Many previously hardcoded values used in feed refresh interval calculation are now exposed as settings (#1677)
|
||||||
## [5.3.0]
|
- Access to local addresses is now blocked to mitigate server-side request forgery (SSRF) attacks, which could potentially expose internal resources. You might want to disable the new `commafeed.http-client.block-local-addresses` setting if you subscribe to feeds only available on your local network and you trust all your users
|
||||||
|
- If a feed responds with a "429 - Too many requests" response, a backoff mechanism is triggered when the response does not contain a "Retry-After" header
|
||||||
- Added a setting to set a cooldown on the "fetch all my feeds" action, disabled by default (#1556)
|
|
||||||
- Fixed an issue that could cause entries to not correctly load when using the "next" header button (#1557)
|
## [5.5.0]
|
||||||
|
|
||||||
## [5.2.0]
|
- CommaFeed now honors the Retry-After response header and will not try to refresh a feed sooner than the value of this header
|
||||||
|
- Audio enclosures (e.g. podcasts) now fill available entry width
|
||||||
- Added an option to keep a number of entries above the selected entry when scrolling
|
- Fix an issue with some labels not correctly internationalized
|
||||||
- Added a cache to the HTTP client to reduce the number of requests made to feeds when subscribing (#1431)
|
|
||||||
- Feeds are no longer refreshed between the moment its last user unsubscribes and the moment the feed is cleaned up (every hour)
|
## [5.4.0]
|
||||||
- Fixed an issue that could cause entries to not correctly load when using keyboard navigation (#1557)
|
|
||||||
|
- An arm64 native executable is now available for download on the releases page
|
||||||
## [5.1.1]
|
- The native executable Docker image now supports arm64
|
||||||
|
- Fixed an issue with feeds that declared an invalid DOCTYPE (#1260)
|
||||||
- Fixed database migration issue when upgrading from 5.0.0 to 5.1.0 on MariaDB (#1544)
|
|
||||||
- When feeds without unread entries are hidden from the tree, the feed is displayed in the tree until another one is selected (#1543)
|
## [5.3.6]
|
||||||
|
|
||||||
## [5.1.0]
|
- Ignore invalid Cache-Control header values (#1619)
|
||||||
|
|
||||||
- Added a setting for showing/hiding unread count in the browser's tab title/favicon (#1518)
|
## [5.3.5]
|
||||||
- Fixed an issue that could prevent the app from starting on some systems (#1532)
|
|
||||||
- Added a cache busting filter for the webapp index.html and openapi documentation to make sure they are always up to date
|
- Fixed an issue with the aspect ratio of images of some feeds (#1595)
|
||||||
- Reduced database cleanup log verbosity
|
- CommaFeed now honors the Cache-Control response header and will not try to refresh a feed sooner than its max-age property (#1615)
|
||||||
|
- Added support for compilation with JDK 23+. If you're building CommaFeed from sources with a JDK 17 or 21, you may need to update it to the most recent patch version to support `-proc:full` (#1618)
|
||||||
## [5.0.2]
|
|
||||||
|
## [5.3.4]
|
||||||
- Fix favicon fetching for Youtube channels in native mode when Google auth key is set
|
|
||||||
- Fix an error that appears in the logs when fetching some favicons
|
- Added support for Internationalized Domain Names (#1588)
|
||||||
|
|
||||||
## [5.0.1]
|
## [5.3.3]
|
||||||
|
|
||||||
- Configure native compilation to support older CPU architectures (#1524)
|
- Removed image bottom margins (#1587)
|
||||||
|
|
||||||
## [5.0.0]
|
## [5.3.2]
|
||||||
|
|
||||||
CommaFeed is now powered by Quarkus instead of Dropwizard. Read the rationale behind this change in
|
- Fixed an issue that could cause some images from not being rendered correctly (#1587)
|
||||||
the [announcement](https://github.com/Athou/commafeed/discussions/1517).
|
|
||||||
The gist of it is that CommaFeed can now be compiled to a native binary, resulting in blazing fast startup times (around
|
## [5.3.1]
|
||||||
0.3s) and very low memory footprint (< 50M).
|
|
||||||
|
- Fixed an issue that could cause some HTTP feeds to return a 400 error (#1572)
|
||||||
- CommaFeed now has a different package for each supported database.
|
|
||||||
- If you are deploying CommaFeed with a precompiled package, please
|
## [5.3.0]
|
||||||
read [this section of the README](https://github.com/Athou/commafeed/tree/master?tab=readme-ov-file#download-a-precompiled-package).
|
|
||||||
- If you are building CommaFeed from sources, please
|
- Added a setting to set a cooldown on the "fetch all my feeds" action, disabled by default (#1556)
|
||||||
read [this section of the README](https://github.com/Athou/commafeed/tree/master?tab=readme-ov-file#build-from-sources).
|
- Fixed an issue that could cause entries to not correctly load when using the "next" header button (#1557)
|
||||||
- If you are using the Docker image, please read the instructions on
|
|
||||||
the [Docker Hub page](https://hub.docker.com/r/athou/commafeed).
|
## [5.2.0]
|
||||||
- Due to the switch to Quarkus, the way CommaFeed is configured is very different (the `config.yml` file is gone).
|
|
||||||
Please
|
- Added an option to keep a number of entries above the selected entry when scrolling
|
||||||
read [this section of the README](https://github.com/Athou/commafeed/tree/master?tab=readme-ov-file#configuration).
|
- Added a cache to the HTTP client to reduce the number of requests made to feeds when subscribing (#1431)
|
||||||
Note that a lot of configuration elements have been removed or renamed and are now nested/grouped by feature.
|
- Feeds are no longer refreshed between the moment its last user unsubscribes and the moment the feed is cleaned up (every hour)
|
||||||
- Added a setting to prevent parsing large feeds to avoid out of memory errors. The default is 5MB.
|
- Fixed an issue that could cause entries to not correctly load when using keyboard navigation (#1557)
|
||||||
- Use a different icon for filtering unread entries and marking an entry as read (#1506)
|
|
||||||
- Added various HTML attributes to ease custom JS/CSS customization (#1507)
|
## [5.1.1]
|
||||||
- The Redis cache has been removed. There have been multiple enhancements to the feed refresh engine and it is no longer
|
|
||||||
needed, even for instances with a large number of feeds.
|
- Fixed database migration issue when upgrading from 5.0.0 to 5.1.0 on MariaDB (#1544)
|
||||||
- The H2 migration tool that automatically upgrades H2 databases from format 2 to 3 has been removed. If you're using
|
- When feeds without unread entries are hidden from the tree, the feed is displayed in the tree until another one is selected (#1543)
|
||||||
the H2 embedded database, please upgrade to at least version 4.3.0 before upgrading to CommaFeed 5.0.0.
|
|
||||||
|
## [5.1.0]
|
||||||
## [4.6.0]
|
|
||||||
|
- Added a setting for showing/hiding unread count in the browser's tab title/favicon (#1518)
|
||||||
- switched from Temurin to OpenJ9 as the JVM used in the Docker image, resulting in memory usage reduction by up to 50%
|
- Fixed an issue that could prevent the app from starting on some systems (#1532)
|
||||||
- fix an issue that could cause old entries to reappear if they were updated by their author (#1486)
|
- Added a cache busting filter for the webapp index.html and openapi documentation to make sure they are always up to date
|
||||||
- show all entries regardless of their read status when searching with keywords, even if the ui is configured to show
|
- Reduced database cleanup log verbosity
|
||||||
unread entries only
|
|
||||||
|
## [5.0.2]
|
||||||
## [4.5.0]
|
|
||||||
|
- Fix favicon fetching for Youtube channels in native mode when Google auth key is set
|
||||||
- significantly reduce the time needed to retrieve entries or mark them as read, especially when there are a lot of
|
- Fix an error that appears in the logs when fetching some favicons
|
||||||
entries (#1452)
|
|
||||||
- fix a race condition where a feed could be refreshed before it was created in the database
|
## [5.0.1]
|
||||||
- fix an issue that could cause the websocket notification to contain the wrong number of unread entries when using
|
|
||||||
mysql/mariadb
|
- Configure native compilation to support older CPU architectures (#1524)
|
||||||
- fix an error when trying to mark all starred entries as read
|
|
||||||
- remove the `onlyIds` parameter from REST endpoints since retrieving all the entries is now just as fast
|
## [5.0.0]
|
||||||
- remove support for microsoft sqlserver because it's not covered with integration tests (please open an issue if you'd
|
|
||||||
like it back)
|
CommaFeed is now powered by Quarkus instead of Dropwizard. Read the rationale behind this change in
|
||||||
|
the [announcement](https://github.com/Athou/commafeed/discussions/1517).
|
||||||
## [4.4.1]
|
The gist of it is that CommaFeed can now be compiled to a native binary, resulting in blazing fast startup times (around
|
||||||
|
0.3s) and very low memory footprint (< 50M).
|
||||||
- fix vertical scrolling issues with Safari (#1168)
|
|
||||||
- the default value for new users for the "star entry" button and the "open in new tab" button in the entry headers is
|
- CommaFeed now has a different package for each supported database.
|
||||||
now "on desktop" instead of "always"
|
- If you are deploying CommaFeed with a precompiled package, please
|
||||||
- the "keyboard shortcuts" help page now shows "Cmd" instead of "Ctrl" on macOS (#1389)
|
read [this section of the README](https://github.com/Athou/commafeed/tree/master?tab=readme-ov-file#download-a-precompiled-package).
|
||||||
- remove a superfluous feed fetch when subscribing to a feed (#1431)
|
- If you are building CommaFeed from sources, please
|
||||||
- the Docker image now uses Java 21
|
read [this section of the README](https://github.com/Athou/commafeed/tree/master?tab=readme-ov-file#build-from-sources).
|
||||||
|
- If you are using the Docker image, please read the instructions on
|
||||||
## [4.4.0]
|
the [Docker Hub page](https://hub.docker.com/r/athou/commafeed).
|
||||||
|
- Due to the switch to Quarkus, the way CommaFeed is configured is very different (the `config.yml` file is gone).
|
||||||
- add support for sharing using the browser native capabilities if available (#1255)
|
Please
|
||||||
- add a button in the entry headers to star an entry (#1025)
|
read [this section of the README](https://github.com/Athou/commafeed/tree/master?tab=readme-ov-file#configuration).
|
||||||
- add a button in the entry headers to open links in a new tab (#1333)
|
Note that a lot of configuration elements have been removed or renamed and are now nested/grouped by feature.
|
||||||
- add two options in the settings to toggle those buttons
|
- Added a setting to prevent parsing large feeds to avoid out of memory errors. The default is 5MB.
|
||||||
- accept .opml file extension when importing and export with the .opml extension
|
- Use a different icon for filtering unread entries and marking an entry as read (#1506)
|
||||||
- the "mark as read" option is no longer shown in the context menu for entries that are too old to be marked as read (
|
- Added various HTML attributes to ease custom JS/CSS customization (#1507)
|
||||||
older than `keepStatusDays`) (#1303)
|
- The Redis cache has been removed. There have been multiple enhancements to the feed refresh engine and it is no longer
|
||||||
|
needed, even for instances with a large number of feeds.
|
||||||
## [4.3.3]
|
- The H2 migration tool that automatically upgrades H2 databases from format 2 to 3 has been removed. If you're using
|
||||||
|
the H2 embedded database, please upgrade to at least version 4.3.0 before upgrading to CommaFeed 5.0.0.
|
||||||
- fix OPML import (#1279)
|
|
||||||
|
## [4.6.0]
|
||||||
## [4.3.2]
|
|
||||||
|
- switched from Temurin to OpenJ9 as the JVM used in the Docker image, resulting in memory usage reduction by up to 50%
|
||||||
- added support for unix sockets (#1278)
|
- fix an issue that could cause old entries to reappear if they were updated by their author (#1486)
|
||||||
|
- show all entries regardless of their read status when searching with keywords, even if the ui is configured to show
|
||||||
## [4.3.1]
|
unread entries only
|
||||||
|
|
||||||
- fix an issue that prevents new feeds from being added when mysql/mariadb is used as the database and the database
|
## [4.5.0]
|
||||||
timezone is not UTC (#1239)
|
|
||||||
- videos in enclosures can no longer have a width larger than the page (#1240)
|
- significantly reduce the time needed to retrieve entries or mark them as read, especially when there are a lot of
|
||||||
|
entries (#1452)
|
||||||
## [4.3.0]
|
- fix a race condition where a feed could be refreshed before it was created in the database
|
||||||
|
- fix an issue that could cause the websocket notification to contain the wrong number of unread entries when using
|
||||||
- h2 (the embedded database) has been upgraded to 2.2.224
|
mysql/mariadb
|
||||||
- this version uses a different file format than 2.1.x, the first time you start CommaFeed with this version, the
|
- fix an error when trying to mark all starred entries as read
|
||||||
database will be automatically converted to the new format
|
- remove the `onlyIds` parameter from REST endpoints since retrieving all the entries is now just as fast
|
||||||
- add a setting to completely disable scrolling to selected entry (#1157)
|
- remove support for microsoft sqlserver because it's not covered with integration tests (please open an issue if you'd
|
||||||
- add a css class reflecting the current view mode to ease custom css rules (#1232)
|
like it back)
|
||||||
- fix an issue that prevents new feeds from being added when mysql/mariadb is used as the database (#1239)
|
|
||||||
|
## [4.4.1]
|
||||||
## [4.2.1]
|
|
||||||
|
- fix vertical scrolling issues with Safari (#1168)
|
||||||
- fix an issue that caused the tree to show an incorrect unread count after a websocket notification because entries
|
- the default value for new users for the "star entry" button and the "open in new tab" button in the entry headers is
|
||||||
that were already marked as read by a filtering expression were not ignored (#1191)
|
now "on desktop" instead of "always"
|
||||||
|
- the "keyboard shortcuts" help page now shows "Cmd" instead of "Ctrl" on macOS (#1389)
|
||||||
## [4.2.0]
|
- remove a superfluous feed fetch when subscribing to a feed (#1431)
|
||||||
|
- the Docker image now uses Java 21
|
||||||
- add a setting to display the action buttons in the footer instead of in the header on mobile (#1121)
|
|
||||||
- the websocket notification now contains everything needed to update the UI, the client no longer needs to make an API
|
## [4.4.0]
|
||||||
call to get the latest data when receiving the notification
|
|
||||||
- add a workaround to the Fever API for the Unread iOS app (#1188)
|
- add support for sharing using the browser native capabilities if available (#1255)
|
||||||
- fix an issue that caused dates to be saved incorrectly if the database server and the application server were in
|
- add a button in the entry headers to star an entry (#1025)
|
||||||
different timezones (#1187)
|
- add a button in the entry headers to open links in a new tab (#1333)
|
||||||
|
- add two options in the settings to toggle those buttons
|
||||||
## [4.1.0]
|
- accept .opml file extension when importing and export with the .opml extension
|
||||||
|
- the "mark as read" option is no longer shown in the context menu for entries that are too old to be marked as read (
|
||||||
- it is now possible to open the sidebar on mobile by swiping to the right (#1098)
|
older than `keepStatusDays`) (#1303)
|
||||||
- swiping to mark entries as read/unread changed from swiping right to left because swiping right now opens the sidebar
|
|
||||||
- the full hierarchy of categories are now displayed in the category dropdown (#1045)
|
## [4.3.3]
|
||||||
- added a setting `maxEntriesAgeDays` to delete old entries based on their age during database cleanup.
|
|
||||||
The setting is disabled by default for existing installations, except for the docker image where it is enabled and set
|
- fix OPML import (#1279)
|
||||||
to 365 days
|
|
||||||
- if user registrations are disabled on your instance which is the default behavior, users are redirected on the login
|
## [4.3.2]
|
||||||
page instead of the welcome page when not logged in (#1185)
|
|
||||||
- the sidebar resizer is no longer shown in the middle of the screen on mobile
|
- added support for unix sockets (#1278)
|
||||||
- when using the system color scheme and the system is using a dark theme, feed entries no longer flicker on load
|
|
||||||
- the demo account (if enabled) cannot register custom javascript code anymore
|
## [4.3.1]
|
||||||
- removed the usage of `toSorted` in the client because older browsers do not support it (#1183)
|
|
||||||
- the openapi documentation is no longer cached by the browser so you always have access to the latest version
|
- fix an issue that prevents new feeds from being added when mysql/mariadb is used as the database and the database
|
||||||
- added a memory management section to the readme, reading it is recommended if you are running CommaFeed on a server
|
timezone is not UTC (#1239)
|
||||||
with limited memory
|
- videos in enclosures can no longer have a width larger than the page (#1240)
|
||||||
- fixed an issue that caused users without an email address set to be unable to edit their profile (#1184)
|
|
||||||
|
## [4.3.0]
|
||||||
## [4.0.0]
|
|
||||||
|
- h2 (the embedded database) has been upgraded to 2.2.224
|
||||||
- migrated from dropwizard 2 to dropwizard 4, Java 17+ is now required
|
- this version uses a different file format than 2.1.x, the first time you start CommaFeed with this version, the
|
||||||
- entries that were fetched and inserted in the database but not yet shown in the UI are no longer marked as read when
|
database will be automatically converted to the new format
|
||||||
marking all entries as read
|
- add a setting to completely disable scrolling to selected entry (#1157)
|
||||||
- your custom sidebar width is now persisted in the local storage of your browser
|
- add a css class reflecting the current view mode to ease custom css rules (#1232)
|
||||||
- there is now a third color scheme option in addition to light and dark: system (follows the system color scheme)
|
- fix an issue that prevents new feeds from being added when mysql/mariadb is used as the database (#1239)
|
||||||
- added support for youtube playlist favicons
|
|
||||||
- custom JS code is now executed when the app is done loading instead of when the page is loaded
|
## [4.2.1]
|
||||||
- the favicon is now correctly returned for feeds that return an invalid content type
|
|
||||||
- the feed refresh engine now uses httpclient5 with connection pooling and no longer creates a new client for each
|
- fix an issue that caused the tree to show an incorrect unread count after a websocket notification because entries
|
||||||
request, reducing CPU usage
|
that were already marked as read by a filtering expression were not ignored (#1191)
|
||||||
- updated UI library Mantine to 7.0, improving performance
|
|
||||||
- the h2 embedded database is now compacted on shutdown to reclaim unused space
|
## [4.2.0]
|
||||||
- the admin connector on port 8084 is now disabled in config.yml.example. Disabling it in your config.yml is
|
|
||||||
recommended (see https://github.com/Athou/commafeed/commit/929df60f09cce56020b0962ab111cd8349b271b0)
|
- add a setting to display the action buttons in the footer instead of in the header on mobile (#1121)
|
||||||
- migrated documentation from swagger 2 to openapi 3
|
- the websocket notification now contains everything needed to update the UI, the client no longer needs to make an API
|
||||||
- added a GET method to the fever api to indicate that the endpoint is working correctly when accessed from a browser
|
call to get the latest data when receiving the notification
|
||||||
- the websocket connection can now be disabled, the websocket ping interval and the tree reload interval can now be
|
- add a workaround to the Fever API for the Unread iOS app (#1188)
|
||||||
configured (see config.yml.example)
|
- fix an issue that caused dates to be saved incorrectly if the database server and the application server were in
|
||||||
- the websocket connection now works correctly when the context root of the application is not "/"
|
different timezones (#1187)
|
||||||
- unstable pubsubhubbub support was removed
|
|
||||||
|
## [4.1.0]
|
||||||
## [3.10.1]
|
|
||||||
|
- it is now possible to open the sidebar on mobile by swiping to the right (#1098)
|
||||||
- swap next and previous buttons (#1159)
|
- swiping to mark entries as read/unread changed from swiping right to left because swiping right now opens the sidebar
|
||||||
- unread count for subscriptions will now be shortened starting at 10k instead of 1k
|
- the full hierarchy of categories are now displayed in the category dropdown (#1045)
|
||||||
- increased websocket ping interval to just under a minute to reduce data and battery usage on mobile
|
- added a setting `maxEntriesAgeDays` to delete old entries based on their age during database cleanup.
|
||||||
- only refresh subscription tree on a timer if websocket connection is unavailable
|
The setting is disabled by default for existing installations, except for the docker image where it is enabled and set
|
||||||
- the Docker image now uses less memory by returning unused memory to the OS
|
to 365 days
|
||||||
- add support for Java 21
|
- if user registrations are disabled on your instance which is the default behavior, users are redirected on the login
|
||||||
|
page instead of the welcome page when not logged in (#1185)
|
||||||
## [3.10.0]
|
- the sidebar resizer is no longer shown in the middle of the screen on mobile
|
||||||
|
- when using the system color scheme and the system is using a dark theme, feed entries no longer flicker on load
|
||||||
- added a Fever-compatible API that is usable with mobile clients that support the Fever API (see instructions in
|
- the demo account (if enabled) cannot register custom javascript code anymore
|
||||||
Settings -> Profile)
|
- removed the usage of `toSorted` in the client because older browsers do not support it (#1183)
|
||||||
- long entry titles are no longer shortened in the detailed view
|
- the openapi documentation is no longer cached by the browser so you always have access to the latest version
|
||||||
- added the "s" keyboard shortcut to star/unstar entries
|
- added a memory management section to the readme, reading it is recommended if you are running CommaFeed on a server
|
||||||
- http sessions are now stored in the database (they were stored on disk before)
|
with limited memory
|
||||||
- fixed an issue that made it impossible to override the database url in a config.yml mounted in the Docker image
|
- fixed an issue that caused users without an email address set to be unable to edit their profile (#1184)
|
||||||
|
|
||||||
## [3.9.0]
|
## [4.0.0]
|
||||||
|
|
||||||
- improve performance by disabling the loader when nothing is loading (most noticeable on mobile)
|
- migrated from dropwizard 2 to dropwizard 4, Java 17+ is now required
|
||||||
- added a setting to disable the 'mark all as read' confirmation
|
- entries that were fetched and inserted in the database but not yet shown in the UI are no longer marked as read when
|
||||||
- added a setting to disable the custom context menu
|
marking all entries as read
|
||||||
- if the custom context is enabled, it can still be disabled by pressing the shift key
|
- your custom sidebar width is now persisted in the local storage of your browser
|
||||||
- the announcement feature is now working again and supports html ('announcement' configuration element in config.yml)
|
- there is now a third color scheme option in addition to light and dark: system (follows the system color scheme)
|
||||||
- add support for MariaDB 11+
|
- added support for youtube playlist favicons
|
||||||
- fix entry header shortly rendered as mobile on desktop, causing a small visual glitch
|
- custom JS code is now executed when the app is done loading instead of when the page is loaded
|
||||||
- fix an issue that could cause a feed to not refresh correctly if the url was very long
|
- the favicon is now correctly returned for feeds that return an invalid content type
|
||||||
- database cleanup batch size is now configurable
|
- the feed refresh engine now uses httpclient5 with connection pooling and no longer creates a new client for each
|
||||||
- css parsing errors are no longer logged to the standard output
|
request, reducing CPU usage
|
||||||
- fix small errors in the api documentation
|
- updated UI library Mantine to 7.0, improving performance
|
||||||
|
- the h2 embedded database is now compacted on shutdown to reclaim unused space
|
||||||
## [3.8.1]
|
- the admin connector on port 8084 is now disabled in config.yml.example. Disabling it in your config.yml is
|
||||||
|
recommended (see https://github.com/Athou/commafeed/commit/929df60f09cce56020b0962ab111cd8349b271b0)
|
||||||
- in expanded mode, don't scroll when clicking on the body of the current entry
|
- migrated documentation from swagger 2 to openapi 3
|
||||||
- improve content cleanup task performance for instances with a very large number of feeds
|
- added a GET method to the fever api to indicate that the endpoint is working correctly when accessed from a browser
|
||||||
|
- the websocket connection can now be disabled, the websocket ping interval and the tree reload interval can now be
|
||||||
## [3.8.0]
|
configured (see config.yml.example)
|
||||||
|
- the websocket connection now works correctly when the context root of the application is not "/"
|
||||||
- add previous and next buttons in the toolbar
|
- unstable pubsubhubbub support was removed
|
||||||
- add a setting to always scroll selected entry to the top of the page, even if it fits entirely on screen
|
|
||||||
- clicking on the body of an entry in expanded mode selects it and marks it as read
|
## [3.10.1]
|
||||||
- add rich text editor with autocomplete for custom css and js code in settings (desktop only)
|
|
||||||
- dramatically improve performance while scrolling
|
- swap next and previous buttons (#1159)
|
||||||
- fix broken welcome page mobile layout
|
- unread count for subscriptions will now be shortened starting at 10k instead of 1k
|
||||||
- format dates in user locale instead of GMT in relative date popups
|
- increased websocket ping interval to just under a minute to reduce data and battery usage on mobile
|
||||||
|
- only refresh subscription tree on a timer if websocket connection is unavailable
|
||||||
## [3.7.0]
|
- the Docker image now uses less memory by returning unused memory to the OS
|
||||||
|
- add support for Java 21
|
||||||
- the sidebar is now resizable
|
|
||||||
- added the "f" keyboard shortcut to hide the sidebar
|
## [3.10.0]
|
||||||
- added tooltips to relative dates with the exact date
|
|
||||||
- add a setting to hide commafeed from search engines (exposes a robots.txt file, enabled by default)
|
- added a Fever-compatible API that is usable with mobile clients that support the Fever API (see instructions in
|
||||||
- the browser extension unread count now updates when articles are marked as read/unread in the app
|
Settings -> Profile)
|
||||||
- The "b" keyboard shortcut now works as expected on Chrome but requires the browser extension to be installed
|
- long entry titles are no longer shortened in the detailed view
|
||||||
- dark mode has been disabled on the api documentation page as it was unreadable
|
- added the "s" keyboard shortcut to star/unstar entries
|
||||||
- improvement to the feed refresh queuing logic when "heavy load" mode is enabled
|
- http sessions are now stored in the database (they were stored on disk before)
|
||||||
- fix a bug that could prevent feeds and categories from being edited
|
- fixed an issue that made it impossible to override the database url in a config.yml mounted in the Docker image
|
||||||
|
|
||||||
## [3.6.0]
|
## [3.9.0]
|
||||||
|
|
||||||
- add a button to open CommaFeed in a new tab and a button to open options when using the browser extension
|
- improve performance by disabling the loader when nothing is loading (most noticeable on mobile)
|
||||||
- clicking on the entry title in expanded mode now opens the link instead of doing nothing
|
- added a setting to disable the 'mark all as read' confirmation
|
||||||
- add tooltips to buttons when the mobile layout is used on desktop
|
- added a setting to disable the custom context menu
|
||||||
- redirect the user to the welcome page if the user was deleted from the database
|
- if the custom context is enabled, it can still be disabled by pressing the shift key
|
||||||
- add link to api documentation on welcome page
|
- the announcement feature is now working again and supports html ('announcement' configuration element in config.yml)
|
||||||
- the unread count is now correctly updated when using the "/next" bookmarklet while redis cache is enabled
|
- add support for MariaDB 11+
|
||||||
|
- fix entry header shortly rendered as mobile on desktop, causing a small visual glitch
|
||||||
## [3.5.0]
|
- fix an issue that could cause a feed to not refresh correctly if the url was very long
|
||||||
|
- database cleanup batch size is now configurable
|
||||||
- add compatibility with the new version of the CommaFeed browser extension
|
- css parsing errors are no longer logged to the standard output
|
||||||
- disable pull-to-refresh on mobile as it messes with vertical scrolling
|
- fix small errors in the api documentation
|
||||||
- add css classes to feed entries to help with custom css rules
|
|
||||||
- api documentation page no longer requires users to be authenticated
|
## [3.8.1]
|
||||||
- add a setting to limit the number of feeds a user can subscribe to
|
|
||||||
- add a setting to disable strict password policy
|
- in expanded mode, don't scroll when clicking on the body of the current entry
|
||||||
- add feed refresh engine metrics
|
- improve content cleanup task performance for instances with a very large number of feeds
|
||||||
- fix redis timeouts
|
|
||||||
|
## [3.8.0]
|
||||||
## [3.4.0]
|
|
||||||
|
- add previous and next buttons in the toolbar
|
||||||
- add support for arm64 docker images
|
- add a setting to always scroll selected entry to the top of the page, even if it fits entirely on screen
|
||||||
- add divider to visually separate read-only information from form on the profile settings page
|
- clicking on the body of an entry in expanded mode selects it and marks it as read
|
||||||
- reduce javascript bundle size by 30% by loading only the necessary translations
|
- add rich text editor with autocomplete for custom css and js code in settings (desktop only)
|
||||||
- add a standalone donate page with all ways to support CommaFeed
|
- dramatically improve performance while scrolling
|
||||||
- fix an issue introduced in 3.1.0 that could make CommaFeed not refresh feeds as fast as before on instances with lots
|
- fix broken welcome page mobile layout
|
||||||
of feeds
|
- format dates in user locale instead of GMT in relative date popups
|
||||||
- fix alignment of icon with text for category tree nodes
|
|
||||||
- fix alignment of burger button with the rest of the header on mobile
|
## [3.7.0]
|
||||||
|
|
||||||
## [3.3.2]
|
- the sidebar is now resizable
|
||||||
|
- added the "f" keyboard shortcut to hide the sidebar
|
||||||
- restore entry selection indicator (left orange border) that was lost with the mantine 6.x upgrade (3.3.0)
|
- added tooltips to relative dates with the exact date
|
||||||
- add dividers to visually separate read-only information from forms on feed and category details pages
|
- add a setting to hide commafeed from search engines (exposes a robots.txt file, enabled by default)
|
||||||
- reduced javascript bundle size by 10%
|
- the browser extension unread count now updates when articles are marked as read/unread in the app
|
||||||
|
- The "b" keyboard shortcut now works as expected on Chrome but requires the browser extension to be installed
|
||||||
## [3.3.1]
|
- dark mode has been disabled on the api documentation page as it was unreadable
|
||||||
|
- improvement to the feed refresh queuing logic when "heavy load" mode is enabled
|
||||||
- fix long feed names not being shortened to respect tree max width
|
- fix a bug that could prevent feeds and categories from being edited
|
||||||
|
|
||||||
## [3.3.0]
|
## [3.6.0]
|
||||||
|
|
||||||
- there are now database changes, rolling back to 2.x will no longer be possible
|
- add a button to open CommaFeed in a new tab and a button to open options when using the browser extension
|
||||||
- restore support for user custom CSS rules
|
- clicking on the entry title in expanded mode now opens the link instead of doing nothing
|
||||||
- add support for user custom JS code that will be executed on page load
|
- add tooltips to buttons when the mobile layout is used on desktop
|
||||||
|
- redirect the user to the welcome page if the user was deleted from the database
|
||||||
## [3.2.0]
|
- add link to api documentation on welcome page
|
||||||
|
- the unread count is now correctly updated when using the "/next" bookmarklet while redis cache is enabled
|
||||||
- restore the welcome page
|
|
||||||
- only apply hover effect for unread entries (same as commafeed v2)
|
## [3.5.0]
|
||||||
- move notifications at the bottom of the screen
|
|
||||||
- always use https for sharing urls
|
- add compatibility with the new version of the CommaFeed browser extension
|
||||||
- add support for redis ACLs
|
- disable pull-to-refresh on mobile as it messes with vertical scrolling
|
||||||
- transition to google analytics v4
|
- add css classes to feed entries to help with custom css rules
|
||||||
|
- api documentation page no longer requires users to be authenticated
|
||||||
## [3.1.0]
|
- add a setting to limit the number of feeds a user can subscribe to
|
||||||
|
- add a setting to disable strict password policy
|
||||||
- add an even more compact layout
|
- add feed refresh engine metrics
|
||||||
- restore hover effect from commafeed 2.x
|
- fix redis timeouts
|
||||||
- view mode (compact, expanded, ...) is now stored on the device so you can have a different view mode on desktop and
|
|
||||||
mobile
|
## [3.4.0]
|
||||||
- fix for the "Illegal attempt to associate a collection with two open sessions." error
|
|
||||||
- feed fetching workflow is now orchestrated with rxjava, removing a lot of code
|
- add support for arm64 docker images
|
||||||
|
- add divider to visually separate read-only information from form on the profile settings page
|
||||||
## [3.0.1]
|
- reduce javascript bundle size by 30% by loading only the necessary translations
|
||||||
|
- add a standalone donate page with all ways to support CommaFeed
|
||||||
- allow env variable substitution in config.yml
|
- fix an issue introduced in 3.1.0 that could make CommaFeed not refresh feeds as fast as before on instances with lots
|
||||||
- e.g. having a custom config.yml file with `app.session.path=${SOME_ENV_VAR}` will substitute `SOME_ENV_VAR` with its
|
of feeds
|
||||||
value
|
- fix alignment of icon with text for category tree nodes
|
||||||
- allow env variable prefixed with `CF_` to override config.yml properties
|
- fix alignment of burger button with the rest of the header on mobile
|
||||||
- e.g. setting `CF_APP_ALLOWREGISTRATIONS=true` will set `app.allowRegistrations` to `true`
|
|
||||||
|
## [3.3.2]
|
||||||
## [3.0.0]
|
|
||||||
|
- restore entry selection indicator (left orange border) that was lost with the mantine 6.x upgrade (3.3.0)
|
||||||
- complete overhaul of the UI
|
- add dividers to visually separate read-only information from forms on feed and category details pages
|
||||||
- backend and frontend are now in separate maven modules
|
- reduced javascript bundle size by 10%
|
||||||
- no changes to the api or the database
|
|
||||||
- Docker images are now automatically built and available at https://hub.docker.com/r/athou/commafeed
|
## [3.3.1]
|
||||||
|
|
||||||
## [2.6.0]
|
- fix long feed names not being shortened to respect tree max width
|
||||||
|
|
||||||
- add support for media content as a backup for missing content (useful for youtube feeds)
|
## [3.3.0]
|
||||||
- correctly follow http error code 308 redirects
|
|
||||||
- fixed a bug that prevented users from deleting their account
|
- there are now database changes, rolling back to 2.x will no longer be possible
|
||||||
- fixed a bug that made commafeed store entry contents multiple times
|
- restore support for user custom CSS rules
|
||||||
- fixed a bug that prevented the app to be used as an installed app on mobile devices if the context path of commafeed
|
- add support for user custom JS code that will be executed on page load
|
||||||
was not "/"
|
|
||||||
- fixed a bug that prevented entries from being "marked as read older than xxx" for a feed that was just added
|
## [3.2.0]
|
||||||
- removed support for google+ and readability as those services no longer exist
|
|
||||||
- removed support for deploying on openshift
|
- restore the welcome page
|
||||||
- removed alphabetical sorting of entries because of really poor performance (title cannot be indexed)
|
- only apply hover effect for unread entries (same as commafeed v2)
|
||||||
- improve performance for instances with the heavy load setting enabled by preventing CommaFeed from fetching feeds from
|
- move notifications at the bottom of the screen
|
||||||
users that did not log in for a long time
|
- always use https for sharing urls
|
||||||
- various dependencies upgrades (notably dropwizard from 1.3 to 2.1)
|
- add support for redis ACLs
|
||||||
- add support for mariadb
|
- transition to google analytics v4
|
||||||
- add support for java17+ runtime
|
|
||||||
- various security improvements
|
## [3.1.0]
|
||||||
|
|
||||||
## [2.5.0]
|
- add an even more compact layout
|
||||||
|
- restore hover effect from commafeed 2.x
|
||||||
- unread count is now displayed in a favicon badge when supported
|
- view mode (compact, expanded, ...) is now stored on the device so you can have a different view mode on desktop and
|
||||||
- the user agent string for the bot fetching feeds is now configurable
|
mobile
|
||||||
- feed parsing performance improvements
|
- fix for the "Illegal attempt to associate a collection with two open sessions." error
|
||||||
- support for java9+ runtime
|
- feed fetching workflow is now orchestrated with rxjava, removing a lot of code
|
||||||
- can now properly start from an empty postgresql database
|
|
||||||
|
## [3.0.1]
|
||||||
## [2.4.0]
|
|
||||||
|
- allow env variable substitution in config.yml
|
||||||
- users were not able to change password or delete account
|
- e.g. having a custom config.yml file with `app.session.path=${SOME_ENV_VAR}` will substitute `SOME_ENV_VAR` with its
|
||||||
- fix api key generation
|
value
|
||||||
- feed entries can now be sorted alphabetically
|
- allow env variable prefixed with `CF_` to override config.yml properties
|
||||||
- fix facebook sharing
|
- e.g. setting `CF_APP_ALLOWREGISTRATIONS=true` will set `app.allowRegistrations` to `true`
|
||||||
- fix layout on iOS
|
|
||||||
- postgresql driver update (fix for postgres 9.6)
|
## [3.0.0]
|
||||||
- various internationalization fixes
|
|
||||||
- security fixes
|
- complete overhaul of the UI
|
||||||
|
- backend and frontend are now in separate maven modules
|
||||||
## [2.3.0]
|
- no changes to the api or the database
|
||||||
|
- Docker images are now automatically built and available at https://hub.docker.com/r/athou/commafeed
|
||||||
- dropwizard upgrade 0.9.1
|
|
||||||
- feed enclosures are hidden if they already displayed in the content
|
## [2.6.0]
|
||||||
- fix youtube favicons
|
|
||||||
- various internationalization fixes
|
- add support for media content as a backup for missing content (useful for youtube feeds)
|
||||||
|
- correctly follow http error code 308 redirects
|
||||||
## [2.2.0]
|
- fixed a bug that prevented users from deleting their account
|
||||||
|
- fixed a bug that made commafeed store entry contents multiple times
|
||||||
- fix youtube and instagram favicon fetching
|
- fixed a bug that prevented the app to be used as an installed app on mobile devices if the context path of commafeed
|
||||||
- mark as read filter was lost when a feed was rearranged with drag&drop
|
was not "/"
|
||||||
- feed entry categories are now displayed if available
|
- fixed a bug that prevented entries from being "marked as read older than xxx" for a feed that was just added
|
||||||
- various performance and dependencies upgrades
|
- removed support for google+ and readability as those services no longer exist
|
||||||
- java8 is now required
|
- removed support for deploying on openshift
|
||||||
|
- removed alphabetical sorting of entries because of really poor performance (title cannot be indexed)
|
||||||
## [2.1.0]
|
- improve performance for instances with the heavy load setting enabled by preventing CommaFeed from fetching feeds from
|
||||||
|
users that did not log in for a long time
|
||||||
- dropwizard upgrade to 0.8.0
|
- various dependencies upgrades (notably dropwizard from 1.3 to 2.1)
|
||||||
- you have to remove the "app.contextPath" setting from your yml file, you can optionally use
|
- add support for mariadb
|
||||||
server.applicationContextPath instead
|
- add support for java17+ runtime
|
||||||
- new setting app.maxFeedCapacity for deleting old entries
|
- various security improvements
|
||||||
- ability to set filtering expressions for subscriptions to automatically mark new entries as read based on title,
|
|
||||||
content, author or url.
|
## [2.5.0]
|
||||||
- ability to use !keyword or -keyword to exclude a keyword from a search query
|
|
||||||
- facebook feeds now show user favicon instead of facebook favicon
|
- unread count is now displayed in a favicon badge when supported
|
||||||
- new dark theme 'nightsky'
|
- the user agent string for the bot fetching feeds is now configurable
|
||||||
|
- feed parsing performance improvements
|
||||||
## [2.0.3]
|
- support for java9+ runtime
|
||||||
|
- can now properly start from an empty postgresql database
|
||||||
- internet explorer ajax cache workaround
|
|
||||||
- categories are now deletable again
|
## [2.4.0]
|
||||||
- openshift support is back
|
|
||||||
- youtube feeds now show user favicon instead of youtube favicon
|
- users were not able to change password or delete account
|
||||||
|
- fix api key generation
|
||||||
## [2.0.2]
|
- feed entries can now be sorted alphabetically
|
||||||
|
- fix facebook sharing
|
||||||
- api using the api key is now working again
|
- fix layout on iOS
|
||||||
- context path is now configurable in config.yml (see app.contextPath in config.yml.example)
|
- postgresql driver update (fix for postgres 9.6)
|
||||||
- fix login on firefox when fields are autofilled by the browser
|
- various internationalization fixes
|
||||||
- fix scrolling of subscriptions list on mobile
|
- security fixes
|
||||||
- user is now logged in after registration
|
|
||||||
- fix link to documentation on home page and about page
|
## [2.3.0]
|
||||||
- fields autocomplete is disabled on the profile page
|
|
||||||
- users are able to delete their account again
|
- dropwizard upgrade 0.9.1
|
||||||
- chinese and malaysian translation files are now correctly loaded
|
- feed enclosures are hidden if they already displayed in the content
|
||||||
- software version in user-agent when fetching feeds is no longer hardcoded
|
- fix youtube favicons
|
||||||
- admin settings page is now read only, settings are configured in config.yml
|
- various internationalization fixes
|
||||||
- added link to metrics on the admin settings page
|
|
||||||
- Rome (rss library) upgrade to 1.5.0
|
## [2.2.0]
|
||||||
|
|
||||||
## [2.0.1]
|
- fix youtube and instagram favicon fetching
|
||||||
|
- mark as read filter was lost when a feed was rearranged with drag&drop
|
||||||
- the redis pool no longer throws an exception when it is unable to aquire a new connection
|
- feed entry categories are now displayed if available
|
||||||
|
- various performance and dependencies upgrades
|
||||||
## [2.0.0]
|
- java8 is now required
|
||||||
|
|
||||||
- The backend has been completely rewritten using Dropwizard instead of TomEE, resulting in a lot less memory
|
## [2.1.0]
|
||||||
consumption and better overall performances.
|
|
||||||
See the README on how to build CommaFeed from now on.
|
- dropwizard upgrade to 0.8.0
|
||||||
- CommaFeed should no longer fetch the same feed multiple times in a row
|
- you have to remove the "app.contextPath" setting from your yml file, you can optionally use
|
||||||
- Users can use their username or email to log in
|
server.applicationContextPath instead
|
||||||
|
- new setting app.maxFeedCapacity for deleting old entries
|
||||||
|
- ability to set filtering expressions for subscriptions to automatically mark new entries as read based on title,
|
||||||
|
content, author or url.
|
||||||
|
- ability to use !keyword or -keyword to exclude a keyword from a search query
|
||||||
|
- facebook feeds now show user favicon instead of facebook favicon
|
||||||
|
- new dark theme 'nightsky'
|
||||||
|
|
||||||
|
## [2.0.3]
|
||||||
|
|
||||||
|
- internet explorer ajax cache workaround
|
||||||
|
- categories are now deletable again
|
||||||
|
- openshift support is back
|
||||||
|
- youtube feeds now show user favicon instead of youtube favicon
|
||||||
|
|
||||||
|
## [2.0.2]
|
||||||
|
|
||||||
|
- api using the api key is now working again
|
||||||
|
- context path is now configurable in config.yml (see app.contextPath in config.yml.example)
|
||||||
|
- fix login on firefox when fields are autofilled by the browser
|
||||||
|
- fix scrolling of subscriptions list on mobile
|
||||||
|
- user is now logged in after registration
|
||||||
|
- fix link to documentation on home page and about page
|
||||||
|
- fields autocomplete is disabled on the profile page
|
||||||
|
- users are able to delete their account again
|
||||||
|
- chinese and malaysian translation files are now correctly loaded
|
||||||
|
- software version in user-agent when fetching feeds is no longer hardcoded
|
||||||
|
- admin settings page is now read only, settings are configured in config.yml
|
||||||
|
- added link to metrics on the admin settings page
|
||||||
|
- Rome (rss library) upgrade to 1.5.0
|
||||||
|
|
||||||
|
## [2.0.1]
|
||||||
|
|
||||||
|
- the redis pool no longer throws an exception when it is unable to aquire a new connection
|
||||||
|
|
||||||
|
## [2.0.0]
|
||||||
|
|
||||||
|
- The backend has been completely rewritten using Dropwizard instead of TomEE, resulting in a lot less memory
|
||||||
|
consumption and better overall performances.
|
||||||
|
See the README on how to build CommaFeed from now on.
|
||||||
|
- CommaFeed should no longer fetch the same feed multiple times in a row
|
||||||
|
- Users can use their username or email to log in
|
||||||
|
|||||||
60
LICENSE
60
LICENSE
@@ -1,31 +1,31 @@
|
|||||||
Apache License, Version 2.0 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
|
Apache License, Version 2.0 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
1. Definitions.
|
1. Definitions.
|
||||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
||||||
2. Grant of Copyright License.
|
2. Grant of Copyright License.
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
||||||
3. Grant of Patent License.
|
3. Grant of Patent License.
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
||||||
4. Redistribution.
|
4. Redistribution.
|
||||||
You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||||
You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
||||||
5. Submission of Contributions.
|
5. Submission of Contributions.
|
||||||
Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
||||||
6. Trademarks.
|
6. Trademarks.
|
||||||
This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
||||||
7. Disclaimer of Warranty.
|
7. Disclaimer of Warranty.
|
||||||
Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
||||||
8. Limitation of Liability.
|
8. Limitation of Liability.
|
||||||
In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
||||||
9. Accepting Warranty or Additional Liability.
|
9. Accepting Warranty or Additional Liability.
|
||||||
While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
13
README.md
13
README.md
@@ -17,6 +17,7 @@ Google Reader inspired self-hosted RSS reader, based on Quarkus and React/TypeSc
|
|||||||
- REST API
|
- REST API
|
||||||
- Fever-compatible API for native mobile apps
|
- Fever-compatible API for native mobile apps
|
||||||
- Can automatically mark articles as read based on user-defined rules
|
- Can automatically mark articles as read based on user-defined rules
|
||||||
|
- Highly customizable with [custom CSS](https://athou.github.io/commafeed/documentation/custom-css) and JavaScript
|
||||||
- [Browser extension](https://github.com/Athou/commafeed-browser-extension)
|
- [Browser extension](https://github.com/Athou/commafeed-browser-extension)
|
||||||
- Compiles to native code for blazing fast startup and low memory usage
|
- Compiles to native code for blazing fast startup and low memory usage
|
||||||
- Supports 4 databases
|
- Supports 4 databases
|
||||||
@@ -48,17 +49,17 @@ system and database of choice.
|
|||||||
|
|
||||||
There are two types of packages:
|
There are two types of packages:
|
||||||
|
|
||||||
- The `linux-x86_64` and `windows-x86_64` packages are compiled natively and contain an executable that can be run
|
- The `linux-x86_64`, `linux-aarch_64` and `windows-x86_64` packages are compiled natively and contain an executable that can be run
|
||||||
directly.
|
directly.
|
||||||
- The `jvm` package is a zip file containing all `.jar` files required to run the application. This package works on all
|
- The `jvm` package is a zip file containing all `.jar` files required to run the application. This package works on all
|
||||||
platforms and is started with `java -jar quarkus-run.jar`.
|
platforms but requires a JRE and is started with `java -jar quarkus-run.jar`.
|
||||||
|
|
||||||
If available for your operating system, the native package is recommended because it has a faster startup time and lower
|
If available for your operating system, the native package is recommended because it has a faster startup time and lower
|
||||||
memory usage.
|
memory usage.
|
||||||
|
|
||||||
### Build from sources
|
### Build from sources
|
||||||
|
|
||||||
./mvnw clean package [-P<database>] [-Pnative] [-DskipTests]
|
./mvnw clean package [-P<database> [-Pnative]] [-DskipTests]
|
||||||
|
|
||||||
- `<database>` can be one of `h2`, `postgresql`, `mysql` or `mariadb`. The default is `h2`.
|
- `<database>` can be one of `h2`, `postgresql`, `mysql` or `mariadb`. The default is `h2`.
|
||||||
- `-Pnative` compiles the application to native code. This requires GraalVM to be installed (`GRAALVM_HOME` environment
|
- `-Pnative` compiles the application to native code. This requires GraalVM to be installed (`GRAALVM_HOME` environment
|
||||||
@@ -73,6 +74,10 @@ When the build is complete:
|
|||||||
- if you used the native profile, the executable is located at
|
- if you used the native profile, the executable is located at
|
||||||
`commafeed-server/target/commafeed-<version>-<database>-<platform>-<arch>-runner[.exe]`
|
`commafeed-server/target/commafeed-<version>-<database>-<platform>-<arch>-runner[.exe]`
|
||||||
|
|
||||||
|
### Distribution packages
|
||||||
|
|
||||||
|
- Arch Linux users can use [the CommaFeed package on AUR](https://aur.archlinux.org/pkgbase/commafeed), which builds native binaries with GraalVM for all supported databases.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
CommaFeed doesn't require any configuration to run with its embedded database (H2). The database file will be stored in
|
CommaFeed doesn't require any configuration to run with its embedded database (H2). The database file will be stored in
|
||||||
@@ -100,7 +105,7 @@ There are multiple ways to configure CommaFeed:
|
|||||||
|
|
||||||
The properties file is recommended because CommaFeed will be able to warn about invalid properties and typos.
|
The properties file is recommended because CommaFeed will be able to warn about invalid properties and typos.
|
||||||
|
|
||||||
All [CommaFeed settings](commafeed-server/doc/commafeed.md) are optional and have sensible default values.
|
All [CommaFeed settings](https://athou.github.io/commafeed/documentation) are optional and have sensible default values.
|
||||||
|
|
||||||
When logging in, credentials are stored in an encrypted cookie. The encryption key is randomly generated at startup,
|
When logging in, credentials are stored in an encrypted cookie. The encryption key is randomly generated at startup,
|
||||||
meaning that you will have to log back in after each restart of the application. To prevent this, you can set the
|
meaning that you will have to log back in after each restart of the application. To prevent this, you can set the
|
||||||
|
|||||||
@@ -2,5 +2,4 @@
|
|||||||
|
|
||||||
## Reporting a Vulnerability
|
## 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.
|
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
|
||||||
Thanks !
|
|
||||||
|
|||||||
@@ -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": {
|
"formatter": {
|
||||||
"indentStyle": "space",
|
"indentStyle": "space",
|
||||||
"indentWidth": 4,
|
"indentWidth": 4,
|
||||||
@@ -14,6 +14,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"ignore": ["dist", "node_modules", "target", "target-ide"]
|
"includes": ["**", "!**/dist", "!**/node_modules", "!**/target", "!**/target-ide"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
<link rel="manifest" href="manifest.json" />
|
<link rel="manifest" href="manifest.json" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
|
<meta name="referrer" content="no-referrer" />
|
||||||
<link rel="stylesheet" type="text/css" href="custom_css.css" />
|
|
||||||
<script type="text/javascript" src="custom_js.js"></script>
|
|
||||||
|
|
||||||
<title>CommaFeed</title>
|
<title>CommaFeed</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
4629
commafeed-client/package-lock.json
generated
4629
commafeed-client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,79 +4,80 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite",
|
||||||
"dev:typescript": "tsc --watch",
|
"dev:host": "vite --host",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:ci": "vitest run",
|
"test:ci": "vitest run",
|
||||||
"lint": "biome check ./src",
|
"lint": "biome check",
|
||||||
"lint:fix": "biome check --write ./src",
|
"lint:fix": "biome check --write",
|
||||||
"i18n:extract": "lingui extract --clean"
|
"i18n:extract": "lingui extract --clean"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@fontsource/open-sans": "^5.1.1",
|
"@fontsource/open-sans": "^5.2.6",
|
||||||
"@mantine/core": "^7.16.3",
|
"@lingui/core": "^5.4.0",
|
||||||
"@mantine/form": "^7.16.3",
|
"@lingui/react": "^5.4.0",
|
||||||
"@mantine/hooks": "^7.16.3",
|
"@mantine/core": "^8.2.3",
|
||||||
"@mantine/modals": "^7.16.3",
|
"@mantine/form": "^8.2.3",
|
||||||
"@mantine/notifications": "^7.16.3",
|
"@mantine/hooks": "^8.2.3",
|
||||||
"@mantine/spotlight": "^7.16.3",
|
"@mantine/modals": "^8.2.3",
|
||||||
"@lingui/core": "^5.2.0",
|
"@mantine/notifications": "^8.2.3",
|
||||||
"@lingui/react": "^5.2.0",
|
"@mantine/spotlight": "^8.2.3",
|
||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@reduxjs/toolkit": "^2.5.1",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.11.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
"interweave": "^13.1.1",
|
"interweave": "^13.1.1",
|
||||||
"monaco-editor": "^0.52.2",
|
"monaco-editor": "^0.52.2",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"react": "^19.0.0",
|
"react": "^19.1.1",
|
||||||
"react-async-hook": "^4.0.0",
|
"react-async-hook": "^4.0.0",
|
||||||
"react-contexify": "^6.0.0",
|
"react-contexify": "^6.0.0",
|
||||||
"react-device-detect": "^2.2.3",
|
"react-device-detect": "^2.2.3",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.1.1",
|
||||||
"react-draggable": "^4.4.6",
|
"react-draggable": "^4.5.0",
|
||||||
"react-ga4": "^2.1.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-icons": "^5.4.0",
|
|
||||||
"react-infinite-scroller": "^1.2.6",
|
"react-infinite-scroller": "^1.2.6",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-router-dom": "^7.1.5",
|
"react-router-dom": "^7.7.1",
|
||||||
"react-swipeable": "^7.0.2",
|
"react-swipeable": "^7.0.2",
|
||||||
"redoc": "^2.4.0",
|
"style-to-object": "^1.0.9",
|
||||||
"style-to-object": "^1.0.8",
|
|
||||||
"throttle-debounce": "^5.0.2",
|
"throttle-debounce": "^5.0.2",
|
||||||
"tinycon": "^0.6.8",
|
"tinycon": "^0.6.8",
|
||||||
"tss-react": "^4.9.15",
|
"tss-react": "^4.9.19",
|
||||||
"websocket-heartbeat-js": "^1.1.3"
|
"websocket-heartbeat-js": "^1.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.4",
|
"@biomejs/biome": "^2.1.3",
|
||||||
"@lingui/babel-plugin-lingui-macro": "^5.2.0",
|
"@lingui/babel-plugin-lingui-macro": "^5.4.0",
|
||||||
"@lingui/cli": "^5.2.0",
|
"@lingui/cli": "^5.4.0",
|
||||||
"@lingui/vite-plugin": "^5.2.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/mousetrap": "^1.6.15",
|
||||||
"@types/react": "^19.0.8",
|
"@types/react": "^19.1.9",
|
||||||
"@types/react-dom": "^19.0.3",
|
"@types/react-dom": "^19.1.7",
|
||||||
"@types/react-infinite-scroller": "^1.2.5",
|
"@types/react-infinite-scroller": "^1.2.5",
|
||||||
"@types/throttle-debounce": "^5.0.2",
|
"@types/throttle-debounce": "^5.0.2",
|
||||||
"@types/tinycon": "^0.6.7",
|
"@types/tinycon": "^0.6.7",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.7.0",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"jsdom": "^26.0.0",
|
"babel-plugin-react-compiler": "^19.1.0-rc.2",
|
||||||
"rollup-plugin-visualizer": "^5.14.0",
|
"jsdom": "^26.1.0",
|
||||||
"typescript": "^5.7.3",
|
"rollup-plugin-visualizer": "^6.0.3",
|
||||||
"vite": "^6.1.0",
|
"typescript": "^5.9.2",
|
||||||
"vite-plugin-checker": "^0.8.0",
|
"vite": "^7.0.6",
|
||||||
|
"vite-plugin-checker": "^0.10.2",
|
||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "^3.0.5",
|
"vitest": "^3.2.4"
|
||||||
"vitest-mock-extended": "^2.0.2"
|
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"react-infinite-scroller": {
|
"react-infinite-scroller": {
|
||||||
"react": "^19.0.0"
|
"react": "^19.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +1,100 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.commafeed</groupId>
|
<groupId>com.commafeed</groupId>
|
||||||
<artifactId>commafeed</artifactId>
|
<artifactId>commafeed</artifactId>
|
||||||
<version>5.6.0</version>
|
<version>5.11.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>commafeed-client</artifactId>
|
<artifactId>commafeed-client</artifactId>
|
||||||
<name>CommaFeed Client</name>
|
<name>CommaFeed Client</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- renovate: datasource=node-version depName=node -->
|
<sonar.sources>package.json,src</sonar.sources>
|
||||||
<node.version>v22.14.0</node.version>
|
<sonar.coverage.exclusions>**/*</sonar.coverage.exclusions>
|
||||||
<!-- renovate: datasource=npm depName=npm -->
|
|
||||||
<npm.version>11.1.0</npm.version>
|
<!-- renovate: datasource=node-version depName=node -->
|
||||||
</properties>
|
<node.version>v22.18.0</node.version>
|
||||||
|
<!-- renovate: datasource=npm depName=npm -->
|
||||||
<build>
|
<npm.version>11.5.2</npm.version>
|
||||||
<plugins>
|
</properties>
|
||||||
<plugin>
|
|
||||||
<groupId>com.github.eirslett</groupId>
|
<build>
|
||||||
<artifactId>frontend-maven-plugin</artifactId>
|
<plugins>
|
||||||
<version>1.15.1</version>
|
<plugin>
|
||||||
<?m2e ignore?>
|
<groupId>com.github.eirslett</groupId>
|
||||||
<executions>
|
<artifactId>frontend-maven-plugin</artifactId>
|
||||||
<execution>
|
<version>1.15.1</version>
|
||||||
<id>install node and npm</id>
|
<?m2e ignore?>
|
||||||
<goals>
|
<executions>
|
||||||
<goal>install-node-and-npm</goal>
|
<execution>
|
||||||
</goals>
|
<id>install node and npm</id>
|
||||||
<phase>compile</phase>
|
<goals>
|
||||||
<configuration>
|
<goal>install-node-and-npm</goal>
|
||||||
<nodeVersion>${node.version}</nodeVersion>
|
</goals>
|
||||||
<npmVersion>${npm.version}</npmVersion>
|
<phase>compile</phase>
|
||||||
</configuration>
|
<configuration>
|
||||||
</execution>
|
<nodeVersion>${node.version}</nodeVersion>
|
||||||
<execution>
|
<npmVersion>${npm.version}</npmVersion>
|
||||||
<id>npm install</id>
|
</configuration>
|
||||||
<goals>
|
</execution>
|
||||||
<goal>npm</goal>
|
<execution>
|
||||||
</goals>
|
<id>npm install</id>
|
||||||
<phase>compile</phase>
|
<goals>
|
||||||
<configuration>
|
<goal>npm</goal>
|
||||||
<arguments>ci</arguments>
|
</goals>
|
||||||
</configuration>
|
<phase>compile</phase>
|
||||||
</execution>
|
<configuration>
|
||||||
<execution>
|
<arguments>ci</arguments>
|
||||||
<id>npm run test</id>
|
</configuration>
|
||||||
<goals>
|
</execution>
|
||||||
<goal>npm</goal>
|
<execution>
|
||||||
</goals>
|
<id>npm run test</id>
|
||||||
<phase>compile</phase>
|
<goals>
|
||||||
<configuration>
|
<goal>npm</goal>
|
||||||
<arguments>run test:ci</arguments>
|
</goals>
|
||||||
</configuration>
|
<phase>compile</phase>
|
||||||
</execution>
|
<configuration>
|
||||||
<execution>
|
<arguments>run test:ci</arguments>
|
||||||
<id>npm run build</id>
|
<skip>${skipTests}</skip>
|
||||||
<goals>
|
</configuration>
|
||||||
<goal>npm</goal>
|
</execution>
|
||||||
</goals>
|
<execution>
|
||||||
<phase>compile</phase>
|
<id>npm run build</id>
|
||||||
<configuration>
|
<goals>
|
||||||
<arguments>run build</arguments>
|
<goal>npm</goal>
|
||||||
</configuration>
|
</goals>
|
||||||
</execution>
|
<phase>compile</phase>
|
||||||
</executions>
|
<configuration>
|
||||||
</plugin>
|
<arguments>run build</arguments>
|
||||||
<plugin>
|
</configuration>
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
</execution>
|
||||||
<version>3.3.1</version>
|
</executions>
|
||||||
<executions>
|
</plugin>
|
||||||
<execution>
|
<plugin>
|
||||||
<id>copy web interface to resources</id>
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
<phase>prepare-package</phase>
|
<version>3.3.1</version>
|
||||||
<goals>
|
<executions>
|
||||||
<goal>copy-resources</goal>
|
<execution>
|
||||||
</goals>
|
<id>copy web interface to resources</id>
|
||||||
<configuration>
|
<phase>prepare-package</phase>
|
||||||
<outputDirectory>${project.build.directory}/classes/META-INF/resources</outputDirectory>
|
<goals>
|
||||||
<resources>
|
<goal>copy-resources</goal>
|
||||||
<resource>
|
</goals>
|
||||||
<directory>dist</directory>
|
<configuration>
|
||||||
<filtering>false</filtering>
|
<outputDirectory>${project.build.directory}/classes/META-INF/resources</outputDirectory>
|
||||||
</resource>
|
<resources>
|
||||||
</resources>
|
<resource>
|
||||||
</configuration>
|
<directory>dist</directory>
|
||||||
</execution>
|
<filtering>false</filtering>
|
||||||
</executions>
|
</resource>
|
||||||
</plugin>
|
</resources>
|
||||||
</plugins>
|
</configuration>
|
||||||
</build>
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
||||||
@@ -3,45 +3,51 @@ import { I18nProvider } from "@lingui/react"
|
|||||||
import { MantineProvider } from "@mantine/core"
|
import { MantineProvider } from "@mantine/core"
|
||||||
import { ModalsProvider } from "@mantine/modals"
|
import { ModalsProvider } from "@mantine/modals"
|
||||||
import { Notifications } from "@mantine/notifications"
|
import { Notifications } from "@mantine/notifications"
|
||||||
import { Constants } from "app/constants"
|
import type React from "react"
|
||||||
import { redirectTo } from "app/redirect/slice"
|
import { useEffect, useState } from "react"
|
||||||
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 { 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 } from "react"
|
|
||||||
import { isSafari } from "react-device-detect"
|
import { isSafari } from "react-device-detect"
|
||||||
import ReactGA from "react-ga4"
|
import { HashRouter, Navigate, Route, Routes, useNavigate } from "react-router-dom"
|
||||||
import { HashRouter, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom"
|
|
||||||
import Tinycon from "tinycon"
|
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 (
|
return (
|
||||||
<I18nProvider i18n={i18n}>
|
<I18nProvider i18n={i18n}>
|
||||||
<MantineProvider
|
<MantineProvider
|
||||||
defaultColorScheme="auto"
|
defaultColorScheme="auto"
|
||||||
theme={{
|
theme={{
|
||||||
primaryColor: "orange",
|
primaryColor: primaryColor,
|
||||||
fontFamily: "Open Sans",
|
fontFamily: "Open Sans",
|
||||||
colors: {
|
colors: {
|
||||||
// keep using dark colors from mantine v6
|
// keep using dark colors from mantine v6
|
||||||
@@ -70,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() {
|
function AppRoutes() {
|
||||||
const sidebarVisible = useAppSelector(state => state.tree.sidebarVisible)
|
const sidebarVisible = useAppSelector(state => state.tree.sidebarVisible)
|
||||||
|
|
||||||
@@ -83,7 +86,6 @@ function AppRoutes() {
|
|||||||
<Route path="login" element={<LoginPage />} />
|
<Route path="login" element={<LoginPage />} />
|
||||||
<Route path="register" element={<RegistrationPage />} />
|
<Route path="register" element={<RegistrationPage />} />
|
||||||
<Route path="passwordRecovery" element={<PasswordRecoveryPage />} />
|
<Route path="passwordRecovery" element={<PasswordRecoveryPage />} />
|
||||||
<Route path="api" element={<ApiDocumentationPage />} />
|
|
||||||
<Route path="app" element={<Layout header={<Header />} sidebar={<Tree />} sidebarVisible={sidebarVisible} />}>
|
<Route path="app" element={<Layout header={<Header />} sidebar={<Tree />} sidebarVisible={sidebarVisible} />}>
|
||||||
<Route path="category">
|
<Route path="category">
|
||||||
<Route path=":id" element={<FeedEntriesPage sourceType="category" />} />
|
<Route path=":id" element={<FeedEntriesPage sourceType="category" />} />
|
||||||
@@ -126,26 +128,19 @@ function RedirectHandler() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function GoogleAnalyticsHandler() {
|
function UnreadCountTitleHandler({
|
||||||
const location = useLocation()
|
enabled,
|
||||||
const googleAnalyticsCode = useAppSelector(state => state.server.serverInfos?.googleAnalyticsCode)
|
}: Readonly<{
|
||||||
|
enabled?: boolean
|
||||||
useEffect(() => {
|
}>) {
|
||||||
if (googleAnalyticsCode) ReactGA.initialize(googleAnalyticsCode)
|
const root = useAppSelector(state => state.tree.rootCategory)
|
||||||
}, [googleAnalyticsCode])
|
const unreadCount = categoryUnreadCount(root)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ReactGA.isInitialized) ReactGA.send({ hitType: "pageview", page: location.pathname })
|
|
||||||
}, [location])
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
function UnreadCountTitleHandler({ unreadCount, enabled }: { unreadCount: number; enabled?: boolean }) {
|
|
||||||
return <title>{enabled && unreadCount > 0 ? `(${unreadCount}) CommaFeed` : "CommaFeed"}</title>
|
return <title>{enabled && unreadCount > 0 ? `(${unreadCount}) CommaFeed` : "CommaFeed"}</title>
|
||||||
}
|
}
|
||||||
|
|
||||||
function UnreadCountFaviconHandler({ unreadCount, enabled }: { unreadCount: number; enabled?: boolean }) {
|
function UnreadCountFaviconHandler({ enabled }: { enabled?: boolean }) {
|
||||||
|
const root = useAppSelector(state => state.tree.rootCategory)
|
||||||
|
const unreadCount = categoryUnreadCount(root)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (enabled && unreadCount > 0) {
|
if (enabled && unreadCount > 0) {
|
||||||
Tinycon.setBubble(unreadCount)
|
Tinycon.setBubble(unreadCount)
|
||||||
@@ -169,36 +164,66 @@ function BrowserExtensionBadgeUnreadCountHandler() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CustomJsHandler() {
|
||||||
|
const [scriptLoaded, setScriptLoaded] = useState(false)
|
||||||
|
const { loading } = useAppLoading()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (scriptLoaded || loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const script = document.createElement("script")
|
||||||
|
script.src = "custom_js.js"
|
||||||
|
script.async = true
|
||||||
|
document.body.appendChild(script)
|
||||||
|
|
||||||
|
setScriptLoaded(true)
|
||||||
|
}, [scriptLoaded, loading])
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function CustomCssHandler() {
|
||||||
|
useEffect(() => {
|
||||||
|
const link = document.createElement("link")
|
||||||
|
link.rel = "stylesheet"
|
||||||
|
link.type = "text/css"
|
||||||
|
link.href = "custom_css.css"
|
||||||
|
document.head.appendChild(link)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
useI18n()
|
useI18n()
|
||||||
const root = useAppSelector(state => state.tree.rootCategory)
|
|
||||||
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
|
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
|
||||||
const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
|
const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const unreadCount = categoryUnreadCount(root)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(reloadServerInfos())
|
dispatch(reloadServerInfos())
|
||||||
}, [dispatch])
|
}, [dispatch])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Providers>
|
<Providers>
|
||||||
<>
|
<UnreadCountTitleHandler enabled={unreadCountTitle} />
|
||||||
<UnreadCountTitleHandler unreadCount={unreadCount} enabled={unreadCountTitle} />
|
<UnreadCountFaviconHandler enabled={unreadCountFavicon} />
|
||||||
<UnreadCountFaviconHandler unreadCount={unreadCount} enabled={unreadCountFavicon} />
|
<BrowserExtensionBadgeUnreadCountHandler />
|
||||||
<BrowserExtensionBadgeUnreadCountHandler />
|
<CustomJsHandler />
|
||||||
<HashRouter>
|
<CustomCssHandler />
|
||||||
<GoogleAnalyticsHandler />
|
|
||||||
<RedirectHandler />
|
{/* disable pull-to-refresh as it messes with vertical scrolling
|
||||||
<AppRoutes />
|
|
||||||
{/* 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
|
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
|
https://github.com/Athou/commafeed/issues/1168
|
||||||
*/}
|
*/}
|
||||||
{!isSafari && <DisablePullToRefresh />}
|
{!isSafari && <DisablePullToRefresh />}
|
||||||
</HashRouter>
|
|
||||||
</>
|
<HashRouter>
|
||||||
|
<RedirectHandler />
|
||||||
|
<AppRoutes />
|
||||||
|
</HashRouter>
|
||||||
</Providers>
|
</Providers>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createAsyncThunk } from "@reduxjs/toolkit"
|
import { createAsyncThunk } from "@reduxjs/toolkit"
|
||||||
import type { AppDispatch, RootState } from "app/store"
|
import type { AppDispatch, RootState } from "@/app/store"
|
||||||
|
|
||||||
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
|
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
|
||||||
state: RootState
|
state: RootState
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export const client = {
|
|||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
getAllUsers: async () => await axiosInstance.get<UserModel[]>("admin/user/getAll"),
|
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),
|
deleteUser: async (req: IDRequest) => await axiosInstance.post("admin/user/delete", req),
|
||||||
getMetrics: async () => await axiosInstance.get<Metrics>("admin/metrics"),
|
getMetrics: async () => await axiosInstance.get<Metrics>("admin/metrics"),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -87,23 +87,25 @@ export const Constants = {
|
|||||||
headerHeight: 60,
|
headerHeight: 60,
|
||||||
entryMaxWidth: 650,
|
entryMaxWidth: 650,
|
||||||
isTopVisible: (div: HTMLElement) => {
|
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)
|
return div.getBoundingClientRect().top >= (header?.bottom ?? 0)
|
||||||
},
|
},
|
||||||
isBottomVisible: (div: HTMLElement) => {
|
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)
|
return div.getBoundingClientRect().bottom <= (footer?.top ?? window.innerHeight)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
dom: {
|
dom: {
|
||||||
headerId: "header",
|
|
||||||
footerId: "footer",
|
|
||||||
entryId: (entry: Entry) => `entry-id-${entry.id}`,
|
entryId: (entry: Entry) => `entry-id-${entry.id}`,
|
||||||
entryContextMenuId: (entry: Entry) => entry.id,
|
entryContextMenuId: (entry: Entry) => entry.id,
|
||||||
},
|
},
|
||||||
|
theme: {
|
||||||
|
defaultPrimaryColor: "orange",
|
||||||
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
delay: 500,
|
delay: 500,
|
||||||
},
|
},
|
||||||
browserExtensionUrl: "https://github.com/Athou/commafeed-browser-extension",
|
browserExtensionUrl: "https://github.com/Athou/commafeed-browser-extension",
|
||||||
|
customCssDocumentationUrl: "https://athou.github.io/commafeed/documentation/custom-css",
|
||||||
bitcoinWalletAddress: "1dymfUxqCWpyD7a6rQSqNy4rLVDBsAr5e",
|
bitcoinWalletAddress: "1dymfUxqCWpyD7a6rQSqNy4rLVDBsAr5e",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,20 @@
|
|||||||
import { configureStore } from "@reduxjs/toolkit"
|
import { configureStore } from "@reduxjs/toolkit"
|
||||||
import type { 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 type { AxiosResponse } from "axios"
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||||
import { any, mockReset } from "vitest-mock-extended"
|
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"
|
||||||
|
|
||||||
const mockClient = await vi.hoisted(async () => {
|
vi.mock(import("@/app/client"))
|
||||||
const mockModule = await import("vitest-mock-extended")
|
|
||||||
return mockModule.mockDeep<typeof client>()
|
|
||||||
})
|
|
||||||
vi.mock("app/client", () => ({ client: mockClient }))
|
|
||||||
|
|
||||||
describe("entries", () => {
|
describe("entries", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockReset(mockClient)
|
vi.resetAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("loads entries", async () => {
|
it("loads entries", async () => {
|
||||||
mockClient.feed.getEntries.calledWith(any()).mockResolvedValue({
|
vi.mocked(client.feed.getEntries).mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
entries: [{ id: "3" } as Entry],
|
entries: [{ id: "3" } as Entry],
|
||||||
hasMore: false,
|
hasMore: false,
|
||||||
@@ -32,7 +27,12 @@ describe("entries", () => {
|
|||||||
} as AxiosResponse<Entries>)
|
} as AxiosResponse<Entries>)
|
||||||
|
|
||||||
const store = configureStore({ reducer: reducers })
|
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.type).toBe("feed")
|
||||||
expect(store.getState().entries.source.id).toBe("feed-id")
|
expect(store.getState().entries.source.id).toBe("feed-id")
|
||||||
@@ -53,7 +53,7 @@ describe("entries", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("loads more entries", async () => {
|
it("loads more entries", async () => {
|
||||||
mockClient.category.getEntries.calledWith(any()).mockResolvedValue({
|
vi.mocked(client.category.getEntries).mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
entries: [{ id: "4" } as Entry],
|
entries: [{ id: "4" } as Entry],
|
||||||
hasMore: false,
|
hasMore: false,
|
||||||
@@ -113,7 +113,7 @@ describe("entries", () => {
|
|||||||
{ id: "3", read: true },
|
{ id: "3", read: true },
|
||||||
{ id: "4", read: false },
|
{ id: "4", read: false },
|
||||||
])
|
])
|
||||||
expect(mockClient.entry.mark).toHaveBeenCalledWith({ id: "3", read: true })
|
expect(client.entry.mark).toHaveBeenCalledWith({ id: "3", read: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
it("marks all entries as read", () => {
|
it("marks all entries as read", () => {
|
||||||
@@ -135,11 +135,19 @@ describe("entries", () => {
|
|||||||
} as RootState,
|
} 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([
|
expect(store.getState().entries.entries).toStrictEqual([
|
||||||
{ id: "3", read: true },
|
{ id: "3", read: true },
|
||||||
{ id: "4", read: true },
|
{ id: "4", read: true },
|
||||||
])
|
])
|
||||||
expect(mockClient.category.markEntries).toHaveBeenCalledWith({ id: "all", read: true })
|
expect(client.category.markEntries).toHaveBeenCalledWith({
|
||||||
|
id: "all",
|
||||||
|
read: true,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
|
import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "@/app/constants"
|
||||||
import { loadEntries, loadMoreEntries, markAllEntries, markEntry, markMultipleEntries, starEntry, tagEntry } from "app/entries/thunks"
|
import { loadEntries, loadMoreEntries, markAllEntries, markEntry, markMultipleEntries, starEntry, tagEntry } from "@/app/entries/thunks"
|
||||||
import type { Entry } from "app/types"
|
import type { Entry } from "@/app/types"
|
||||||
|
|
||||||
export type EntrySourceType = "category" | "feed" | "tag"
|
export type EntrySourceType = "category" | "feed" | "tag"
|
||||||
|
|
||||||
@@ -28,6 +28,7 @@ interface EntriesState {
|
|||||||
loading: boolean
|
loading: boolean
|
||||||
search?: string
|
search?: string
|
||||||
scrollingToEntry: boolean
|
scrollingToEntry: boolean
|
||||||
|
markAllAsReadConfirmationDialogOpen: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: EntriesState = {
|
const initialState: EntriesState = {
|
||||||
@@ -41,6 +42,7 @@ const initialState: EntriesState = {
|
|||||||
hasMore: true,
|
hasMore: true,
|
||||||
loading: false,
|
loading: false,
|
||||||
scrollingToEntry: false,
|
scrollingToEntry: false,
|
||||||
|
markAllAsReadConfirmationDialogOpen: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const entriesSlice = createSlice({
|
export const entriesSlice = createSlice({
|
||||||
@@ -61,6 +63,9 @@ export const entriesSlice = createSlice({
|
|||||||
setSearch: (state, action: PayloadAction<string>) => {
|
setSearch: (state, action: PayloadAction<string>) => {
|
||||||
state.search = action.payload
|
state.search = action.payload
|
||||||
},
|
},
|
||||||
|
setMarkAllAsReadConfirmationDialogOpen: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.markAllAsReadConfirmationDialogOpen = action.payload
|
||||||
|
},
|
||||||
},
|
},
|
||||||
extraReducers: builder => {
|
extraReducers: builder => {
|
||||||
builder.addCase(markEntry.pending, (state, action) => {
|
builder.addCase(markEntry.pending, (state, action) => {
|
||||||
@@ -119,4 +124,4 @@ export const entriesSlice = createSlice({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const { setSearch } = entriesSlice.actions
|
export const { setSearch, setMarkAllAsReadConfirmationDialogOpen } = entriesSlice.actions
|
||||||
|
|||||||
@@ -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, 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 { 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) =>
|
const getEndpoint = (sourceType: EntrySourceType) =>
|
||||||
sourceType === "category" || sourceType === "tag" ? client.category.getEntries : client.feed.getEntries
|
sourceType === "category" || sourceType === "tag" ? client.category.getEntries : client.feed.getEntries
|
||||||
@@ -50,7 +56,7 @@ const buildGetEntriesPaginatedRequest = (state: RootState, source: EntrySource,
|
|||||||
keywords: state.entries.search,
|
keywords: state.entries.search,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const reloadEntries = createAppAsyncThunk("entries/reload", (arg, thunkApi) => {
|
export const reloadEntries = createAppAsyncThunk("entries/reload", (_, thunkApi) => {
|
||||||
const state = thunkApi.getState()
|
const state = thunkApi.getState()
|
||||||
thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false }))
|
thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false }))
|
||||||
})
|
})
|
||||||
@@ -123,6 +129,36 @@ export const markAllEntries = createAppAsyncThunk(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const markAllAsReadWithConfirmationIfRequired = createAppAsyncThunk(
|
||||||
|
"entries/entry/markAllAsReadWithConfirmationIfRequired",
|
||||||
|
async (_, thunkApi) => {
|
||||||
|
const state = thunkApi.getState()
|
||||||
|
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 {
|
||||||
|
await thunkApi.dispatch(
|
||||||
|
markAllEntries({
|
||||||
|
sourceType: source.type,
|
||||||
|
req: {
|
||||||
|
id: source.id,
|
||||||
|
read: true,
|
||||||
|
olderThan: Date.now(),
|
||||||
|
insertedBefore: entriesTimestamp,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const isAllCategorySelected = source.type === "category" && source.id === Constants.categories.all.id
|
||||||
|
if (markAllAsReadNavigateToNextUnread && !isAllCategorySelected)
|
||||||
|
await thunkApi.dispatch(selectNextUnreadTreeItem({ direction: "forward" }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
export const starEntry = createAppAsyncThunk(
|
export const starEntry = createAppAsyncThunk(
|
||||||
"entries/entry/star",
|
"entries/entry/star",
|
||||||
(arg: { entry: Entry; starred: boolean }) => {
|
(arg: { entry: Entry; starred: boolean }) => {
|
||||||
@@ -204,7 +240,7 @@ export const selectEntry = createAppAsyncThunk(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const scrollToEntry = (entryElement: HTMLElement, margin: number, scrollSpeed: number | undefined, onScrollEnded: () => void) => {
|
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
|
const offset = (header?.bottom ?? 0) + margin
|
||||||
scrollToWithCallback({
|
scrollToWithCallback({
|
||||||
options: {
|
options: {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { redirectToCategory } from "app/redirect/thunks"
|
|
||||||
import { store } from "app/store"
|
|
||||||
import { describe, expect, it } from "vitest"
|
import { describe, expect, it } from "vitest"
|
||||||
|
import { redirectToCategory } from "@/app/redirect/thunks"
|
||||||
|
import { store } from "@/app/store"
|
||||||
|
|
||||||
describe("redirects", () => {
|
describe("redirects", () => {
|
||||||
it("redirects to category", async () => {
|
it("redirects to category", async () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
|
import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
|
||||||
|
|
||||||
interface RedirectState {
|
interface RedirectState {
|
||||||
to?: string
|
to?: string
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { createAppAsyncThunk } from "app/async-thunk"
|
import { createAppAsyncThunk } from "@/app/async-thunk"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "@/app/constants"
|
||||||
import { redirectTo } from "app/redirect/slice"
|
import { redirectTo } from "@/app/redirect/slice"
|
||||||
|
|
||||||
export const redirectToLogin = createAppAsyncThunk("redirect/login", (_, thunkApi) => thunkApi.dispatch(redirectTo("/login")))
|
export const redirectToLogin = createAppAsyncThunk("redirect/login", (_, thunkApi) => thunkApi.dispatch(redirectTo("/login")))
|
||||||
|
|
||||||
export const redirectToRegistration = createAppAsyncThunk("redirect/register", (_, thunkApi) => thunkApi.dispatch(redirectTo("/register")))
|
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) => {
|
export const redirectToSelectedSource = createAppAsyncThunk("redirect/selectedSource", (_, thunkApi) => {
|
||||||
const { source } = thunkApi.getState().entries
|
const { source } = thunkApi.getState().entries
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
|
import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
|
||||||
import { reloadServerInfos } from "app/server/thunks"
|
import { reloadServerInfos } from "@/app/server/thunks"
|
||||||
import type { ServerInfo } from "app/types"
|
import type { ServerInfo } from "@/app/types"
|
||||||
|
|
||||||
interface ServerState {
|
interface ServerState {
|
||||||
serverInfos?: ServerInfo
|
serverInfos?: ServerInfo
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createAppAsyncThunk } from "app/async-thunk"
|
import { createAppAsyncThunk } from "@/app/async-thunk"
|
||||||
import { client } from "app/client"
|
import { client } from "@/app/client"
|
||||||
|
|
||||||
export const reloadServerInfos = createAppAsyncThunk("server/infos", async () => await client.server.getServerInfos().then(r => r.data))
|
export const reloadServerInfos = createAppAsyncThunk("server/infos", async () => await client.server.getServerInfos().then(r => r.data))
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { configureStore } from "@reduxjs/toolkit"
|
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 { 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 = {
|
export const reducers = {
|
||||||
entries: entriesSlice.reducer,
|
entries: entriesSlice.reducer,
|
||||||
@@ -17,19 +17,9 @@ export const reducers = {
|
|||||||
|
|
||||||
const loadLocalSettings = (): LocalSettings => {
|
const loadLocalSettings = (): LocalSettings => {
|
||||||
const json = localStorage.getItem("commafeed-local-settings")
|
const json = localStorage.getItem("commafeed-local-settings")
|
||||||
if (json) {
|
|
||||||
return JSON.parse(json)
|
|
||||||
}
|
|
||||||
|
|
||||||
// load old settings
|
|
||||||
const viewMode = localStorage.getItem("view-mode")
|
|
||||||
const sidebarWidth = localStorage.getItem("sidebar-width")
|
|
||||||
const announcementHash = localStorage.getItem("announcement-hash")
|
|
||||||
return {
|
return {
|
||||||
...initialLocalSettings,
|
...initialLocalSettings,
|
||||||
viewMode: viewMode ? JSON.parse(viewMode) : initialLocalSettings.viewMode,
|
...(json ? JSON.parse(json) : {}),
|
||||||
sidebarWidth: sidebarWidth ? JSON.parse(sidebarWidth) : initialLocalSettings.sidebarWidth,
|
|
||||||
announcementHash: announcementHash ? JSON.parse(announcementHash) : initialLocalSettings.announcementHash,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
|
import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
|
||||||
import { markEntry } from "app/entries/thunks"
|
import { loadEntries, markEntry } from "@/app/entries/thunks"
|
||||||
import { redirectTo } from "app/redirect/slice"
|
import { redirectTo } from "@/app/redirect/slice"
|
||||||
import { collapseTreeCategory, reloadTree } from "app/tree/thunks"
|
import { collapseTreeCategory, reloadTree } from "@/app/tree/thunks"
|
||||||
import type { Category } from "app/types"
|
import type { Category, Subscription } from "@/app/types"
|
||||||
import { visitCategoryTree } from "app/utils"
|
import { flattenCategoryTree, visitCategoryTree } from "@/app/utils"
|
||||||
|
|
||||||
|
export interface TreeSubscription extends Subscription {
|
||||||
|
// client-side only flag
|
||||||
|
hasNewEntries?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TreeCategory extends Category {
|
||||||
|
feeds: TreeSubscription[]
|
||||||
|
children: TreeCategory[]
|
||||||
|
}
|
||||||
|
|
||||||
interface TreeState {
|
interface TreeState {
|
||||||
rootCategory?: Category
|
rootCategory?: TreeCategory
|
||||||
mobileMenuOpen: boolean
|
mobileMenuOpen: boolean
|
||||||
sidebarVisible: boolean
|
sidebarVisible: boolean
|
||||||
}
|
}
|
||||||
@@ -37,12 +47,27 @@ export const treeSlice = createSlice({
|
|||||||
visitCategoryTree(state.rootCategory, c => {
|
visitCategoryTree(state.rootCategory, c => {
|
||||||
for (const f of c.feeds.filter(f => f.id === action.payload.feedId)) {
|
for (const f of c.feeds.filter(f => f.id === action.payload.feedId)) {
|
||||||
f.unread += action.payload.amount
|
f.unread += action.payload.amount
|
||||||
|
f.hasNewEntries = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraReducers: builder => {
|
extraReducers: builder => {
|
||||||
builder.addCase(reloadTree.fulfilled, (state, action) => {
|
builder.addCase(reloadTree.fulfilled, (state, action) => {
|
||||||
|
// set hasNewEntries to true if new unread > previous unread
|
||||||
|
if (state.rootCategory) {
|
||||||
|
const oldFeeds = flattenCategoryTree(state.rootCategory).flatMap(c => c.feeds)
|
||||||
|
const oldFeedsById = new Map(oldFeeds.map(f => [f.id, f]))
|
||||||
|
|
||||||
|
const newFeeds = flattenCategoryTree(action.payload).flatMap(c => c.feeds)
|
||||||
|
for (const newFeed of newFeeds) {
|
||||||
|
const oldFeed = oldFeedsById.get(newFeed.id)
|
||||||
|
if (oldFeed && newFeed.unread > oldFeed.unread) {
|
||||||
|
newFeed.hasNewEntries = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state.rootCategory = action.payload
|
state.rootCategory = action.payload
|
||||||
})
|
})
|
||||||
builder.addCase(collapseTreeCategory.pending, (state, action) => {
|
builder.addCase(collapseTreeCategory.pending, (state, action) => {
|
||||||
@@ -59,6 +84,25 @@ export const treeSlice = createSlice({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
builder.addCase(loadEntries.fulfilled, (state, action) => {
|
||||||
|
if (!state.rootCategory) return
|
||||||
|
|
||||||
|
const { source } = action.meta.arg
|
||||||
|
if (source.type === "category") {
|
||||||
|
visitCategoryTree(state.rootCategory, c => {
|
||||||
|
if (c.id === source.id) {
|
||||||
|
for (const f of flattenCategoryTree(c).flatMap(c => c.feeds)) {
|
||||||
|
f.hasNewEntries = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (source.type === "feed") {
|
||||||
|
const feeds = flattenCategoryTree(state.rootCategory).flatMap(c => c.feeds)
|
||||||
|
for (const f of feeds.filter(f => f.id === +source.id)) {
|
||||||
|
f.hasNewEntries = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
builder.addCase(redirectTo, state => {
|
builder.addCase(redirectTo, state => {
|
||||||
state.mobileMenuOpen = false
|
state.mobileMenuOpen = false
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,14 +1,63 @@
|
|||||||
import { createAppAsyncThunk } from "app/async-thunk"
|
import { createAppAsyncThunk } from "@/app/async-thunk"
|
||||||
import { client } from "app/client"
|
import { client } from "@/app/client"
|
||||||
import { incrementUnreadCount } from "app/tree/slice"
|
import { Constants } from "@/app/constants"
|
||||||
import type { CollapseRequest } from "app/types"
|
import { redirectToCategory, redirectToFeed } from "@/app/redirect/thunks"
|
||||||
import { flattenCategoryTree } from "app/utils"
|
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))
|
export const reloadTree = createAppAsyncThunk("tree/reload", async () => await client.category.getRoot().then(r => r.data))
|
||||||
|
|
||||||
export const collapseTreeCategory = createAppAsyncThunk(
|
export const collapseTreeCategory = createAppAsyncThunk(
|
||||||
"tree/category/collapse",
|
"tree/category/collapse",
|
||||||
async (req: CollapseRequest) => await client.category.collapse(req)
|
async (req: CollapseRequest) => await client.category.collapse(req).then(r => r.data)
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectNextUnreadTreeItem = createAppAsyncThunk(
|
||||||
|
"tree/selectNextUnreadItem",
|
||||||
|
(
|
||||||
|
arg: {
|
||||||
|
direction: "forward" | "backward"
|
||||||
|
},
|
||||||
|
thunkApi
|
||||||
|
) => {
|
||||||
|
const state = thunkApi.getState()
|
||||||
|
const root = state.tree.rootCategory
|
||||||
|
if (!root) return
|
||||||
|
|
||||||
|
const { source } = state.entries
|
||||||
|
if (source.type === "category") {
|
||||||
|
const categories = flattenCategoryTree(root)
|
||||||
|
if (arg.direction === "backward") categories.reverse()
|
||||||
|
|
||||||
|
const index = categories.findIndex(c => c.id === source.id)
|
||||||
|
if (index === -1) return
|
||||||
|
|
||||||
|
for (let i = index + 1; i < categories.length; i++) {
|
||||||
|
const c = categories[i]
|
||||||
|
if (c.feeds.some(f => f.unread > 0)) {
|
||||||
|
return thunkApi.dispatch(redirectToCategory(String(c.id)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (source.type === "feed") {
|
||||||
|
const feeds: Subscription[] = []
|
||||||
|
visitCategoryTree(root, c => feeds.push(...c.feeds), { childrenFirst: true })
|
||||||
|
if (arg.direction === "backward") feeds.reverse()
|
||||||
|
|
||||||
|
const index = feeds.findIndex(f => f.id === +source.id)
|
||||||
|
if (index === -1) return
|
||||||
|
|
||||||
|
for (let i = index + 1; i < feeds.length; i++) {
|
||||||
|
const f = feeds[i]
|
||||||
|
if (f.unread > 0) {
|
||||||
|
return thunkApi.dispatch(redirectToFeed(String(f.id)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
export const newFeedEntriesDiscovered = createAppAsyncThunk(
|
export const newFeedEntriesDiscovered = createAppAsyncThunk(
|
||||||
|
|||||||
172
commafeed-client/src/app/tree/tree.test.ts
Normal file
172
commafeed-client/src/app/tree/tree.test.ts
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import { configureStore } from "@reduxjs/toolkit"
|
||||||
|
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"))
|
||||||
|
|
||||||
|
const createCategory = (id: string): Category => ({
|
||||||
|
id,
|
||||||
|
name: id,
|
||||||
|
children: [],
|
||||||
|
feeds: [],
|
||||||
|
expanded: true,
|
||||||
|
position: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
const createFeed = (id: number, unread: number): Subscription => ({
|
||||||
|
id,
|
||||||
|
name: String(id),
|
||||||
|
unread,
|
||||||
|
errorCount: 0,
|
||||||
|
position: 0,
|
||||||
|
feedUrl: "",
|
||||||
|
feedLink: "",
|
||||||
|
iconUrl: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
const root = createCategory("root")
|
||||||
|
|
||||||
|
const catA = createCategory("catA")
|
||||||
|
catA.feeds.push(createFeed(1, 0), createFeed(2, 0), createFeed(3, 1))
|
||||||
|
|
||||||
|
const catB = createCategory("catB")
|
||||||
|
|
||||||
|
const catC = createCategory("catC")
|
||||||
|
catC.feeds.push(createFeed(4, 1))
|
||||||
|
|
||||||
|
root.children.push(catA, catB, catC)
|
||||||
|
|
||||||
|
describe("selectNextUnreadTreeItem", () => {
|
||||||
|
it("selects the next unread category", async () => {
|
||||||
|
const store = configureStore({
|
||||||
|
reducer: reducers,
|
||||||
|
preloadedState: {
|
||||||
|
tree: {
|
||||||
|
rootCategory: root,
|
||||||
|
},
|
||||||
|
entries: {
|
||||||
|
source: {
|
||||||
|
type: "category",
|
||||||
|
id: "catA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as RootState,
|
||||||
|
})
|
||||||
|
|
||||||
|
await store.dispatch(selectNextUnreadTreeItem({ direction: "forward" }))
|
||||||
|
expect(store.getState().redirect.to).toBe("/app/category/catC")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("selects the previous unread category", async () => {
|
||||||
|
const store = configureStore({
|
||||||
|
reducer: reducers,
|
||||||
|
preloadedState: {
|
||||||
|
tree: {
|
||||||
|
rootCategory: root,
|
||||||
|
},
|
||||||
|
entries: {
|
||||||
|
source: {
|
||||||
|
type: "category",
|
||||||
|
id: "catC",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as RootState,
|
||||||
|
})
|
||||||
|
|
||||||
|
await store.dispatch(selectNextUnreadTreeItem({ direction: "backward" }))
|
||||||
|
expect(store.getState().redirect.to).toBe("/app/category/catA")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("selects the next unread feed", async () => {
|
||||||
|
const store = configureStore({
|
||||||
|
reducer: reducers,
|
||||||
|
preloadedState: {
|
||||||
|
tree: {
|
||||||
|
rootCategory: root,
|
||||||
|
},
|
||||||
|
entries: {
|
||||||
|
source: {
|
||||||
|
type: "feed",
|
||||||
|
id: "1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as RootState,
|
||||||
|
})
|
||||||
|
|
||||||
|
await store.dispatch(selectNextUnreadTreeItem({ direction: "forward" }))
|
||||||
|
expect(store.getState().redirect.to).toBe("/app/feed/3")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("selects the previous unread feed", async () => {
|
||||||
|
const store = configureStore({
|
||||||
|
reducer: reducers,
|
||||||
|
preloadedState: {
|
||||||
|
tree: {
|
||||||
|
rootCategory: root,
|
||||||
|
},
|
||||||
|
entries: {
|
||||||
|
source: {
|
||||||
|
type: "feed",
|
||||||
|
id: "4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as RootState,
|
||||||
|
})
|
||||||
|
|
||||||
|
await store.dispatch(selectNextUnreadTreeItem({ direction: "backward" }))
|
||||||
|
expect(store.getState().redirect.to).toBe("/app/feed/3")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("hasNewEntries", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.resetAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("sets and clear flag for a feed", async () => {
|
||||||
|
vi.mocked(client.feed.getEntries).mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
entries: [{ id: "3" } as Entry],
|
||||||
|
hasMore: false,
|
||||||
|
name: "my-feed",
|
||||||
|
errorCount: 3,
|
||||||
|
feedLink: "https://mysite.com/feed",
|
||||||
|
timestamp: 123,
|
||||||
|
ignoredReadStatus: false,
|
||||||
|
},
|
||||||
|
} as AxiosResponse<Entries>)
|
||||||
|
|
||||||
|
const store = configureStore({
|
||||||
|
reducer: reducers,
|
||||||
|
preloadedState: {
|
||||||
|
tree: {
|
||||||
|
rootCategory: root,
|
||||||
|
},
|
||||||
|
entries: {
|
||||||
|
source: {
|
||||||
|
type: "feed",
|
||||||
|
id: "1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as RootState,
|
||||||
|
})
|
||||||
|
|
||||||
|
// initial state
|
||||||
|
expect(store.getState().tree.rootCategory?.children[0].feeds[0].unread).toBe(0)
|
||||||
|
expect(store.getState().tree.rootCategory?.children[0].feeds[0].hasNewEntries).toBeFalsy()
|
||||||
|
|
||||||
|
// increments unread count and sets hasNewEntries to true
|
||||||
|
await store.dispatch(newFeedEntriesDiscovered({ feedId: 1, amount: 3 }))
|
||||||
|
expect(store.getState().tree.rootCategory?.children[0].feeds[0].unread).toBe(3)
|
||||||
|
expect(store.getState().tree.rootCategory?.children[0].feeds[0].hasNewEntries).toBe(true)
|
||||||
|
|
||||||
|
// reload entries and sets hasNewEntries to false
|
||||||
|
await store.dispatch(loadEntries({ source: { type: "feed", id: "1" }, clearSearch: true }))
|
||||||
|
expect(store.getState().tree.rootCategory?.children[0].feeds[0].hasNewEntries).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -214,7 +214,6 @@ export interface ServerInfo {
|
|||||||
version: string
|
version: string
|
||||||
gitCommit: string
|
gitCommit: string
|
||||||
allowRegistrations: boolean
|
allowRegistrations: boolean
|
||||||
googleAnalyticsCode?: string
|
|
||||||
smtpEnabled: boolean
|
smtpEnabled: boolean
|
||||||
demoAccountEnabled: boolean
|
demoAccountEnabled: boolean
|
||||||
websocketEnabled: boolean
|
websocketEnabled: boolean
|
||||||
@@ -248,10 +247,12 @@ export interface Settings {
|
|||||||
starIconDisplayMode: IconDisplayMode
|
starIconDisplayMode: IconDisplayMode
|
||||||
externalLinkIconDisplayMode: IconDisplayMode
|
externalLinkIconDisplayMode: IconDisplayMode
|
||||||
markAllAsReadConfirmation: boolean
|
markAllAsReadConfirmation: boolean
|
||||||
|
markAllAsReadNavigateToNextUnread: boolean
|
||||||
customContextMenu: boolean
|
customContextMenu: boolean
|
||||||
mobileFooter: boolean
|
mobileFooter: boolean
|
||||||
unreadCountTitle: boolean
|
unreadCountTitle: boolean
|
||||||
unreadCountFavicon: boolean
|
unreadCountFavicon: boolean
|
||||||
|
primaryColor?: string
|
||||||
sharingSettings: SharingSettings
|
sharingSettings: SharingSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,6 +260,7 @@ export interface LocalSettings {
|
|||||||
viewMode: ViewMode
|
viewMode: ViewMode
|
||||||
sidebarWidth: number
|
sidebarWidth: number
|
||||||
announcementHash: string
|
announcementHash: string
|
||||||
|
fontSizePercentage: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StarRequest {
|
export interface StarRequest {
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { showNotification } from "@mantine/notifications"
|
import { showNotification } from "@mantine/notifications"
|
||||||
import { type PayloadAction, createSlice, isAnyOf } from "@reduxjs/toolkit"
|
import { createSlice, isAnyOf, type PayloadAction } from "@reduxjs/toolkit"
|
||||||
import type { LocalSettings, Settings, UserModel, ViewMode } from "app/types"
|
import type { LocalSettings, Settings, UserModel, ViewMode } from "@/app/types"
|
||||||
import {
|
import {
|
||||||
changeCustomContextMenu,
|
changeCustomContextMenu,
|
||||||
changeEntriesToKeepOnTopWhenScrolling,
|
changeEntriesToKeepOnTopWhenScrolling,
|
||||||
changeExternalLinkIconDisplayMode,
|
changeExternalLinkIconDisplayMode,
|
||||||
changeLanguage,
|
changeLanguage,
|
||||||
changeMarkAllAsReadConfirmation,
|
changeMarkAllAsReadConfirmation,
|
||||||
|
changeMarkAllAsReadNavigateToUnread,
|
||||||
changeMobileFooter,
|
changeMobileFooter,
|
||||||
|
changePrimaryColor,
|
||||||
changeReadingMode,
|
changeReadingMode,
|
||||||
changeReadingOrder,
|
changeReadingOrder,
|
||||||
changeScrollMarks,
|
changeScrollMarks,
|
||||||
@@ -35,6 +37,7 @@ export const initialLocalSettings: LocalSettings = {
|
|||||||
viewMode: "detailed",
|
viewMode: "detailed",
|
||||||
sidebarWidth: 360,
|
sidebarWidth: 360,
|
||||||
announcementHash: "no-hash",
|
announcementHash: "no-hash",
|
||||||
|
fontSizePercentage: 100,
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: UserState = {
|
const initialState: UserState = {
|
||||||
@@ -48,6 +51,9 @@ export const userSlice = createSlice({
|
|||||||
setViewMode: (state, action: PayloadAction<ViewMode>) => {
|
setViewMode: (state, action: PayloadAction<ViewMode>) => {
|
||||||
state.localSettings.viewMode = action.payload
|
state.localSettings.viewMode = action.payload
|
||||||
},
|
},
|
||||||
|
setFontSizePercentage: (state, action: PayloadAction<number>) => {
|
||||||
|
state.localSettings.fontSizePercentage = action.payload
|
||||||
|
},
|
||||||
setSidebarWidth: (state, action: PayloadAction<number>) => {
|
setSidebarWidth: (state, action: PayloadAction<number>) => {
|
||||||
state.localSettings.sidebarWidth = action.payload
|
state.localSettings.sidebarWidth = action.payload
|
||||||
},
|
},
|
||||||
@@ -109,6 +115,10 @@ export const userSlice = createSlice({
|
|||||||
if (!state.settings) return
|
if (!state.settings) return
|
||||||
state.settings.markAllAsReadConfirmation = action.meta.arg
|
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) => {
|
builder.addCase(changeCustomContextMenu.pending, (state, action) => {
|
||||||
if (!state.settings) return
|
if (!state.settings) return
|
||||||
state.settings.customContextMenu = action.meta.arg
|
state.settings.customContextMenu = action.meta.arg
|
||||||
@@ -125,6 +135,10 @@ export const userSlice = createSlice({
|
|||||||
if (!state.settings) return
|
if (!state.settings) return
|
||||||
state.settings.unreadCountFavicon = action.meta.arg
|
state.settings.unreadCountFavicon = action.meta.arg
|
||||||
})
|
})
|
||||||
|
builder.addCase(changePrimaryColor.pending, (state, action) => {
|
||||||
|
if (!state.settings) return
|
||||||
|
state.settings.primaryColor = action.meta.arg
|
||||||
|
})
|
||||||
builder.addCase(changeSharingSetting.pending, (state, action) => {
|
builder.addCase(changeSharingSetting.pending, (state, action) => {
|
||||||
if (!state.settings) return
|
if (!state.settings) return
|
||||||
state.settings.sharingSettings[action.meta.arg.site] = action.meta.arg.value
|
state.settings.sharingSettings[action.meta.arg.site] = action.meta.arg.value
|
||||||
@@ -140,10 +154,12 @@ export const userSlice = createSlice({
|
|||||||
changeStarIconDisplayMode.fulfilled,
|
changeStarIconDisplayMode.fulfilled,
|
||||||
changeExternalLinkIconDisplayMode.fulfilled,
|
changeExternalLinkIconDisplayMode.fulfilled,
|
||||||
changeMarkAllAsReadConfirmation.fulfilled,
|
changeMarkAllAsReadConfirmation.fulfilled,
|
||||||
|
changeMarkAllAsReadNavigateToUnread.fulfilled,
|
||||||
changeCustomContextMenu.fulfilled,
|
changeCustomContextMenu.fulfilled,
|
||||||
changeMobileFooter.fulfilled,
|
changeMobileFooter.fulfilled,
|
||||||
changeUnreadCountTitle.fulfilled,
|
changeUnreadCountTitle.fulfilled,
|
||||||
changeUnreadCountFavicon.fulfilled,
|
changeUnreadCountFavicon.fulfilled,
|
||||||
|
changePrimaryColor.fulfilled,
|
||||||
changeSharingSetting.fulfilled
|
changeSharingSetting.fulfilled
|
||||||
),
|
),
|
||||||
() => {
|
() => {
|
||||||
@@ -156,4 +172,4 @@ export const userSlice = createSlice({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const { setViewMode, setSidebarWidth, setAnnouncementHash } = userSlice.actions
|
export const { setViewMode, setSidebarWidth, setAnnouncementHash, setFontSizePercentage } = userSlice.actions
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createAppAsyncThunk } from "app/async-thunk"
|
import { createAppAsyncThunk } from "@/app/async-thunk"
|
||||||
import { client } from "app/client"
|
import { client } from "@/app/client"
|
||||||
import { reloadEntries } from "app/entries/thunks"
|
import { reloadEntries } from "@/app/entries/thunks"
|
||||||
import type { IconDisplayMode, ReadingMode, ReadingOrder, ScrollMode, SharingSettings } from "app/types"
|
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))
|
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) => {
|
export const changeCustomContextMenu = createAppAsyncThunk("settings/customContextMenu", (customContextMenu: boolean, thunkApi) => {
|
||||||
const { settings } = thunkApi.getState().user
|
const { settings } = thunkApi.getState().user
|
||||||
if (!settings) return
|
if (!settings) return
|
||||||
@@ -113,6 +122,12 @@ export const changeUnreadCountFavicon = createAppAsyncThunk("settings/unreadCoun
|
|||||||
client.user.saveSettings({ ...settings, unreadCountFavicon })
|
client.user.saveSettings({ ...settings, unreadCountFavicon })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const changePrimaryColor = createAppAsyncThunk("settings/primaryColor", (primaryColor: string, thunkApi) => {
|
||||||
|
const { settings } = thunkApi.getState().user
|
||||||
|
if (!settings) return
|
||||||
|
client.user.saveSettings({ ...settings, primaryColor })
|
||||||
|
})
|
||||||
|
|
||||||
export const changeSharingSetting = createAppAsyncThunk(
|
export const changeSharingSetting = createAppAsyncThunk(
|
||||||
"settings/sharingSetting",
|
"settings/sharingSetting",
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -1,20 +1,32 @@
|
|||||||
import { throttle } from "throttle-debounce"
|
import { throttle } from "throttle-debounce"
|
||||||
|
import type { TreeCategory } from "@/app/tree/slice"
|
||||||
import type { Category } from "./types"
|
import type { Category } from "./types"
|
||||||
|
|
||||||
export function visitCategoryTree(category: Category, visitor: (category: Category) => void): void {
|
export function visitCategoryTree(
|
||||||
visitor(category)
|
category: TreeCategory,
|
||||||
for (const child of category.children) {
|
visitor: (category: TreeCategory) => void,
|
||||||
visitCategoryTree(child, visitor)
|
options?: {
|
||||||
|
childrenFirst?: boolean
|
||||||
}
|
}
|
||||||
|
): void {
|
||||||
|
const childrenFirst = options?.childrenFirst
|
||||||
|
|
||||||
|
if (!childrenFirst) visitor(category)
|
||||||
|
|
||||||
|
for (const child of category.children) {
|
||||||
|
visitCategoryTree(child, visitor, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childrenFirst) visitor(category)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flattenCategoryTree(category: Category): Category[] {
|
export function flattenCategoryTree(category: TreeCategory): TreeCategory[] {
|
||||||
const categories: Category[] = []
|
const categories: Category[] = []
|
||||||
visitCategoryTree(category, c => categories.push(c))
|
visitCategoryTree(category, c => categories.push(c))
|
||||||
return categories
|
return categories
|
||||||
}
|
}
|
||||||
|
|
||||||
export function categoryUnreadCount(category?: Category): number {
|
export function categoryUnreadCount(category?: TreeCategory): number {
|
||||||
if (!category) return 0
|
if (!category) return 0
|
||||||
|
|
||||||
return flattenCategoryTree(category)
|
return flattenCategoryTree(category)
|
||||||
@@ -23,6 +35,14 @@ export function categoryUnreadCount(category?: Category): number {
|
|||||||
.reduce((total, current) => total + current, 0)
|
.reduce((total, current) => total + current, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function categoryHasNewEntries(category?: TreeCategory): boolean {
|
||||||
|
if (!category) return false
|
||||||
|
|
||||||
|
return flattenCategoryTree(category)
|
||||||
|
.flatMap(c => c.feeds)
|
||||||
|
.some(f => f.hasNewEntries)
|
||||||
|
}
|
||||||
|
|
||||||
export const calculatePlaceholderSize = ({ width, height, maxWidth }: { width?: number; height?: number; maxWidth: number }) => {
|
export const calculatePlaceholderSize = ({ width, height, maxWidth }: { width?: number; height?: number; maxWidth: number }) => {
|
||||||
const placeholderWidth = width && Math.min(width, maxWidth)
|
const placeholderWidth = width && Math.min(width, maxWidth)
|
||||||
const placeholderHeight = height && width && width > maxWidth ? height * (maxWidth / width) : height
|
const placeholderHeight = height && width && width > maxWidth ? height * (maxWidth / width) : height
|
||||||
|
|||||||
53
commafeed-client/src/components/ActionButton.test.tsx
Normal file
53
commafeed-client/src/components/ActionButton.test.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import type { I18nContext } from "@lingui/react"
|
||||||
|
import { MantineProvider } from "@mantine/core"
|
||||||
|
import { fireEvent, render, screen, waitFor } from "@testing-library/react"
|
||||||
|
import { describe, expect, it, vi } from "vitest"
|
||||||
|
import { useActionButton } from "@/hooks/useActionButton"
|
||||||
|
import { ActionButton } from "./ActionButton"
|
||||||
|
|
||||||
|
vi.mock(import("@lingui/react"), () => ({
|
||||||
|
useLingui: vi.fn().mockReturnValue({
|
||||||
|
_: msg => msg,
|
||||||
|
} as I18nContext),
|
||||||
|
}))
|
||||||
|
vi.mock(import("@/hooks/useActionButton"))
|
||||||
|
|
||||||
|
const label = "Test Label"
|
||||||
|
const icon = "Test Icon"
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
expect(screen.getByText(label)).toBeInTheDocument()
|
||||||
|
expect(screen.getByText(icon)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("renders ActionIcon with tooltip on mobile", async () => {
|
||||||
|
vi.mocked(useActionButton).mockReturnValue({ mobile: true, spacing: 0 })
|
||||||
|
|
||||||
|
render(<ActionButton label={label} icon={icon} />, {
|
||||||
|
wrapper: MantineProvider,
|
||||||
|
})
|
||||||
|
expect(screen.queryByText(label)).not.toBeInTheDocument()
|
||||||
|
expect(screen.getByText(icon)).toBeInTheDocument()
|
||||||
|
|
||||||
|
fireEvent.mouseEnter(screen.getByRole("button"))
|
||||||
|
const tooltip = await waitFor(() => screen.getByRole("tooltip"))
|
||||||
|
expect(tooltip).toContainHTML(label)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls onClick handler when clicked", () => {
|
||||||
|
vi.mocked(useActionButton).mockReturnValue({ mobile: false, spacing: 0 })
|
||||||
|
const clickListener = vi.fn()
|
||||||
|
|
||||||
|
render(<ActionButton label={label} icon={icon} onClick={clickListener} />, {
|
||||||
|
wrapper: MantineProvider,
|
||||||
|
})
|
||||||
|
fireEvent.click(screen.getByRole("button"))
|
||||||
|
|
||||||
|
expect(clickListener).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
import type { MessageDescriptor } from "@lingui/core"
|
import type { MessageDescriptor } from "@lingui/core"
|
||||||
import { useLingui } from "@lingui/react"
|
import { useLingui } from "@lingui/react"
|
||||||
import { ActionIcon, Button, type ButtonVariant, Tooltip, useMantineTheme } from "@mantine/core"
|
import { ActionIcon, Box, Button, type ButtonVariant, Tooltip, useMantineTheme } from "@mantine/core"
|
||||||
import type { ActionIconVariant } from "@mantine/core/lib/components/ActionIcon/ActionIcon"
|
import type { ActionIconVariant } from "@mantine/core/lib/components/ActionIcon/ActionIcon"
|
||||||
import { Constants } from "app/constants"
|
import { forwardRef, type MouseEventHandler, type ReactNode } from "react"
|
||||||
import { useActionButton } from "hooks/useActionButton"
|
import { Constants } from "@/app/constants"
|
||||||
import { type MouseEventHandler, type ReactNode, forwardRef } from "react"
|
import { useActionButton } from "@/hooks/useActionButton"
|
||||||
|
|
||||||
interface ActionButtonProps {
|
interface ActionButtonProps {
|
||||||
|
icon: ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
icon?: ReactNode
|
|
||||||
label?: string | MessageDescriptor
|
label?: string | MessageDescriptor
|
||||||
onClick?: MouseEventHandler
|
onClick?: MouseEventHandler
|
||||||
variant?: ActionIconVariant & ButtonVariant
|
variant?: ActionIconVariant & ButtonVariant
|
||||||
@@ -19,7 +19,7 @@ interface ActionButtonProps {
|
|||||||
/**
|
/**
|
||||||
* Switches between Button with label (desktop) and ActionIcon (mobile)
|
* Switches between Button with label (desktop) and ActionIcon (mobile)
|
||||||
*/
|
*/
|
||||||
export const ActionButton = forwardRef<HTMLButtonElement, ActionButtonProps>((props: ActionButtonProps, ref) => {
|
export const ActionButton = forwardRef<HTMLDivElement, ActionButtonProps>((props: ActionButtonProps, ref) => {
|
||||||
const { mobile } = useActionButton()
|
const { mobile } = useActionButton()
|
||||||
const theme = useMantineTheme()
|
const theme = useMantineTheme()
|
||||||
const { _ } = useLingui()
|
const { _ } = useLingui()
|
||||||
@@ -27,31 +27,36 @@ export const ActionButton = forwardRef<HTMLButtonElement, ActionButtonProps>((pr
|
|||||||
const label = typeof props.label === "string" ? props.label : props.label && _(props.label)
|
const label = typeof props.label === "string" ? props.label : props.label && _(props.label)
|
||||||
const variant = props.variant ?? "subtle"
|
const variant = props.variant ?? "subtle"
|
||||||
const iconOnly = (mobile && !props.showLabelOnMobile) || (!mobile && props.hideLabelOnDesktop)
|
const iconOnly = (mobile && !props.showLabelOnMobile) || (!mobile && props.hideLabelOnDesktop)
|
||||||
return iconOnly ? (
|
|
||||||
<Tooltip label={label} openDelay={Constants.tooltip.delay}>
|
return (
|
||||||
<ActionIcon
|
<Box ref={ref} className="cf-action-button">
|
||||||
ref={ref}
|
{iconOnly && (
|
||||||
color={theme.primaryColor}
|
<Tooltip label={label} openDelay={Constants.tooltip.delay}>
|
||||||
variant={variant}
|
<ActionIcon
|
||||||
className={props.className}
|
color={theme.primaryColor}
|
||||||
onClick={props.onClick}
|
variant={variant}
|
||||||
aria-label={label}
|
className={props.className}
|
||||||
>
|
onClick={props.onClick}
|
||||||
{props.icon}
|
aria-label={label}
|
||||||
</ActionIcon>
|
>
|
||||||
</Tooltip>
|
{props.icon}
|
||||||
) : (
|
</ActionIcon>
|
||||||
<Button
|
</Tooltip>
|
||||||
ref={ref}
|
)}
|
||||||
variant={variant}
|
{!iconOnly && (
|
||||||
size="xs"
|
<Button
|
||||||
className={props.className}
|
variant={variant}
|
||||||
leftSection={props.icon}
|
size="xs"
|
||||||
onClick={props.onClick}
|
className={props.className}
|
||||||
aria-label={label}
|
leftSection={props.icon}
|
||||||
>
|
onClick={props.onClick}
|
||||||
{label}
|
aria-label={label}
|
||||||
</Button>
|
>
|
||||||
|
{label}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
ActionButton.displayName = "HeaderButton"
|
ActionButton.displayName = "HeaderButton"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export interface ErrorsAlertProps {
|
|||||||
messages: string[]
|
messages: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Alert(props: ErrorsAlertProps) {
|
export function Alert(props: Readonly<ErrorsAlertProps>) {
|
||||||
let title: React.ReactNode
|
let title: React.ReactNode
|
||||||
let color: string
|
let color: string
|
||||||
let icon: React.ReactNode
|
let icon: React.ReactNode
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Box, Dialog, Text } from "@mantine/core"
|
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 { 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 sha256Hex = async (input: string | undefined) => {
|
||||||
const data = new TextEncoder().encode(input)
|
const data = new TextEncoder().encode(input)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export const DisablePullToRefresh = () => {
|
export const DisablePullToRefresh = () => {
|
||||||
import("./DisablePullToRefresh.css")
|
import("./DisablePullToRefresh.css")
|
||||||
return <></>
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ErrorPage } from "pages/ErrorPage"
|
|
||||||
import React, { type ReactNode } from "react"
|
import React, { type ReactNode } from "react"
|
||||||
|
import { ErrorPage } from "@/pages/ErrorPage"
|
||||||
|
|
||||||
interface ErrorBoundaryProps {
|
interface ErrorBoundaryProps {
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Box, Center } from "@mantine/core"
|
import { Box, Center } from "@mantine/core"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { TbPhoto } from "react-icons/tb"
|
import { TbPhoto } from "react-icons/tb"
|
||||||
import { tss } from "tss"
|
import { tss } from "@/tss"
|
||||||
|
|
||||||
interface ImageWithPlaceholderWhileLoadingProps {
|
interface ImageWithPlaceholderWhileLoadingProps {
|
||||||
src: string
|
src: string
|
||||||
@@ -44,7 +44,7 @@ export function ImageWithPlaceholderWhileLoading({
|
|||||||
title,
|
title,
|
||||||
width,
|
width,
|
||||||
style,
|
style,
|
||||||
}: ImageWithPlaceholderWhileLoadingProps) {
|
}: Readonly<ImageWithPlaceholderWhileLoadingProps>) {
|
||||||
const { classes } = useStyles({
|
const { classes } = useStyles({
|
||||||
placeholderWidth,
|
placeholderWidth,
|
||||||
placeholderHeight,
|
placeholderHeight,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Anchor, Box, Kbd, Stack, Table } from "@mantine/core"
|
import { Anchor, Box, Kbd, Stack, Table } from "@mantine/core"
|
||||||
import { useOs } from "@mantine/hooks"
|
import { useOs } from "@mantine/hooks"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "@/app/constants"
|
||||||
|
|
||||||
export function KeyboardShortcutsHelp() {
|
export function KeyboardShortcutsHelp() {
|
||||||
const isMacOS = useOs() === "macos"
|
const isMacOS = useOs() === "macos"
|
||||||
@@ -33,6 +33,26 @@ export function KeyboardShortcutsHelp() {
|
|||||||
<Kbd>K</Kbd>
|
<Kbd>K</Kbd>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
</Table.Tr>
|
</Table.Tr>
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td>
|
||||||
|
<Trans>Select next unread feed/category</Trans>
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<Kbd>Shift</Kbd>
|
||||||
|
<span> + </span>
|
||||||
|
<Kbd>J</Kbd>
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td>
|
||||||
|
<Trans>Select previous unread feed/category</Trans>
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<Kbd>Shift</Kbd>
|
||||||
|
<span> + </span>
|
||||||
|
<Kbd>K</Kbd>
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
<Table.Tr>
|
<Table.Tr>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Trans>Set focus on next entry without opening it</Trans>
|
<Trans>Set focus on next entry without opening it</Trans>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Image } from "@mantine/core"
|
import { Image } from "@mantine/core"
|
||||||
import logo from "assets/logo.svg"
|
import logo from "@/assets/logo.svg"
|
||||||
|
|
||||||
export interface LogoProps {
|
export interface LogoProps {
|
||||||
size: number
|
size: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Logo(props: LogoProps) {
|
export function Logo(props: Readonly<LogoProps>) {
|
||||||
return <Image src={logo} w={props.size} />
|
return <Image src={logo} w={props.size} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { Trans } from "@lingui/react/macro"
|
||||||
|
import { Button, Code, Group, Modal, Slider, Stack, Text } from "@mantine/core"
|
||||||
|
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)
|
||||||
|
const open = useAppSelector(state => state.entries.markAllAsReadConfirmationDialogOpen)
|
||||||
|
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 = async () => {
|
||||||
|
dispatch(setMarkAllAsReadConfirmationDialogOpen(false))
|
||||||
|
await dispatch(
|
||||||
|
markAllEntries({
|
||||||
|
sourceType: source.type,
|
||||||
|
req: {
|
||||||
|
id: source.id,
|
||||||
|
read: true,
|
||||||
|
olderThan: Date.now() - threshold * 24 * 60 * 60 * 1000,
|
||||||
|
insertedBefore: entriesTimestamp,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const isAllCategorySelected = source.type === "category" && source.id === Constants.categories.all.id
|
||||||
|
if (markAllAsReadNavigateToNextUnread && !isAllCategorySelected) await dispatch(selectNextUnreadTreeItem({ direction: "forward" }))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
opened={open}
|
||||||
|
onClose={() => dispatch(setMarkAllAsReadConfirmationDialogOpen(false))}
|
||||||
|
title={<Trans>Mark all entries as read</Trans>}
|
||||||
|
>
|
||||||
|
<Stack>
|
||||||
|
<Text size="sm">
|
||||||
|
{threshold === 0 && (
|
||||||
|
<Trans>
|
||||||
|
Are you sure you want to mark all entries of <Code>{sourceLabel}</Code> as read?
|
||||||
|
</Trans>
|
||||||
|
)}
|
||||||
|
{threshold > 0 && (
|
||||||
|
<Trans>
|
||||||
|
Are you sure you want to mark entries older than {threshold} days of <Code>{sourceLabel}</Code> as read?
|
||||||
|
</Trans>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
<Slider
|
||||||
|
py="xl"
|
||||||
|
min={0}
|
||||||
|
max={28}
|
||||||
|
marks={[
|
||||||
|
{ value: 0, label: "0" },
|
||||||
|
{ value: 7, label: "7" },
|
||||||
|
{ value: 14, label: "14" },
|
||||||
|
{ value: 21, label: "21" },
|
||||||
|
{ value: 28, label: "28" },
|
||||||
|
]}
|
||||||
|
value={threshold}
|
||||||
|
onChange={setThreshold}
|
||||||
|
data-autofocus
|
||||||
|
onKeyDown={e => e.key === "Enter" && onConfirm()}
|
||||||
|
/>
|
||||||
|
<Group justify="flex-end">
|
||||||
|
<Button variant="default" onClick={() => dispatch(setMarkAllAsReadConfirmationDialogOpen(false))}>
|
||||||
|
<Trans>Cancel</Trans>
|
||||||
|
</Button>
|
||||||
|
<Button color="red" onClick={onConfirm}>
|
||||||
|
<Trans>Confirm</Trans>
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Tooltip } from "@mantine/core"
|
import { Tooltip } from "@mantine/core"
|
||||||
import { Constants } from "app/constants"
|
|
||||||
import dayjs from "dayjs"
|
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)
|
const now = useNow(60 * 1000)
|
||||||
|
|
||||||
if (!props.date) return <Trans>N/A</Trans>
|
if (!props.date) return <Trans>N/A</Trans>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Box, Button, Checkbox, Group, PasswordInput, Stack, TextInput } from "@mantine/core"
|
import { Box, Button, Checkbox, Group, PasswordInput, Stack, TextInput } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
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 { useAsyncCallback } from "react-async-hook"
|
||||||
import { TbDeviceFloppy } from "react-icons/tb"
|
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 {
|
interface UserEditProps {
|
||||||
user?: UserModel
|
user?: UserModel
|
||||||
@@ -13,7 +13,7 @@ interface UserEditProps {
|
|||||||
onSave: () => void
|
onSave: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UserEdit(props: UserEditProps) {
|
export function UserEdit(props: Readonly<UserEditProps>) {
|
||||||
const form = useForm<AdminSaveUserRequest>({
|
const form = useForm<AdminSaveUserRequest>({
|
||||||
initialValues: props.user ?? {
|
initialValues: props.user ?? {
|
||||||
name: "",
|
name: "",
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import { Input, Textarea } from "@mantine/core"
|
import { Input, Textarea } from "@mantine/core"
|
||||||
import RichCodeEditor from "components/code/RichCodeEditor"
|
|
||||||
import { useMobile } from "hooks/useMobile"
|
|
||||||
import type { ReactNode } from "react"
|
import type { ReactNode } from "react"
|
||||||
|
import RichCodeEditor from "@/components/code/RichCodeEditor"
|
||||||
|
import { useMobile } from "@/hooks/useMobile"
|
||||||
|
|
||||||
interface CodeEditorProps {
|
interface CodeEditorProps {
|
||||||
|
label?: ReactNode
|
||||||
description?: ReactNode
|
description?: ReactNode
|
||||||
language: "css" | "javascript"
|
language: "css" | "javascript"
|
||||||
value?: string
|
value?: string
|
||||||
onChange: (value: string | undefined) => void
|
onChange: (value: string | undefined) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CodeEditor(props: CodeEditorProps) {
|
export function CodeEditor(props: Readonly<CodeEditorProps>) {
|
||||||
const mobile = useMobile()
|
const mobile = useMobile()
|
||||||
|
|
||||||
return mobile ? (
|
return mobile ? (
|
||||||
@@ -19,6 +20,7 @@ export function CodeEditor(props: CodeEditorProps) {
|
|||||||
autosize
|
autosize
|
||||||
minRows={4}
|
minRows={4}
|
||||||
maxRows={15}
|
maxRows={15}
|
||||||
|
label={props.label}
|
||||||
description={props.description}
|
description={props.description}
|
||||||
styles={{
|
styles={{
|
||||||
input: {
|
input: {
|
||||||
@@ -29,7 +31,7 @@ export function CodeEditor(props: CodeEditorProps) {
|
|||||||
onChange={e => props.onChange(e.currentTarget.value)}
|
onChange={e => props.onChange(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Input.Wrapper description={props.description}>
|
<Input.Wrapper label={props.label} description={props.description}>
|
||||||
<RichCodeEditor height="30vh" language={props.language} value={props.value} onChange={props.onChange} />
|
<RichCodeEditor height="30vh" language={props.language} value={props.value} onChange={props.onChange} />
|
||||||
</Input.Wrapper>
|
</Input.Wrapper>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Loader } from "components/Loader"
|
|
||||||
import { useColorScheme } from "hooks/useColorScheme"
|
|
||||||
import { useAsync } from "react-async-hook"
|
import { useAsync } from "react-async-hook"
|
||||||
|
import { Loader } from "@/components/Loader"
|
||||||
|
import { useColorScheme } from "@/hooks/useColorScheme"
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
window.MonacoEnvironment = {
|
window.MonacoEnvironment = {
|
||||||
@@ -30,7 +30,7 @@ interface RichCodeEditorProps {
|
|||||||
onChange: (value: string | undefined) => void
|
onChange: (value: string | undefined) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function RichCodeEditor(props: RichCodeEditorProps) {
|
function RichCodeEditor(props: Readonly<RichCodeEditorProps>) {
|
||||||
const colorScheme = useColorScheme()
|
const colorScheme = useColorScheme()
|
||||||
const editorTheme = colorScheme === "dark" ? "vs-dark" : "light"
|
const editorTheme = colorScheme === "dark" ? "vs-dark" : "light"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { TypographyStylesProvider } from "@mantine/core"
|
import { Typography } from "@mantine/core"
|
||||||
import type { ReactNode } from "react"
|
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.
|
* 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 }) => {
|
export const BasicHtmlStyles = (props: { children: ReactNode }) => {
|
||||||
const { classes } = useStyles()
|
const { classes } = useStyles()
|
||||||
return <TypographyStylesProvider className={classes.content}>{props.children}</TypographyStylesProvider>
|
return <Typography className={classes.content}>{props.children}</Typography>
|
||||||
}
|
}
|
||||||
|
|||||||
26
commafeed-client/src/components/content/Content.test.tsx
Normal file
26
commafeed-client/src/components/content/Content.test.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { MantineProvider } from "@mantine/core"
|
||||||
|
import { render } from "@testing-library/react"
|
||||||
|
import { describe, expect, it } from "vitest"
|
||||||
|
import { Content } from "@/components/content/Content"
|
||||||
|
|
||||||
|
describe("Content component", () => {
|
||||||
|
it("renders basic content", () => {
|
||||||
|
const { container } = render(<Content content="<p>Hello World</p>" />, { wrapper: MantineProvider })
|
||||||
|
expect(container.querySelector("p")).toHaveTextContent("Hello World")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("renders highlighted text when highlight prop is provided", () => {
|
||||||
|
const { container } = render(<Content content="Hello World" highlight="World" />, { wrapper: MantineProvider })
|
||||||
|
expect(container.querySelector("mark")).toHaveTextContent("World")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("renders iframe tag when included in content", () => {
|
||||||
|
const { container } = render(<Content content='<iframe src="https://example.com"></iframe>' />, { wrapper: MantineProvider })
|
||||||
|
expect(container.querySelector("iframe")).toHaveAttribute("src", "https://example.com")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("does not render unsupported tags", () => {
|
||||||
|
const { container } = render(<Content content='<script>alert("test")</script>' />, { wrapper: MantineProvider })
|
||||||
|
expect(container.querySelector("script")).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Box, Mark } from "@mantine/core"
|
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 escapeStringRegexp from "escape-string-regexp"
|
||||||
import { 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 React from "react"
|
||||||
import styleToObject from "style-to-object"
|
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 {
|
export interface ContentProps {
|
||||||
content: string
|
content: string
|
||||||
@@ -67,20 +67,19 @@ const transform: TransformCallback = node => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class HighlightMatcher extends Matcher {
|
class HighlightMatcher extends Matcher {
|
||||||
private readonly search: string
|
private readonly regexp: RegExp
|
||||||
|
|
||||||
constructor(search: string) {
|
constructor(search: string) {
|
||||||
super("highlight")
|
super("highlight")
|
||||||
this.search = escapeStringRegexp(search)
|
this.regexp = new RegExp(escapeStringRegexp(search).split(" ").join("|"), "i")
|
||||||
}
|
}
|
||||||
|
|
||||||
match(string: string): MatchResponse<unknown> | null {
|
match(string: string): MatchResponse<unknown> | null {
|
||||||
const pattern = this.search.split(" ").join("|")
|
return this.doMatch(string, this.regexp, () => ({}))
|
||||||
return this.doMatch(string, new RegExp(pattern, "i"), () => ({}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceWith(children: ChildrenNode): Node {
|
replaceWith(children: ChildrenNode): Node {
|
||||||
return <Mark>{children}</Mark>
|
return <Mark key={0}>{children}</Mark>
|
||||||
}
|
}
|
||||||
|
|
||||||
asTag(): string {
|
asTag(): string {
|
||||||
@@ -88,6 +87,9 @@ class HighlightMatcher extends Matcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allow iframe tag
|
||||||
|
const allowList = [...ALLOWED_TAG_LIST, "iframe"]
|
||||||
|
|
||||||
// memoize component because Interweave is costly
|
// memoize component because Interweave is costly
|
||||||
const Content = React.memo((props: ContentProps) => {
|
const Content = React.memo((props: ContentProps) => {
|
||||||
const { classes } = useStyles()
|
const { classes } = useStyles()
|
||||||
@@ -96,7 +98,7 @@ const Content = React.memo((props: ContentProps) => {
|
|||||||
return (
|
return (
|
||||||
<BasicHtmlStyles>
|
<BasicHtmlStyles>
|
||||||
<Box className={classes.content}>
|
<Box className={classes.content}>
|
||||||
<Interweave content={props.content} transform={transform} matchers={matchers} />
|
<Interweave content={props.content} transform={transform} matchers={matchers} allowList={allowList} />
|
||||||
</Box>
|
</Box>
|
||||||
</BasicHtmlStyles>
|
</BasicHtmlStyles>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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: {
|
export function Enclosure(
|
||||||
enclosureType: string
|
props: Readonly<{
|
||||||
enclosureUrl: string
|
enclosureType: string
|
||||||
}) {
|
enclosureUrl: string
|
||||||
|
}>
|
||||||
|
) {
|
||||||
const hasVideo = props.enclosureType.startsWith("video")
|
const hasVideo = props.enclosureType.startsWith("video")
|
||||||
const hasAudio = props.enclosureType.startsWith("audio")
|
const hasAudio = props.enclosureType.startsWith("audio")
|
||||||
const hasImage = props.enclosureType.startsWith("image")
|
const hasImage = props.enclosureType.startsWith("image")
|
||||||
|
|||||||
@@ -1,35 +1,34 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Box } from "@mantine/core"
|
import { Box } from "@mantine/core"
|
||||||
import { openModal } from "@mantine/modals"
|
import { openModal } from "@mantine/modals"
|
||||||
import { Constants } from "app/constants"
|
import { useEffect } from "react"
|
||||||
import type { ExpendableEntry } from "app/entries/slice"
|
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 {
|
import {
|
||||||
loadMoreEntries,
|
loadMoreEntries,
|
||||||
markAllEntries,
|
markAllAsReadWithConfirmationIfRequired,
|
||||||
markEntry,
|
markEntry,
|
||||||
reloadEntries,
|
reloadEntries,
|
||||||
selectEntry,
|
selectEntry,
|
||||||
selectNextEntry,
|
selectNextEntry,
|
||||||
selectPreviousEntry,
|
selectPreviousEntry,
|
||||||
starEntry,
|
starEntry,
|
||||||
} from "app/entries/thunks"
|
} from "@/app/entries/thunks"
|
||||||
import { redirectToRootCategory } from "app/redirect/thunks"
|
import { redirectToRootCategory } from "@/app/redirect/thunks"
|
||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "@/app/store"
|
||||||
import { toggleSidebar } from "app/tree/slice"
|
import { toggleSidebar } from "@/app/tree/slice"
|
||||||
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp"
|
import { selectNextUnreadTreeItem } from "@/app/tree/thunks"
|
||||||
import { Loader } from "components/Loader"
|
import { KeyboardShortcutsHelp } from "@/components/KeyboardShortcutsHelp"
|
||||||
import { useBrowserExtension } from "hooks/useBrowserExtension"
|
import { Loader } from "@/components/Loader"
|
||||||
import { useMousetrap } from "hooks/useMousetrap"
|
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
|
||||||
import { useEffect } from "react"
|
import { useMousetrap } from "@/hooks/useMousetrap"
|
||||||
import { useContextMenu } from "react-contexify"
|
|
||||||
import InfiniteScroll from "react-infinite-scroller"
|
|
||||||
import { throttle } from "throttle-debounce"
|
|
||||||
import { FeedEntry } from "./FeedEntry"
|
import { FeedEntry } from "./FeedEntry"
|
||||||
|
|
||||||
export function FeedEntries() {
|
export function FeedEntries() {
|
||||||
const source = useAppSelector(state => state.entries.source)
|
|
||||||
const entries = useAppSelector(state => state.entries.entries)
|
const entries = useAppSelector(state => state.entries.entries)
|
||||||
const entriesTimestamp = useAppSelector(state => state.entries.timestamp)
|
|
||||||
const selectedEntryId = useAppSelector(state => state.entries.selectedEntryId)
|
const selectedEntryId = useAppSelector(state => state.entries.selectedEntryId)
|
||||||
const hasMore = useAppSelector(state => state.entries.hasMore)
|
const hasMore = useAppSelector(state => state.entries.hasMore)
|
||||||
const loading = useAppSelector(state => state.entries.loading)
|
const loading = useAppSelector(state => state.entries.loading)
|
||||||
@@ -172,6 +171,8 @@ export function FeedEntries() {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
useMousetrap("shift+j", async () => await dispatch(selectNextUnreadTreeItem({ direction: "forward" })))
|
||||||
|
useMousetrap("shift+k", async () => await dispatch(selectNextUnreadTreeItem({ direction: "backward" })))
|
||||||
useMousetrap("space", () => {
|
useMousetrap("space", () => {
|
||||||
if (selectedEntry) {
|
if (selectedEntry) {
|
||||||
if (selectedEntry.expanded) {
|
if (selectedEntry.expanded) {
|
||||||
@@ -272,17 +273,7 @@ export function FeedEntries() {
|
|||||||
})
|
})
|
||||||
useMousetrap("shift+a", () => {
|
useMousetrap("shift+a", () => {
|
||||||
// mark all entries as read
|
// mark all entries as read
|
||||||
dispatch(
|
dispatch(markAllAsReadWithConfirmationIfRequired())
|
||||||
markAllEntries({
|
|
||||||
sourceType: source.type,
|
|
||||||
req: {
|
|
||||||
id: source.id,
|
|
||||||
read: true,
|
|
||||||
olderThan: Date.now(),
|
|
||||||
insertedBefore: entriesTimestamp,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
useMousetrap("g a", async () => await dispatch(redirectToRootCategory()))
|
useMousetrap("g a", async () => await dispatch(redirectToRootCategory()))
|
||||||
useMousetrap("f", () => dispatch(toggleSidebar()))
|
useMousetrap("f", () => dispatch(toggleSidebar()))
|
||||||
@@ -296,33 +287,25 @@ export function FeedEntries() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
id="entries"
|
className={`cf-entries cf-view-mode-${viewMode}`}
|
||||||
className={`view-mode-${viewMode}`}
|
|
||||||
initialLoad={false}
|
initialLoad={false}
|
||||||
loadMore={async () => await (!loading && dispatch(loadMoreEntries()))}
|
loadMore={async () => await (!loading && dispatch(loadMoreEntries()))}
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
loader={<Box key={0}>{loading && <Loader />}</Box>}
|
loader={<Box key={0}>{loading && <Loader />}</Box>}
|
||||||
>
|
>
|
||||||
{entries.map(entry => (
|
{entries.map(entry => (
|
||||||
<article
|
<FeedEntry
|
||||||
key={entry.id}
|
key={entry.id}
|
||||||
ref={el => {
|
entry={entry}
|
||||||
if (el) el.id = Constants.dom.entryId(entry)
|
expanded={!!entry.expanded || viewMode === "expanded"}
|
||||||
}}
|
selected={entry.id === selectedEntryId}
|
||||||
data-id={entry.id}
|
showSelectionIndicator={entry.id === selectedEntryId && (!entry.expanded || viewMode === "expanded")}
|
||||||
>
|
maxWidth={sidebarVisible ? Constants.layout.entryMaxWidth : undefined}
|
||||||
<FeedEntry
|
onHeaderClick={event => headerClicked(entry, event)}
|
||||||
entry={entry}
|
onHeaderRightClick={event => headerRightClicked(entry, event)}
|
||||||
expanded={!!entry.expanded || viewMode === "expanded"}
|
onBodyClick={() => bodyClicked(entry)}
|
||||||
selected={entry.id === selectedEntryId}
|
onSwipedLeft={async () => await swipedLeft(entry)}
|
||||||
showSelectionIndicator={entry.id === selectedEntryId && (!entry.expanded || viewMode === "expanded")}
|
/>
|
||||||
maxWidth={sidebarVisible ? Constants.layout.entryMaxWidth : undefined}
|
|
||||||
onHeaderClick={event => headerClicked(entry, event)}
|
|
||||||
onHeaderRightClick={event => headerRightClicked(entry, event)}
|
|
||||||
onBodyClick={() => bodyClicked(entry)}
|
|
||||||
onSwipedLeft={async () => await swipedLeft(entry)}
|
|
||||||
/>
|
|
||||||
</article>
|
|
||||||
))}
|
))}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Box, Divider, type MantineRadius, type MantineSpacing, Paper } from "@mantine/core"
|
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 type React from "react"
|
||||||
import { useSwipeable } from "react-swipeable"
|
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 { FeedEntryBody } from "./FeedEntryBody"
|
||||||
import { FeedEntryContextMenu } from "./FeedEntryContextMenu"
|
import { FeedEntryContextMenu } from "./FeedEntryContextMenu"
|
||||||
import { FeedEntryFooter } from "./FeedEntryFooter"
|
import { FeedEntryFooter } from "./FeedEntryFooter"
|
||||||
@@ -32,8 +32,9 @@ const useStyles = tss
|
|||||||
rtl: boolean
|
rtl: boolean
|
||||||
showSelectionIndicator: boolean
|
showSelectionIndicator: boolean
|
||||||
maxWidth?: number
|
maxWidth?: number
|
||||||
|
fontSizePercentage: number
|
||||||
}>()
|
}>()
|
||||||
.create(({ theme, colorScheme, read, expanded, viewMode, rtl, showSelectionIndicator, maxWidth }) => {
|
.create(({ theme, colorScheme, read, expanded, viewMode, rtl, showSelectionIndicator, maxWidth, fontSizePercentage }) => {
|
||||||
let backgroundColor: string
|
let backgroundColor: string
|
||||||
if (colorScheme === "dark") {
|
if (colorScheme === "dark") {
|
||||||
backgroundColor = read ? "inherit" : theme.colors.dark[5]
|
backgroundColor = read ? "inherit" : theme.colors.dark[5]
|
||||||
@@ -83,18 +84,21 @@ const useStyles = tss
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
headerLink: {
|
headerLink: {
|
||||||
|
fontSize: `${fontSizePercentage}%`,
|
||||||
color: "inherit",
|
color: "inherit",
|
||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
},
|
},
|
||||||
body: {
|
body: {
|
||||||
|
fontSize: `${fontSizePercentage}%`,
|
||||||
direction: rtl ? "rtl" : "ltr",
|
direction: rtl ? "rtl" : "ltr",
|
||||||
maxWidth: maxWidth ?? "100%",
|
maxWidth: maxWidth ?? "100%",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export function FeedEntry(props: FeedEntryProps) {
|
export function FeedEntry(props: Readonly<FeedEntryProps>) {
|
||||||
const viewMode = useAppSelector(state => state.user.localSettings.viewMode)
|
const viewMode = useAppSelector(state => state.user.localSettings.viewMode)
|
||||||
|
const fontSizePercentage = useAppSelector(state => state.user.localSettings.fontSizePercentage)
|
||||||
const { classes, cx } = useStyles({
|
const { classes, cx } = useStyles({
|
||||||
read: props.entry.read,
|
read: props.entry.read,
|
||||||
expanded: props.expanded,
|
expanded: props.expanded,
|
||||||
@@ -102,6 +106,7 @@ export function FeedEntry(props: FeedEntryProps) {
|
|||||||
rtl: props.entry.rtl,
|
rtl: props.entry.rtl,
|
||||||
showSelectionIndicator: props.showSelectionIndicator,
|
showSelectionIndicator: props.showSelectionIndicator,
|
||||||
maxWidth: props.maxWidth,
|
maxWidth: props.maxWidth,
|
||||||
|
fontSizePercentage,
|
||||||
})
|
})
|
||||||
|
|
||||||
const externalLinkDisplayMode = useAppSelector(state => state.user.settings?.externalLinkIconDisplayMode)
|
const externalLinkDisplayMode = useAppSelector(state => state.user.settings?.externalLinkIconDisplayMode)
|
||||||
@@ -137,6 +142,9 @@ export function FeedEntry(props: FeedEntryProps) {
|
|||||||
const compactHeader = !props.expanded && (viewMode === "title" || viewMode === "cozy")
|
const compactHeader = !props.expanded && (viewMode === "title" || viewMode === "cozy")
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
|
component="article"
|
||||||
|
id={Constants.dom.entryId(props.entry)}
|
||||||
|
data-id={props.entry.id}
|
||||||
withBorder
|
withBorder
|
||||||
radius={borderRadius}
|
radius={borderRadius}
|
||||||
className={cx(classes.paper, {
|
className={cx(classes.paper, {
|
||||||
@@ -176,10 +184,10 @@ export function FeedEntry(props: FeedEntryProps) {
|
|||||||
</a>
|
</a>
|
||||||
{props.expanded && (
|
{props.expanded && (
|
||||||
<Box px={paddingX} pb={paddingY} onClick={props.onBodyClick}>
|
<Box px={paddingX} pb={paddingY} onClick={props.onBodyClick}>
|
||||||
<Box className={classes.body}>
|
<Box className={`${classes.body} cf-content`}>
|
||||||
<FeedEntryBody entry={props.entry} />
|
<FeedEntryBody entry={props.entry} />
|
||||||
</Box>
|
</Box>
|
||||||
<Divider variant="dashed" my={paddingY} />
|
<Divider variant="dashed" my={paddingY} className="cf-footer-divider" />
|
||||||
<FeedEntryFooter entry={props.entry} />
|
<FeedEntryFooter entry={props.entry} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Box } from "@mantine/core"
|
import { Box } from "@mantine/core"
|
||||||
import { useAppSelector } from "app/store"
|
import { useAppSelector } from "@/app/store"
|
||||||
import type { Entry } from "app/types"
|
import type { Entry } from "@/app/types"
|
||||||
import { Content } from "./Content"
|
import { Content } from "./Content"
|
||||||
import { Enclosure } from "./Enclosure"
|
import { Enclosure } from "./Enclosure"
|
||||||
import { Media } from "./Media"
|
import { Media } from "./Media"
|
||||||
@@ -9,7 +9,7 @@ export interface FeedEntryBodyProps {
|
|||||||
entry: Entry
|
entry: Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeedEntryBody(props: FeedEntryBodyProps) {
|
export function FeedEntryBody(props: Readonly<FeedEntryBodyProps>) {
|
||||||
const search = useAppSelector(state => state.entries.search)
|
const search = useAppSelector(state => state.entries.search)
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Group } from "@mantine/core"
|
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 { Item, Menu, Separator } from "react-contexify"
|
||||||
import { TbArrowBarToDown, TbExternalLink, TbMail, TbMailOpened, TbRss, TbStar, TbStarOff } from "react-icons/tb"
|
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 {
|
interface FeedEntryContextMenuProps {
|
||||||
entry: Entry
|
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 colorScheme = useColorScheme()
|
||||||
const { classes } = useStyles()
|
const { classes } = useStyles()
|
||||||
const sourceType = useAppSelector(state => state.entries.source.type)
|
const sourceType = useAppSelector(state => state.entries.source.type)
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { msg } from "@lingui/core/macro"
|
import { msg } from "@lingui/core/macro"
|
||||||
import { useLingui } from "@lingui/react"
|
import { useLingui } from "@lingui/react"
|
||||||
import { Group, Indicator, Popover, TagsInput } from "@mantine/core"
|
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 { 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"
|
import { ShareButtons } from "./ShareButtons"
|
||||||
|
|
||||||
interface FeedEntryFooterProps {
|
interface FeedEntryFooterProps {
|
||||||
entry: Entry
|
entry: Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeedEntryFooter(props: FeedEntryFooterProps) {
|
export function FeedEntryFooter(props: Readonly<FeedEntryFooterProps>) {
|
||||||
const tags = useAppSelector(state => state.user.tags)
|
const tags = useAppSelector(state => state.user.tags)
|
||||||
const mobile = useMobile()
|
const mobile = useMobile()
|
||||||
const { spacing } = useActionButton()
|
const { spacing } = useActionButton()
|
||||||
@@ -37,7 +37,7 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group justify="space-between">
|
<Group justify="space-between" className="cf-footer">
|
||||||
<Group gap={spacing}>
|
<Group gap={spacing}>
|
||||||
{props.entry.markable && (
|
{props.entry.markable && (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
|
import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
|
||||||
|
|
||||||
export interface FeedFaviconProps {
|
export interface FeedFaviconProps {
|
||||||
url: string
|
url: string
|
||||||
size?: number
|
size?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeedFavicon({ url, size = 18 }: FeedFaviconProps) {
|
export function FeedFavicon({ url, size = 18 }: Readonly<FeedFaviconProps>) {
|
||||||
return (
|
return (
|
||||||
<ImageWithPlaceholderWhileLoading
|
<ImageWithPlaceholderWhileLoading
|
||||||
src={url}
|
src={url}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Box } from "@mantine/core"
|
import { Box } from "@mantine/core"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "@/app/constants"
|
||||||
import { calculatePlaceholderSize } from "app/utils"
|
import { calculatePlaceholderSize } from "@/app/utils"
|
||||||
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
|
import { BasicHtmlStyles } from "@/components/content/BasicHtmlStyles"
|
||||||
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
|
import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
|
||||||
import { Content } from "./Content"
|
import { Content } from "./Content"
|
||||||
|
|
||||||
export interface MediaProps {
|
export interface MediaProps {
|
||||||
@@ -12,7 +12,7 @@ export interface MediaProps {
|
|||||||
description?: string
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Media(props: MediaProps) {
|
export function Media(props: Readonly<MediaProps>) {
|
||||||
const width = props.thumbnailWidth
|
const width = props.thumbnailWidth
|
||||||
const height = props.thumbnailHeight
|
const height = props.thumbnailHeight
|
||||||
const placeholderSize = calculatePlaceholderSize({
|
const placeholderSize = calculatePlaceholderSize({
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { ActionIcon, Box, CopyButton, Divider, SimpleGrid } from "@mantine/core"
|
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 type { IconType } from "react-icons"
|
||||||
import { TbCheck, TbCopy, TbDeviceDesktopShare, TbDeviceMobileShare } from "react-icons/tb"
|
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}`
|
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({
|
const { classes } = useStyles({
|
||||||
color,
|
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 = () => {
|
const onClick = () => {
|
||||||
window.open(url, "", "menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=800,height=600")
|
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} />
|
return <ShareButton icon={icon} color={color} onClick={onClick} />
|
||||||
}
|
}
|
||||||
|
|
||||||
function CopyUrlButton({ url }: { url: string }) {
|
function CopyUrlButton({
|
||||||
|
url,
|
||||||
|
}: Readonly<{
|
||||||
|
url: string
|
||||||
|
}>) {
|
||||||
return (
|
return (
|
||||||
<CopyButton value={url}>
|
<CopyButton value={url}>
|
||||||
{({ copied, copy }) => <ShareButton icon={copied ? TbCheck : TbCopy} color="#000" onClick={copy} />}
|
{({ 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 mobile = useMobile()
|
||||||
const { isBrowserExtensionPopup } = useBrowserExtension()
|
const { isBrowserExtensionPopup } = useBrowserExtension()
|
||||||
const onClick = () => {
|
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 sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings)
|
||||||
const enabledSharingSites = (Object.keys(Constants.sharing) as Array<keyof SharingSettings>).filter(site => sharingSettings?.[site])
|
const enabledSharingSites = (Object.keys(Constants.sharing) as Array<keyof SharingSettings>).filter(site => sharingSettings?.[site])
|
||||||
const url = encodeURIComponent(props.url)
|
const url = encodeURIComponent(props.url)
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { useLingui } from "@lingui/react"
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Box, Button, Group, Stack, TextInput } from "@mantine/core"
|
import { Box, Button, Group, Stack, TextInput } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
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 { useAsyncCallback } from "react-async-hook"
|
||||||
import { TbFolderPlus } from "react-icons/tb"
|
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"
|
import { CategorySelect } from "./CategorySelect"
|
||||||
|
|
||||||
export function AddCategory() {
|
export function AddCategory() {
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { msg } from "@lingui/core/macro"
|
|||||||
import { useLingui } from "@lingui/react"
|
import { useLingui } from "@lingui/react"
|
||||||
import { Select, type SelectProps } from "@mantine/core"
|
import { Select, type SelectProps } from "@mantine/core"
|
||||||
import type { ComboboxItem } from "@mantine/core/lib/components/Combobox/Combobox.types"
|
import type { ComboboxItem } from "@mantine/core/lib/components/Combobox/Combobox.types"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "@/app/constants"
|
||||||
import { useAppSelector } from "app/store"
|
import { useAppSelector } from "@/app/store"
|
||||||
import type { Category } from "app/types"
|
import type { Category } from "@/app/types"
|
||||||
import { flattenCategoryTree } from "app/utils"
|
import { flattenCategoryTree } from "@/app/utils"
|
||||||
|
|
||||||
type CategorySelectProps = Partial<SelectProps> & {
|
type CategorySelectProps = Partial<SelectProps> & {
|
||||||
withAll?: boolean
|
withAll?: boolean
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { useLingui } from "@lingui/react"
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Box, Button, FileInput, Group, Stack } from "@mantine/core"
|
import { Box, Button, FileInput, Group, Stack } from "@mantine/core"
|
||||||
import { isNotEmpty, useForm } from "@mantine/form"
|
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 { useAsyncCallback } from "react-async-hook"
|
||||||
import { TbFileImport } from "react-icons/tb"
|
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() {
|
export function ImportOpml() {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Box, Button, Group, Stack, Stepper, TextInput } from "@mantine/core"
|
import { Box, Button, Group, Stack, Stepper, TextInput } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
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 { useState } from "react"
|
||||||
import { useAsyncCallback } from "react-async-hook"
|
import { useAsyncCallback } from "react-async-hook"
|
||||||
import { TbRss } from "react-icons/tb"
|
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"
|
import { CategorySelect } from "./CategorySelect"
|
||||||
|
|
||||||
export function Subscribe() {
|
export function Subscribe() {
|
||||||
@@ -39,9 +39,8 @@ export function Subscribe() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
const subscribe = useAsyncCallback(client.feed.subscribe, {
|
const subscribe = useAsyncCallback(client.feed.subscribe, {
|
||||||
onSuccess: async sub => {
|
onSuccess: sub => {
|
||||||
await dispatch(reloadTree())
|
dispatch(reloadTree()).then(() => dispatch(redirectToFeed(sub.data)))
|
||||||
dispatch(redirectToFeed(sub.data))
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Box, Text } from "@mantine/core"
|
import { Box } from "@mantine/core"
|
||||||
import type { Entry } from "app/types"
|
import type { Entry } from "@/app/types"
|
||||||
import { RelativeDate } from "components/RelativeDate"
|
import { FeedFavicon } from "@/components/content/FeedFavicon"
|
||||||
import { FeedFavicon } from "components/content/FeedFavicon"
|
import { OpenExternalLink } from "@/components/content/header/OpenExternalLink"
|
||||||
import { OpenExternalLink } from "components/content/header/OpenExternalLink"
|
import { Star } from "@/components/content/header/Star"
|
||||||
import { Star } from "components/content/header/Star"
|
import { RelativeDate } from "@/components/RelativeDate"
|
||||||
import { OnDesktop } from "components/responsive/OnDesktop"
|
import { OnDesktop } from "@/components/responsive/OnDesktop"
|
||||||
import { tss } from "tss"
|
import { tss } from "@/tss"
|
||||||
import { FeedEntryTitle } from "./FeedEntryTitle"
|
import { FeedEntryTitle } from "./FeedEntryTitle"
|
||||||
|
|
||||||
export interface FeedEntryHeaderProps {
|
export interface FeedEntryHeaderProps {
|
||||||
@@ -43,7 +43,7 @@ const useStyles = tss
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
export function FeedEntryCompactHeader(props: FeedEntryHeaderProps) {
|
export function FeedEntryCompactHeader(props: Readonly<FeedEntryHeaderProps>) {
|
||||||
const { classes } = useStyles({
|
const { classes } = useStyles({
|
||||||
read: props.entry.read,
|
read: props.entry.read,
|
||||||
})
|
})
|
||||||
@@ -54,17 +54,17 @@ export function FeedEntryCompactHeader(props: FeedEntryHeaderProps) {
|
|||||||
<FeedFavicon url={props.entry.iconUrl} />
|
<FeedFavicon url={props.entry.iconUrl} />
|
||||||
</Box>
|
</Box>
|
||||||
<OnDesktop>
|
<OnDesktop>
|
||||||
<Text c="dimmed" className={classes.feedName}>
|
<Box c="dimmed" className={classes.feedName}>
|
||||||
{props.entry.feedName}
|
{props.entry.feedName}
|
||||||
</Text>
|
</Box>
|
||||||
</OnDesktop>
|
</OnDesktop>
|
||||||
<Box className={classes.title}>
|
<Box className={classes.title}>
|
||||||
<FeedEntryTitle entry={props.entry} />
|
<FeedEntryTitle entry={props.entry} />
|
||||||
</Box>
|
</Box>
|
||||||
<OnDesktop>
|
<OnDesktop>
|
||||||
<Text c="dimmed" className={classes.date}>
|
<Box c="dimmed" className={classes.date}>
|
||||||
<RelativeDate date={props.entry.date} />
|
<RelativeDate date={props.entry.date} />
|
||||||
</Text>
|
</Box>
|
||||||
</OnDesktop>
|
</OnDesktop>
|
||||||
{props.showExternalLinkIcon && <OpenExternalLink entry={props.entry} />}
|
{props.showExternalLinkIcon && <OpenExternalLink entry={props.entry} />}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Box, Flex, Space, Text } from "@mantine/core"
|
import { Box, Flex, Space } from "@mantine/core"
|
||||||
import type { Entry } from "app/types"
|
import type { Entry } from "@/app/types"
|
||||||
import { RelativeDate } from "components/RelativeDate"
|
import { FeedFavicon } from "@/components/content/FeedFavicon"
|
||||||
import { FeedFavicon } from "components/content/FeedFavicon"
|
import { OpenExternalLink } from "@/components/content/header/OpenExternalLink"
|
||||||
import { OpenExternalLink } from "components/content/header/OpenExternalLink"
|
import { Star } from "@/components/content/header/Star"
|
||||||
import { Star } from "components/content/header/Star"
|
import { RelativeDate } from "@/components/RelativeDate"
|
||||||
import { tss } from "tss"
|
import { tss } from "@/tss"
|
||||||
import { FeedEntryTitle } from "./FeedEntryTitle"
|
import { FeedEntryTitle } from "./FeedEntryTitle"
|
||||||
|
|
||||||
export interface FeedEntryHeaderProps {
|
export interface FeedEntryHeaderProps {
|
||||||
@@ -22,18 +22,15 @@ const useStyles = tss
|
|||||||
main: {
|
main: {
|
||||||
fontWeight: colorScheme === "light" && !read ? "bold" : "inherit",
|
fontWeight: colorScheme === "light" && !read ? "bold" : "inherit",
|
||||||
},
|
},
|
||||||
details: {
|
|
||||||
fontSize: "90%",
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
export function FeedEntryHeader(props: FeedEntryHeaderProps) {
|
export function FeedEntryHeader(props: Readonly<FeedEntryHeaderProps>) {
|
||||||
const { classes } = useStyles({
|
const { classes } = useStyles({
|
||||||
read: props.entry.read,
|
read: props.entry.read,
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box className="cf-header">
|
||||||
<Flex align="flex-start" justify="space-between">
|
<Flex align="flex-start" justify="space-between" className="cf-header-title">
|
||||||
<Flex align="flex-start" className={classes.main}>
|
<Flex align="flex-start" className={classes.main}>
|
||||||
{props.showStarIcon && (
|
{props.showStarIcon && (
|
||||||
<Box ml={-5}>
|
<Box ml={-5}>
|
||||||
@@ -44,22 +41,20 @@ export function FeedEntryHeader(props: FeedEntryHeaderProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
{props.showExternalLinkIcon && <OpenExternalLink entry={props.entry} />}
|
{props.showExternalLinkIcon && <OpenExternalLink entry={props.entry} />}
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex align="center" className={classes.details}>
|
<Flex align="center" className="cf-header-subtitle">
|
||||||
<FeedFavicon url={props.entry.iconUrl} />
|
<FeedFavicon url={props.entry.iconUrl} />
|
||||||
<Space w={6} />
|
<Space w={6} />
|
||||||
<Text c="dimmed">
|
<Box c="dimmed">
|
||||||
{props.entry.feedName}
|
{props.entry.feedName}
|
||||||
<span> · </span>
|
<span> · </span>
|
||||||
<RelativeDate date={props.entry.date} />
|
<RelativeDate date={props.entry.date} />
|
||||||
</Text>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
{props.expanded && (
|
{props.expanded && (
|
||||||
<Box className={classes.details}>
|
<Box className="cf-header-details">
|
||||||
<Text c="dimmed">
|
{props.entry.author && <span>by {props.entry.author}</span>}
|
||||||
{props.entry.author && <span>by {props.entry.author}</span>}
|
{props.entry.author && props.entry.categories && <span> · </span>}
|
||||||
{props.entry.author && props.entry.categories && <span> · </span>}
|
{props.entry.categories && <span>{props.entry.categories}</span>}
|
||||||
{props.entry.categories && <span>{props.entry.categories}</span>}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Highlight } from "@mantine/core"
|
import { Highlight } from "@mantine/core"
|
||||||
import { useAppSelector } from "app/store"
|
import { useAppSelector } from "@/app/store"
|
||||||
import type { Entry } from "app/types"
|
import type { Entry } from "@/app/types"
|
||||||
|
|
||||||
export interface FeedEntryTitleProps {
|
export interface FeedEntryTitleProps {
|
||||||
entry: Entry
|
entry: Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeedEntryTitle(props: FeedEntryTitleProps) {
|
export function FeedEntryTitle(props: Readonly<FeedEntryTitleProps>) {
|
||||||
const search = useAppSelector(state => state.entries.search)
|
const search = useAppSelector(state => state.entries.search)
|
||||||
const keywords = search?.split(" ")
|
const keywords = search?.split(" ")
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { ActionIcon, Anchor, Tooltip } from "@mantine/core"
|
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 { 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 dispatch = useAppDispatch()
|
||||||
const onClick = (e: React.MouseEvent) => {
|
const onClick = (e: React.MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { ActionIcon, Tooltip } from "@mantine/core"
|
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 { 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 dispatch = useAppDispatch()
|
||||||
const onClick = (e: React.MouseEvent) => {
|
const onClick = (e: React.MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|||||||
@@ -2,18 +2,11 @@ import { msg } from "@lingui/core/macro"
|
|||||||
import { useLingui } from "@lingui/react"
|
import { useLingui } from "@lingui/react"
|
||||||
import { Box, Center, CloseButton, Divider, Group, Indicator, Popover, TextInput } from "@mantine/core"
|
import { Box, Center, CloseButton, Divider, Group, Indicator, Popover, TextInput } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { 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 { useEffect } from "react"
|
||||||
import {
|
import {
|
||||||
TbArrowDown,
|
TbArrowDown,
|
||||||
TbArrowUp,
|
TbArrowUp,
|
||||||
|
TbChecks,
|
||||||
TbExternalLink,
|
TbExternalLink,
|
||||||
TbEye,
|
TbEye,
|
||||||
TbEyeOff,
|
TbEyeOff,
|
||||||
@@ -24,7 +17,14 @@ import {
|
|||||||
TbSortDescending,
|
TbSortDescending,
|
||||||
TbUser,
|
TbUser,
|
||||||
} from "react-icons/tb"
|
} from "react-icons/tb"
|
||||||
import { MarkAllAsReadButton } from "./MarkAllAsReadButton"
|
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"
|
import { ProfileMenu } from "./ProfileMenu"
|
||||||
|
|
||||||
function HeaderDivider() {
|
function HeaderDivider() {
|
||||||
@@ -42,11 +42,14 @@ function HeaderToolbar(props: { children: React.ReactNode }) {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
|
className="cf-toolbar"
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Group gap={spacing}>{props.children}</Group>
|
<Group gap={spacing} className="cf-toolbar">
|
||||||
|
{props.children}
|
||||||
|
</Group>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +78,7 @@ export function Header() {
|
|||||||
|
|
||||||
if (!settings) return <Loader />
|
if (!settings) return <Loader />
|
||||||
return (
|
return (
|
||||||
<Center>
|
<Center className="cf-toolbar-wrapper">
|
||||||
<HeaderToolbar>
|
<HeaderToolbar>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={<TbArrowUp size={iconSize} />}
|
icon={<TbArrowUp size={iconSize} />}
|
||||||
@@ -111,7 +114,11 @@ export function Header() {
|
|||||||
label={msg`Refresh`}
|
label={msg`Refresh`}
|
||||||
onClick={async () => await dispatch(reloadEntries())}
|
onClick={async () => await dispatch(reloadEntries())}
|
||||||
/>
|
/>
|
||||||
<MarkAllAsReadButton iconSize={iconSize} />
|
<ActionButton
|
||||||
|
icon={<TbChecks size={iconSize} />}
|
||||||
|
label={msg`Mark all as read`}
|
||||||
|
onClick={() => dispatch(markAllAsReadWithConfirmationIfRequired())}
|
||||||
|
/>
|
||||||
|
|
||||||
<HeaderDivider />
|
<HeaderDivider />
|
||||||
|
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
import { msg } from "@lingui/core/macro"
|
|
||||||
import { Trans } from "@lingui/react/macro"
|
|
||||||
|
|
||||||
import { Button, Code, Group, Modal, Slider, Stack, Text } from "@mantine/core"
|
|
||||||
import { markAllEntries } from "app/entries/thunks"
|
|
||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
|
||||||
import { ActionButton } from "components/ActionButton"
|
|
||||||
import { useState } from "react"
|
|
||||||
import { TbChecks } from "react-icons/tb"
|
|
||||||
|
|
||||||
export function MarkAllAsReadButton(props: { iconSize: number }) {
|
|
||||||
const [opened, setOpened] = useState(false)
|
|
||||||
const [threshold, setThreshold] = useState(0)
|
|
||||||
const source = useAppSelector(state => state.entries.source)
|
|
||||||
const sourceLabel = useAppSelector(state => state.entries.sourceLabel)
|
|
||||||
const entriesTimestamp = useAppSelector(state => state.entries.timestamp) ?? Date.now()
|
|
||||||
const markAllAsReadConfirmation = useAppSelector(state => state.user.settings?.markAllAsReadConfirmation)
|
|
||||||
const dispatch = useAppDispatch()
|
|
||||||
|
|
||||||
const buttonClicked = () => {
|
|
||||||
if (markAllAsReadConfirmation) {
|
|
||||||
setThreshold(0)
|
|
||||||
setOpened(true)
|
|
||||||
} else {
|
|
||||||
dispatch(
|
|
||||||
markAllEntries({
|
|
||||||
sourceType: source.type,
|
|
||||||
req: {
|
|
||||||
id: source.id,
|
|
||||||
read: true,
|
|
||||||
olderThan: Date.now(),
|
|
||||||
insertedBefore: entriesTimestamp,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Modal opened={opened} onClose={() => setOpened(false)} title={<Trans>Mark all entries as read</Trans>}>
|
|
||||||
<Stack>
|
|
||||||
<Text size="sm">
|
|
||||||
{threshold === 0 && (
|
|
||||||
<Trans>
|
|
||||||
Are you sure you want to mark all entries of <Code>{sourceLabel}</Code> as read?
|
|
||||||
</Trans>
|
|
||||||
)}
|
|
||||||
{threshold > 0 && (
|
|
||||||
<Trans>
|
|
||||||
Are you sure you want to mark entries older than {threshold} days of <Code>{sourceLabel}</Code> as read?
|
|
||||||
</Trans>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
<Slider
|
|
||||||
py="xl"
|
|
||||||
min={0}
|
|
||||||
max={28}
|
|
||||||
marks={[
|
|
||||||
{ value: 0, label: "0" },
|
|
||||||
{ value: 7, label: "7" },
|
|
||||||
{ value: 14, label: "14" },
|
|
||||||
{ value: 21, label: "21" },
|
|
||||||
{ value: 28, label: "28" },
|
|
||||||
]}
|
|
||||||
value={threshold}
|
|
||||||
onChange={setThreshold}
|
|
||||||
/>
|
|
||||||
<Group justify="flex-end">
|
|
||||||
<Button variant="default" onClick={() => setOpened(false)}>
|
|
||||||
<Trans>Cancel</Trans>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
color="red"
|
|
||||||
onClick={() => {
|
|
||||||
setOpened(false)
|
|
||||||
dispatch(
|
|
||||||
markAllEntries({
|
|
||||||
sourceType: source.type,
|
|
||||||
req: {
|
|
||||||
id: source.id,
|
|
||||||
read: true,
|
|
||||||
olderThan: Date.now() - threshold * 24 * 60 * 60 * 1000,
|
|
||||||
insertedBefore: entriesTimestamp,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Trans>Confirm</Trans>
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Modal>
|
|
||||||
<ActionButton icon={<TbChecks size={props.iconSize} />} label={msg`Mark all as read`} onClick={buttonClicked} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -7,17 +7,11 @@ import {
|
|||||||
Menu,
|
Menu,
|
||||||
SegmentedControl,
|
SegmentedControl,
|
||||||
type SegmentedControlItem,
|
type SegmentedControlItem,
|
||||||
|
Slider,
|
||||||
useMantineColorScheme,
|
useMantineColorScheme,
|
||||||
} from "@mantine/core"
|
} from "@mantine/core"
|
||||||
import { showNotification } from "@mantine/notifications"
|
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 { setViewMode } from "app/user/slice"
|
|
||||||
import { reloadProfile } from "app/user/thunks"
|
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import { useNow } from "hooks/useNow"
|
|
||||||
import { type ReactNode, useState } from "react"
|
import { type ReactNode, useState } from "react"
|
||||||
import {
|
import {
|
||||||
TbChartLine,
|
TbChartLine,
|
||||||
@@ -35,6 +29,13 @@ import {
|
|||||||
TbUsers,
|
TbUsers,
|
||||||
TbWorldDownload,
|
TbWorldDownload,
|
||||||
} from "react-icons/tb"
|
} 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 {
|
interface ProfileMenuProps {
|
||||||
control: React.ReactElement
|
control: React.ReactElement
|
||||||
@@ -93,13 +94,14 @@ const viewModeData: ViewModeControlItem[] = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export function ProfileMenu(props: ProfileMenuProps) {
|
export function ProfileMenu(props: Readonly<ProfileMenuProps>) {
|
||||||
const [opened, setOpened] = useState(false)
|
const [opened, setOpened] = useState(false)
|
||||||
const now = useNow()
|
const now = useNow()
|
||||||
const profile = useAppSelector(state => state.user.profile)
|
const profile = useAppSelector(state => state.user.profile)
|
||||||
const admin = useAppSelector(state => state.user.profile?.admin)
|
const admin = useAppSelector(state => state.user.profile?.admin)
|
||||||
const viewMode = useAppSelector(state => state.user.localSettings.viewMode)
|
const viewMode = useAppSelector(state => state.user.localSettings.viewMode)
|
||||||
const forceRefreshCooldownDuration = useAppSelector(state => state.server.serverInfos?.forceRefreshCooldownDuration)
|
const forceRefreshCooldownDuration = useAppSelector(state => state.server.serverInfos?.forceRefreshCooldownDuration)
|
||||||
|
const fontSizePercentage = useAppSelector(state => state.user.localSettings.fontSizePercentage)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { colorScheme, setColorScheme } = useMantineColorScheme()
|
const { colorScheme, setColorScheme } = useMantineColorScheme()
|
||||||
|
|
||||||
@@ -143,7 +145,7 @@ export function ProfileMenu(props: ProfileMenuProps) {
|
|||||||
color: "green",
|
color: "green",
|
||||||
autoClose: 1000,
|
autoClose: 1000,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch {
|
||||||
showNotification({
|
showNotification({
|
||||||
message: <Trans>Force fetching feeds is not yet available.</Trans>,
|
message: <Trans>Force fetching feeds is not yet available.</Trans>,
|
||||||
color: "red",
|
color: "red",
|
||||||
@@ -184,6 +186,22 @@ export function ProfileMenu(props: ProfileMenuProps) {
|
|||||||
mb="xs"
|
mb="xs"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Menu.Label>
|
||||||
|
<Trans>Font size</Trans>
|
||||||
|
</Menu.Label>
|
||||||
|
<Slider
|
||||||
|
min={50}
|
||||||
|
max={150}
|
||||||
|
step={5}
|
||||||
|
marks={[{ value: 100 }]}
|
||||||
|
label={v => `${v}%`}
|
||||||
|
mb="xs"
|
||||||
|
value={fontSizePercentage}
|
||||||
|
onChange={value => dispatch(setFontSizePercentage(value))}
|
||||||
|
/>
|
||||||
|
|
||||||
{admin && (
|
{admin && (
|
||||||
<>
|
<>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { NumberFormatter } from "@mantine/core"
|
import { NumberFormatter } from "@mantine/core"
|
||||||
import type { MetricGauge } from "app/types"
|
import type { MetricGauge } from "@/app/types"
|
||||||
|
|
||||||
interface MeterProps {
|
interface GaugeProps {
|
||||||
gauge: MetricGauge
|
gauge: MetricGauge
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Gauge(props: MeterProps) {
|
export function Gauge(props: Readonly<GaugeProps>) {
|
||||||
return <NumberFormatter value={props.gauge.value} thousandSeparator />
|
return <NumberFormatter value={props.gauge.value} thousandSeparator />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Box } from "@mantine/core"
|
import { Box } from "@mantine/core"
|
||||||
import type { MetricMeter } from "app/types"
|
import type { MetricMeter } from "@/app/types"
|
||||||
|
|
||||||
interface MeterProps {
|
interface MeterProps {
|
||||||
meter: MetricMeter
|
meter: MetricMeter
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Meter(props: MeterProps) {
|
export function Meter(props: Readonly<MeterProps>) {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box>Mean: {props.meter.mean_rate.toFixed(2)}</Box>
|
<Box>Mean: {props.meter.mean_rate.toFixed(2)}</Box>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ interface MetricAccordionItemProps {
|
|||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MetricAccordionItem({ metricKey, name, headerValue, children }: MetricAccordionItemProps) {
|
export function MetricAccordionItem({ metricKey, name, headerValue, children }: Readonly<MetricAccordionItemProps>) {
|
||||||
return (
|
return (
|
||||||
<Accordion.Item value={metricKey} key={metricKey}>
|
<Accordion.Item value={metricKey} key={metricKey}>
|
||||||
<Accordion.Control>
|
<Accordion.Control>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Box } from "@mantine/core"
|
import { Box } from "@mantine/core"
|
||||||
import type { MetricTimer } from "app/types"
|
import type { MetricTimer } from "@/app/types"
|
||||||
|
|
||||||
interface MetricTimerProps {
|
interface MetricTimerProps {
|
||||||
timer: MetricTimer
|
timer: MetricTimer
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Timer(props: MetricTimerProps) {
|
export function Timer(props: Readonly<MetricTimerProps>) {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box>Mean: {props.timer.mean_rate.toFixed(2)}</Box>
|
<Box>Mean: {props.timer.mean_rate.toFixed(2)}</Box>
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import { Box } from "@mantine/core"
|
import { Box } from "@mantine/core"
|
||||||
import { useMobile } from "hooks/useMobile"
|
|
||||||
import type React from "react"
|
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()
|
const mobile = useMobile()
|
||||||
return <Box>{!mobile && props.children}</Box>
|
return <Box>{!mobile && props.children}</Box>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import { Box } from "@mantine/core"
|
import { Box } from "@mantine/core"
|
||||||
import { useMobile } from "hooks/useMobile"
|
|
||||||
import type React from "react"
|
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()
|
const mobile = useMobile()
|
||||||
return <Box>{mobile && props.children}</Box>
|
return <Box>{mobile && props.children}</Box>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Box, Button, Group, Stack } from "@mantine/core"
|
import { Anchor, Box, Button, Group, Stack } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { client, errorToStrings } from "app/client"
|
|
||||||
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 { useEffect } from "react"
|
||||||
import { useAsyncCallback } from "react-async-hook"
|
import { useAsyncCallback } from "react-async-hook"
|
||||||
import { TbDeviceFloppy } from "react-icons/tb"
|
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 {
|
interface FormData {
|
||||||
customCss: string
|
customCss: string
|
||||||
@@ -57,13 +58,23 @@ export function CustomCodeSettings() {
|
|||||||
<form onSubmit={form.onSubmit(saveCustomCode.execute)}>
|
<form onSubmit={form.onSubmit(saveCustomCode.execute)}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
description={<Trans>Custom CSS rules that will be applied</Trans>}
|
label={<Trans>Custom CSS rules that will be applied</Trans>}
|
||||||
|
description={
|
||||||
|
<Anchor
|
||||||
|
href={Constants.customCssDocumentationUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
style={{ fontSize: "inherit" }}
|
||||||
|
>
|
||||||
|
<Trans>Link to the documentation</Trans>
|
||||||
|
</Anchor>
|
||||||
|
}
|
||||||
language="css"
|
language="css"
|
||||||
{...form.getInputProps("customCss")}
|
{...form.getInputProps("customCss")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
description={<Trans>Custom JS code that will be executed on page load</Trans>}
|
label={<Trans>Custom JS code that will be executed on page load</Trans>}
|
||||||
language="javascript"
|
language="javascript"
|
||||||
{...form.getInputProps("customJs")}
|
{...form.getInputProps("customJs")}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
import { msg } from "@lingui/core/macro"
|
import { msg } from "@lingui/core/macro"
|
||||||
import { useLingui } from "@lingui/react"
|
import { useLingui } from "@lingui/react"
|
||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Divider, Group, NumberInput, Radio, Select, SimpleGrid, Stack, Switch } from "@mantine/core"
|
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 type { ComboboxData } from "@mantine/core/lib/components/Combobox/Combobox.types"
|
||||||
import { Constants } from "app/constants"
|
import type { ReactNode } from "react"
|
||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { Constants } from "@/app/constants"
|
||||||
import type { IconDisplayMode, ScrollMode, SharingSettings } from "app/types"
|
import { useAppDispatch, useAppSelector } from "@/app/store"
|
||||||
|
import type { IconDisplayMode, ScrollMode, SharingSettings } from "@/app/types"
|
||||||
import {
|
import {
|
||||||
changeCustomContextMenu,
|
changeCustomContextMenu,
|
||||||
changeEntriesToKeepOnTopWhenScrolling,
|
changeEntriesToKeepOnTopWhenScrolling,
|
||||||
changeExternalLinkIconDisplayMode,
|
changeExternalLinkIconDisplayMode,
|
||||||
changeLanguage,
|
changeLanguage,
|
||||||
changeMarkAllAsReadConfirmation,
|
changeMarkAllAsReadConfirmation,
|
||||||
|
changeMarkAllAsReadNavigateToUnread,
|
||||||
changeMobileFooter,
|
changeMobileFooter,
|
||||||
|
changePrimaryColor,
|
||||||
changeScrollMarks,
|
changeScrollMarks,
|
||||||
changeScrollMode,
|
changeScrollMode,
|
||||||
changeScrollSpeed,
|
changeScrollSpeed,
|
||||||
@@ -21,9 +24,8 @@ import {
|
|||||||
changeStarIconDisplayMode,
|
changeStarIconDisplayMode,
|
||||||
changeUnreadCountFavicon,
|
changeUnreadCountFavicon,
|
||||||
changeUnreadCountTitle,
|
changeUnreadCountTitle,
|
||||||
} from "app/user/thunks"
|
} from "@/app/user/thunks"
|
||||||
import { locales } from "i18n"
|
import { locales } from "@/i18n"
|
||||||
import type { ReactNode } from "react"
|
|
||||||
|
|
||||||
export function DisplaySettings() {
|
export function DisplaySettings() {
|
||||||
const language = useAppSelector(state => state.user.settings?.language)
|
const language = useAppSelector(state => state.user.settings?.language)
|
||||||
@@ -35,13 +37,15 @@ export function DisplaySettings() {
|
|||||||
const starIconDisplayMode = useAppSelector(state => state.user.settings?.starIconDisplayMode)
|
const starIconDisplayMode = useAppSelector(state => state.user.settings?.starIconDisplayMode)
|
||||||
const externalLinkIconDisplayMode = useAppSelector(state => state.user.settings?.externalLinkIconDisplayMode)
|
const externalLinkIconDisplayMode = useAppSelector(state => state.user.settings?.externalLinkIconDisplayMode)
|
||||||
const markAllAsReadConfirmation = useAppSelector(state => state.user.settings?.markAllAsReadConfirmation)
|
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 customContextMenu = useAppSelector(state => state.user.settings?.customContextMenu)
|
||||||
const mobileFooter = useAppSelector(state => state.user.settings?.mobileFooter)
|
const mobileFooter = useAppSelector(state => state.user.settings?.mobileFooter)
|
||||||
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
|
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
|
||||||
const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
|
const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
|
||||||
const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings)
|
const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings)
|
||||||
const dispatch = useAppDispatch()
|
const primaryColor = useAppSelector(state => state.user.settings?.primaryColor) || Constants.theme.defaultPrimaryColor
|
||||||
const { _ } = useLingui()
|
const { _ } = useLingui()
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const scrollModeOptions: Record<ScrollMode, ReactNode> = {
|
const scrollModeOptions: Record<ScrollMode, ReactNode> = {
|
||||||
always: <Trans>Always</Trans>,
|
always: <Trans>Always</Trans>,
|
||||||
@@ -68,10 +72,35 @@ export function DisplaySettings() {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const colorData: ComboboxData = [
|
||||||
|
{ value: "dark", label: _(msg`Dark`) },
|
||||||
|
{ value: "gray", label: _(msg`Gray`) },
|
||||||
|
{ value: "red", label: _(msg`Red`) },
|
||||||
|
{ value: "pink", label: _(msg`Pink`) },
|
||||||
|
{ value: "grape", label: _(msg`Grape`) },
|
||||||
|
{ value: "violet", label: _(msg`Violet`) },
|
||||||
|
{ value: "indigo", label: _(msg`Indigo`) },
|
||||||
|
{ value: "blue", label: _(msg`Blue`) },
|
||||||
|
{ value: "cyan", label: _(msg`Cyan`) },
|
||||||
|
{ value: "green", label: _(msg`Green`) },
|
||||||
|
{ value: "lime", label: _(msg`Lime`) },
|
||||||
|
{ value: "yellow", label: _(msg`Yellow`) },
|
||||||
|
{ value: "orange", label: _(msg`Orange`) },
|
||||||
|
{ value: "teal", label: _(msg`Teal`) },
|
||||||
|
].sort((a, b) => a.label.localeCompare(b.label))
|
||||||
|
const colorRenderer: SelectProps["renderOption"] = ({ option }) => (
|
||||||
|
<Group>
|
||||||
|
<Box h={18} w={18} bg={option.value} />
|
||||||
|
<Box>{option.label}</Box>
|
||||||
|
</Group>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
|
<Divider label={<Trans>Display</Trans>} labelPosition="center" />
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
description={<Trans>Language</Trans>}
|
label={<Trans>Language</Trans>}
|
||||||
value={language}
|
value={language}
|
||||||
data={locales.map(l => ({
|
data={locales.map(l => ({
|
||||||
value: l.key,
|
value: l.key,
|
||||||
@@ -80,6 +109,14 @@ export function DisplaySettings() {
|
|||||||
onChange={async s => await (s && dispatch(changeLanguage(s)))}
|
onChange={async s => await (s && dispatch(changeLanguage(s)))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
label={<Trans>Primary color</Trans>}
|
||||||
|
data={colorData}
|
||||||
|
value={primaryColor}
|
||||||
|
onChange={async value => value && (await dispatch(changePrimaryColor(value)))}
|
||||||
|
renderOption={colorRenderer}
|
||||||
|
/>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
label={<Trans>Show feeds and categories with no unread entries</Trans>}
|
label={<Trans>Show feeds and categories with no unread entries</Trans>}
|
||||||
checked={showRead}
|
checked={showRead}
|
||||||
@@ -92,6 +129,12 @@ export function DisplaySettings() {
|
|||||||
onChange={async e => await dispatch(changeMarkAllAsReadConfirmation(e.currentTarget.checked))}
|
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
|
<Switch
|
||||||
label={<Trans>On mobile, show action buttons at the bottom of the screen</Trans>}
|
label={<Trans>On mobile, show action buttons at the bottom of the screen</Trans>}
|
||||||
checked={mobileFooter}
|
checked={mobileFooter}
|
||||||
@@ -115,14 +158,14 @@ export function DisplaySettings() {
|
|||||||
<Divider label={<Trans>Entry headers</Trans>} labelPosition="center" />
|
<Divider label={<Trans>Entry headers</Trans>} labelPosition="center" />
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
description={<Trans>Show star icon</Trans>}
|
label={<Trans>Show star icon</Trans>}
|
||||||
value={starIconDisplayMode}
|
value={starIconDisplayMode}
|
||||||
data={displayModeData}
|
data={displayModeData}
|
||||||
onChange={async s => await dispatch(changeStarIconDisplayMode(s as IconDisplayMode))}
|
onChange={async s => await dispatch(changeStarIconDisplayMode(s as IconDisplayMode))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
description={<Trans>Show external link icon</Trans>}
|
label={<Trans>Show external link icon</Trans>}
|
||||||
value={externalLinkIconDisplayMode}
|
value={externalLinkIconDisplayMode}
|
||||||
data={displayModeData}
|
data={displayModeData}
|
||||||
onChange={async s => await dispatch(changeExternalLinkIconDisplayMode(s as IconDisplayMode))}
|
onChange={async s => await dispatch(changeExternalLinkIconDisplayMode(s as IconDisplayMode))}
|
||||||
|
|||||||
@@ -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 { Anchor, Box, Button, Checkbox, Divider, Group, Input, PasswordInput, Stack, Text, TextInput } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { openConfirmModal } from "@mantine/modals"
|
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 { useEffect } from "react"
|
||||||
import { useAsyncCallback } from "react-async-hook"
|
import { useAsyncCallback } from "react-async-hook"
|
||||||
import { TbDeviceFloppy, TbTrash } from "react-icons/tb"
|
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 {
|
interface FormData extends ProfileModificationRequest {
|
||||||
newPasswordConfirmation?: string
|
newPasswordConfirmation?: string
|
||||||
@@ -52,7 +52,9 @@ export function ProfileSettings() {
|
|||||||
),
|
),
|
||||||
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
|
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
|
||||||
confirmProps: { color: "red" },
|
confirmProps: { color: "red" },
|
||||||
onConfirm: async () => await deleteProfile.execute(),
|
onConfirm: () => {
|
||||||
|
deleteProfile.execute()
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { Box, Stack } from "@mantine/core"
|
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 {
|
import {
|
||||||
redirectToCategory,
|
redirectToCategory,
|
||||||
redirectToCategoryDetails,
|
redirectToCategoryDetails,
|
||||||
@@ -8,15 +10,14 @@ import {
|
|||||||
redirectToFeedDetails,
|
redirectToFeedDetails,
|
||||||
redirectToTag,
|
redirectToTag,
|
||||||
redirectToTagDetails,
|
redirectToTagDetails,
|
||||||
} from "app/redirect/thunks"
|
} from "@/app/redirect/thunks"
|
||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "@/app/store"
|
||||||
import { collapseTreeCategory } from "app/tree/thunks"
|
import type { TreeSubscription } from "@/app/tree/slice"
|
||||||
import type { Category, Subscription } from "app/types"
|
import { collapseTreeCategory } from "@/app/tree/thunks"
|
||||||
import { categoryUnreadCount, flattenCategoryTree } from "app/utils"
|
import type { Category, Subscription } from "@/app/types"
|
||||||
import { Loader } from "components/Loader"
|
import { categoryHasNewEntries, categoryUnreadCount, flattenCategoryTree } from "@/app/utils"
|
||||||
import { OnDesktop } from "components/responsive/OnDesktop"
|
import { Loader } from "@/components/Loader"
|
||||||
import React from "react"
|
import { OnDesktop } from "@/components/responsive/OnDesktop"
|
||||||
import { TbChevronDown, TbChevronRight, TbInbox, TbStar, TbTag } from "react-icons/tb"
|
|
||||||
import { TreeNode } from "./TreeNode"
|
import { TreeNode } from "./TreeNode"
|
||||||
import { TreeSearch } from "./TreeSearch"
|
import { TreeSearch } from "./TreeSearch"
|
||||||
|
|
||||||
@@ -89,6 +90,7 @@ export function Tree() {
|
|||||||
name={<Trans>All</Trans>}
|
name={<Trans>All</Trans>}
|
||||||
icon={allIcon}
|
icon={allIcon}
|
||||||
unread={categoryUnreadCount(root)}
|
unread={categoryUnreadCount(root)}
|
||||||
|
hasNewEntries={categoryHasNewEntries(root)}
|
||||||
selected={source.type === "category" && source.id === Constants.categories.all.id}
|
selected={source.type === "category" && source.id === Constants.categories.all.id}
|
||||||
expanded={false}
|
expanded={false}
|
||||||
level={0}
|
level={0}
|
||||||
@@ -103,6 +105,7 @@ export function Tree() {
|
|||||||
name={<Trans>Starred</Trans>}
|
name={<Trans>Starred</Trans>}
|
||||||
icon={starredIcon}
|
icon={starredIcon}
|
||||||
unread={0}
|
unread={0}
|
||||||
|
hasNewEntries={false}
|
||||||
selected={source.type === "category" && source.id === Constants.categories.starred.id}
|
selected={source.type === "category" && source.id === Constants.categories.starred.id}
|
||||||
expanded={false}
|
expanded={false}
|
||||||
level={0}
|
level={0}
|
||||||
@@ -122,6 +125,7 @@ export function Tree() {
|
|||||||
name={category.name}
|
name={category.name}
|
||||||
icon={category.expanded ? expandedIcon : collapsedIcon}
|
icon={category.expanded ? expandedIcon : collapsedIcon}
|
||||||
unread={categoryUnreadCount(category)}
|
unread={categoryUnreadCount(category)}
|
||||||
|
hasNewEntries={categoryHasNewEntries(category)}
|
||||||
selected={source.type === "category" && source.id === category.id}
|
selected={source.type === "category" && source.id === category.id}
|
||||||
expanded={category.expanded}
|
expanded={category.expanded}
|
||||||
level={level}
|
level={level}
|
||||||
@@ -133,7 +137,7 @@ export function Tree() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const feedNode = (feed: Subscription, level = 0) => {
|
const feedNode = (feed: TreeSubscription, level = 0) => {
|
||||||
if (!isFeedDisplayed(feed)) return null
|
if (!isFeedDisplayed(feed)) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -143,6 +147,7 @@ export function Tree() {
|
|||||||
name={feed.name}
|
name={feed.name}
|
||||||
icon={feed.iconUrl}
|
icon={feed.iconUrl}
|
||||||
unread={feed.unread}
|
unread={feed.unread}
|
||||||
|
hasNewEntries={!!feed.hasNewEntries}
|
||||||
selected={source.type === "feed" && source.id === String(feed.id)}
|
selected={source.type === "feed" && source.id === String(feed.id)}
|
||||||
level={level}
|
level={level}
|
||||||
hasError={feed.errorCount > errorThreshold}
|
hasError={feed.errorCount > errorThreshold}
|
||||||
@@ -159,6 +164,7 @@ export function Tree() {
|
|||||||
name={tag}
|
name={tag}
|
||||||
icon={tagIcon}
|
icon={tagIcon}
|
||||||
unread={0}
|
unread={0}
|
||||||
|
hasNewEntries={false}
|
||||||
selected={source.type === "tag" && source.id === tag}
|
selected={source.type === "tag" && source.id === tag}
|
||||||
level={0}
|
level={0}
|
||||||
hasError={false}
|
hasError={false}
|
||||||
@@ -182,7 +188,7 @@ export function Tree() {
|
|||||||
<OnDesktop>
|
<OnDesktop>
|
||||||
<TreeSearch feeds={feeds} />
|
<TreeSearch feeds={feeds} />
|
||||||
</OnDesktop>
|
</OnDesktop>
|
||||||
<Box>
|
<Box className="cf-tree">
|
||||||
{allCategoryNode()}
|
{allCategoryNode()}
|
||||||
{starredCategoryNode()}
|
{starredCategoryNode()}
|
||||||
{root.children.map(c => recursiveCategoryNode(c))}
|
{root.children.map(c => recursiveCategoryNode(c))}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Box, Center } from "@mantine/core"
|
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 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"
|
import { UnreadCount } from "./UnreadCount"
|
||||||
|
|
||||||
interface TreeNodeProps {
|
interface TreeNodeProps {
|
||||||
@@ -15,6 +15,7 @@ interface TreeNodeProps {
|
|||||||
expanded?: boolean
|
expanded?: boolean
|
||||||
level: number
|
level: number
|
||||||
hasError: boolean
|
hasError: boolean
|
||||||
|
hasNewEntries: boolean
|
||||||
onClick: (e: React.MouseEvent, id: string) => void
|
onClick: (e: React.MouseEvent, id: string) => void
|
||||||
onIconClick?: (e: React.MouseEvent, id: string) => void
|
onIconClick?: (e: React.MouseEvent, id: string) => void
|
||||||
}
|
}
|
||||||
@@ -58,7 +59,7 @@ const useStyles = tss
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export function TreeNode(props: TreeNodeProps) {
|
export function TreeNode(props: Readonly<TreeNodeProps>) {
|
||||||
const { classes } = useStyles({
|
const { classes } = useStyles({
|
||||||
selected: props.selected,
|
selected: props.selected,
|
||||||
hasError: props.hasError,
|
hasError: props.hasError,
|
||||||
@@ -68,19 +69,19 @@ export function TreeNode(props: TreeNodeProps) {
|
|||||||
<Box
|
<Box
|
||||||
py={1}
|
py={1}
|
||||||
pl={props.level * 20}
|
pl={props.level * 20}
|
||||||
className={classes.node}
|
className={`${classes.node} cf-treenode cf-treenode-${props.type}`}
|
||||||
onClick={(e: React.MouseEvent) => props.onClick(e, props.id)}
|
onClick={(e: React.MouseEvent) => props.onClick(e, props.id)}
|
||||||
data-id={props.id}
|
data-id={props.id}
|
||||||
data-type={props.type}
|
data-type={props.type}
|
||||||
data-unread-count={props.unread}
|
data-unread-count={props.unread}
|
||||||
>
|
>
|
||||||
<Box mr={6} onClick={(e: React.MouseEvent) => props.onIconClick?.(e, props.id)}>
|
<Box mr={6} onClick={(e: React.MouseEvent) => props.onIconClick?.(e, props.id)} className="cf-treenode-icon">
|
||||||
<Center>{typeof props.icon === "string" ? <FeedFavicon url={props.icon} /> : props.icon}</Center>
|
<Center>{typeof props.icon === "string" ? <FeedFavicon url={props.icon} /> : props.icon}</Center>
|
||||||
</Box>
|
</Box>
|
||||||
<Box className={classes.nodeText}>{props.name}</Box>
|
<Box className={classes.nodeText}>{props.name}</Box>
|
||||||
{!props.expanded && (
|
{!props.expanded && (
|
||||||
<Box>
|
<Box className="cf-treenode-unread-count">
|
||||||
<UnreadCount unreadCount={props.unread} />
|
<UnreadCount unreadCount={props.unread} showIndicator={props.hasNewEntries} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { msg } from "@lingui/core/macro"
|
import { msg } from "@lingui/core/macro"
|
||||||
import { useLingui } from "@lingui/react"
|
import { useLingui } from "@lingui/react"
|
||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { TextInput } from "@mantine/core"
|
import { Box, TextInput } from "@mantine/core"
|
||||||
import { Spotlight, type SpotlightActionData, spotlight } from "@mantine/spotlight"
|
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 { 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 {
|
export interface TreeSearchProps {
|
||||||
feeds: Subscription[]
|
feeds: Subscription[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TreeSearch(props: TreeSearchProps) {
|
export function TreeSearch(props: Readonly<TreeSearchProps>) {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { _ } = useLingui()
|
const { _ } = useLingui()
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ export function TreeSearch(props: TreeSearchProps) {
|
|||||||
useMousetrap("g u", () => spotlight.open())
|
useMousetrap("g u", () => spotlight.open())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box className="cf-treesearch">
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder={_(msg`Search`)}
|
placeholder={_(msg`Search`)}
|
||||||
leftSection={searchIcon}
|
leftSection={searchIcon}
|
||||||
@@ -58,6 +58,6 @@ export function TreeSearch(props: TreeSearchProps) {
|
|||||||
}}
|
}}
|
||||||
nothingFound={<Trans>Nothing found</Trans>}
|
nothingFound={<Trans>Nothing found</Trans>}
|
||||||
/>
|
/>
|
||||||
</>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Badge, Tooltip } from "@mantine/core"
|
import { Badge, Indicator, Tooltip } from "@mantine/core"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "@/app/constants"
|
||||||
import { tss } from "tss"
|
import { tss } from "@/tss"
|
||||||
|
|
||||||
const useStyles = tss.create(() => ({
|
const useStyles = tss.create(() => ({
|
||||||
badge: {
|
badge: {
|
||||||
@@ -10,7 +10,12 @@ const useStyles = tss.create(() => ({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
export function UnreadCount(props: { unreadCount: number }) {
|
export function UnreadCount(
|
||||||
|
props: Readonly<{
|
||||||
|
unreadCount: number
|
||||||
|
showIndicator: boolean
|
||||||
|
}>
|
||||||
|
) {
|
||||||
const { classes } = useStyles()
|
const { classes } = useStyles()
|
||||||
|
|
||||||
if (props.unreadCount <= 0) return null
|
if (props.unreadCount <= 0) return null
|
||||||
@@ -18,9 +23,11 @@ export function UnreadCount(props: { unreadCount: number }) {
|
|||||||
const count = props.unreadCount >= 10000 ? "10k+" : props.unreadCount
|
const count = props.unreadCount >= 10000 ? "10k+" : props.unreadCount
|
||||||
return (
|
return (
|
||||||
<Tooltip label={props.unreadCount} disabled={props.unreadCount === count} openDelay={Constants.tooltip.delay}>
|
<Tooltip label={props.unreadCount} disabled={props.unreadCount === count} openDelay={Constants.tooltip.delay}>
|
||||||
<Badge className={classes.badge} variant="light" fullWidth>
|
<Indicator disabled={!props.showIndicator} size={4} offset={10} position="middle-start">
|
||||||
{count}
|
<Badge className={`${classes.badge} cf-badge`} variant="light" fullWidth>
|
||||||
</Badge>
|
{count}
|
||||||
|
</Badge>
|
||||||
|
</Indicator>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMantineTheme } from "@mantine/core"
|
import { useMantineTheme } from "@mantine/core"
|
||||||
import { useMobile } from "hooks/useMobile"
|
import { useMobile } from "@/hooks/useMobile"
|
||||||
|
|
||||||
export const useActionButton = () => {
|
export const useActionButton = () => {
|
||||||
const theme = useMantineTheme()
|
const theme = useMantineTheme()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { msg } from "@lingui/core/macro"
|
import { msg } from "@lingui/core/macro"
|
||||||
import { useLingui } from "@lingui/react"
|
import { useLingui } from "@lingui/react"
|
||||||
import { useAppSelector } from "app/store"
|
import { useAppSelector } from "@/app/store"
|
||||||
|
|
||||||
interface Step {
|
interface Step {
|
||||||
label: string
|
label: string
|
||||||
@@ -8,28 +8,28 @@ interface Step {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useAppLoading = () => {
|
export const useAppLoading = () => {
|
||||||
const profile = useAppSelector(state => state.user.profile)
|
const profileLoaded = useAppSelector(state => !!state.user.profile)
|
||||||
const settings = useAppSelector(state => state.user.settings)
|
const settingsLoaded = useAppSelector(state => !!state.user.settings)
|
||||||
const rootCategory = useAppSelector(state => state.tree.rootCategory)
|
const rootCategoryLoaded = useAppSelector(state => !!state.tree.rootCategory)
|
||||||
const tags = useAppSelector(state => state.user.tags)
|
const tagsLoaded = useAppSelector(state => !!state.user.tags)
|
||||||
const { _ } = useLingui()
|
const { _ } = useLingui()
|
||||||
|
|
||||||
const steps: Step[] = [
|
const steps: Step[] = [
|
||||||
{
|
{
|
||||||
label: _(msg`Loading settings...`),
|
label: _(msg`Loading settings...`),
|
||||||
done: !!settings,
|
done: settingsLoaded,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _(msg`Loading profile...`),
|
label: _(msg`Loading profile...`),
|
||||||
done: !!profile,
|
done: profileLoaded,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _(msg`Loading subscriptions...`),
|
label: _(msg`Loading subscriptions...`),
|
||||||
done: !!rootCategory,
|
done: rootCategoryLoaded,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _(msg`Loading tags...`),
|
label: _(msg`Loading tags...`),
|
||||||
done: !!tags,
|
done: tagsLoaded,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMediaQuery } from "@mantine/hooks"
|
import { useMediaQuery } from "@mantine/hooks"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "@/app/constants"
|
||||||
|
|
||||||
export const useMobile = (breakpoint: string | number = Constants.layout.mobileBreakpoint) => {
|
export const useMobile = (breakpoint: string | number = Constants.layout.mobileBreakpoint) => {
|
||||||
const bp = typeof breakpoint === "number" ? `${breakpoint}px` : breakpoint
|
const bp = typeof breakpoint === "number" ? `${breakpoint}px` : breakpoint
|
||||||
|
|||||||
@@ -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 { useEffect } from "react"
|
||||||
import WebsocketHeartbeatJs from "websocket-heartbeat-js"
|
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 handleMessage = (dispatch: AppDispatch, message: string) => {
|
||||||
const parts = message.split(":")
|
const parts = message.split(":")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { type Messages, i18n } from "@lingui/core"
|
import { i18n, type Messages } from "@lingui/core"
|
||||||
import { useAppSelector } from "app/store"
|
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
|
import { useAppSelector } from "@/app/store"
|
||||||
|
|
||||||
interface Locale {
|
interface Locale {
|
||||||
key: string
|
key: string
|
||||||
@@ -12,34 +12,146 @@ interface Locale {
|
|||||||
// add an object to the array to add a new 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
|
// don't forget to also add it to the 'locales' array in lingui.config.ts
|
||||||
export const locales: Locale[] = [
|
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: "ar",
|
||||||
{ key: "cs", label: "Čeština", dayjsImportFn: async () => await import("dayjs/locale/cs") },
|
label: "العربية",
|
||||||
{ key: "cy", label: "Cymraeg", dayjsImportFn: async () => await import("dayjs/locale/cy") },
|
dayjsImportFn: async () => await import("dayjs/locale/ar"),
|
||||||
{ 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: "ca",
|
||||||
{ key: "es", label: "Español", dayjsImportFn: async () => await import("dayjs/locale/es") },
|
label: "Català",
|
||||||
{ key: "fa", label: "فارسی", dayjsImportFn: async () => await import("dayjs/locale/fa") },
|
dayjsImportFn: async () => await import("dayjs/locale/ca"),
|
||||||
{ 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: "cs",
|
||||||
{ key: "hu", label: "Magyar", dayjsImportFn: async () => await import("dayjs/locale/hu") },
|
label: "Čeština",
|
||||||
{ key: "id", label: "Indonesian", dayjsImportFn: async () => await import("dayjs/locale/id") },
|
dayjsImportFn: async () => await import("dayjs/locale/cs"),
|
||||||
{ 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: "cy",
|
||||||
{ key: "ms", label: "Bahasa Malaysian", dayjsImportFn: async () => await import("dayjs/locale/ms") },
|
label: "Cymraeg",
|
||||||
{ key: "nb", label: "Norsk (bokmål)", dayjsImportFn: async () => await import("dayjs/locale/nb") },
|
dayjsImportFn: async () => await import("dayjs/locale/cy"),
|
||||||
{ 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: "da",
|
||||||
{ key: "pt", label: "Português", dayjsImportFn: async () => await import("dayjs/locale/pt") },
|
label: "Danish",
|
||||||
{ key: "ru", label: "Русский", dayjsImportFn: async () => await import("dayjs/locale/ru") },
|
dayjsImportFn: async () => await import("dayjs/locale/da"),
|
||||||
{ 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: "de",
|
||||||
{ key: "zh", label: "简体中文", dayjsImportFn: async () => await import("dayjs/locale/zh") },
|
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) {
|
function activateLocale(locale: string) {
|
||||||
@@ -57,8 +169,12 @@ function activateLocale(locale: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useI18n = () => {
|
export const useI18n = () => {
|
||||||
const locale = useAppSelector(state => state.user.settings?.language)
|
const locale =
|
||||||
|
useAppSelector(state => state.user.settings?.language) ??
|
||||||
|
navigator.languages.map(l => l.split("-")[0]).find(l => locales.some(locale => locale.key === l)) ??
|
||||||
|
"en"
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
activateLocale(locale ?? "en")
|
activateLocale(locale)
|
||||||
}, [locale])
|
}, [locale])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
@@ -33,8 +33,8 @@ msgstr ""
|
|||||||
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
||||||
msgstr "<0> هل تحتاج إلى حساب؟ </0> <1> اشترك! </ 1>"
|
msgstr "<0> هل تحتاج إلى حساب؟ </0> <1> اشترك! </ 1>"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr "حول"
|
msgstr "حول"
|
||||||
|
|
||||||
@@ -54,16 +54,17 @@ msgstr "إضافة فئة"
|
|||||||
msgid "Add user"
|
msgid "Add user"
|
||||||
msgstr "إضافة مستخدم"
|
msgstr "إضافة مستخدم"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "إداري"
|
msgstr "إداري"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "الكل"
|
msgstr "الكل"
|
||||||
|
|
||||||
@@ -104,11 +105,11 @@ msgstr "هل أنت متأكد أنك تريد حذف المستخدم <0> {user
|
|||||||
msgid "Are you sure you want to delete your account? There's no turning back!"
|
msgid "Are you sure you want to delete your account? There's no turning back!"
|
||||||
msgstr "هل أنت متأكد أنك تريد حذف حسابك؟ "
|
msgstr "هل أنت متأكد أنك تريد حذف حسابك؟ "
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "هل أنت متأكد أنك تريد تعليم كافة إدخالات <0> {sourceLabel} </0> كمقروءة؟"
|
msgstr "هل أنت متأكد أنك تريد تعليم كافة إدخالات <0> {sourceLabel} </0> كمقروءة؟"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "هل أنت متأكد أنك تريد وضع علامة على الإدخالات الأقدم من {عتبة} يوم من <0> {sourceLabel} </0> كمقروءة؟"
|
msgstr "هل أنت متأكد أنك تريد وضع علامة على الإدخالات الأقدم من {عتبة} يوم من <0> {sourceLabel} </0> كمقروءة؟"
|
||||||
|
|
||||||
@@ -132,6 +133,10 @@ msgstr "العودة"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "العودة لتسجيل الدخول"
|
msgstr "العودة لتسجيل الدخول"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Blue"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Browser extension required for Chrome"
|
msgid "Browser extension required for Chrome"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -144,27 +149,27 @@ msgstr ""
|
|||||||
msgid "Browser tab"
|
msgid "Browser tab"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "إلغاء"
|
msgstr "إلغاء"
|
||||||
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "الفئة"
|
msgstr "الفئة"
|
||||||
|
|
||||||
@@ -204,11 +209,11 @@ msgstr ""
|
|||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
msgstr "مضغوط"
|
msgstr "مضغوط"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "تأكيد"
|
msgstr "تأكيد"
|
||||||
|
|
||||||
@@ -240,6 +245,11 @@ msgstr ""
|
|||||||
msgid "Custom JS code that will be executed on page load"
|
msgid "Custom JS code that will be executed on page load"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Cyan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -273,13 +283,14 @@ msgstr "تنازلي"
|
|||||||
msgid "Detailed"
|
msgid "Detailed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "عرض"
|
msgstr "عرض"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Donate"
|
msgid "Donate"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -291,11 +302,11 @@ msgstr "تنزيل"
|
|||||||
msgid "Drag link to bookmark bar"
|
msgid "Drag link to bookmark bar"
|
||||||
msgstr "اسحب الرابط إلى شريط الإشارات"
|
msgstr "اسحب الرابط إلى شريط الإشارات"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "E-mail"
|
msgid "E-mail"
|
||||||
msgstr "البريد الإلكتروني"
|
msgstr "البريد الإلكتروني"
|
||||||
|
|
||||||
@@ -308,8 +319,8 @@ msgstr "عنوان البريد الإلكتروني"
|
|||||||
msgid "Edit user"
|
msgid "Edit user"
|
||||||
msgstr "تحرير المستخدم"
|
msgstr "تحرير المستخدم"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "ممكن"
|
msgstr "ممكن"
|
||||||
|
|
||||||
@@ -345,8 +356,8 @@ msgstr "موسع"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "قم بتصدير اشتراكاتك وفئاتك كملف OPML يمكن استيراده في خدمات قراءة الأعلاف الأخرى"
|
msgstr "قم بتصدير اشتراكاتك وفئاتك كملف OPML يمكن استيراده في خدمات قراءة الأعلاف الأخرى"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Extension options"
|
msgid "Extension options"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -354,9 +365,9 @@ msgstr ""
|
|||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "اسم الخلاصة"
|
msgstr "اسم الخلاصة"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed URL"
|
msgid "Feed URL"
|
||||||
msgstr "موجز URL"
|
msgstr "موجز URL"
|
||||||
|
|
||||||
@@ -376,6 +387,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "تصفية التعبير"
|
msgstr "تصفية التعبير"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Font size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Force fetching feeds is not yet available."
|
msgid "Force fetching feeds is not yet available."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -384,9 +399,9 @@ msgstr ""
|
|||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "هل نسيت كلمة المرور؟"
|
msgstr "هل نسيت كلمة المرور؟"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generate an API key in your profile first."
|
msgid "Generate an API key in your profile first."
|
||||||
msgstr "قم بإنشاء مفتاح API في ملف التعريف الخاص بك أولاً."
|
msgstr "قم بإنشاء مفتاح API في ملف التعريف الخاص بك أولاً."
|
||||||
|
|
||||||
@@ -394,12 +409,13 @@ msgstr "قم بإنشاء مفتاح API في ملف التعريف الخاص
|
|||||||
msgid "Generate new API key"
|
msgid "Generate new API key"
|
||||||
msgstr "إنشاء مفتاح API جديد"
|
msgstr "إنشاء مفتاح API جديد"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generated feed url"
|
msgid "Generated feed url"
|
||||||
msgstr "رابط الخلاصة المولدة"
|
msgstr "رابط الخلاصة المولدة"
|
||||||
|
|
||||||
|
#. placeholder {0}: truncate(props.entry.feedName, 30)
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -416,6 +432,18 @@ msgstr "انتقل إلى وثائق API."
|
|||||||
msgid "Goodies"
|
msgid "Goodies"
|
||||||
msgstr "الأشياء الجيدة"
|
msgstr "الأشياء الجيدة"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Grape"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Gray"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Green"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr "المرجع نفسه"
|
msgstr "المرجع نفسه"
|
||||||
@@ -440,13 +468,17 @@ msgstr "استيراد"
|
|||||||
msgid "In expanded view, scrolling through entries mark them as read"
|
msgid "In expanded view, scrolling through entries mark them as read"
|
||||||
msgstr "في العرض الموسع ، التمرير عبر الإدخالات وضع علامة عليها كمقروءة"
|
msgstr "في العرض الموسع ، التمرير عبر الإدخالات وضع علامة عليها كمقروءة"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Indigo"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Keep unread"
|
msgid "Keep unread"
|
||||||
msgstr "إبقاء غير مقروءة"
|
msgstr "إبقاء غير مقروءة"
|
||||||
|
|
||||||
#: src/components/content/FeedEntries.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/FeedEntries.tsx
|
||||||
msgid "Keyboard shortcuts"
|
msgid "Keyboard shortcuts"
|
||||||
msgstr "اختصارات لوحة المفاتيح"
|
msgstr "اختصارات لوحة المفاتيح"
|
||||||
|
|
||||||
@@ -470,12 +502,20 @@ msgstr "آخر رسالة تحديث"
|
|||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
msgid "Lime"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr "رابط"
|
msgstr "رابط"
|
||||||
|
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
msgid "Link to the documentation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading profile..."
|
msgid "Loading profile..."
|
||||||
msgstr "تحميل ملف التعريف ..."
|
msgstr "تحميل ملف التعريف ..."
|
||||||
@@ -492,9 +532,9 @@ msgstr "تحميل الاشتراكات ..."
|
|||||||
msgid "Loading tags..."
|
msgid "Loading tags..."
|
||||||
msgstr "تحميل العلامات ..."
|
msgstr "تحميل العلامات ..."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "تسجيل الدخول"
|
msgstr "تسجيل الدخول"
|
||||||
|
|
||||||
@@ -506,27 +546,27 @@ msgstr "تسجيل الخروج"
|
|||||||
msgid "Long press"
|
msgid "Long press"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Manage users"
|
msgid "Manage users"
|
||||||
msgstr "إدارة المستخدمين"
|
msgstr "إدارة المستخدمين"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Mark all as read"
|
msgid "Mark all as read"
|
||||||
msgstr "تعليم الكل كمقروء"
|
msgstr "تعليم الكل كمقروء"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "تعليم كافة الإدخالات كمقروءة"
|
msgstr "تعليم كافة الإدخالات كمقروءة"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read"
|
msgid "Mark as read"
|
||||||
msgstr "وضع علامة كمقروء"
|
msgstr "وضع علامة كمقروء"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read up to here"
|
msgid "Mark as read up to here"
|
||||||
msgstr "وضع علامة كمقروءة حتى هنا"
|
msgstr "وضع علامة كمقروءة حتى هنا"
|
||||||
|
|
||||||
@@ -546,15 +586,15 @@ msgstr "تحريك الصفحة لأسفل"
|
|||||||
msgid "Move the page up"
|
msgid "Move the page up"
|
||||||
msgstr "تحريك الصفحة لأعلى"
|
msgstr "تحريك الصفحة لأعلى"
|
||||||
|
|
||||||
#: src/components/RelativeDate.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/RelativeDate.tsx
|
||||||
msgid "N/A"
|
msgid "N/A"
|
||||||
msgstr "لا"
|
msgstr "لا"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "الاسم"
|
msgstr "الاسم"
|
||||||
|
|
||||||
@@ -562,6 +602,10 @@ msgstr "الاسم"
|
|||||||
msgid "Navigate to a subscription by entering its name"
|
msgid "Navigate to a subscription by entering its name"
|
||||||
msgstr "انتقل إلى اشتراك بإدخال اسمه"
|
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
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Never"
|
msgid "Never"
|
||||||
@@ -575,8 +619,8 @@ msgstr "كلمة مرور جديدة"
|
|||||||
msgid "Newest first"
|
msgid "Newest first"
|
||||||
msgstr "الأحدث أولاً"
|
msgstr "الأحدث أولاً"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "التالي"
|
msgstr "التالي"
|
||||||
|
|
||||||
@@ -682,6 +726,10 @@ msgstr "ملف OPML"
|
|||||||
msgid "OPML file is required"
|
msgid "OPML file is required"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Orange"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Order"
|
msgid "Order"
|
||||||
msgstr "طلب"
|
msgstr "طلب"
|
||||||
@@ -694,11 +742,11 @@ msgstr "والد"
|
|||||||
msgid "Parent Category"
|
msgid "Parent Category"
|
||||||
msgstr "الفئة الأصل"
|
msgstr "الفئة الأصل"
|
||||||
|
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "كلمة المرور"
|
msgstr "كلمة المرور"
|
||||||
|
|
||||||
@@ -710,8 +758,12 @@ msgstr "استعادة كلمة المرور"
|
|||||||
msgid "Passwords do not match"
|
msgid "Passwords do not match"
|
||||||
msgstr "كلمات المرور غير متطابقة"
|
msgstr "كلمات المرور غير متطابقة"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Pink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Position"
|
msgid "Position"
|
||||||
msgstr "المنـصب"
|
msgstr "المنـصب"
|
||||||
|
|
||||||
@@ -719,6 +771,10 @@ msgstr "المنـصب"
|
|||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Primary color"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "الملف الشخصي"
|
msgstr "الملف الشخصي"
|
||||||
@@ -727,8 +783,12 @@ msgstr "الملف الشخصي"
|
|||||||
msgid "Recover password"
|
msgid "Recover password"
|
||||||
msgstr "استعادة كلمة السر"
|
msgstr "استعادة كلمة السر"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Red"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr "تحديث"
|
msgstr "تحديث"
|
||||||
|
|
||||||
@@ -745,11 +805,11 @@ msgstr ""
|
|||||||
msgid "Right click"
|
msgid "Right click"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "حفظ"
|
msgstr "حفظ"
|
||||||
|
|
||||||
@@ -765,10 +825,10 @@ msgstr "قم بالتمرير بسلاسة عند التنقل بين الإدخ
|
|||||||
msgid "Scrolling"
|
msgid "Scrolling"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "بحث"
|
msgstr "بحث"
|
||||||
|
|
||||||
@@ -776,6 +836,14 @@ msgstr "بحث"
|
|||||||
msgid "Search requires at least 3 characters"
|
msgid "Search requires at least 3 characters"
|
||||||
msgstr "يتطلب البحث 3 أحرف على الأقل"
|
msgstr "يتطلب البحث 3 أحرف على الأقل"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select next unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select previous unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on next entry without opening it"
|
msgid "Set focus on next entry without opening it"
|
||||||
msgstr "ضع التركيز على الإدخال التالي دون فتحه"
|
msgstr "ضع التركيز على الإدخال التالي دون فتحه"
|
||||||
@@ -850,9 +918,9 @@ msgstr ""
|
|||||||
msgid "Show unread count in tab title"
|
msgid "Show unread count in tab title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "قم بالتسجيل"
|
msgstr "قم بالتسجيل"
|
||||||
|
|
||||||
@@ -865,20 +933,21 @@ msgstr "شيء سيء حدث للتو ..."
|
|||||||
msgid "Space"
|
msgid "Space"
|
||||||
msgstr "فضاء"
|
msgstr "فضاء"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Star"
|
msgid "Star"
|
||||||
msgstr "النجم"
|
msgstr "النجم"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
msgid "Starred"
|
msgid "Starred"
|
||||||
msgstr "مميز بنجمة"
|
msgstr "مميز بنجمة"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AddPage.tsx
|
#: src/pages/app/AddPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Subscribe"
|
msgid "Subscribe"
|
||||||
msgstr "اشتراك"
|
msgstr "اشتراك"
|
||||||
|
|
||||||
@@ -915,6 +984,10 @@ msgstr ""
|
|||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "الكلمات"
|
msgstr "الكلمات"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Teal"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: 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."
|
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."
|
||||||
msgstr "عنوان URL للتغذية التي تريد الاشتراك فيها. "
|
msgstr "عنوان URL للتغذية التي تريد الاشتراك فيها. "
|
||||||
@@ -951,8 +1024,8 @@ msgstr ""
|
|||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
msgstr "غير مقروءة"
|
msgstr "غير مقروءة"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Unstar"
|
msgid "Unstar"
|
||||||
msgstr "إلغاء النجم"
|
msgstr "إلغاء النجم"
|
||||||
@@ -971,6 +1044,10 @@ msgstr "اسم المستخدم"
|
|||||||
msgid "User Name or E-mail"
|
msgid "User Name or E-mail"
|
||||||
msgstr "اسم المستخدم أو البريد الإلكتروني"
|
msgstr "اسم المستخدم أو البريد الإلكتروني"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Violet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Alert.tsx
|
#: src/components/Alert.tsx
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr "تحذير"
|
msgstr "تحذير"
|
||||||
@@ -979,6 +1056,10 @@ msgstr "تحذير"
|
|||||||
msgid "Website"
|
msgid "Website"
|
||||||
msgstr "موقع الكتروني"
|
msgstr "موقع الكتروني"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Yellow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedEntriesPage.tsx
|
#: 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?"
|
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
||||||
msgstr "ليس لديك أي اشتراكات حتى الآن. "
|
msgstr "ليس لديك أي اشتراكات حتى الآن. "
|
||||||
|
|||||||
@@ -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>."
|
msgstr "<0>CommaFeed és un projecte de codi obert. El codi font està allotjat a </0><1>GitHub</1>."
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: 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 "<0>La sintaxi completa està disponible </0><1>aquí</1>."
|
msgstr "<0>La sintaxi completa està disponible </0><1>aquí</1><2>.</2>"
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "<0>Have an account?</0><1>Log in!</1>"
|
msgid "<0>Have an account?</0><1>Log in!</1>"
|
||||||
@@ -33,8 +33,8 @@ 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>"
|
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
||||||
msgstr "<0>Necessites un compte?</0><1>Registreu-vos!</1>"
|
msgstr "<0>Necessites un compte?</0><1>Registreu-vos!</1>"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr "Sobre"
|
msgstr "Sobre"
|
||||||
|
|
||||||
@@ -54,16 +54,17 @@ msgstr "Afegeix categoria"
|
|||||||
msgid "Add user"
|
msgid "Add user"
|
||||||
msgstr "Afegeix usuari"
|
msgstr "Afegeix usuari"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Administrador"
|
msgstr "Administrador"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Tot"
|
msgstr "Tot"
|
||||||
|
|
||||||
@@ -104,11 +105,11 @@ msgstr "Esteu segur que voleu suprimir l'usuari <0>{userName}</0>?"
|
|||||||
msgid "Are you sure you want to delete your account? There's no turning back!"
|
msgid "Are you sure you want to delete your account? There's no turning back!"
|
||||||
msgstr "Esteu segur que voleu suprimir el vostre compte? "
|
msgstr "Esteu segur que voleu suprimir el vostre compte? "
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Esteu segur que voleu marcar totes les entrades de <0>{sourceLabel}</0> com a llegides?"
|
msgstr "Esteu segur que voleu marcar totes les entrades de <0>{sourceLabel}</0> com a llegides?"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Esteu segur que voleu marcar les entrades més antigues de {threshold} dies de <0>{sourceLabel}</0> com a llegides?"
|
msgstr "Esteu segur que voleu marcar les entrades més antigues de {threshold} dies de <0>{sourceLabel}</0> com a llegides?"
|
||||||
|
|
||||||
@@ -132,6 +133,10 @@ msgstr "Enrere"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Tornar a iniciar sessió"
|
msgstr "Tornar a iniciar sessió"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Blue"
|
||||||
|
msgstr "Blau"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Browser extension required for Chrome"
|
msgid "Browser extension required for Chrome"
|
||||||
msgstr "Extensió del navegador necessària per a Chrome"
|
msgstr "Extensió del navegador necessària per a Chrome"
|
||||||
@@ -142,29 +147,29 @@ msgstr "Extensió del navegador"
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Browser tab"
|
msgid "Browser tab"
|
||||||
msgstr ""
|
msgstr "Pestanya del navegador"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Cancel·la"
|
msgstr "Cancel·la"
|
||||||
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Categoria"
|
msgstr "Categoria"
|
||||||
|
|
||||||
@@ -182,7 +187,7 @@ msgstr "Tanca el menu"
|
|||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Cmd"
|
msgid "Cmd"
|
||||||
msgstr ""
|
msgstr "Cmd"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
@@ -204,11 +209,11 @@ msgstr "CommaFeed versió {version} ({version})."
|
|||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
msgstr "Compacte"
|
msgstr "Compacte"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Confirma"
|
msgstr "Confirma"
|
||||||
|
|
||||||
@@ -240,6 +245,11 @@ msgstr "Regles CSS personalitzades que s'aplicaran"
|
|||||||
msgid "Custom JS code that will be executed on page load"
|
msgid "Custom JS code that will be executed on page load"
|
||||||
msgstr "Codi JS personalitzat que s'executarà en carregar la pàgina"
|
msgstr "Codi JS personalitzat que s'executarà en carregar la pàgina"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Cyan"
|
||||||
|
msgstr "Cian"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr "Fosc"
|
msgstr "Fosc"
|
||||||
@@ -273,13 +283,14 @@ msgstr "Desc"
|
|||||||
msgid "Detailed"
|
msgid "Detailed"
|
||||||
msgstr "Detallat"
|
msgstr "Detallat"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Mostra"
|
msgstr "Mostra"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Donate"
|
msgid "Donate"
|
||||||
msgstr "Donar"
|
msgstr "Donar"
|
||||||
|
|
||||||
@@ -291,11 +302,11 @@ msgstr "Descarrega"
|
|||||||
msgid "Drag link to bookmark bar"
|
msgid "Drag link to bookmark bar"
|
||||||
msgstr "Arrossegueu l'enllaç a la barra d'adreces d'interès"
|
msgstr "Arrossegueu l'enllaç a la barra d'adreces d'interès"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "E-mail"
|
msgid "E-mail"
|
||||||
msgstr "Correu electrònic"
|
msgstr "Correu electrònic"
|
||||||
|
|
||||||
@@ -308,8 +319,8 @@ msgstr "Adreça de correu electrònic"
|
|||||||
msgid "Edit user"
|
msgid "Edit user"
|
||||||
msgstr "Edita l'usuari"
|
msgstr "Edita l'usuari"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "activat"
|
msgstr "activat"
|
||||||
|
|
||||||
@@ -323,11 +334,11 @@ msgstr "introduïu la vostra contrasenya actual per canviar la configuració del
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Entries to keep above the selected entry when scrolling"
|
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
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Entry headers"
|
msgid "Entry headers"
|
||||||
msgstr ""
|
msgstr "Encapçalaments d'entrada"
|
||||||
|
|
||||||
#: src/components/Alert.tsx
|
#: src/components/Alert.tsx
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
@@ -345,8 +356,8 @@ msgstr "Ampliat"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "exporteu les vostres subscripcions i categories com a fitxer OPML que es pot importar a altres serveis de lectura de feeds"
|
msgstr "exporteu les vostres subscripcions i categories com a fitxer OPML que es pot importar a altres serveis de lectura de feeds"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Extension options"
|
msgid "Extension options"
|
||||||
msgstr "Opcions de l'extensió"
|
msgstr "Opcions de l'extensió"
|
||||||
|
|
||||||
@@ -354,9 +365,9 @@ msgstr "Opcions de l'extensió"
|
|||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Nom del canal"
|
msgstr "Nom del canal"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed URL"
|
msgid "Feed URL"
|
||||||
msgstr "URL del canal"
|
msgstr "URL del canal"
|
||||||
|
|
||||||
@@ -366,27 +377,31 @@ msgstr "Carrega tots els meus feeds ara"
|
|||||||
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Fever API"
|
msgid "Fever API"
|
||||||
msgstr ""
|
msgstr "Fever API"
|
||||||
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Fever API URL"
|
msgid "Fever API URL"
|
||||||
msgstr ""
|
msgstr "URL de Fever API"
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Expressió de filtratge"
|
msgstr "Expressió de filtratge"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Font size"
|
||||||
|
msgstr "Mida de la lletra"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Force fetching feeds is not yet available."
|
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
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Heu oblidat la contrasenya?"
|
msgstr "Heu oblidat la contrasenya?"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generate an API key in your profile first."
|
msgid "Generate an API key in your profile first."
|
||||||
msgstr "primer genereu una clau API al vostre perfil."
|
msgstr "primer genereu una clau API al vostre perfil."
|
||||||
|
|
||||||
@@ -394,12 +409,13 @@ msgstr "primer genereu una clau API al vostre perfil."
|
|||||||
msgid "Generate new API key"
|
msgid "Generate new API key"
|
||||||
msgstr "Genera una nova clau d'API"
|
msgstr "Genera una nova clau d'API"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generated feed url"
|
msgid "Generated feed url"
|
||||||
msgstr "URL del feed generat"
|
msgstr "URL del feed generat"
|
||||||
|
|
||||||
|
#. placeholder {0}: truncate(props.entry.feedName, 30)
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr "Vés a {0}"
|
msgstr "Vés a {0}"
|
||||||
@@ -416,6 +432,18 @@ msgstr "Vés a la documentació de l'API."
|
|||||||
msgid "Goodies"
|
msgid "Goodies"
|
||||||
msgstr "Bones"
|
msgstr "Bones"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Grape"
|
||||||
|
msgstr "Raïm"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Gray"
|
||||||
|
msgstr "Gris"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Green"
|
||||||
|
msgstr "Verd"
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr "Id"
|
msgstr "Id"
|
||||||
@@ -438,15 +466,19 @@ msgstr "Importació"
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "In expanded view, scrolling through entries mark them as read"
|
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 "Indi"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Keep unread"
|
msgid "Keep unread"
|
||||||
msgstr "Mantenir sense llegir"
|
msgstr "Mantenir sense llegir"
|
||||||
|
|
||||||
#: src/components/content/FeedEntries.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/FeedEntries.tsx
|
||||||
msgid "Keyboard shortcuts"
|
msgid "Keyboard shortcuts"
|
||||||
msgstr "Dreceres de teclat"
|
msgstr "Dreceres de teclat"
|
||||||
|
|
||||||
@@ -470,12 +502,20 @@ msgstr "últim missatge d'actualització"
|
|||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr "Clar"
|
msgstr "Clar"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
msgid "Lime"
|
||||||
|
msgstr "Llima"
|
||||||
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr "Enllaç"
|
msgstr "Enllaç"
|
||||||
|
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
msgid "Link to the documentation"
|
||||||
|
msgstr "Enllaç a la documentació"
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading profile..."
|
msgid "Loading profile..."
|
||||||
msgstr "Carregant el perfil..."
|
msgstr "Carregant el perfil..."
|
||||||
@@ -492,9 +532,9 @@ msgstr "S'estan carregant les subscripcions..."
|
|||||||
msgid "Loading tags..."
|
msgid "Loading tags..."
|
||||||
msgstr "Carregant les etiquetes..."
|
msgstr "Carregant les etiquetes..."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "Inicia sessió"
|
msgstr "Inicia sessió"
|
||||||
|
|
||||||
@@ -504,29 +544,29 @@ msgstr "Tanca sessió"
|
|||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Long press"
|
msgid "Long press"
|
||||||
msgstr ""
|
msgstr "Prem llargament la tecla"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Manage users"
|
msgid "Manage users"
|
||||||
msgstr "Gestionar usuaris"
|
msgstr "Gestionar usuaris"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Mark all as read"
|
msgid "Mark all as read"
|
||||||
msgstr "Marca-ho tot com a llegit"
|
msgstr "Marca-ho tot com a llegit"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "Marqueu totes les entrades com a llegides"
|
msgstr "Marqueu totes les entrades com a llegides"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read"
|
msgid "Mark as read"
|
||||||
msgstr "Marca com a llegit"
|
msgstr "Marca com a llegit"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read up to here"
|
msgid "Mark as read up to here"
|
||||||
msgstr "Marca com a llegit fins aquí"
|
msgstr "Marca com a llegit fins aquí"
|
||||||
|
|
||||||
@@ -546,15 +586,15 @@ msgstr "Mou la pàgina cap avall"
|
|||||||
msgid "Move the page up"
|
msgid "Move the page up"
|
||||||
msgstr "Mou la pàgina cap amunt"
|
msgstr "Mou la pàgina cap amunt"
|
||||||
|
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
#: src/components/RelativeDate.tsx
|
#: src/components/RelativeDate.tsx
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
msgid "N/A"
|
msgid "N/A"
|
||||||
msgstr ""
|
msgstr "No es coneix"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nom"
|
msgstr "Nom"
|
||||||
|
|
||||||
@@ -562,6 +602,10 @@ msgstr "Nom"
|
|||||||
msgid "Navigate to a subscription by entering its name"
|
msgid "Navigate to a subscription by entering its name"
|
||||||
msgstr "Navegueu a una subscripció introduint-ne el nom"
|
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
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Never"
|
msgid "Never"
|
||||||
@@ -575,8 +619,8 @@ msgstr "Contrasenya nova"
|
|||||||
msgid "Newest first"
|
msgid "Newest first"
|
||||||
msgstr "El més nou primer"
|
msgstr "El més nou primer"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "Següent"
|
msgstr "Següent"
|
||||||
|
|
||||||
@@ -594,7 +638,7 @@ msgstr "No hi ha més entrades"
|
|||||||
|
|
||||||
#: src/components/content/ShareButtons.tsx
|
#: src/components/content/ShareButtons.tsx
|
||||||
msgid "No sharing options available."
|
msgid "No sharing options available."
|
||||||
msgstr ""
|
msgstr "No hi ha opcions de compartició disponibles."
|
||||||
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
msgid "Nothing found"
|
msgid "Nothing found"
|
||||||
@@ -606,11 +650,11 @@ msgstr "el més vell primer"
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "On desktop"
|
msgid "On desktop"
|
||||||
msgstr ""
|
msgstr "A l'scriptori"
|
||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "On mobile"
|
msgid "On mobile"
|
||||||
msgstr ""
|
msgstr "Al mòbil"
|
||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "On mobile, show action buttons at the bottom of the screen"
|
msgid "On mobile, show action buttons at the bottom of the screen"
|
||||||
@@ -618,7 +662,7 @@ msgstr "Al mòbil, mostra els botons d'acció a la part inferior de la pantalla"
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Only applies to compact, cozy and detailed modes"
|
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
|
#: src/pages/ErrorPage.tsx
|
||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
@@ -680,7 +724,11 @@ msgstr "Fitxer OPML"
|
|||||||
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
msgid "OPML file is required"
|
msgid "OPML file is required"
|
||||||
msgstr ""
|
msgstr "Cal un fitxer OPML"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Orange"
|
||||||
|
msgstr "Taronja"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Order"
|
msgid "Order"
|
||||||
@@ -694,11 +742,11 @@ msgstr "pares"
|
|||||||
msgid "Parent Category"
|
msgid "Parent Category"
|
||||||
msgstr "Categoria pare"
|
msgstr "Categoria pare"
|
||||||
|
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Contrasenya"
|
msgstr "Contrasenya"
|
||||||
|
|
||||||
@@ -710,8 +758,12 @@ msgstr "Recuperació de contrasenya"
|
|||||||
msgid "Passwords do not match"
|
msgid "Passwords do not match"
|
||||||
msgstr "Les contrasenyes no coincideixen"
|
msgstr "Les contrasenyes no coincideixen"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Pink"
|
||||||
|
msgstr "Rosa"
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Position"
|
msgid "Position"
|
||||||
msgstr "Posició"
|
msgstr "Posició"
|
||||||
|
|
||||||
@@ -719,6 +771,10 @@ msgstr "Posició"
|
|||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr "Anterior"
|
msgstr "Anterior"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Primary color"
|
||||||
|
msgstr "Color primari"
|
||||||
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "Perfil"
|
msgstr "Perfil"
|
||||||
@@ -727,8 +783,12 @@ msgstr "Perfil"
|
|||||||
msgid "Recover password"
|
msgid "Recover password"
|
||||||
msgstr "Recuperar la contrasenya"
|
msgstr "Recuperar la contrasenya"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Red"
|
||||||
|
msgstr "Vermell"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr "Actualitzar"
|
msgstr "Actualitzar"
|
||||||
|
|
||||||
@@ -745,11 +805,11 @@ msgstr "API REST"
|
|||||||
msgid "Right click"
|
msgid "Right click"
|
||||||
msgstr "Clic dret"
|
msgstr "Clic dret"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Desa"
|
msgstr "Desa"
|
||||||
|
|
||||||
@@ -765,20 +825,28 @@ msgstr "Desplaceu-vos suaument quan navegueu entre entrades"
|
|||||||
msgid "Scrolling"
|
msgid "Scrolling"
|
||||||
msgstr "Desplaçament"
|
msgstr "Desplaçament"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Cerca"
|
msgstr "Cerca"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Search requires at least 3 characters"
|
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 "Selecciona el següent canal/categoria no llegit"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select previous unread feed/category"
|
||||||
|
msgstr "Selecciona el canal/categoria anterior sense llegir"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on next entry without opening it"
|
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
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on previous entry without opening it"
|
msgid "Set focus on previous entry without opening it"
|
||||||
@@ -798,7 +866,7 @@ msgstr "Comparteix"
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Sharing sites"
|
msgid "Sharing sites"
|
||||||
msgstr "Compartir llocs"
|
msgstr "Compartir a altres llocs web"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
@@ -824,7 +892,7 @@ msgstr "Mostra el menú d'entrada (mòbil)"
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Show external link icon"
|
msgid "Show external link icon"
|
||||||
msgstr ""
|
msgstr "Mostra la icona d'enllaç extern"
|
||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Show feeds and categories with no unread entries"
|
msgid "Show feeds and categories with no unread entries"
|
||||||
@@ -840,19 +908,19 @@ msgstr "Mostra el menú natiu (escriptori)"
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Show star icon"
|
msgid "Show star icon"
|
||||||
msgstr ""
|
msgstr "Mostra la icona d'estrella"
|
||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Show unread count in tab favicon"
|
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
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Show unread count in tab title"
|
msgid "Show unread count in tab title"
|
||||||
msgstr ""
|
msgstr "Mostra el recompte de no llegits al títol de la pestanya"
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "Registra't"
|
msgstr "Registra't"
|
||||||
|
|
||||||
@@ -865,20 +933,21 @@ msgstr "Acaba de passar una cosa dolenta..."
|
|||||||
msgid "Space"
|
msgid "Space"
|
||||||
msgstr "Espai"
|
msgstr "Espai"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Star"
|
msgid "Star"
|
||||||
msgstr "Estrella"
|
msgstr "Estrella"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
msgid "Starred"
|
msgid "Starred"
|
||||||
msgstr "Estrellat"
|
msgstr "Estrellat"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AddPage.tsx
|
#: src/pages/app/AddPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Subscribe"
|
msgid "Subscribe"
|
||||||
msgstr "Subscriu-te"
|
msgstr "Subscriu-te"
|
||||||
|
|
||||||
@@ -915,6 +984,10 @@ msgstr "Sistema"
|
|||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etiquetes"
|
msgstr "Etiquetes"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Teal"
|
||||||
|
msgstr "Blau verdós"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: 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."
|
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."
|
||||||
msgstr "l'URL del canal al qual us voleu subscriure. "
|
msgstr "l'URL del canal al qual us voleu subscriure. "
|
||||||
@@ -951,8 +1024,8 @@ msgstr "Prova la demostració!"
|
|||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
msgstr "Sense llegir"
|
msgstr "Sense llegir"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Unstar"
|
msgid "Unstar"
|
||||||
msgstr "Desestrellar"
|
msgstr "Desestrellar"
|
||||||
@@ -971,6 +1044,10 @@ msgstr "Nom d'usuari"
|
|||||||
msgid "User Name or E-mail"
|
msgid "User Name or E-mail"
|
||||||
msgstr "Nom d'usuari o correu electrònic"
|
msgstr "Nom d'usuari o correu electrònic"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Violet"
|
||||||
|
msgstr "Violeta"
|
||||||
|
|
||||||
#: src/components/Alert.tsx
|
#: src/components/Alert.tsx
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr "Avís"
|
msgstr "Avís"
|
||||||
@@ -979,6 +1056,10 @@ msgstr "Avís"
|
|||||||
msgid "Website"
|
msgid "Website"
|
||||||
msgstr "Lloc web"
|
msgstr "Lloc web"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Yellow"
|
||||||
|
msgstr "Groc"
|
||||||
|
|
||||||
#: src/pages/app/FeedEntriesPage.tsx
|
#: 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?"
|
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
||||||
msgstr "Encara no teniu cap subscripció. "
|
msgstr "Encara no teniu cap subscripció. "
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
@@ -33,8 +33,8 @@ msgstr ""
|
|||||||
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
||||||
msgstr "<0>Potřebujete účet?</0><1>Zaregistrujte se!</1>"
|
msgstr "<0>Potřebujete účet?</0><1>Zaregistrujte se!</1>"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr "Asi"
|
msgstr "Asi"
|
||||||
|
|
||||||
@@ -54,16 +54,17 @@ msgstr "Přidat kategorii"
|
|||||||
msgid "Add user"
|
msgid "Add user"
|
||||||
msgstr "Přidat uživatele"
|
msgstr "Přidat uživatele"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Správce"
|
msgstr "Správce"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Všechny"
|
msgstr "Všechny"
|
||||||
|
|
||||||
@@ -104,11 +105,11 @@ msgstr "Opravdu chcete smazat uživatele <0>{userName}</0>?"
|
|||||||
msgid "Are you sure you want to delete your account? There's no turning back!"
|
msgid "Are you sure you want to delete your account? There's no turning back!"
|
||||||
msgstr "Opravdu chcete smazat svůj účet? "
|
msgstr "Opravdu chcete smazat svůj účet? "
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Opravdu chcete označit všechny položky <0>{sourceLabel}</0> jako přečtené?"
|
msgstr "Opravdu chcete označit všechny položky <0>{sourceLabel}</0> jako přečtené?"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Opravdu chcete označit záznamy starší než {threshold} dnů <0>{sourceLabel}</0> jako přečtené?"
|
msgstr "Opravdu chcete označit záznamy starší než {threshold} dnů <0>{sourceLabel}</0> jako přečtené?"
|
||||||
|
|
||||||
@@ -132,6 +133,10 @@ msgstr "Zpět"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Zpět k přihlášení"
|
msgstr "Zpět k přihlášení"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Blue"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Browser extension required for Chrome"
|
msgid "Browser extension required for Chrome"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -144,27 +149,27 @@ msgstr ""
|
|||||||
msgid "Browser tab"
|
msgid "Browser tab"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Zrušit"
|
msgstr "Zrušit"
|
||||||
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Kategorie"
|
msgstr "Kategorie"
|
||||||
|
|
||||||
@@ -204,11 +209,11 @@ msgstr ""
|
|||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
msgstr "Kompaktní"
|
msgstr "Kompaktní"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Potvrdit"
|
msgstr "Potvrdit"
|
||||||
|
|
||||||
@@ -240,6 +245,11 @@ msgstr ""
|
|||||||
msgid "Custom JS code that will be executed on page load"
|
msgid "Custom JS code that will be executed on page load"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Cyan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -273,13 +283,14 @@ msgstr ""
|
|||||||
msgid "Detailed"
|
msgid "Detailed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Displej"
|
msgstr "Displej"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Donate"
|
msgid "Donate"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -291,11 +302,11 @@ msgstr "Stáhnout"
|
|||||||
msgid "Drag link to bookmark bar"
|
msgid "Drag link to bookmark bar"
|
||||||
msgstr "Přetáhněte odkaz na lištu záložek"
|
msgstr "Přetáhněte odkaz na lištu záložek"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "E-mail"
|
msgid "E-mail"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -308,8 +319,8 @@ msgstr "E-mailová adresa"
|
|||||||
msgid "Edit user"
|
msgid "Edit user"
|
||||||
msgstr "Upravit uživatele"
|
msgstr "Upravit uživatele"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "Povoleno"
|
msgstr "Povoleno"
|
||||||
|
|
||||||
@@ -345,8 +356,8 @@ msgstr "Rozbaleno"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exportujte svá předplatná a kategorie jako soubor OPML, který lze importovat do jiných služeb čtení kanálů"
|
msgstr "Exportujte svá předplatná a kategorie jako soubor OPML, který lze importovat do jiných služeb čtení kanálů"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Extension options"
|
msgid "Extension options"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -354,9 +365,9 @@ msgstr ""
|
|||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Název zdroje"
|
msgstr "Název zdroje"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed URL"
|
msgid "Feed URL"
|
||||||
msgstr "URL zdroje"
|
msgstr "URL zdroje"
|
||||||
|
|
||||||
@@ -376,6 +387,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtrování výrazu"
|
msgstr "Filtrování výrazu"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Font size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Force fetching feeds is not yet available."
|
msgid "Force fetching feeds is not yet available."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -384,9 +399,9 @@ msgstr ""
|
|||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Zapomněli jste heslo?"
|
msgstr "Zapomněli jste heslo?"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generate an API key in your profile first."
|
msgid "Generate an API key in your profile first."
|
||||||
msgstr "Nejprve ve svém profilu vygenerujte klíč API."
|
msgstr "Nejprve ve svém profilu vygenerujte klíč API."
|
||||||
|
|
||||||
@@ -394,12 +409,13 @@ msgstr "Nejprve ve svém profilu vygenerujte klíč API."
|
|||||||
msgid "Generate new API key"
|
msgid "Generate new API key"
|
||||||
msgstr "Vygenerujte nový klíč API"
|
msgstr "Vygenerujte nový klíč API"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generated feed url"
|
msgid "Generated feed url"
|
||||||
msgstr "Generovaná adresa URL zdroje"
|
msgstr "Generovaná adresa URL zdroje"
|
||||||
|
|
||||||
|
#. placeholder {0}: truncate(props.entry.feedName, 30)
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -416,6 +432,18 @@ msgstr "Přejděte na dokumentaci API."
|
|||||||
msgid "Goodies"
|
msgid "Goodies"
|
||||||
msgstr "Dobroty"
|
msgstr "Dobroty"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Grape"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Gray"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Green"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -440,13 +468,17 @@ msgstr ""
|
|||||||
msgid "In expanded view, scrolling through entries mark them as read"
|
msgid "In expanded view, scrolling through entries mark them as read"
|
||||||
msgstr "V rozšířeném zobrazení je procházením označíte jako přečtené"
|
msgstr "V rozšířeném zobrazení je procházením označíte jako přečtené"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Indigo"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Keep unread"
|
msgid "Keep unread"
|
||||||
msgstr "Ponechat nepřečtené"
|
msgstr "Ponechat nepřečtené"
|
||||||
|
|
||||||
#: src/components/content/FeedEntries.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/FeedEntries.tsx
|
||||||
msgid "Keyboard shortcuts"
|
msgid "Keyboard shortcuts"
|
||||||
msgstr "Klávesové zkratky"
|
msgstr "Klávesové zkratky"
|
||||||
|
|
||||||
@@ -470,12 +502,20 @@ msgstr "Poslední obnovovací zpráva"
|
|||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
msgid "Lime"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr "Odkaz"
|
msgstr "Odkaz"
|
||||||
|
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
msgid "Link to the documentation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading profile..."
|
msgid "Loading profile..."
|
||||||
msgstr "Načítání profilu..."
|
msgstr "Načítání profilu..."
|
||||||
@@ -492,9 +532,9 @@ msgstr "Načítání odběrů..."
|
|||||||
msgid "Loading tags..."
|
msgid "Loading tags..."
|
||||||
msgstr "Načítání značek..."
|
msgstr "Načítání značek..."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "Přihlaste se"
|
msgstr "Přihlaste se"
|
||||||
|
|
||||||
@@ -506,27 +546,27 @@ msgstr "Odhlášení"
|
|||||||
msgid "Long press"
|
msgid "Long press"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Manage users"
|
msgid "Manage users"
|
||||||
msgstr "Spravujte uživatele"
|
msgstr "Spravujte uživatele"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Mark all as read"
|
msgid "Mark all as read"
|
||||||
msgstr "Označit vše jako přečtené"
|
msgstr "Označit vše jako přečtené"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "Označte všechny položky jako přečtené"
|
msgstr "Označte všechny položky jako přečtené"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read"
|
msgid "Mark as read"
|
||||||
msgstr "Označit jako přečtené"
|
msgstr "Označit jako přečtené"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read up to here"
|
msgid "Mark as read up to here"
|
||||||
msgstr "Označit jako přečtené až sem"
|
msgstr "Označit jako přečtené až sem"
|
||||||
|
|
||||||
@@ -546,15 +586,15 @@ msgstr "Přesuňte stránku dolů"
|
|||||||
msgid "Move the page up"
|
msgid "Move the page up"
|
||||||
msgstr "Přesuňte stránku nahoru"
|
msgstr "Přesuňte stránku nahoru"
|
||||||
|
|
||||||
#: src/components/RelativeDate.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/RelativeDate.tsx
|
||||||
msgid "N/A"
|
msgid "N/A"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Jméno"
|
msgstr "Jméno"
|
||||||
|
|
||||||
@@ -562,6 +602,10 @@ msgstr "Jméno"
|
|||||||
msgid "Navigate to a subscription by entering its name"
|
msgid "Navigate to a subscription by entering its name"
|
||||||
msgstr "Přejděte na předplatné zadáním jeho názvu"
|
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
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Never"
|
msgid "Never"
|
||||||
@@ -575,8 +619,8 @@ msgstr "Nové heslo"
|
|||||||
msgid "Newest first"
|
msgid "Newest first"
|
||||||
msgstr "Nejnovější jako první"
|
msgstr "Nejnovější jako první"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "Další"
|
msgstr "Další"
|
||||||
|
|
||||||
@@ -682,6 +726,10 @@ msgstr "soubor OPML"
|
|||||||
msgid "OPML file is required"
|
msgid "OPML file is required"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Orange"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Order"
|
msgid "Order"
|
||||||
msgstr "Objednávka"
|
msgstr "Objednávka"
|
||||||
@@ -694,11 +742,11 @@ msgstr "Rodič"
|
|||||||
msgid "Parent Category"
|
msgid "Parent Category"
|
||||||
msgstr "Rodičovská kategorie"
|
msgstr "Rodičovská kategorie"
|
||||||
|
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Heslo"
|
msgstr "Heslo"
|
||||||
|
|
||||||
@@ -710,8 +758,12 @@ msgstr "Obnovení hesla"
|
|||||||
msgid "Passwords do not match"
|
msgid "Passwords do not match"
|
||||||
msgstr "Hesla se neshodují"
|
msgstr "Hesla se neshodují"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Pink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Position"
|
msgid "Position"
|
||||||
msgstr "Pozice"
|
msgstr "Pozice"
|
||||||
|
|
||||||
@@ -719,6 +771,10 @@ msgstr "Pozice"
|
|||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Primary color"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "Profil"
|
msgstr "Profil"
|
||||||
@@ -727,8 +783,12 @@ msgstr "Profil"
|
|||||||
msgid "Recover password"
|
msgid "Recover password"
|
||||||
msgstr "Obnovte heslo"
|
msgstr "Obnovte heslo"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Red"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr "Obnovit"
|
msgstr "Obnovit"
|
||||||
|
|
||||||
@@ -745,11 +805,11 @@ msgstr ""
|
|||||||
msgid "Right click"
|
msgid "Right click"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Uložit"
|
msgstr "Uložit"
|
||||||
|
|
||||||
@@ -765,10 +825,10 @@ msgstr "Posouvejte plynule při navigaci mezi položkami"
|
|||||||
msgid "Scrolling"
|
msgid "Scrolling"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Hledej"
|
msgstr "Hledej"
|
||||||
|
|
||||||
@@ -776,6 +836,14 @@ msgstr "Hledej"
|
|||||||
msgid "Search requires at least 3 characters"
|
msgid "Search requires at least 3 characters"
|
||||||
msgstr "Hledání vyžaduje alespoň 3 znaky"
|
msgstr "Hledání vyžaduje alespoň 3 znaky"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select next unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select previous unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on next entry without opening it"
|
msgid "Set focus on next entry without opening it"
|
||||||
msgstr "Zaměřte se na další položku, aniž byste ji otevřeli"
|
msgstr "Zaměřte se na další položku, aniž byste ji otevřeli"
|
||||||
@@ -850,9 +918,9 @@ msgstr ""
|
|||||||
msgid "Show unread count in tab title"
|
msgid "Show unread count in tab title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "Zaregistrujte se"
|
msgstr "Zaregistrujte se"
|
||||||
|
|
||||||
@@ -865,20 +933,21 @@ msgstr "Právě se stalo něco špatného..."
|
|||||||
msgid "Space"
|
msgid "Space"
|
||||||
msgstr "Vesmír"
|
msgstr "Vesmír"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Star"
|
msgid "Star"
|
||||||
msgstr "Hvězda"
|
msgstr "Hvězda"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
msgid "Starred"
|
msgid "Starred"
|
||||||
msgstr "S hvězdičkou"
|
msgstr "S hvězdičkou"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AddPage.tsx
|
#: src/pages/app/AddPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Subscribe"
|
msgid "Subscribe"
|
||||||
msgstr "Přihlaste se"
|
msgstr "Přihlaste se"
|
||||||
|
|
||||||
@@ -915,6 +984,10 @@ msgstr ""
|
|||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Značky"
|
msgstr "Značky"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Teal"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: 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."
|
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."
|
||||||
msgstr "Adresa URL kanálu, k jehož odběru se chcete přihlásit. "
|
msgstr "Adresa URL kanálu, k jehož odběru se chcete přihlásit. "
|
||||||
@@ -951,8 +1024,8 @@ msgstr ""
|
|||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
msgstr "Nepřečteno"
|
msgstr "Nepřečteno"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Unstar"
|
msgid "Unstar"
|
||||||
msgstr "Odstranit hvězdu"
|
msgstr "Odstranit hvězdu"
|
||||||
@@ -971,6 +1044,10 @@ msgstr "Uživatelské jméno"
|
|||||||
msgid "User Name or E-mail"
|
msgid "User Name or E-mail"
|
||||||
msgstr "Uživatelské jméno nebo e-mail"
|
msgstr "Uživatelské jméno nebo e-mail"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Violet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Alert.tsx
|
#: src/components/Alert.tsx
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr "Varování"
|
msgstr "Varování"
|
||||||
@@ -979,6 +1056,10 @@ msgstr "Varování"
|
|||||||
msgid "Website"
|
msgid "Website"
|
||||||
msgstr "Webové stránky"
|
msgstr "Webové stránky"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Yellow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedEntriesPage.tsx
|
#: 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?"
|
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
||||||
msgstr "Zatím nemáte žádné předplatné. "
|
msgstr "Zatím nemáte žádné předplatné. "
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
@@ -33,8 +33,8 @@ msgstr ""
|
|||||||
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
||||||
msgstr "<0>Angen cyfrif?</0><1>Ymunwch!</1>"
|
msgstr "<0>Angen cyfrif?</0><1>Ymunwch!</1>"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr "Ynghylch"
|
msgstr "Ynghylch"
|
||||||
|
|
||||||
@@ -54,16 +54,17 @@ msgstr "Ychwanegu categori"
|
|||||||
msgid "Add user"
|
msgid "Add user"
|
||||||
msgstr "Ychwanegu defnyddiwr"
|
msgstr "Ychwanegu defnyddiwr"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Gweinyddol"
|
msgstr "Gweinyddol"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Pawb"
|
msgstr "Pawb"
|
||||||
|
|
||||||
@@ -104,11 +105,11 @@ msgstr "Ydych chi'n siŵr eich bod am ddileu defnyddiwr <0>{userName}</0> ?"
|
|||||||
msgid "Are you sure you want to delete your account? There's no turning back!"
|
msgid "Are you sure you want to delete your account? There's no turning back!"
|
||||||
msgstr "Ydych chi'n siŵr eich bod am ddileu eich cyfrif? "
|
msgstr "Ydych chi'n siŵr eich bod am ddileu eich cyfrif? "
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Ydych chi'n siŵr eich bod am farcio bod pob cofnod o <0>{sourceLabel}</0> wedi'i ddarllen?"
|
msgstr "Ydych chi'n siŵr eich bod am farcio bod pob cofnod o <0>{sourceLabel}</0> wedi'i ddarllen?"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Ydych chi'n siŵr eich bod am farcio cofnodion sy'n hŷn na {trothwy} diwrnod o <0>{sourceLabel}</0> fel rhai sydd wedi'u darllen?"
|
msgstr "Ydych chi'n siŵr eich bod am farcio cofnodion sy'n hŷn na {trothwy} diwrnod o <0>{sourceLabel}</0> fel rhai sydd wedi'u darllen?"
|
||||||
|
|
||||||
@@ -132,6 +133,10 @@ msgstr "Yn ôl"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Yn ôl i fewngofnodi"
|
msgstr "Yn ôl i fewngofnodi"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Blue"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Browser extension required for Chrome"
|
msgid "Browser extension required for Chrome"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -144,27 +149,27 @@ msgstr ""
|
|||||||
msgid "Browser tab"
|
msgid "Browser tab"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Diddymu"
|
msgstr "Diddymu"
|
||||||
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "categori"
|
msgstr "categori"
|
||||||
|
|
||||||
@@ -204,11 +209,11 @@ msgstr ""
|
|||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
msgstr "cryno"
|
msgstr "cryno"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Cadarnhau"
|
msgstr "Cadarnhau"
|
||||||
|
|
||||||
@@ -240,6 +245,11 @@ msgstr ""
|
|||||||
msgid "Custom JS code that will be executed on page load"
|
msgid "Custom JS code that will be executed on page load"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Cyan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -273,13 +283,14 @@ msgstr "Rhag"
|
|||||||
msgid "Detailed"
|
msgid "Detailed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Arddangos"
|
msgstr "Arddangos"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Donate"
|
msgid "Donate"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -291,11 +302,11 @@ msgstr "Lawrlwytho"
|
|||||||
msgid "Drag link to bookmark bar"
|
msgid "Drag link to bookmark bar"
|
||||||
msgstr "Llusgwch y ddolen i'r bar nod tudalen"
|
msgstr "Llusgwch y ddolen i'r bar nod tudalen"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "E-mail"
|
msgid "E-mail"
|
||||||
msgstr "E-bost"
|
msgstr "E-bost"
|
||||||
|
|
||||||
@@ -308,8 +319,8 @@ msgstr "cyfeiriad e-bost"
|
|||||||
msgid "Edit user"
|
msgid "Edit user"
|
||||||
msgstr "Golygu defnyddiwr"
|
msgstr "Golygu defnyddiwr"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "Wedi'i alluogi"
|
msgstr "Wedi'i alluogi"
|
||||||
|
|
||||||
@@ -345,8 +356,8 @@ msgstr "Ehangu"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Allforio eich tanysgrifiadau a'ch categorïau fel ffeil OPML y gellir ei mewnforio i wasanaethau darllen porthiant eraill"
|
msgstr "Allforio eich tanysgrifiadau a'ch categorïau fel ffeil OPML y gellir ei mewnforio i wasanaethau darllen porthiant eraill"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Extension options"
|
msgid "Extension options"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -354,9 +365,9 @@ msgstr ""
|
|||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Enw porthiant"
|
msgstr "Enw porthiant"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed URL"
|
msgid "Feed URL"
|
||||||
msgstr "URL porthiant"
|
msgstr "URL porthiant"
|
||||||
|
|
||||||
@@ -376,6 +387,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Hidlo mynegiant"
|
msgstr "Hidlo mynegiant"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Font size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Force fetching feeds is not yet available."
|
msgid "Force fetching feeds is not yet available."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -384,9 +399,9 @@ msgstr ""
|
|||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Wedi anghofio cyfrinair?"
|
msgstr "Wedi anghofio cyfrinair?"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generate an API key in your profile first."
|
msgid "Generate an API key in your profile first."
|
||||||
msgstr "Cynhyrchu allwedd API yn eich proffil yn gyntaf."
|
msgstr "Cynhyrchu allwedd API yn eich proffil yn gyntaf."
|
||||||
|
|
||||||
@@ -394,12 +409,13 @@ msgstr "Cynhyrchu allwedd API yn eich proffil yn gyntaf."
|
|||||||
msgid "Generate new API key"
|
msgid "Generate new API key"
|
||||||
msgstr "Cynhyrchu allwedd API newydd"
|
msgstr "Cynhyrchu allwedd API newydd"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generated feed url"
|
msgid "Generated feed url"
|
||||||
msgstr "url porthiant a gynhyrchir"
|
msgstr "url porthiant a gynhyrchir"
|
||||||
|
|
||||||
|
#. placeholder {0}: truncate(props.entry.feedName, 30)
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -416,6 +432,18 @@ msgstr "Ewch i'r ddogfennaeth API."
|
|||||||
msgid "Goodies"
|
msgid "Goodies"
|
||||||
msgstr "nwyddau"
|
msgstr "nwyddau"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Grape"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Gray"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Green"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -440,13 +468,17 @@ msgstr "Mewnforio"
|
|||||||
msgid "In expanded view, scrolling through entries mark them as read"
|
msgid "In expanded view, scrolling through entries mark them as read"
|
||||||
msgstr "Mewn gwedd estynedig, mae sgrolio trwy gofnodion yn nodi eu bod wedi'u darllen"
|
msgstr "Mewn gwedd estynedig, mae sgrolio trwy gofnodion yn nodi eu bod wedi'u darllen"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Indigo"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Keep unread"
|
msgid "Keep unread"
|
||||||
msgstr "Cadwch heb ei ddarllen"
|
msgstr "Cadwch heb ei ddarllen"
|
||||||
|
|
||||||
#: src/components/content/FeedEntries.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/FeedEntries.tsx
|
||||||
msgid "Keyboard shortcuts"
|
msgid "Keyboard shortcuts"
|
||||||
msgstr "llwybrau byr bysellfwrdd"
|
msgstr "llwybrau byr bysellfwrdd"
|
||||||
|
|
||||||
@@ -470,12 +502,20 @@ msgstr "Neges adnewyddu ddiwethaf"
|
|||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
msgid "Lime"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr "Cyswllt"
|
msgstr "Cyswllt"
|
||||||
|
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
msgid "Link to the documentation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading profile..."
|
msgid "Loading profile..."
|
||||||
msgstr "Wrthi'n llwytho proffil..."
|
msgstr "Wrthi'n llwytho proffil..."
|
||||||
@@ -492,9 +532,9 @@ msgstr "Yn llwytho tanysgrifiadau..."
|
|||||||
msgid "Loading tags..."
|
msgid "Loading tags..."
|
||||||
msgstr "Wrthi'n llwytho tagiau..."
|
msgstr "Wrthi'n llwytho tagiau..."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "Mewngofnodi"
|
msgstr "Mewngofnodi"
|
||||||
|
|
||||||
@@ -506,27 +546,27 @@ msgstr "Allgofnodi"
|
|||||||
msgid "Long press"
|
msgid "Long press"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Manage users"
|
msgid "Manage users"
|
||||||
msgstr "Rheoli defnyddwyr"
|
msgstr "Rheoli defnyddwyr"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Mark all as read"
|
msgid "Mark all as read"
|
||||||
msgstr "Marciwch y cyfan wedi'i ddarllen"
|
msgstr "Marciwch y cyfan wedi'i ddarllen"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "Marciwch bob cofnod wedi'i ddarllen"
|
msgstr "Marciwch bob cofnod wedi'i ddarllen"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read"
|
msgid "Mark as read"
|
||||||
msgstr "Marciwch ei fod wedi'i ddarllen"
|
msgstr "Marciwch ei fod wedi'i ddarllen"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read up to here"
|
msgid "Mark as read up to here"
|
||||||
msgstr "Marciwch fel y darllenwyd hyd yma"
|
msgstr "Marciwch fel y darllenwyd hyd yma"
|
||||||
|
|
||||||
@@ -546,15 +586,15 @@ msgstr "Symudwch y dudalen i lawr"
|
|||||||
msgid "Move the page up"
|
msgid "Move the page up"
|
||||||
msgstr "Symudwch y dudalen i fyny"
|
msgstr "Symudwch y dudalen i fyny"
|
||||||
|
|
||||||
#: src/components/RelativeDate.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/RelativeDate.tsx
|
||||||
msgid "N/A"
|
msgid "N/A"
|
||||||
msgstr "Amh"
|
msgstr "Amh"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Enw"
|
msgstr "Enw"
|
||||||
|
|
||||||
@@ -562,6 +602,10 @@ msgstr "Enw"
|
|||||||
msgid "Navigate to a subscription by entering its name"
|
msgid "Navigate to a subscription by entering its name"
|
||||||
msgstr "Llywiwch i danysgrifiad trwy nodi ei enw"
|
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
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Never"
|
msgid "Never"
|
||||||
@@ -575,8 +619,8 @@ msgstr "Cyfrinair newydd"
|
|||||||
msgid "Newest first"
|
msgid "Newest first"
|
||||||
msgstr "Y diweddaraf yn gyntaf"
|
msgstr "Y diweddaraf yn gyntaf"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "Nesaf"
|
msgstr "Nesaf"
|
||||||
|
|
||||||
@@ -682,6 +726,10 @@ msgstr "ffeil OPML"
|
|||||||
msgid "OPML file is required"
|
msgid "OPML file is required"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Orange"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Order"
|
msgid "Order"
|
||||||
msgstr "gorchymyn"
|
msgstr "gorchymyn"
|
||||||
@@ -694,11 +742,11 @@ msgstr "rhiant"
|
|||||||
msgid "Parent Category"
|
msgid "Parent Category"
|
||||||
msgstr "Categori Rhiant"
|
msgstr "Categori Rhiant"
|
||||||
|
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "cyfrinair"
|
msgstr "cyfrinair"
|
||||||
|
|
||||||
@@ -710,8 +758,12 @@ msgstr "Adfer Cyfrinair"
|
|||||||
msgid "Passwords do not match"
|
msgid "Passwords do not match"
|
||||||
msgstr "Nid yw cyfrineiriau yn cyfateb"
|
msgstr "Nid yw cyfrineiriau yn cyfateb"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Pink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Position"
|
msgid "Position"
|
||||||
msgstr "Swydd"
|
msgstr "Swydd"
|
||||||
|
|
||||||
@@ -719,6 +771,10 @@ msgstr "Swydd"
|
|||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Primary color"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "Proffil"
|
msgstr "Proffil"
|
||||||
@@ -727,8 +783,12 @@ msgstr "Proffil"
|
|||||||
msgid "Recover password"
|
msgid "Recover password"
|
||||||
msgstr "Adfer cyfrinair"
|
msgstr "Adfer cyfrinair"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Red"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr "Adnewyddu"
|
msgstr "Adnewyddu"
|
||||||
|
|
||||||
@@ -745,11 +805,11 @@ msgstr ""
|
|||||||
msgid "Right click"
|
msgid "Right click"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Arbed"
|
msgstr "Arbed"
|
||||||
|
|
||||||
@@ -765,10 +825,10 @@ msgstr "Sgroliwch yn esmwyth wrth lywio rhwng cofnodion"
|
|||||||
msgid "Scrolling"
|
msgid "Scrolling"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Chwilio"
|
msgstr "Chwilio"
|
||||||
|
|
||||||
@@ -776,6 +836,14 @@ msgstr "Chwilio"
|
|||||||
msgid "Search requires at least 3 characters"
|
msgid "Search requires at least 3 characters"
|
||||||
msgstr "Mae angen o leiaf 3 nod ar gyfer chwilio"
|
msgstr "Mae angen o leiaf 3 nod ar gyfer chwilio"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select next unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select previous unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on next entry without opening it"
|
msgid "Set focus on next entry without opening it"
|
||||||
msgstr "Gosodwch ffocws ar y cofnod nesaf heb ei agor"
|
msgstr "Gosodwch ffocws ar y cofnod nesaf heb ei agor"
|
||||||
@@ -850,9 +918,9 @@ msgstr ""
|
|||||||
msgid "Show unread count in tab title"
|
msgid "Show unread count in tab title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "Cofrestrwch"
|
msgstr "Cofrestrwch"
|
||||||
|
|
||||||
@@ -865,20 +933,21 @@ msgstr "Mae rhywbeth drwg newydd ddigwydd ..."
|
|||||||
msgid "Space"
|
msgid "Space"
|
||||||
msgstr "Gofod"
|
msgstr "Gofod"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Star"
|
msgid "Star"
|
||||||
msgstr "seren"
|
msgstr "seren"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
msgid "Starred"
|
msgid "Starred"
|
||||||
msgstr "serennog"
|
msgstr "serennog"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AddPage.tsx
|
#: src/pages/app/AddPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Subscribe"
|
msgid "Subscribe"
|
||||||
msgstr "Tanysgrifio"
|
msgstr "Tanysgrifio"
|
||||||
|
|
||||||
@@ -915,6 +984,10 @@ msgstr ""
|
|||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Tagiau"
|
msgstr "Tagiau"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Teal"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: 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."
|
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."
|
||||||
msgstr "Y URL ar gyfer y porthwr rydych chi am danysgrifio iddo. "
|
msgstr "Y URL ar gyfer y porthwr rydych chi am danysgrifio iddo. "
|
||||||
@@ -951,8 +1024,8 @@ msgstr ""
|
|||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
msgstr "Heb ei ddarllen"
|
msgstr "Heb ei ddarllen"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Unstar"
|
msgid "Unstar"
|
||||||
msgstr "dad-seren"
|
msgstr "dad-seren"
|
||||||
@@ -971,6 +1044,10 @@ msgstr "Enw defnyddiwr"
|
|||||||
msgid "User Name or E-mail"
|
msgid "User Name or E-mail"
|
||||||
msgstr "Enw Defnyddiwr neu E-bost"
|
msgstr "Enw Defnyddiwr neu E-bost"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Violet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Alert.tsx
|
#: src/components/Alert.tsx
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr "Rhybudd"
|
msgstr "Rhybudd"
|
||||||
@@ -979,6 +1056,10 @@ msgstr "Rhybudd"
|
|||||||
msgid "Website"
|
msgid "Website"
|
||||||
msgstr "Gwefan"
|
msgstr "Gwefan"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Yellow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedEntriesPage.tsx
|
#: 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?"
|
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
||||||
msgstr "Nid oes gennych unrhyw danysgrifiadau eto. "
|
msgstr "Nid oes gennych unrhyw danysgrifiadau eto. "
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
@@ -33,8 +33,8 @@ msgstr ""
|
|||||||
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
||||||
msgstr "<0>Har du brug for en konto?</0><1>Tilmeld dig!</1>"
|
msgstr "<0>Har du brug for en konto?</0><1>Tilmeld dig!</1>"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr "Omkring"
|
msgstr "Omkring"
|
||||||
|
|
||||||
@@ -54,16 +54,17 @@ msgstr "Tilføj kategori"
|
|||||||
msgid "Add user"
|
msgid "Add user"
|
||||||
msgstr "Tilføj bruger"
|
msgstr "Tilføj bruger"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Alle"
|
msgstr "Alle"
|
||||||
|
|
||||||
@@ -104,11 +105,11 @@ msgstr "Er du sikker på, at du vil slette bruger <0>{brugernavn}</0>?"
|
|||||||
msgid "Are you sure you want to delete your account? There's no turning back!"
|
msgid "Are you sure you want to delete your account? There's no turning back!"
|
||||||
msgstr "Er du sikker på, at du vil slette din konto? "
|
msgstr "Er du sikker på, at du vil slette din konto? "
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Er du sikker på, at du vil markere alle poster i <0>{sourceLabel}</0> som læst?"
|
msgstr "Er du sikker på, at du vil markere alle poster i <0>{sourceLabel}</0> som læst?"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Er du sikker på, at du vil markere poster, der er ældre end {threshold} dage af <0>{sourceLabel}</0> som læst?"
|
msgstr "Er du sikker på, at du vil markere poster, der er ældre end {threshold} dage af <0>{sourceLabel}</0> som læst?"
|
||||||
|
|
||||||
@@ -132,6 +133,10 @@ msgstr "Tilbage"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Tilbage for at logge ind"
|
msgstr "Tilbage for at logge ind"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Blue"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Browser extension required for Chrome"
|
msgid "Browser extension required for Chrome"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -144,27 +149,27 @@ msgstr ""
|
|||||||
msgid "Browser tab"
|
msgid "Browser tab"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Annuller"
|
msgstr "Annuller"
|
||||||
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Kategori"
|
msgstr "Kategori"
|
||||||
|
|
||||||
@@ -204,11 +209,11 @@ msgstr ""
|
|||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
msgstr "Kompakt"
|
msgstr "Kompakt"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Bekræft"
|
msgstr "Bekræft"
|
||||||
|
|
||||||
@@ -240,6 +245,11 @@ msgstr ""
|
|||||||
msgid "Custom JS code that will be executed on page load"
|
msgid "Custom JS code that will be executed on page load"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Cyan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -273,13 +283,14 @@ msgstr ""
|
|||||||
msgid "Detailed"
|
msgid "Detailed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Skærm"
|
msgstr "Skærm"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Donate"
|
msgid "Donate"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -291,11 +302,11 @@ msgstr ""
|
|||||||
msgid "Drag link to bookmark bar"
|
msgid "Drag link to bookmark bar"
|
||||||
msgstr "Træk linket til bogmærkelinjen"
|
msgstr "Træk linket til bogmærkelinjen"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "E-mail"
|
msgid "E-mail"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -308,8 +319,8 @@ msgstr "E-mailadresse"
|
|||||||
msgid "Edit user"
|
msgid "Edit user"
|
||||||
msgstr "Rediger bruger"
|
msgstr "Rediger bruger"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "Aktiveret"
|
msgstr "Aktiveret"
|
||||||
|
|
||||||
@@ -345,8 +356,8 @@ msgstr "Udvidet"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Eksporter dine abonnementer og kategorier som en OPML-fil, der kan importeres i andre feed-læsningstjenester"
|
msgstr "Eksporter dine abonnementer og kategorier som en OPML-fil, der kan importeres i andre feed-læsningstjenester"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Extension options"
|
msgid "Extension options"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -354,9 +365,9 @@ msgstr ""
|
|||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Feednavn"
|
msgstr "Feednavn"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed URL"
|
msgid "Feed URL"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -376,6 +387,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtrerende udtryk"
|
msgstr "Filtrerende udtryk"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Font size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Force fetching feeds is not yet available."
|
msgid "Force fetching feeds is not yet available."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -384,9 +399,9 @@ msgstr ""
|
|||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Glemt adgangskode?"
|
msgstr "Glemt adgangskode?"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generate an API key in your profile first."
|
msgid "Generate an API key in your profile first."
|
||||||
msgstr "Generer først en API-nøgle i din profil."
|
msgstr "Generer først en API-nøgle i din profil."
|
||||||
|
|
||||||
@@ -394,12 +409,13 @@ msgstr "Generer først en API-nøgle i din profil."
|
|||||||
msgid "Generate new API key"
|
msgid "Generate new API key"
|
||||||
msgstr "Generer ny API-nøgle"
|
msgstr "Generer ny API-nøgle"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generated feed url"
|
msgid "Generated feed url"
|
||||||
msgstr "Genereret feed-url"
|
msgstr "Genereret feed-url"
|
||||||
|
|
||||||
|
#. placeholder {0}: truncate(props.entry.feedName, 30)
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -416,6 +432,18 @@ msgstr "Gå til API-dokumentationen."
|
|||||||
msgid "Goodies"
|
msgid "Goodies"
|
||||||
msgstr "Godbidder"
|
msgstr "Godbidder"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Grape"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Gray"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Green"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -440,13 +468,17 @@ msgstr ""
|
|||||||
msgid "In expanded view, scrolling through entries mark them as read"
|
msgid "In expanded view, scrolling through entries mark them as read"
|
||||||
msgstr "I udvidet visning markerer du dem som læst, når du ruller gennem poster"
|
msgstr "I udvidet visning markerer du dem som læst, når du ruller gennem poster"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Indigo"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Keep unread"
|
msgid "Keep unread"
|
||||||
msgstr "Forbehold ulæst"
|
msgstr "Forbehold ulæst"
|
||||||
|
|
||||||
#: src/components/content/FeedEntries.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/FeedEntries.tsx
|
||||||
msgid "Keyboard shortcuts"
|
msgid "Keyboard shortcuts"
|
||||||
msgstr "Tastaturgenveje"
|
msgstr "Tastaturgenveje"
|
||||||
|
|
||||||
@@ -470,12 +502,20 @@ msgstr "Sidste opdateringsmeddelelse"
|
|||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
msgid "Lime"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
msgid "Link to the documentation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading profile..."
|
msgid "Loading profile..."
|
||||||
msgstr "Indlæser profil..."
|
msgstr "Indlæser profil..."
|
||||||
@@ -492,9 +532,9 @@ msgstr "Indlæser abonnementer..."
|
|||||||
msgid "Loading tags..."
|
msgid "Loading tags..."
|
||||||
msgstr "Indlæser tags..."
|
msgstr "Indlæser tags..."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "Log ind"
|
msgstr "Log ind"
|
||||||
|
|
||||||
@@ -506,27 +546,27 @@ msgstr "Log ud"
|
|||||||
msgid "Long press"
|
msgid "Long press"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Manage users"
|
msgid "Manage users"
|
||||||
msgstr "Administrer brugere"
|
msgstr "Administrer brugere"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Mark all as read"
|
msgid "Mark all as read"
|
||||||
msgstr "Marker alle som læst"
|
msgstr "Marker alle som læst"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "Marker alle poster som læst"
|
msgstr "Marker alle poster som læst"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read"
|
msgid "Mark as read"
|
||||||
msgstr "Markér som læst"
|
msgstr "Markér som læst"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read up to here"
|
msgid "Mark as read up to here"
|
||||||
msgstr "Markér som læst indtil her"
|
msgstr "Markér som læst indtil her"
|
||||||
|
|
||||||
@@ -546,15 +586,15 @@ msgstr "Flyt siden ned"
|
|||||||
msgid "Move the page up"
|
msgid "Move the page up"
|
||||||
msgstr "Flyt siden op"
|
msgstr "Flyt siden op"
|
||||||
|
|
||||||
#: src/components/RelativeDate.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/RelativeDate.tsx
|
||||||
msgid "N/A"
|
msgid "N/A"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Navn"
|
msgstr "Navn"
|
||||||
|
|
||||||
@@ -562,6 +602,10 @@ msgstr "Navn"
|
|||||||
msgid "Navigate to a subscription by entering its name"
|
msgid "Navigate to a subscription by entering its name"
|
||||||
msgstr "Naviger til et abonnement ved at indtaste dets navn"
|
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
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Never"
|
msgid "Never"
|
||||||
@@ -575,8 +619,8 @@ msgstr "Ny adgangskode"
|
|||||||
msgid "Newest first"
|
msgid "Newest first"
|
||||||
msgstr "Nyeste først"
|
msgstr "Nyeste først"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "Næste"
|
msgstr "Næste"
|
||||||
|
|
||||||
@@ -682,6 +726,10 @@ msgstr "OPML fil"
|
|||||||
msgid "OPML file is required"
|
msgid "OPML file is required"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Orange"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Order"
|
msgid "Order"
|
||||||
msgstr "Bestilling"
|
msgstr "Bestilling"
|
||||||
@@ -694,11 +742,11 @@ msgstr "Forælder"
|
|||||||
msgid "Parent Category"
|
msgid "Parent Category"
|
||||||
msgstr "Forældrekategori"
|
msgstr "Forældrekategori"
|
||||||
|
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Adgangskode"
|
msgstr "Adgangskode"
|
||||||
|
|
||||||
@@ -710,8 +758,12 @@ msgstr "Gendannelse af adgangskode"
|
|||||||
msgid "Passwords do not match"
|
msgid "Passwords do not match"
|
||||||
msgstr "Adgangskoder stemmer ikke overens"
|
msgstr "Adgangskoder stemmer ikke overens"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Pink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Position"
|
msgid "Position"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -719,6 +771,10 @@ msgstr ""
|
|||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Primary color"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "Profil"
|
msgstr "Profil"
|
||||||
@@ -727,8 +783,12 @@ msgstr "Profil"
|
|||||||
msgid "Recover password"
|
msgid "Recover password"
|
||||||
msgstr "Gendan adgangskode"
|
msgstr "Gendan adgangskode"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Red"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr "Opdater"
|
msgstr "Opdater"
|
||||||
|
|
||||||
@@ -745,11 +805,11 @@ msgstr ""
|
|||||||
msgid "Right click"
|
msgid "Right click"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Gem"
|
msgstr "Gem"
|
||||||
|
|
||||||
@@ -765,10 +825,10 @@ msgstr "Rul jævnt, når du navigerer mellem poster"
|
|||||||
msgid "Scrolling"
|
msgid "Scrolling"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Søg"
|
msgstr "Søg"
|
||||||
|
|
||||||
@@ -776,6 +836,14 @@ msgstr "Søg"
|
|||||||
msgid "Search requires at least 3 characters"
|
msgid "Search requires at least 3 characters"
|
||||||
msgstr "Søgning kræver mindst 3 tegn"
|
msgstr "Søgning kræver mindst 3 tegn"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select next unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select previous unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on next entry without opening it"
|
msgid "Set focus on next entry without opening it"
|
||||||
msgstr "Sæt fokus på næste post uden at åbne den"
|
msgstr "Sæt fokus på næste post uden at åbne den"
|
||||||
@@ -850,9 +918,9 @@ msgstr ""
|
|||||||
msgid "Show unread count in tab title"
|
msgid "Show unread count in tab title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "Tilmeld dig"
|
msgstr "Tilmeld dig"
|
||||||
|
|
||||||
@@ -865,20 +933,21 @@ msgstr "Der er lige sket noget slemt..."
|
|||||||
msgid "Space"
|
msgid "Space"
|
||||||
msgstr "Rum"
|
msgstr "Rum"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Star"
|
msgid "Star"
|
||||||
msgstr "Stjerne"
|
msgstr "Stjerne"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
msgid "Starred"
|
msgid "Starred"
|
||||||
msgstr "Medvirkende"
|
msgstr "Medvirkende"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AddPage.tsx
|
#: src/pages/app/AddPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Subscribe"
|
msgid "Subscribe"
|
||||||
msgstr "Tilmeld"
|
msgstr "Tilmeld"
|
||||||
|
|
||||||
@@ -915,6 +984,10 @@ msgstr ""
|
|||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Teal"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: 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."
|
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."
|
||||||
msgstr "URL'en til det feed, du vil abonnere på. "
|
msgstr "URL'en til det feed, du vil abonnere på. "
|
||||||
@@ -951,8 +1024,8 @@ msgstr ""
|
|||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
msgstr "Ulæst"
|
msgstr "Ulæst"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Unstar"
|
msgid "Unstar"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -971,6 +1044,10 @@ msgstr "Brugernavn"
|
|||||||
msgid "User Name or E-mail"
|
msgid "User Name or E-mail"
|
||||||
msgstr "Brugernavn eller e-mail"
|
msgstr "Brugernavn eller e-mail"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Violet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Alert.tsx
|
#: src/components/Alert.tsx
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr "Advarsel"
|
msgstr "Advarsel"
|
||||||
@@ -979,6 +1056,10 @@ msgstr "Advarsel"
|
|||||||
msgid "Website"
|
msgid "Website"
|
||||||
msgstr "Hjemmeside"
|
msgstr "Hjemmeside"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Yellow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedEntriesPage.tsx
|
#: 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?"
|
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
||||||
msgstr "Du har ingen abonnementer endnu. "
|
msgstr "Du har ingen abonnementer endnu. "
|
||||||
|
|||||||
@@ -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."
|
msgstr "<0>CommaFeed ist ein Open Source Projekt. Der Quellcode wird auf auf </0><1>GitHub</1> gehostet."
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: 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 "<0>Die vollständige Syntax ist </0><1>hier</1> verfügbar."
|
msgstr "<0>Die vollständige Syntax ist </0><1>hier</1> verfügbar<2>.</2>"
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "<0>Have an account?</0><1>Log in!</1>"
|
msgid "<0>Have an account?</0><1>Log in!</1>"
|
||||||
@@ -33,8 +33,8 @@ 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>"
|
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>"
|
msgstr "<0>Benötigen Sie ein Konto?</0><1>Hier geht's zur Registrierung!</1>"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr "Über"
|
msgstr "Über"
|
||||||
|
|
||||||
@@ -54,16 +54,17 @@ msgstr "Kategorie hinzufügen"
|
|||||||
msgid "Add user"
|
msgid "Add user"
|
||||||
msgstr "Benutzer hinzufügen"
|
msgstr "Benutzer hinzufügen"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Verwaltung"
|
msgstr "Verwaltung"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Alle"
|
msgstr "Alle"
|
||||||
|
|
||||||
@@ -104,11 +105,11 @@ msgstr "Sind Sie sicher, dass Sie Benutzer <0>{userName}</0> löschen möchten?"
|
|||||||
msgid "Are you sure you want to delete your account? There's no turning back!"
|
msgid "Are you sure you want to delete your account? There's no turning back!"
|
||||||
msgstr "Sind Sie sicher, dass Sie Ihr Konto löschen möchten?"
|
msgstr "Sind Sie sicher, dass Sie Ihr Konto löschen möchten?"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Sind Sie sicher, dass Sie alle Einträge von <0>{sourceLabel}</0> als gelesen markieren möchten?"
|
msgstr "Sind Sie sicher, dass Sie alle Einträge von <0>{sourceLabel}</0> als gelesen markieren möchten?"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Sind Sie sicher, dass Sie Einträge, die älter als {threshold} Tage von <0>{sourceLabel}</0> sind, als gelesen markieren möchten?"
|
msgstr "Sind Sie sicher, dass Sie Einträge, die älter als {threshold} Tage von <0>{sourceLabel}</0> sind, als gelesen markieren möchten?"
|
||||||
|
|
||||||
@@ -132,6 +133,10 @@ msgstr "Zurück"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Zurück zum Login"
|
msgstr "Zurück zum Login"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Blue"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Browser extension required for Chrome"
|
msgid "Browser extension required for Chrome"
|
||||||
msgstr "Browser-Erweiterung für Chrome benötigt"
|
msgstr "Browser-Erweiterung für Chrome benötigt"
|
||||||
@@ -144,27 +149,27 @@ msgstr "Browser-Erweiterung"
|
|||||||
msgid "Browser tab"
|
msgid "Browser tab"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Abbrechen"
|
msgstr "Abbrechen"
|
||||||
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Kategorie"
|
msgstr "Kategorie"
|
||||||
|
|
||||||
@@ -204,11 +209,11 @@ msgstr "CommaFeed version {version} ({revision})."
|
|||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
msgstr "Kompakt"
|
msgstr "Kompakt"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Bestätigen"
|
msgstr "Bestätigen"
|
||||||
|
|
||||||
@@ -240,6 +245,11 @@ msgstr "Eigene CSS Regeln die angewandt werden"
|
|||||||
msgid "Custom JS code that will be executed on page load"
|
msgid "Custom JS code that will be executed on page load"
|
||||||
msgstr "Einer JS Code der beim Laden der Seite ausgeführt wird"
|
msgstr "Einer JS Code der beim Laden der Seite ausgeführt wird"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Cyan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr "Dunkel"
|
msgstr "Dunkel"
|
||||||
@@ -273,13 +283,14 @@ msgstr "Beschr"
|
|||||||
msgid "Detailed"
|
msgid "Detailed"
|
||||||
msgstr "Detailliert"
|
msgstr "Detailliert"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Anzeige"
|
msgstr "Anzeige"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Donate"
|
msgid "Donate"
|
||||||
msgstr "Spenden"
|
msgstr "Spenden"
|
||||||
|
|
||||||
@@ -291,11 +302,11 @@ msgstr "Herunterladen"
|
|||||||
msgid "Drag link to bookmark bar"
|
msgid "Drag link to bookmark bar"
|
||||||
msgstr "Link in Lesezeichenleiste ziehen"
|
msgstr "Link in Lesezeichenleiste ziehen"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "E-mail"
|
msgid "E-mail"
|
||||||
msgstr "E-Mail"
|
msgstr "E-Mail"
|
||||||
|
|
||||||
@@ -308,8 +319,8 @@ msgstr "E-Mail-Adresse"
|
|||||||
msgid "Edit user"
|
msgid "Edit user"
|
||||||
msgstr "Benutzer bearbeiten"
|
msgstr "Benutzer bearbeiten"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "Aktiviert"
|
msgstr "Aktiviert"
|
||||||
|
|
||||||
@@ -345,8 +356,8 @@ msgstr "Erweitert"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exportieren Sie Ihre Abonnements und Kategorien als OPML-Datei, die in andere Feed-Lesedienste importiert werden kann"
|
msgstr "Exportieren Sie Ihre Abonnements und Kategorien als OPML-Datei, die in andere Feed-Lesedienste importiert werden kann"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Extension options"
|
msgid "Extension options"
|
||||||
msgstr "Erweiterungsoptionen"
|
msgstr "Erweiterungsoptionen"
|
||||||
|
|
||||||
@@ -354,9 +365,9 @@ msgstr "Erweiterungsoptionen"
|
|||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Feedname"
|
msgstr "Feedname"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed URL"
|
msgid "Feed URL"
|
||||||
msgstr "Feed-URL"
|
msgstr "Feed-URL"
|
||||||
|
|
||||||
@@ -376,6 +387,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filterausdruck"
|
msgstr "Filterausdruck"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Font size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Force fetching feeds is not yet available."
|
msgid "Force fetching feeds is not yet available."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -384,9 +399,9 @@ msgstr ""
|
|||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Passwort vergessen?"
|
msgstr "Passwort vergessen?"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generate an API key in your profile first."
|
msgid "Generate an API key in your profile first."
|
||||||
msgstr "Generieren Sie zuerst einen API-Schlüssel in Ihrem Profil."
|
msgstr "Generieren Sie zuerst einen API-Schlüssel in Ihrem Profil."
|
||||||
|
|
||||||
@@ -394,12 +409,13 @@ msgstr "Generieren Sie zuerst einen API-Schlüssel in Ihrem Profil."
|
|||||||
msgid "Generate new API key"
|
msgid "Generate new API key"
|
||||||
msgstr "Neuen API-Schlüssel generieren"
|
msgstr "Neuen API-Schlüssel generieren"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generated feed url"
|
msgid "Generated feed url"
|
||||||
msgstr "Generierte Feed-URL"
|
msgstr "Generierte Feed-URL"
|
||||||
|
|
||||||
|
#. placeholder {0}: truncate(props.entry.feedName, 30)
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr "Gehe zu {0}"
|
msgstr "Gehe zu {0}"
|
||||||
@@ -416,6 +432,18 @@ msgstr "Gehen Sie zur API-Dokumentation."
|
|||||||
msgid "Goodies"
|
msgid "Goodies"
|
||||||
msgstr "Goodies"
|
msgstr "Goodies"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Grape"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Gray"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Green"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -440,13 +468,17 @@ msgstr "Importieren"
|
|||||||
msgid "In expanded view, scrolling through entries mark them as read"
|
msgid "In expanded view, scrolling through entries mark them as read"
|
||||||
msgstr "In der erweiterten Ansicht werden Einträge beim Scrollen als gelesen markiert"
|
msgstr "In der erweiterten Ansicht werden Einträge beim Scrollen als gelesen markiert"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Indigo"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Keep unread"
|
msgid "Keep unread"
|
||||||
msgstr "Ungelesen lassen"
|
msgstr "Ungelesen lassen"
|
||||||
|
|
||||||
#: src/components/content/FeedEntries.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/FeedEntries.tsx
|
||||||
msgid "Keyboard shortcuts"
|
msgid "Keyboard shortcuts"
|
||||||
msgstr "Tastaturkürzel"
|
msgstr "Tastaturkürzel"
|
||||||
|
|
||||||
@@ -470,12 +502,20 @@ msgstr "Letzte Aktualisierungsmeldung"
|
|||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr "Hell"
|
msgstr "Hell"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
msgid "Lime"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr "Verbindung"
|
msgstr "Verbindung"
|
||||||
|
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
msgid "Link to the documentation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading profile..."
|
msgid "Loading profile..."
|
||||||
msgstr "Lade Profil..."
|
msgstr "Lade Profil..."
|
||||||
@@ -492,9 +532,9 @@ msgstr "Abonnements werden geladen..."
|
|||||||
msgid "Loading tags..."
|
msgid "Loading tags..."
|
||||||
msgstr "Tags werden geladen..."
|
msgstr "Tags werden geladen..."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "Einloggen"
|
msgstr "Einloggen"
|
||||||
|
|
||||||
@@ -506,27 +546,27 @@ msgstr "Abmelden"
|
|||||||
msgid "Long press"
|
msgid "Long press"
|
||||||
msgstr "Langer Tastendruck"
|
msgstr "Langer Tastendruck"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Manage users"
|
msgid "Manage users"
|
||||||
msgstr "Benutzer verwalten"
|
msgstr "Benutzer verwalten"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Mark all as read"
|
msgid "Mark all as read"
|
||||||
msgstr "Alle als gelesen markieren"
|
msgstr "Alle als gelesen markieren"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "Alle Einträge als gelesen markieren"
|
msgstr "Alle Einträge als gelesen markieren"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read"
|
msgid "Mark as read"
|
||||||
msgstr "Als gelesen markieren"
|
msgstr "Als gelesen markieren"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read up to here"
|
msgid "Mark as read up to here"
|
||||||
msgstr "Bis hierhin als gelesen markieren"
|
msgstr "Bis hierhin als gelesen markieren"
|
||||||
|
|
||||||
@@ -546,15 +586,15 @@ msgstr "Seite nach unten verschieben"
|
|||||||
msgid "Move the page up"
|
msgid "Move the page up"
|
||||||
msgstr "Bewege die Seite nach oben"
|
msgstr "Bewege die Seite nach oben"
|
||||||
|
|
||||||
#: src/components/RelativeDate.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/RelativeDate.tsx
|
||||||
msgid "N/A"
|
msgid "N/A"
|
||||||
msgstr "n.v."
|
msgstr "n.v."
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -562,6 +602,10 @@ msgstr ""
|
|||||||
msgid "Navigate to a subscription by entering its name"
|
msgid "Navigate to a subscription by entering its name"
|
||||||
msgstr "Navigieren Sie zu einem Abonnement, indem Sie seinen Namen eingeben"
|
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
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Never"
|
msgid "Never"
|
||||||
@@ -575,8 +619,8 @@ msgstr "Neues Passwort"
|
|||||||
msgid "Newest first"
|
msgid "Newest first"
|
||||||
msgstr "Neueste zuerst"
|
msgstr "Neueste zuerst"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "Weiter"
|
msgstr "Weiter"
|
||||||
|
|
||||||
@@ -682,6 +726,10 @@ msgstr "OPML-Datei"
|
|||||||
msgid "OPML file is required"
|
msgid "OPML file is required"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Orange"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Order"
|
msgid "Order"
|
||||||
msgstr "Bestellung"
|
msgstr "Bestellung"
|
||||||
@@ -694,11 +742,11 @@ msgstr "Übergeordnet"
|
|||||||
msgid "Parent Category"
|
msgid "Parent Category"
|
||||||
msgstr "Übergeordnete Kategorie"
|
msgstr "Übergeordnete Kategorie"
|
||||||
|
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Passwort"
|
msgstr "Passwort"
|
||||||
|
|
||||||
@@ -710,8 +758,12 @@ msgstr "Passwortwiederherstellung"
|
|||||||
msgid "Passwords do not match"
|
msgid "Passwords do not match"
|
||||||
msgstr "Passwörter stimmen nicht überein"
|
msgstr "Passwörter stimmen nicht überein"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Pink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Position"
|
msgid "Position"
|
||||||
msgstr "Position"
|
msgstr "Position"
|
||||||
|
|
||||||
@@ -719,6 +771,10 @@ msgstr "Position"
|
|||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr "Vorheriges"
|
msgstr "Vorheriges"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Primary color"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "Profil"
|
msgstr "Profil"
|
||||||
@@ -727,8 +783,12 @@ msgstr "Profil"
|
|||||||
msgid "Recover password"
|
msgid "Recover password"
|
||||||
msgstr "Kennwort wiederherstellen"
|
msgstr "Kennwort wiederherstellen"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Red"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr "Aktualisieren"
|
msgstr "Aktualisieren"
|
||||||
|
|
||||||
@@ -745,11 +805,11 @@ msgstr "REST-API"
|
|||||||
msgid "Right click"
|
msgid "Right click"
|
||||||
msgstr "Rechtsklick"
|
msgstr "Rechtsklick"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Speichern"
|
msgstr "Speichern"
|
||||||
|
|
||||||
@@ -765,10 +825,10 @@ msgstr "Schnelles Scrollen beim Navigieren zwischen Einträgen"
|
|||||||
msgid "Scrolling"
|
msgid "Scrolling"
|
||||||
msgstr "Scrollen"
|
msgstr "Scrollen"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Suche"
|
msgstr "Suche"
|
||||||
|
|
||||||
@@ -776,6 +836,14 @@ msgstr "Suche"
|
|||||||
msgid "Search requires at least 3 characters"
|
msgid "Search requires at least 3 characters"
|
||||||
msgstr "Suche erfordert mindestens 3 Zeichen"
|
msgstr "Suche erfordert mindestens 3 Zeichen"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select next unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select previous unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on next entry without opening it"
|
msgid "Set focus on next entry without opening it"
|
||||||
msgstr "Fokus auf den nächsten Eintrag setzen, ohne ihn zu öffnen"
|
msgstr "Fokus auf den nächsten Eintrag setzen, ohne ihn zu öffnen"
|
||||||
@@ -850,9 +918,9 @@ msgstr ""
|
|||||||
msgid "Show unread count in tab title"
|
msgid "Show unread count in tab title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "Melden Sie sich an"
|
msgstr "Melden Sie sich an"
|
||||||
|
|
||||||
@@ -865,20 +933,21 @@ msgstr "Etwas Schlimmes ist gerade passiert..."
|
|||||||
msgid "Space"
|
msgid "Space"
|
||||||
msgstr "Raum"
|
msgstr "Raum"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Star"
|
msgid "Star"
|
||||||
msgstr "Stern"
|
msgstr "Stern"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
msgid "Starred"
|
msgid "Starred"
|
||||||
msgstr "Markiert"
|
msgstr "Markiert"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AddPage.tsx
|
#: src/pages/app/AddPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Subscribe"
|
msgid "Subscribe"
|
||||||
msgstr "Abonnieren"
|
msgstr "Abonnieren"
|
||||||
|
|
||||||
@@ -915,6 +984,10 @@ msgstr ""
|
|||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Teal"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: 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."
|
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."
|
||||||
msgstr "Die URL für den Feed, den Sie abonnieren möchten. "
|
msgstr "Die URL für den Feed, den Sie abonnieren möchten. "
|
||||||
@@ -951,8 +1024,8 @@ msgstr "Testen Sie die Demo!"
|
|||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
msgstr "Ungelesen"
|
msgstr "Ungelesen"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Unstar"
|
msgid "Unstar"
|
||||||
msgstr "Stern entfernen"
|
msgstr "Stern entfernen"
|
||||||
@@ -971,6 +1044,10 @@ msgstr "Benutzername"
|
|||||||
msgid "User Name or E-mail"
|
msgid "User Name or E-mail"
|
||||||
msgstr "Benutzername oder E-Mail"
|
msgstr "Benutzername oder E-Mail"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Violet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Alert.tsx
|
#: src/components/Alert.tsx
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr "Warnung"
|
msgstr "Warnung"
|
||||||
@@ -979,6 +1056,10 @@ msgstr "Warnung"
|
|||||||
msgid "Website"
|
msgid "Website"
|
||||||
msgstr "Webseite"
|
msgstr "Webseite"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Yellow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedEntriesPage.tsx
|
#: 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?"
|
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
||||||
msgstr "Sie haben noch keine Abonnements."
|
msgstr "Sie haben noch keine Abonnements."
|
||||||
|
|||||||
@@ -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>."
|
msgstr "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: 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 "<0>Complete syntax is available </0><1>here</1>."
|
msgstr "<0>Complete syntax is available </0><1>here</1><2>.</2>"
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "<0>Have an account?</0><1>Log in!</1>"
|
msgid "<0>Have an account?</0><1>Log in!</1>"
|
||||||
@@ -33,8 +33,8 @@ 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>"
|
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
||||||
msgstr "<0>Need an account?</0><1>Sign up!</1>"
|
msgstr "<0>Need an account?</0><1>Sign up!</1>"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr "About"
|
msgstr "About"
|
||||||
|
|
||||||
@@ -54,16 +54,17 @@ msgstr "Add category"
|
|||||||
msgid "Add user"
|
msgid "Add user"
|
||||||
msgstr "Add user"
|
msgstr "Add user"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr "Admin"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "All"
|
msgstr "All"
|
||||||
|
|
||||||
@@ -104,11 +105,11 @@ msgstr "Are you sure you want to delete user <0>{userName}</0> ?"
|
|||||||
msgid "Are you sure you want to delete your account? There's no turning back!"
|
msgid "Are you sure you want to delete your account? There's no turning back!"
|
||||||
msgstr "Are you sure you want to delete your account? There's no turning back!"
|
msgstr "Are you sure you want to delete your account? There's no turning back!"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgstr "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgstr "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
|
|
||||||
@@ -132,6 +133,10 @@ msgstr "Back"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Back to log in"
|
msgstr "Back to log in"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Blue"
|
||||||
|
msgstr "Blue"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Browser extension required for Chrome"
|
msgid "Browser extension required for Chrome"
|
||||||
msgstr "Browser extension required for Chrome"
|
msgstr "Browser extension required for Chrome"
|
||||||
@@ -144,27 +149,27 @@ msgstr "Browser extention"
|
|||||||
msgid "Browser tab"
|
msgid "Browser tab"
|
||||||
msgstr "Browser tab"
|
msgstr "Browser tab"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Cancel"
|
msgstr "Cancel"
|
||||||
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Category"
|
msgstr "Category"
|
||||||
|
|
||||||
@@ -204,11 +209,11 @@ msgstr "CommaFeed version {version} ({revision})."
|
|||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
msgstr "Compact"
|
msgstr "Compact"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Confirm"
|
msgstr "Confirm"
|
||||||
|
|
||||||
@@ -240,6 +245,11 @@ msgstr "Custom CSS rules that will be applied"
|
|||||||
msgid "Custom JS code that will be executed on page load"
|
msgid "Custom JS code that will be executed on page load"
|
||||||
msgstr "Custom JS code that will be executed on page load"
|
msgstr "Custom JS code that will be executed on page load"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Cyan"
|
||||||
|
msgstr "Cyan"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr "Dark"
|
msgstr "Dark"
|
||||||
@@ -273,13 +283,14 @@ msgstr "Desc"
|
|||||||
msgid "Detailed"
|
msgid "Detailed"
|
||||||
msgstr "Detailed"
|
msgstr "Detailed"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Display"
|
msgstr "Display"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Donate"
|
msgid "Donate"
|
||||||
msgstr "Donate"
|
msgstr "Donate"
|
||||||
|
|
||||||
@@ -291,11 +302,11 @@ msgstr "Download"
|
|||||||
msgid "Drag link to bookmark bar"
|
msgid "Drag link to bookmark bar"
|
||||||
msgstr "Drag link to bookmark bar"
|
msgstr "Drag link to bookmark bar"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "E-mail"
|
msgid "E-mail"
|
||||||
msgstr "E-mail"
|
msgstr "E-mail"
|
||||||
|
|
||||||
@@ -308,8 +319,8 @@ msgstr "E-mail address"
|
|||||||
msgid "Edit user"
|
msgid "Edit user"
|
||||||
msgstr "Edit user"
|
msgstr "Edit user"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "Enabled"
|
msgstr "Enabled"
|
||||||
|
|
||||||
@@ -345,8 +356,8 @@ msgstr "Expanded"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgstr "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Extension options"
|
msgid "Extension options"
|
||||||
msgstr "Extension options"
|
msgstr "Extension options"
|
||||||
|
|
||||||
@@ -354,9 +365,9 @@ msgstr "Extension options"
|
|||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Feed name"
|
msgstr "Feed name"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed URL"
|
msgid "Feed URL"
|
||||||
msgstr "Feed URL"
|
msgstr "Feed URL"
|
||||||
|
|
||||||
@@ -376,6 +387,10 @@ msgstr "Fever API URL"
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtering expression"
|
msgstr "Filtering expression"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Font size"
|
||||||
|
msgstr "Font size"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Force fetching feeds is not yet available."
|
msgid "Force fetching feeds is not yet available."
|
||||||
msgstr "Force fetching feeds is not yet available."
|
msgstr "Force fetching feeds is not yet available."
|
||||||
@@ -384,9 +399,9 @@ msgstr "Force fetching feeds is not yet available."
|
|||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Forgot password?"
|
msgstr "Forgot password?"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generate an API key in your profile first."
|
msgid "Generate an API key in your profile first."
|
||||||
msgstr "Generate an API key in your profile first."
|
msgstr "Generate an API key in your profile first."
|
||||||
|
|
||||||
@@ -394,12 +409,13 @@ msgstr "Generate an API key in your profile first."
|
|||||||
msgid "Generate new API key"
|
msgid "Generate new API key"
|
||||||
msgstr "Generate new API key"
|
msgstr "Generate new API key"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generated feed url"
|
msgid "Generated feed url"
|
||||||
msgstr "Generated feed url"
|
msgstr "Generated feed url"
|
||||||
|
|
||||||
|
#. placeholder {0}: truncate(props.entry.feedName, 30)
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr "Go to {0}"
|
msgstr "Go to {0}"
|
||||||
@@ -416,6 +432,18 @@ msgstr "Go to the API documentation."
|
|||||||
msgid "Goodies"
|
msgid "Goodies"
|
||||||
msgstr "Goodies"
|
msgstr "Goodies"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Grape"
|
||||||
|
msgstr "Grape"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Gray"
|
||||||
|
msgstr "Gray"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Green"
|
||||||
|
msgstr "Green"
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr "Id"
|
msgstr "Id"
|
||||||
@@ -440,13 +468,17 @@ msgstr "Import"
|
|||||||
msgid "In expanded view, scrolling through entries mark them as read"
|
msgid "In expanded view, scrolling through entries mark them as read"
|
||||||
msgstr "In expanded view, scrolling through entries mark them as read"
|
msgstr "In expanded view, scrolling through entries mark them as read"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Indigo"
|
||||||
|
msgstr "Indigo"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Keep unread"
|
msgid "Keep unread"
|
||||||
msgstr "Keep unread"
|
msgstr "Keep unread"
|
||||||
|
|
||||||
#: src/components/content/FeedEntries.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/FeedEntries.tsx
|
||||||
msgid "Keyboard shortcuts"
|
msgid "Keyboard shortcuts"
|
||||||
msgstr "Keyboard shortcuts"
|
msgstr "Keyboard shortcuts"
|
||||||
|
|
||||||
@@ -470,12 +502,20 @@ msgstr "Last refresh message"
|
|||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr "Light"
|
msgstr "Light"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
msgid "Lime"
|
||||||
|
msgstr "Lime"
|
||||||
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr "Link"
|
msgstr "Link"
|
||||||
|
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
msgid "Link to the documentation"
|
||||||
|
msgstr "Link to the documentation"
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading profile..."
|
msgid "Loading profile..."
|
||||||
msgstr "Loading profile..."
|
msgstr "Loading profile..."
|
||||||
@@ -492,9 +532,9 @@ msgstr "Loading subscriptions..."
|
|||||||
msgid "Loading tags..."
|
msgid "Loading tags..."
|
||||||
msgstr "Loading tags..."
|
msgstr "Loading tags..."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "Log in"
|
msgstr "Log in"
|
||||||
|
|
||||||
@@ -506,27 +546,27 @@ msgstr "Logout"
|
|||||||
msgid "Long press"
|
msgid "Long press"
|
||||||
msgstr "Long press"
|
msgstr "Long press"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Manage users"
|
msgid "Manage users"
|
||||||
msgstr "Manage users"
|
msgstr "Manage users"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Mark all as read"
|
msgid "Mark all as read"
|
||||||
msgstr "Mark all as read"
|
msgstr "Mark all as read"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "Mark all entries as read"
|
msgstr "Mark all entries as read"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read"
|
msgid "Mark as read"
|
||||||
msgstr "Mark as read"
|
msgstr "Mark as read"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read up to here"
|
msgid "Mark as read up to here"
|
||||||
msgstr "Mark as read up to here"
|
msgstr "Mark as read up to here"
|
||||||
|
|
||||||
@@ -546,15 +586,15 @@ msgstr "Move the page down"
|
|||||||
msgid "Move the page up"
|
msgid "Move the page up"
|
||||||
msgstr "Move the page up"
|
msgstr "Move the page up"
|
||||||
|
|
||||||
#: src/components/RelativeDate.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/RelativeDate.tsx
|
||||||
msgid "N/A"
|
msgid "N/A"
|
||||||
msgstr "N/A"
|
msgstr "N/A"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
@@ -562,6 +602,10 @@ msgstr "Name"
|
|||||||
msgid "Navigate to a subscription by entering its name"
|
msgid "Navigate to a subscription by entering its name"
|
||||||
msgstr "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
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Never"
|
msgid "Never"
|
||||||
@@ -575,8 +619,8 @@ msgstr "New password"
|
|||||||
msgid "Newest first"
|
msgid "Newest first"
|
||||||
msgstr "Newest first"
|
msgstr "Newest first"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "Next"
|
msgstr "Next"
|
||||||
|
|
||||||
@@ -682,6 +726,10 @@ msgstr "OPML file"
|
|||||||
msgid "OPML file is required"
|
msgid "OPML file is required"
|
||||||
msgstr "OPML file is required"
|
msgstr "OPML file is required"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Orange"
|
||||||
|
msgstr "Orange"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Order"
|
msgid "Order"
|
||||||
msgstr "Order"
|
msgstr "Order"
|
||||||
@@ -694,11 +742,11 @@ msgstr "Parent"
|
|||||||
msgid "Parent Category"
|
msgid "Parent Category"
|
||||||
msgstr "Parent Category"
|
msgstr "Parent Category"
|
||||||
|
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Password"
|
msgstr "Password"
|
||||||
|
|
||||||
@@ -710,8 +758,12 @@ msgstr "Password Recovery"
|
|||||||
msgid "Passwords do not match"
|
msgid "Passwords do not match"
|
||||||
msgstr "Passwords do not match"
|
msgstr "Passwords do not match"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Pink"
|
||||||
|
msgstr "Pink"
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Position"
|
msgid "Position"
|
||||||
msgstr "Position"
|
msgstr "Position"
|
||||||
|
|
||||||
@@ -719,6 +771,10 @@ msgstr "Position"
|
|||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr "Previous"
|
msgstr "Previous"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Primary color"
|
||||||
|
msgstr "Primary color"
|
||||||
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "Profile"
|
msgstr "Profile"
|
||||||
@@ -727,8 +783,12 @@ msgstr "Profile"
|
|||||||
msgid "Recover password"
|
msgid "Recover password"
|
||||||
msgstr "Recover password"
|
msgstr "Recover password"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Red"
|
||||||
|
msgstr "Red"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr "Refresh"
|
msgstr "Refresh"
|
||||||
|
|
||||||
@@ -745,11 +805,11 @@ msgstr "REST API"
|
|||||||
msgid "Right click"
|
msgid "Right click"
|
||||||
msgstr "Right click"
|
msgstr "Right click"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Save"
|
msgstr "Save"
|
||||||
|
|
||||||
@@ -765,10 +825,10 @@ msgstr "Scroll smoothly when navigating between entries"
|
|||||||
msgid "Scrolling"
|
msgid "Scrolling"
|
||||||
msgstr "Scrolling"
|
msgstr "Scrolling"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Search"
|
msgstr "Search"
|
||||||
|
|
||||||
@@ -776,6 +836,14 @@ msgstr "Search"
|
|||||||
msgid "Search requires at least 3 characters"
|
msgid "Search requires at least 3 characters"
|
||||||
msgstr "Search requires at least 3 characters"
|
msgstr "Search requires at least 3 characters"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select next unread feed/category"
|
||||||
|
msgstr "Select next unread feed/category"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select previous unread feed/category"
|
||||||
|
msgstr "Select previous unread feed/category"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on next entry without opening it"
|
msgid "Set focus on next entry without opening it"
|
||||||
msgstr "Set focus on next entry without opening it"
|
msgstr "Set focus on next entry without opening it"
|
||||||
@@ -850,9 +918,9 @@ msgstr "Show unread count in tab favicon"
|
|||||||
msgid "Show unread count in tab title"
|
msgid "Show unread count in tab title"
|
||||||
msgstr "Show unread count in tab title"
|
msgstr "Show unread count in tab title"
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "Sign up"
|
msgstr "Sign up"
|
||||||
|
|
||||||
@@ -865,20 +933,21 @@ msgstr "Something bad just happened..."
|
|||||||
msgid "Space"
|
msgid "Space"
|
||||||
msgstr "Space"
|
msgstr "Space"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Star"
|
msgid "Star"
|
||||||
msgstr "Star"
|
msgstr "Star"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
msgid "Starred"
|
msgid "Starred"
|
||||||
msgstr "Starred"
|
msgstr "Starred"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AddPage.tsx
|
#: src/pages/app/AddPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Subscribe"
|
msgid "Subscribe"
|
||||||
msgstr "Subscribe"
|
msgstr "Subscribe"
|
||||||
|
|
||||||
@@ -915,6 +984,10 @@ msgstr "System"
|
|||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Tags"
|
msgstr "Tags"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Teal"
|
||||||
|
msgstr "Teal"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: 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."
|
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."
|
||||||
msgstr "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."
|
msgstr "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."
|
||||||
@@ -951,8 +1024,8 @@ msgstr "Try the demo!"
|
|||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
msgstr "Unread"
|
msgstr "Unread"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Unstar"
|
msgid "Unstar"
|
||||||
msgstr "Unstar"
|
msgstr "Unstar"
|
||||||
@@ -971,6 +1044,10 @@ msgstr "User name"
|
|||||||
msgid "User Name or E-mail"
|
msgid "User Name or E-mail"
|
||||||
msgstr "User Name or E-mail"
|
msgstr "User Name or E-mail"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Violet"
|
||||||
|
msgstr "Violet"
|
||||||
|
|
||||||
#: src/components/Alert.tsx
|
#: src/components/Alert.tsx
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr "Warning"
|
msgstr "Warning"
|
||||||
@@ -979,6 +1056,10 @@ msgstr "Warning"
|
|||||||
msgid "Website"
|
msgid "Website"
|
||||||
msgstr "Website"
|
msgstr "Website"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Yellow"
|
||||||
|
msgstr "Yellow"
|
||||||
|
|
||||||
#: src/pages/app/FeedEntriesPage.tsx
|
#: 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?"
|
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
||||||
msgstr "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
msgstr "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
||||||
|
|||||||
@@ -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>."
|
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
|
#: 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 "<0>La sintaxis completa está disponible </0><1>aquí</1>."
|
msgstr "<0>La sintaxis completa está disponible </0><1>aquí</1><2>.</2>"
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "<0>Have an account?</0><1>Log in!</1>"
|
msgid "<0>Have an account?</0><1>Log in!</1>"
|
||||||
@@ -34,8 +34,8 @@ 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>"
|
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
||||||
msgstr "<0>¿Necesitas una cuenta?</0><1>¡Regístrate!</1>"
|
msgstr "<0>¿Necesitas una cuenta?</0><1>¡Regístrate!</1>"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr "Acerca de"
|
msgstr "Acerca de"
|
||||||
|
|
||||||
@@ -55,16 +55,17 @@ msgstr "Añadir categoría"
|
|||||||
msgid "Add user"
|
msgid "Add user"
|
||||||
msgstr "Añadir usuario"
|
msgstr "Añadir usuario"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Administrador"
|
msgstr "Administrador"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Todo"
|
msgstr "Todo"
|
||||||
|
|
||||||
@@ -105,11 +106,11 @@ msgstr "¿Estás seguro de que deseas eliminar el usuario <0>{userName}</0> ?"
|
|||||||
msgid "Are you sure you want to delete your account? There's no turning back!"
|
msgid "Are you sure you want to delete your account? There's no turning back!"
|
||||||
msgstr "¿Estás seguro de que quieres eliminar tu cuenta? ¡No hay vuelta atrás!"
|
msgstr "¿Estás seguro de que quieres eliminar tu cuenta? ¡No hay vuelta atrás!"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "¿Estás seguro de que deseas marcar todas las entradas de <0>{sourceLabel}</0> como leídas?"
|
msgstr "¿Estás seguro de que deseas marcar todas las entradas de <0>{sourceLabel}</0> como leídas?"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "¿Estás seguro de que deseas marcar las entradas anteriores a {threshold} días de <0>{sourceLabel}</0> como leídas?"
|
msgstr "¿Estás seguro de que deseas marcar las entradas anteriores a {threshold} días de <0>{sourceLabel}</0> como leídas?"
|
||||||
|
|
||||||
@@ -133,6 +134,10 @@ msgstr "Atrás"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Volver a iniciar sesión"
|
msgstr "Volver a iniciar sesión"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Blue"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Browser extension required for Chrome"
|
msgid "Browser extension required for Chrome"
|
||||||
msgstr "Se requiere extensión de navegador para Chrome"
|
msgstr "Se requiere extensión de navegador para Chrome"
|
||||||
@@ -145,27 +150,27 @@ msgstr "Extensión del navegador"
|
|||||||
msgid "Browser tab"
|
msgid "Browser tab"
|
||||||
msgstr "Pestaña del navegador"
|
msgstr "Pestaña del navegador"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Cancelar"
|
msgstr "Cancelar"
|
||||||
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Categoría"
|
msgstr "Categoría"
|
||||||
|
|
||||||
@@ -205,11 +210,11 @@ msgstr "Versión de CommaFeed {version} ({revision})."
|
|||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
msgstr "Compacto"
|
msgstr "Compacto"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Confirmar"
|
msgstr "Confirmar"
|
||||||
|
|
||||||
@@ -241,6 +246,11 @@ msgstr "Reglas CSS personalizadas que se aplicarán"
|
|||||||
msgid "Custom JS code that will be executed on page load"
|
msgid "Custom JS code that will be executed on page load"
|
||||||
msgstr "Código JS personalizado que se ejecutará al cargar la página"
|
msgstr "Código JS personalizado que se ejecutará al cargar la página"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Cyan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr "Oscuro"
|
msgstr "Oscuro"
|
||||||
@@ -274,13 +284,14 @@ msgstr "Desc"
|
|||||||
msgid "Detailed"
|
msgid "Detailed"
|
||||||
msgstr "Detallado"
|
msgstr "Detallado"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Mostrar"
|
msgstr "Mostrar"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Donate"
|
msgid "Donate"
|
||||||
msgstr "Donar"
|
msgstr "Donar"
|
||||||
|
|
||||||
@@ -292,11 +303,11 @@ msgstr "Descargar"
|
|||||||
msgid "Drag link to bookmark bar"
|
msgid "Drag link to bookmark bar"
|
||||||
msgstr "Arrastra el enlace a la barra de marcadores"
|
msgstr "Arrastra el enlace a la barra de marcadores"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "E-mail"
|
msgid "E-mail"
|
||||||
msgstr "Correo electrónico"
|
msgstr "Correo electrónico"
|
||||||
|
|
||||||
@@ -309,8 +320,8 @@ msgstr "Dirección de correo electrónico"
|
|||||||
msgid "Edit user"
|
msgid "Edit user"
|
||||||
msgstr "Editar usuario"
|
msgstr "Editar usuario"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "Habilitado"
|
msgstr "Habilitado"
|
||||||
|
|
||||||
@@ -346,8 +357,8 @@ msgstr "Expandido"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exporta tus suscripciones y categorías como un archivo OPML que se puede importar en otros servicios de lectura de feeds"
|
msgstr "Exporta tus suscripciones y categorías como un archivo OPML que se puede importar en otros servicios de lectura de feeds"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Extension options"
|
msgid "Extension options"
|
||||||
msgstr "Opciones de la extensión"
|
msgstr "Opciones de la extensión"
|
||||||
|
|
||||||
@@ -355,9 +366,9 @@ msgstr "Opciones de la extensión"
|
|||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Nombre del feed"
|
msgstr "Nombre del feed"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed URL"
|
msgid "Feed URL"
|
||||||
msgstr "URL del feed"
|
msgstr "URL del feed"
|
||||||
|
|
||||||
@@ -377,6 +388,10 @@ msgstr "URL de la API de Fever"
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Expresión de filtrado"
|
msgstr "Expresión de filtrado"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Font size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Force fetching feeds is not yet available."
|
msgid "Force fetching feeds is not yet available."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -385,9 +400,9 @@ msgstr ""
|
|||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "¿Olvidaste la contraseña?"
|
msgstr "¿Olvidaste la contraseña?"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generate an API key in your profile first."
|
msgid "Generate an API key in your profile first."
|
||||||
msgstr "Primero genere una clave API en su perfil."
|
msgstr "Primero genere una clave API en su perfil."
|
||||||
|
|
||||||
@@ -395,12 +410,13 @@ msgstr "Primero genere una clave API en su perfil."
|
|||||||
msgid "Generate new API key"
|
msgid "Generate new API key"
|
||||||
msgstr "Generar nueva clave API"
|
msgstr "Generar nueva clave API"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generated feed url"
|
msgid "Generated feed url"
|
||||||
msgstr "URL del feed generado"
|
msgstr "URL del feed generado"
|
||||||
|
|
||||||
|
#. placeholder {0}: truncate(props.entry.feedName, 30)
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr "Ir a {0}"
|
msgstr "Ir a {0}"
|
||||||
@@ -417,6 +433,18 @@ msgstr "Ir a la documentación de la API."
|
|||||||
msgid "Goodies"
|
msgid "Goodies"
|
||||||
msgstr "Golosinas"
|
msgstr "Golosinas"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Grape"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Gray"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Green"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr "Identificación"
|
msgstr "Identificación"
|
||||||
@@ -441,13 +469,17 @@ msgstr "Importar"
|
|||||||
msgid "In expanded view, scrolling through entries mark them as read"
|
msgid "In expanded view, scrolling through entries mark them as read"
|
||||||
msgstr "En la vista ampliada, al desplazarse por las entradas marcarlas como leídas"
|
msgstr "En la vista ampliada, al desplazarse por las entradas marcarlas como leídas"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Indigo"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Keep unread"
|
msgid "Keep unread"
|
||||||
msgstr "Mantener sin leer"
|
msgstr "Mantener sin leer"
|
||||||
|
|
||||||
#: src/components/content/FeedEntries.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/FeedEntries.tsx
|
||||||
msgid "Keyboard shortcuts"
|
msgid "Keyboard shortcuts"
|
||||||
msgstr "Atajos de teclado"
|
msgstr "Atajos de teclado"
|
||||||
|
|
||||||
@@ -471,12 +503,20 @@ msgstr "Último mensaje de actualización"
|
|||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr "Claro"
|
msgstr "Claro"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
msgid "Lime"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr "Enlace"
|
msgstr "Enlace"
|
||||||
|
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
msgid "Link to the documentation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading profile..."
|
msgid "Loading profile..."
|
||||||
msgstr "Cargando perfil..."
|
msgstr "Cargando perfil..."
|
||||||
@@ -493,9 +533,9 @@ msgstr "Cargando suscripciones..."
|
|||||||
msgid "Loading tags..."
|
msgid "Loading tags..."
|
||||||
msgstr "Cargando etiquetas..."
|
msgstr "Cargando etiquetas..."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "Iniciar sesión"
|
msgstr "Iniciar sesión"
|
||||||
|
|
||||||
@@ -507,27 +547,27 @@ msgstr "Cerrar sesión"
|
|||||||
msgid "Long press"
|
msgid "Long press"
|
||||||
msgstr "Pulsación larga"
|
msgstr "Pulsación larga"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Manage users"
|
msgid "Manage users"
|
||||||
msgstr "Administrar usuarios"
|
msgstr "Administrar usuarios"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Mark all as read"
|
msgid "Mark all as read"
|
||||||
msgstr "Marcar todo como leído"
|
msgstr "Marcar todo como leído"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "Marcar todas las entradas como leídas"
|
msgstr "Marcar todas las entradas como leídas"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read"
|
msgid "Mark as read"
|
||||||
msgstr "Marcar como leído"
|
msgstr "Marcar como leído"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read up to here"
|
msgid "Mark as read up to here"
|
||||||
msgstr "Marcar como leído hasta aquí"
|
msgstr "Marcar como leído hasta aquí"
|
||||||
|
|
||||||
@@ -547,15 +587,15 @@ msgstr "Mover la página hacia abajo"
|
|||||||
msgid "Move the page up"
|
msgid "Move the page up"
|
||||||
msgstr "Mover la página hacia arriba"
|
msgstr "Mover la página hacia arriba"
|
||||||
|
|
||||||
#: src/components/RelativeDate.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/RelativeDate.tsx
|
||||||
msgid "N/A"
|
msgid "N/A"
|
||||||
msgstr "N/D"
|
msgstr "N/D"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nombre"
|
msgstr "Nombre"
|
||||||
|
|
||||||
@@ -563,6 +603,10 @@ msgstr "Nombre"
|
|||||||
msgid "Navigate to a subscription by entering its name"
|
msgid "Navigate to a subscription by entering its name"
|
||||||
msgstr "Navegar a una suscripción introduciendo su nombre"
|
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
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Never"
|
msgid "Never"
|
||||||
@@ -576,8 +620,8 @@ msgstr "Nueva contraseña"
|
|||||||
msgid "Newest first"
|
msgid "Newest first"
|
||||||
msgstr "Las más recientes primero"
|
msgstr "Las más recientes primero"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "Siguiente"
|
msgstr "Siguiente"
|
||||||
|
|
||||||
@@ -683,6 +727,10 @@ msgstr "Archivo OPML"
|
|||||||
msgid "OPML file is required"
|
msgid "OPML file is required"
|
||||||
msgstr "Es necesario un archivo OPML"
|
msgstr "Es necesario un archivo OPML"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Orange"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Order"
|
msgid "Order"
|
||||||
msgstr "Orden"
|
msgstr "Orden"
|
||||||
@@ -695,11 +743,11 @@ msgstr "Padre"
|
|||||||
msgid "Parent Category"
|
msgid "Parent Category"
|
||||||
msgstr "Categoría principal"
|
msgstr "Categoría principal"
|
||||||
|
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Contraseña"
|
msgstr "Contraseña"
|
||||||
|
|
||||||
@@ -711,8 +759,12 @@ msgstr "Recuperación de contraseña"
|
|||||||
msgid "Passwords do not match"
|
msgid "Passwords do not match"
|
||||||
msgstr "Las contraseñas no coinciden"
|
msgstr "Las contraseñas no coinciden"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Pink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Position"
|
msgid "Position"
|
||||||
msgstr "Posición"
|
msgstr "Posición"
|
||||||
|
|
||||||
@@ -720,6 +772,10 @@ msgstr "Posición"
|
|||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr "Previo"
|
msgstr "Previo"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Primary color"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "Perfil"
|
msgstr "Perfil"
|
||||||
@@ -728,8 +784,12 @@ msgstr "Perfil"
|
|||||||
msgid "Recover password"
|
msgid "Recover password"
|
||||||
msgstr "Recuperar contraseña"
|
msgstr "Recuperar contraseña"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Red"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr "Actualizar"
|
msgstr "Actualizar"
|
||||||
|
|
||||||
@@ -746,11 +806,11 @@ msgstr "API REST"
|
|||||||
msgid "Right click"
|
msgid "Right click"
|
||||||
msgstr "Clic derecho"
|
msgstr "Clic derecho"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Guardar"
|
msgstr "Guardar"
|
||||||
|
|
||||||
@@ -766,10 +826,10 @@ msgstr "Desplazarse suavemente al navegar entre entradas"
|
|||||||
msgid "Scrolling"
|
msgid "Scrolling"
|
||||||
msgstr "Desplazarse"
|
msgstr "Desplazarse"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Buscar"
|
msgstr "Buscar"
|
||||||
|
|
||||||
@@ -777,6 +837,14 @@ msgstr "Buscar"
|
|||||||
msgid "Search requires at least 3 characters"
|
msgid "Search requires at least 3 characters"
|
||||||
msgstr "La búsqueda requiere al menos 3 caracteres"
|
msgstr "La búsqueda requiere al menos 3 caracteres"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select next unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select previous unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on next entry without opening it"
|
msgid "Set focus on next entry without opening it"
|
||||||
msgstr "Establecer el foco en la siguiente entrada sin abrirla"
|
msgstr "Establecer el foco en la siguiente entrada sin abrirla"
|
||||||
@@ -851,9 +919,9 @@ msgstr "Mostrar recuento de no leídos en la pestaña favicon"
|
|||||||
msgid "Show unread count in tab title"
|
msgid "Show unread count in tab title"
|
||||||
msgstr "Mostrar recuento de no leídos en el título de la pestaña"
|
msgstr "Mostrar recuento de no leídos en el título de la pestaña"
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "Registrarse"
|
msgstr "Registrarse"
|
||||||
|
|
||||||
@@ -866,20 +934,21 @@ msgstr "Algo malo acaba de pasar..."
|
|||||||
msgid "Space"
|
msgid "Space"
|
||||||
msgstr "Espacio"
|
msgstr "Espacio"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Star"
|
msgid "Star"
|
||||||
msgstr "Estrella"
|
msgstr "Estrella"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
msgid "Starred"
|
msgid "Starred"
|
||||||
msgstr "Destacado"
|
msgstr "Destacado"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AddPage.tsx
|
#: src/pages/app/AddPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Subscribe"
|
msgid "Subscribe"
|
||||||
msgstr "Suscribirse"
|
msgstr "Suscribirse"
|
||||||
|
|
||||||
@@ -916,6 +985,10 @@ msgstr "Sistema"
|
|||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etiquetas"
|
msgstr "Etiquetas"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Teal"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: 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."
|
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."
|
||||||
msgstr "La URL del feed al que desea suscribirse. También puede utilizar la URL del sitio web directamente y CommaFeed intentará encontrar el feed en la página."
|
msgstr "La URL del feed al que desea suscribirse. También puede utilizar la URL del sitio web directamente y CommaFeed intentará encontrar el feed en la página."
|
||||||
@@ -952,8 +1025,8 @@ msgstr "¡Prueba la demostración!"
|
|||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
msgstr "No leído"
|
msgstr "No leído"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Unstar"
|
msgid "Unstar"
|
||||||
msgstr "Desmarcar"
|
msgstr "Desmarcar"
|
||||||
@@ -972,6 +1045,10 @@ msgstr "Nombre de usuario"
|
|||||||
msgid "User Name or E-mail"
|
msgid "User Name or E-mail"
|
||||||
msgstr "Nombre de usuario o correo electrónico"
|
msgstr "Nombre de usuario o correo electrónico"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Violet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Alert.tsx
|
#: src/components/Alert.tsx
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr "Advertencia"
|
msgstr "Advertencia"
|
||||||
@@ -980,6 +1057,10 @@ msgstr "Advertencia"
|
|||||||
msgid "Website"
|
msgid "Website"
|
||||||
msgstr "Sitio web"
|
msgstr "Sitio web"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Yellow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedEntriesPage.tsx
|
#: 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?"
|
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
||||||
msgstr "Aún no tienes ninguna suscripción. ¿Por qué no intentas agregar una haciendo clic en el signo + en la parte superior de la página?"
|
msgstr "Aún no tienes ninguna suscripción. ¿Por qué no intentas agregar una haciendo clic en el signo + en la parte superior de la página?"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitH
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
@@ -33,8 +33,8 @@ msgstr ""
|
|||||||
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
||||||
msgstr "<0>به یک حساب نیاز دارید؟</0><1>ثبت نام کنید!</1>"
|
msgstr "<0>به یک حساب نیاز دارید؟</0><1>ثبت نام کنید!</1>"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr "در مورد"
|
msgstr "در مورد"
|
||||||
|
|
||||||
@@ -54,16 +54,17 @@ msgstr "اضافه کردن دسته"
|
|||||||
msgid "Add user"
|
msgid "Add user"
|
||||||
msgstr "افزودن کاربر"
|
msgstr "افزودن کاربر"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "مدیر"
|
msgstr "مدیر"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "همه"
|
msgstr "همه"
|
||||||
|
|
||||||
@@ -104,11 +105,11 @@ msgstr "آیا مطمئن هستید که می خواهید کاربر <0>{userN
|
|||||||
msgid "Are you sure you want to delete your account? There's no turning back!"
|
msgid "Are you sure you want to delete your account? There's no turning back!"
|
||||||
msgstr "آیا مطمئن هستید که می خواهید حساب خود را حذف کنید؟ "
|
msgstr "آیا مطمئن هستید که می خواهید حساب خود را حذف کنید؟ "
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "آیا مطمئن هستید که می خواهید همه ورودی های <0>{sourceLabel}</0> را به عنوان خوانده شده علامت گذاری کنید؟"
|
msgstr "آیا مطمئن هستید که می خواهید همه ورودی های <0>{sourceLabel}</0> را به عنوان خوانده شده علامت گذاری کنید؟"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "آیا مطمئن هستید که می خواهید ورودی های قدیمی تر از {threshold} روز <0>{sourceLabel}</0> را به عنوان خوانده شده علامت گذاری کنید؟"
|
msgstr "آیا مطمئن هستید که می خواهید ورودی های قدیمی تر از {threshold} روز <0>{sourceLabel}</0> را به عنوان خوانده شده علامت گذاری کنید؟"
|
||||||
|
|
||||||
@@ -132,6 +133,10 @@ msgstr "برگشت"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "بازگشت برای ورود به سیستم"
|
msgstr "بازگشت برای ورود به سیستم"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Blue"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Browser extension required for Chrome"
|
msgid "Browser extension required for Chrome"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -144,27 +149,27 @@ msgstr ""
|
|||||||
msgid "Browser tab"
|
msgid "Browser tab"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "لغو"
|
msgstr "لغو"
|
||||||
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/AddCategory.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
|
#: src/components/content/add/AddCategory.tsx
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "مقوله"
|
msgstr "مقوله"
|
||||||
|
|
||||||
@@ -204,11 +209,11 @@ msgstr ""
|
|||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
msgstr "فشرده"
|
msgstr "فشرده"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "تأیید کنید"
|
msgstr "تأیید کنید"
|
||||||
|
|
||||||
@@ -240,6 +245,11 @@ msgstr ""
|
|||||||
msgid "Custom JS code that will be executed on page load"
|
msgid "Custom JS code that will be executed on page load"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Cyan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -273,13 +283,14 @@ msgstr "توصیف"
|
|||||||
msgid "Detailed"
|
msgid "Detailed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "نمایش"
|
msgstr "نمایش"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Donate"
|
msgid "Donate"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -291,11 +302,11 @@ msgstr "دانلود"
|
|||||||
msgid "Drag link to bookmark bar"
|
msgid "Drag link to bookmark bar"
|
||||||
msgstr "پیوند را به نوار نشانک بکشید"
|
msgstr "پیوند را به نوار نشانک بکشید"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/pages/auth/PasswordRecoveryPage.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
#: src/pages/auth/PasswordRecoveryPage.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "E-mail"
|
msgid "E-mail"
|
||||||
msgstr "ایمیل"
|
msgstr "ایمیل"
|
||||||
|
|
||||||
@@ -308,8 +319,8 @@ msgstr "آدرس ایمیل"
|
|||||||
msgid "Edit user"
|
msgid "Edit user"
|
||||||
msgstr "ویرایش کاربر"
|
msgstr "ویرایش کاربر"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "فعال"
|
msgstr "فعال"
|
||||||
|
|
||||||
@@ -345,8 +356,8 @@ msgstr "گسترش یافت"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "اشتراک ها و دسته های خود را به عنوان یک فایل OPML صادر کنید که می تواند در سایر خدمات خواندن فید وارد شود"
|
msgstr "اشتراک ها و دسته های خود را به عنوان یک فایل OPML صادر کنید که می تواند در سایر خدمات خواندن فید وارد شود"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Extension options"
|
msgid "Extension options"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -354,9 +365,9 @@ msgstr ""
|
|||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "نام فید"
|
msgstr "نام فید"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed URL"
|
msgid "Feed URL"
|
||||||
msgstr "URL فید"
|
msgstr "URL فید"
|
||||||
|
|
||||||
@@ -376,6 +387,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "بیان فیلتر"
|
msgstr "بیان فیلتر"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Font size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Force fetching feeds is not yet available."
|
msgid "Force fetching feeds is not yet available."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -384,9 +399,9 @@ msgstr ""
|
|||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "رمز عبور را فراموش کرده اید؟"
|
msgstr "رمز عبور را فراموش کرده اید؟"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generate an API key in your profile first."
|
msgid "Generate an API key in your profile first."
|
||||||
msgstr "ابتدا یک کلید API در نمایه خود ایجاد کنید."
|
msgstr "ابتدا یک کلید API در نمایه خود ایجاد کنید."
|
||||||
|
|
||||||
@@ -394,12 +409,13 @@ msgstr "ابتدا یک کلید API در نمایه خود ایجاد کنید.
|
|||||||
msgid "Generate new API key"
|
msgid "Generate new API key"
|
||||||
msgstr "کلید API جدید ایجاد کنید"
|
msgstr "کلید API جدید ایجاد کنید"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Generated feed url"
|
msgid "Generated feed url"
|
||||||
msgstr "آدرس اینترنتی فید تولید شده"
|
msgstr "آدرس اینترنتی فید تولید شده"
|
||||||
|
|
||||||
|
#. placeholder {0}: truncate(props.entry.feedName, 30)
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -416,6 +432,18 @@ msgstr "به مستندات API بروید."
|
|||||||
msgid "Goodies"
|
msgid "Goodies"
|
||||||
msgstr "خوبی ها"
|
msgstr "خوبی ها"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Grape"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Gray"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Green"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr "شناسه"
|
msgstr "شناسه"
|
||||||
@@ -440,13 +468,17 @@ msgstr "واردات"
|
|||||||
msgid "In expanded view, scrolling through entries mark them as read"
|
msgid "In expanded view, scrolling through entries mark them as read"
|
||||||
msgstr "در نمای بازشده، پیمایش در ورودیها، آنها را به عنوان خوانده شده علامتگذاری میکند"
|
msgstr "در نمای بازشده، پیمایش در ورودیها، آنها را به عنوان خوانده شده علامتگذاری میکند"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Indigo"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Keep unread"
|
msgid "Keep unread"
|
||||||
msgstr "خوانده نشده نگه دارید"
|
msgstr "خوانده نشده نگه دارید"
|
||||||
|
|
||||||
#: src/components/content/FeedEntries.tsx
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
#: src/components/content/FeedEntries.tsx
|
||||||
msgid "Keyboard shortcuts"
|
msgid "Keyboard shortcuts"
|
||||||
msgstr "میانبرهای صفحه کلید"
|
msgstr "میانبرهای صفحه کلید"
|
||||||
|
|
||||||
@@ -470,12 +502,20 @@ msgstr "آخرین پیام تازه کردن"
|
|||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
msgid "Lime"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/TagDetailsPage.tsx
|
#: src/pages/app/TagDetailsPage.tsx
|
||||||
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr "پیوند"
|
msgstr "پیوند"
|
||||||
|
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
msgid "Link to the documentation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading profile..."
|
msgid "Loading profile..."
|
||||||
msgstr "بارگیری نمایه..."
|
msgstr "بارگیری نمایه..."
|
||||||
@@ -492,9 +532,9 @@ msgstr "بارگیری اشتراک ها..."
|
|||||||
msgid "Loading tags..."
|
msgid "Loading tags..."
|
||||||
msgstr "بارگیری برچسب ها..."
|
msgstr "بارگیری برچسب ها..."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "وارد شوید"
|
msgstr "وارد شوید"
|
||||||
|
|
||||||
@@ -506,27 +546,27 @@ msgstr "خروج"
|
|||||||
msgid "Long press"
|
msgid "Long press"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Manage users"
|
msgid "Manage users"
|
||||||
msgstr "کاربران را مدیریت کنید"
|
msgstr "کاربران را مدیریت کنید"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Mark all as read"
|
msgid "Mark all as read"
|
||||||
msgstr "همه را به عنوان خوانده شده علامت گذاری کنید"
|
msgstr "همه را به عنوان خوانده شده علامت گذاری کنید"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/MarkAllAsReadConfirmationDialog.tsx
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "همه ورودی ها را به عنوان خوانده شده علامت گذاری کنید"
|
msgstr "همه ورودی ها را به عنوان خوانده شده علامت گذاری کنید"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read"
|
msgid "Mark as read"
|
||||||
msgstr "علامت گذاری به عنوان خوانده شده"
|
msgstr "علامت گذاری به عنوان خوانده شده"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Mark as read up to here"
|
msgid "Mark as read up to here"
|
||||||
msgstr "تا اینجا به عنوان خوانده شده علامت بزنید"
|
msgstr "تا اینجا به عنوان خوانده شده علامت بزنید"
|
||||||
|
|
||||||
@@ -546,15 +586,15 @@ msgstr "صفحه را به پایین ببرید"
|
|||||||
msgid "Move the page up"
|
msgid "Move the page up"
|
||||||
msgstr "صفحه را به بالا ببرید"
|
msgstr "صفحه را به بالا ببرید"
|
||||||
|
|
||||||
#: src/components/RelativeDate.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/components/RelativeDate.tsx
|
||||||
msgid "N/A"
|
msgid "N/A"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "نام"
|
msgstr "نام"
|
||||||
|
|
||||||
@@ -562,6 +602,10 @@ msgstr "نام"
|
|||||||
msgid "Navigate to a subscription by entering its name"
|
msgid "Navigate to a subscription by entering its name"
|
||||||
msgstr "با وارد کردن نام اشتراک، به آن بروید"
|
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
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Never"
|
msgid "Never"
|
||||||
@@ -575,8 +619,8 @@ msgstr "رمز عبور جدید"
|
|||||||
msgid "Newest first"
|
msgid "Newest first"
|
||||||
msgstr "ابتدا جدیدترین"
|
msgstr "ابتدا جدیدترین"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "بعد"
|
msgstr "بعد"
|
||||||
|
|
||||||
@@ -682,6 +726,10 @@ msgstr "فایل OPML"
|
|||||||
msgid "OPML file is required"
|
msgid "OPML file is required"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Orange"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Order"
|
msgid "Order"
|
||||||
msgstr "سفارش"
|
msgstr "سفارش"
|
||||||
@@ -694,11 +742,11 @@ msgstr "پدر و مادر"
|
|||||||
msgid "Parent Category"
|
msgid "Parent Category"
|
||||||
msgstr "دسته والد"
|
msgstr "دسته والد"
|
||||||
|
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
|
#: src/pages/auth/LoginPage.tsx
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "رمز عبور"
|
msgstr "رمز عبور"
|
||||||
|
|
||||||
@@ -710,8 +758,12 @@ msgstr "بازیابی رمز عبور"
|
|||||||
msgid "Passwords do not match"
|
msgid "Passwords do not match"
|
||||||
msgstr "گذرواژه ها مطابقت ندارند"
|
msgstr "گذرواژه ها مطابقت ندارند"
|
||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Pink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Position"
|
msgid "Position"
|
||||||
msgstr "موقعیت"
|
msgstr "موقعیت"
|
||||||
|
|
||||||
@@ -719,6 +771,10 @@ msgstr "موقعیت"
|
|||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Primary color"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "نمایه"
|
msgstr "نمایه"
|
||||||
@@ -727,8 +783,12 @@ msgstr "نمایه"
|
|||||||
msgid "Recover password"
|
msgid "Recover password"
|
||||||
msgstr "بازیابی رمز عبور"
|
msgstr "بازیابی رمز عبور"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Red"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr "تازه کردن"
|
msgstr "تازه کردن"
|
||||||
|
|
||||||
@@ -745,11 +805,11 @@ msgstr ""
|
|||||||
msgid "Right click"
|
msgid "Right click"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
|
#: src/components/admin/UserEdit.tsx
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "ذخیره کنید"
|
msgstr "ذخیره کنید"
|
||||||
|
|
||||||
@@ -765,10 +825,10 @@ msgstr "هنگام پیمایش بین ورودیها به آرامی حرک
|
|||||||
msgid "Scrolling"
|
msgid "Scrolling"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/header/Header.tsx
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "جستجو"
|
msgstr "جستجو"
|
||||||
|
|
||||||
@@ -776,6 +836,14 @@ msgstr "جستجو"
|
|||||||
msgid "Search requires at least 3 characters"
|
msgid "Search requires at least 3 characters"
|
||||||
msgstr "جستجو به حداقل 3 کاراکتر نیاز دارد"
|
msgstr "جستجو به حداقل 3 کاراکتر نیاز دارد"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select next unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Select previous unread feed/category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on next entry without opening it"
|
msgid "Set focus on next entry without opening it"
|
||||||
msgstr "فوکوس را روی ورودی بعدی بدون باز کردن آن تنظیم کنید"
|
msgstr "فوکوس را روی ورودی بعدی بدون باز کردن آن تنظیم کنید"
|
||||||
@@ -850,9 +918,9 @@ msgstr ""
|
|||||||
msgid "Show unread count in tab title"
|
msgid "Show unread count in tab title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/auth/RegistrationPage.tsx
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
|
#: src/pages/auth/RegistrationPage.tsx
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "ثبت نام کنید"
|
msgstr "ثبت نام کنید"
|
||||||
|
|
||||||
@@ -865,20 +933,21 @@ msgstr "اتفاق بدی افتاد..."
|
|||||||
msgid "Space"
|
msgid "Space"
|
||||||
msgstr "فضا"
|
msgstr "فضا"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Star"
|
msgid "Star"
|
||||||
msgstr "ستاره"
|
msgstr "ستاره"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
#: src/components/sidebar/Tree.tsx
|
#: src/components/sidebar/Tree.tsx
|
||||||
msgid "Starred"
|
msgid "Starred"
|
||||||
msgstr "ستاره دار"
|
msgstr "ستاره دار"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
|
||||||
#: src/pages/app/AddPage.tsx
|
#: src/pages/app/AddPage.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Subscribe"
|
msgid "Subscribe"
|
||||||
msgstr "مشترک شوید"
|
msgstr "مشترک شوید"
|
||||||
|
|
||||||
@@ -915,6 +984,10 @@ msgstr ""
|
|||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "برچسب ها"
|
msgstr "برچسب ها"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Teal"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: 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."
|
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."
|
||||||
msgstr "URL فیدی که می خواهید در آن مشترک شوید. "
|
msgstr "URL فیدی که می خواهید در آن مشترک شوید. "
|
||||||
@@ -951,8 +1024,8 @@ msgstr ""
|
|||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
msgstr "خوانده نشده"
|
msgstr "خوانده نشده"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
#: src/components/content/header/Star.tsx
|
#: src/components/content/header/Star.tsx
|
||||||
msgid "Unstar"
|
msgid "Unstar"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -971,6 +1044,10 @@ msgstr "نام کاربری"
|
|||||||
msgid "User Name or E-mail"
|
msgid "User Name or E-mail"
|
||||||
msgstr "نام کاربری یا ایمیل"
|
msgstr "نام کاربری یا ایمیل"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Violet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Alert.tsx
|
#: src/components/Alert.tsx
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr "هشدار"
|
msgstr "هشدار"
|
||||||
@@ -979,6 +1056,10 @@ msgstr "هشدار"
|
|||||||
msgid "Website"
|
msgid "Website"
|
||||||
msgstr "وب سایت"
|
msgstr "وب سایت"
|
||||||
|
|
||||||
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
|
msgid "Yellow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedEntriesPage.tsx
|
#: 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?"
|
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
|
||||||
msgstr "شما هنوز هیچ اشتراکی ندارید. "
|
msgstr "شما هنوز هیچ اشتراکی ندارید. "
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user