mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-01 08:06:13 +00:00
Compare commits
572
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec94ec5305 | ||
|
|
f27bbcb345 | ||
|
|
8d2117c39c | ||
|
|
0b1698d078 | ||
|
|
b1d1b5f94d | ||
|
|
5666b11ecf | ||
|
|
76c256dd8e | ||
|
|
9caed413d7 | ||
|
|
10f0066ed1 | ||
|
|
3c95bba0d2 | ||
|
|
34c65765ba | ||
|
|
a832f6ca3b | ||
|
|
2c40bc6709 | ||
|
|
5d91243451 | ||
|
|
2ebe5fc728 | ||
|
|
6a1c40e7eb | ||
|
|
81b2a7c485 | ||
|
|
3165b40127 | ||
|
|
1286e6cb82 | ||
|
|
681e2546f0 | ||
|
|
718fd00c6d | ||
|
|
cb0f50198e | ||
|
|
421fa6592a | ||
|
|
d20532af91 | ||
|
|
67cfe0a97d | ||
|
|
db1fac4085 | ||
|
|
93de202b38 | ||
|
|
305b836001 | ||
|
|
ba67e67339 | ||
|
|
818c45c280 | ||
|
|
3b3a2fc670 | ||
|
|
d88d3dfbaf | ||
|
|
bac51fbc46 | ||
|
|
7c39730616 | ||
|
|
62e4806353 | ||
|
|
b9dcbdea52 | ||
|
|
889e1c02f6 | ||
|
|
835508c0c0 | ||
|
|
633574203f | ||
|
|
b188c2dba0 | ||
|
|
93c6307027 | ||
|
|
2c5d95184e | ||
|
|
75213f6a55 | ||
|
|
ac875ffe1d | ||
|
|
1c33f8daef | ||
|
|
e2d4915edc | ||
|
|
b1a0eaf805 | ||
|
|
2e90f43181 | ||
|
|
3c0d7ad505 | ||
|
|
a5b40d5a3a | ||
|
|
2662ad404e | ||
|
|
d2d6023084 | ||
|
|
f21f6a126b | ||
|
|
17ed1b2309 | ||
|
|
cef88b9384 | ||
|
|
1194bf284f | ||
|
|
d2d44e0dec | ||
|
|
1aba571ac3 | ||
|
|
8345be43ef | ||
|
|
43bf8da760 | ||
|
|
9192311df8 | ||
|
|
5ead7a8164 | ||
|
|
e5027c45aa | ||
|
|
7a06ab03c0 | ||
|
|
be98acdc8b | ||
|
|
b0997c0a13 | ||
|
|
8aa1d8e03c | ||
|
|
d9d7a46667 | ||
|
|
69f277e1ce | ||
|
|
aabfe3f6a7 | ||
|
|
da41b3f060 | ||
|
|
6a52715704 | ||
|
|
b6d364f732 | ||
|
|
1b6384edec | ||
|
|
1769a4aa62 | ||
|
|
897d4ae32d | ||
|
|
b75b4faa69 | ||
|
|
8fae4e6398 | ||
|
|
2acce326b7 | ||
|
|
05bc0f567b | ||
|
|
4c818d64ea | ||
|
|
22893d327f | ||
|
|
48e427bfa1 | ||
|
|
48a0590a93 | ||
|
|
d254d7b5ab | ||
|
|
c66e6209ed | ||
|
|
bc47c6a1af | ||
|
|
ab226b03b4 | ||
|
|
051f6bf2d5 | ||
|
|
cfca6b9c24 | ||
|
|
0ae33f3a07 | ||
|
|
f9a3b76174 | ||
|
|
b2cd83b188 | ||
|
|
38257f81c6 | ||
|
|
368c90ce3e | ||
|
|
fe3f860e73 | ||
|
|
7e60d32449 | ||
|
|
13dcbdc086 | ||
|
|
0865c1d6e7 | ||
|
|
e4295dab22 | ||
|
|
b43472dda4 | ||
|
|
6b7e752c4b | ||
|
|
f15b7bb3cf | ||
|
|
ff01246c92 | ||
|
|
83ddd13726 | ||
|
|
609d36b0d3 | ||
|
|
44366848d1 | ||
|
|
f27e2cebb4 | ||
|
|
409ada9428 | ||
|
|
efb92593b2 | ||
|
|
0ec809eb49 | ||
|
|
df1e211780 | ||
|
|
62a6c22f10 | ||
|
|
1d654bbeaf | ||
|
|
fbe5457604 | ||
|
|
417f1843a2 | ||
|
|
83981e8b8d | ||
|
|
8ba2f27af2 | ||
|
|
a69bc071d1 | ||
|
|
ba150060cf | ||
|
|
b7b3dd4c4d | ||
|
|
585c70a5db | ||
|
|
c704c7e6af | ||
|
|
e1ad5c0945 | ||
|
|
2b3e84a5a0 | ||
|
|
9d9777738e | ||
|
|
6a27c59672 | ||
|
|
8389db8fcb | ||
|
|
21dd5a2405 | ||
|
|
6e5980e4e0 | ||
|
|
97b71e22f5 | ||
|
|
649d3adc0f | ||
|
|
ab2e5a60f8 | ||
|
|
10fb7ab758 | ||
|
|
c6501ec886 | ||
|
|
66b4305e6d | ||
|
|
7b249ab9d9 | ||
|
|
b4f1082915 | ||
|
|
4724c28f53 | ||
|
|
77914b0a46 | ||
|
|
f1297b6be8 | ||
|
|
36cc49b706 | ||
|
|
3704c66637 | ||
|
|
23b2bf9732 | ||
|
|
172d0f63e8 | ||
|
|
ce950430ea | ||
|
|
d663a5e282 | ||
|
|
268b1105b8 | ||
|
|
9669e07e95 | ||
|
|
b803da5df5 | ||
|
|
e6a89ad07a | ||
|
|
423e2780d5 | ||
|
|
58c8054339 | ||
|
|
350b0e8e74 | ||
|
|
9b0bba7c15 | ||
|
|
5ecf6f3256 | ||
|
|
f606866dd1 | ||
|
|
14ec671c4e | ||
|
|
73823292e2 | ||
|
|
e69bfc502b | ||
|
|
5a301d62dc | ||
|
|
e0184cfb46 | ||
|
|
56c8b98bb6 | ||
|
|
ce21f273a3 | ||
|
|
f2513de025 | ||
|
|
9bc7b4a5b8 | ||
|
|
461e4da0b2 | ||
|
|
66cdc07d31 | ||
|
|
ff878fc6bf | ||
|
|
ccf93ed667 | ||
|
|
9f64f8abb3 | ||
|
|
9763036c70 | ||
|
|
ecf814b66e | ||
|
|
d1c44f52ba | ||
|
|
7eb7289472 | ||
|
|
b17d4d4069 | ||
|
|
47b922cab0 | ||
|
|
2fe8366938 | ||
|
|
9ce89e2672 | ||
|
|
d6b9b342e9 | ||
|
|
72b41eabde | ||
|
|
149e5bcee5 | ||
|
|
236e9117a8 | ||
|
|
e7fd665bb4 | ||
|
|
d00605492a | ||
|
|
9781b30478 | ||
|
|
fe6781745f | ||
|
|
bd5c54d654 | ||
|
|
8e0b447d8f | ||
|
|
e9816809f2 | ||
|
|
11fa0365f9 | ||
|
|
65a8ac3469 | ||
|
|
826d5ad2be | ||
|
|
dcca32a780 | ||
|
|
f9a36b2892 | ||
|
|
8626cd11ef | ||
|
|
e4eda0a840 | ||
|
|
75513969ef | ||
|
|
688e8bef3b | ||
|
|
980243469c | ||
|
|
9ec58be4fb | ||
|
|
426f740359 | ||
|
|
db717dcd51 | ||
|
|
1e21540c6a | ||
|
|
7139390a85 | ||
|
|
62867f3f3d | ||
|
|
6452b47ca2 | ||
|
|
50b91d67d5 | ||
|
|
7ba00be60e | ||
|
|
01578ac2fb | ||
|
|
a4e5f81f37 | ||
|
|
90ce7c1b3c | ||
|
|
9fd55d94d3 | ||
|
|
7467764e43 | ||
|
|
b35d15cada | ||
|
|
ecf1f3ecd9 | ||
|
|
254d192709 | ||
|
|
96b163a117 | ||
|
|
bc9680b014 | ||
|
|
cec68f0d91 | ||
|
|
beae63e144 | ||
|
|
287b0e58ac | ||
|
|
3048525d9b | ||
|
|
dae154431f | ||
|
|
8a5bb72823 | ||
|
|
d684b00e9b | ||
|
|
98b962a8d4 | ||
|
|
6c1cb0f9ef | ||
|
|
12b798c0c8 | ||
|
|
cad9296a84 | ||
|
|
a7a3757378 | ||
|
|
3c3274e4b7 | ||
|
|
aac8a40367 | ||
|
|
e3fc67e771 | ||
|
|
113e96bdec | ||
|
|
1342e2a0f8 | ||
|
|
8ac1997149 | ||
|
|
ecefd1cbcc | ||
|
|
4932764a66 | ||
|
|
fe45537037 | ||
|
|
7a15c0c6ac | ||
|
|
97173d4af0 | ||
|
|
bb45198f23 | ||
|
|
6da985a225 | ||
|
|
90e4e230af | ||
|
|
f643df8164 | ||
|
|
3769b24b3a | ||
|
|
53f95945b1 | ||
|
|
30e0631582 | ||
|
|
470f12d232 | ||
|
|
693c1c8c73 | ||
|
|
beff87440f | ||
|
|
c6ad3fe1d2 | ||
|
|
d9a759b78e | ||
|
|
e5c9433a0e | ||
|
|
446a2eef38 | ||
|
|
88d19db6ef | ||
|
|
5a5f708d82 | ||
|
|
c90a35c403 | ||
|
|
982a30effc | ||
|
|
56e4a1e051 | ||
|
|
01dd7461f5 | ||
|
|
542fff8872 | ||
|
|
c7cb3f60b2 | ||
|
|
fac54b415f | ||
|
|
a0f586f9c2 | ||
|
|
ed490b7188 | ||
|
|
2055df8c21 | ||
|
|
b943246373 | ||
|
|
e0f0db5d67 | ||
|
|
aa25d08dcc | ||
|
|
327b2cae1b | ||
|
|
9067908fef | ||
|
|
18c6b03fc1 | ||
|
|
1821fda728 | ||
|
|
4470a4ba82 | ||
|
|
219aef046a | ||
|
|
65ffef7108 | ||
|
|
04a962a69e | ||
|
|
43bb5e474a | ||
|
|
cd677e7787 | ||
|
|
6715df7f44 | ||
|
|
67732348c2 | ||
|
|
e988dc2928 | ||
|
|
eb6bf6d38e | ||
|
|
af86339bf1 | ||
|
|
baa3ff5956 | ||
|
|
f5875f87b1 | ||
|
|
574db42652 | ||
|
|
a6066a092c | ||
|
|
5f2ef4c1c3 | ||
|
|
b6995bb638 | ||
|
|
37e72c129f | ||
|
|
e3a34eb112 | ||
|
|
9b5692a8ab | ||
|
|
7ecac4ab43 | ||
|
|
1cbe28b18c | ||
|
|
c8f417dd8f | ||
|
|
d3f6d6aea3 | ||
|
|
505fbc59ad | ||
|
|
da1da0ec6b | ||
|
|
64eb97f4c3 | ||
|
|
294023e2b2 | ||
|
|
b205c13a6e | ||
|
|
e09cd395a2 | ||
|
|
9bd4674c9b | ||
|
|
c9b3bdfea0 | ||
|
|
9a4515f905 | ||
|
|
6c6991ace8 | ||
|
|
b95f2c296d | ||
|
|
4849cf233e | ||
|
|
73a6adf27c | ||
|
|
6d701c4b06 | ||
|
|
813be0c10f | ||
|
|
2951672548 | ||
|
|
357a03c062 | ||
|
|
10ad3a40c7 | ||
|
|
6c3046c8dc | ||
|
|
dcbfc907fa | ||
|
|
43973608d6 | ||
|
|
25f9b810f2 | ||
|
|
303dea01e9 | ||
|
|
e8bc54ebaa | ||
|
|
6f66fa065a | ||
|
|
a4d3891140 | ||
|
|
d23af55e4b | ||
|
|
2c1f266fba | ||
|
|
7772da8463 | ||
|
|
352973793f | ||
|
|
9fd8099369 | ||
|
|
f6e6f8f570 | ||
|
|
6e697baa46 | ||
|
|
9794dc03f7 | ||
|
|
a3650ebf34 | ||
|
|
853cb5d052 | ||
|
|
1bef47199c | ||
|
|
482e9a2014 | ||
|
|
a43b55ec25 | ||
|
|
7b551fc3e0 | ||
|
|
ae2eb46b29 | ||
|
|
d812caf025 | ||
|
|
8bb7b60728 | ||
|
|
10e9f81dc1 | ||
|
|
7dc457780d | ||
|
|
8fe5798bf5 | ||
|
|
ff5a8c91d6 | ||
|
|
a19afe9b48 | ||
|
|
1cc4ccca8e | ||
|
|
a930d17786 | ||
|
|
1152299583 | ||
|
|
0647cbf171 | ||
|
|
3243714db9 | ||
|
|
55d008a2e8 | ||
|
|
8e10e73e29 | ||
|
|
0b0fc021fb | ||
|
|
60d0e98fc7 | ||
|
|
a2344ea0ae | ||
|
|
db8eb731f1 | ||
|
|
49d4372918 | ||
|
|
0e69c86c51 | ||
|
|
948cad814f | ||
|
|
7a42a15f8a | ||
|
|
9d547b9d16 | ||
|
|
cd6724f754 | ||
|
|
e6e5e521cf | ||
|
|
fe33dfc53f | ||
|
|
595df26e40 | ||
|
|
c817f978a4 | ||
|
|
51b3208b3b | ||
|
|
7594e45937 | ||
|
|
39e82f99e4 | ||
|
|
67407c1119 | ||
|
|
b767df9fad | ||
|
|
6133bcd951 | ||
|
|
47f2971899 | ||
|
|
98dc13fe9c | ||
|
|
c8600e859a | ||
|
|
fdf8eece62 | ||
|
|
4e3da668f4 | ||
|
|
ddec876e36 | ||
|
|
8adef1e13f | ||
|
|
893f40a21d | ||
|
|
67cdb4e792 | ||
|
|
918571c3c6 | ||
|
|
e9a360d750 | ||
|
|
d201ca24f8 | ||
|
|
3c156673d5 | ||
|
|
ba67205052 | ||
|
|
7b7236f05d | ||
|
|
af1f8197d9 | ||
|
|
2b5257bf6b | ||
|
|
54aa88425a | ||
|
|
fc9c164d3d | ||
|
|
440a2e7ac7 | ||
|
|
47a1d24999 | ||
|
|
e7e40c309e | ||
|
|
07679cbf8c | ||
|
|
79ee9c34ca | ||
|
|
91175a3382 | ||
|
|
6414b0d6a9 | ||
|
|
1ffce863d9 | ||
|
|
25425cb940 | ||
|
|
b50270d8f3 | ||
|
|
fac318dc75 | ||
|
|
7dc9ee24db | ||
|
|
64ce86b372 | ||
|
|
9b87100a15 | ||
|
|
1bce37bb56 | ||
|
|
8e07b7d891 | ||
|
|
96c7c0455d | ||
|
|
ada20598d4 | ||
|
|
61654d7c40 | ||
|
|
01983a3810 | ||
|
|
81061c0d0e | ||
|
|
7a1031a990 | ||
|
|
16bbb97ed2 | ||
|
|
b7b9c7e076 | ||
|
|
639aa4aa42 | ||
|
|
d546d50923 | ||
|
|
31d8e4a4b7 | ||
|
|
cfd81737e3 | ||
|
|
acbd085ab4 | ||
|
|
06d68d73ee | ||
|
|
0fd14a837a | ||
|
|
a8e7a3f49d | ||
|
|
fe4f92112d | ||
|
|
f83284d7e5 | ||
|
|
484cac58b7 | ||
|
|
7c94039a7e | ||
|
|
a412b5163a | ||
|
|
e64f8629da | ||
|
|
07fd03e284 | ||
|
|
32f70c103d | ||
|
|
59c3278259 | ||
|
|
152f308966 | ||
|
|
14d009de32 | ||
|
|
04ff8728da | ||
|
|
0fb7c8cd09 | ||
|
|
b352e470c1 | ||
|
|
946304ab76 | ||
|
|
7a95d43f1a | ||
|
|
58bc4acac5 | ||
|
|
22f2ac5b29 | ||
|
|
90e250d376 | ||
|
|
3bfdf8cef9 | ||
|
|
4de856923b | ||
|
|
5ea19a8581 | ||
|
|
b1550f143b | ||
|
|
6f475c0c52 | ||
|
|
4a56e825e4 | ||
|
|
9d1337bf71 | ||
|
|
d57bdfaace | ||
|
|
01abe1007c | ||
|
|
6b9552fcb4 | ||
|
|
d908f95fd7 | ||
|
|
c6c3d66a9b | ||
|
|
cc21317b5a | ||
|
|
e1e6af3cca | ||
|
|
df1a5ab4b6 | ||
|
|
855d304b4f | ||
|
|
3eac700aa2 | ||
|
|
986216f4d6 | ||
|
|
8be9743b80 | ||
|
|
c84c9ddde7 | ||
|
|
942f45179c | ||
|
|
caeec2bdab | ||
|
|
99a4212804 | ||
|
|
a3b8b9fa9f | ||
|
|
c0c3429c75 | ||
|
|
4e57b19112 | ||
|
|
ef9561f16b | ||
|
|
d92a2ca835 | ||
|
|
323fea7451 | ||
|
|
41e8f00509 | ||
|
|
2167a9d700 | ||
|
|
e81f00714c | ||
|
|
f5e997ba92 | ||
|
|
be04fcb179 | ||
|
|
9ccb62286e | ||
|
|
e47c05c58a | ||
|
|
a2caa4cca5 | ||
|
|
e993540ae6 | ||
|
|
65f00640ba | ||
|
|
77327ea856 | ||
|
|
8a71495e1a | ||
|
|
28d928f6e9 | ||
|
|
ce47c73f33 | ||
|
|
388f46b5b9 | ||
|
|
d3d28d551e | ||
|
|
867ef20e51 | ||
|
|
2c05e7bd00 | ||
|
|
db47162367 | ||
|
|
93ea948045 | ||
|
|
7c91418336 | ||
|
|
80962e3079 | ||
|
|
3047d5c495 | ||
|
|
8893a9e21e | ||
|
|
111af93a09 | ||
|
|
d76f296587 | ||
|
|
547d9e5939 | ||
|
|
9daa3441c8 | ||
|
|
f733a54777 | ||
|
|
b6c0cf1820 | ||
|
|
7c495d03ab | ||
|
|
e03a38001d | ||
|
|
15171637cd | ||
|
|
87472ae937 | ||
|
|
2f43adbc07 | ||
|
|
9804b49979 | ||
|
|
1f190c9402 | ||
|
|
c6eb02d4cd | ||
|
|
1456e15d5c | ||
|
|
6ffd0473e5 | ||
|
|
96ef85159b | ||
|
|
ec7e8e0530 | ||
|
|
4d545f8f58 | ||
|
|
4ec2ccac11 | ||
|
|
18b8aa096f | ||
|
|
20be110537 | ||
|
|
7c775571c9 | ||
|
|
7d8e073587 | ||
|
|
f3e11d4c7c | ||
|
|
913b37661f | ||
|
|
d2bb479ac6 | ||
|
|
174c3fc8fa | ||
|
|
24232bf2e9 | ||
|
|
df4cb7688d | ||
|
|
f1db6b488a | ||
|
|
f358d60584 | ||
|
|
9857fe1711 | ||
|
|
b4331a9bf9 | ||
|
|
c08e9d6fa8 | ||
|
|
3424bcf2e9 | ||
|
|
d13769a9f2 | ||
|
|
0c8b8179b2 | ||
|
|
45b4feeb58 | ||
|
|
e420e3d576 | ||
|
|
726e087795 | ||
|
|
b307ad1b26 | ||
|
|
8918b789e9 | ||
|
|
bda118bc55 | ||
|
|
e6baa751a9 | ||
|
|
9025efee77 | ||
|
|
198e952f61 | ||
|
|
3e6e3d28e1 | ||
|
|
5dcf5991ab | ||
|
|
7b18063630 | ||
|
|
1caf73a3e9 | ||
|
|
a330d77d6d | ||
|
|
efd31de051 | ||
|
|
206756e13c | ||
|
|
7326fec611 | ||
|
|
00619751d7 | ||
|
|
a562ea59b8 | ||
|
|
f490886a8b | ||
|
|
a2ca98e0e8 | ||
|
|
2e26ff7b64 | ||
|
|
42751a6b29 | ||
|
|
97daf76f05 | ||
|
|
e8a9c58d18 | ||
|
|
3f4a81b7ad | ||
|
|
1129ba339b | ||
|
|
e2c76d46c7 | ||
|
|
90b78da8c8 | ||
|
|
6eeee8df54 | ||
|
|
6be51a2bab | ||
|
|
2af3892fec | ||
|
|
2a3febcc79 | ||
|
|
ef6f1540dd | ||
|
|
f52ef7db7e | ||
|
|
fe0facfea5 | ||
|
|
28ade775e3 |
@@ -32,7 +32,9 @@ tags:
|
||||
visible: "Photos & Videos"
|
||||
timeout: 10000
|
||||
- assertVisible: "Camera"
|
||||
- assertVisible: "Files"
|
||||
# A server with Files offers "Upload from this device" and "Attach from
|
||||
# Files" instead of a plain "Files".
|
||||
- assertVisible: "Upload from this device|Files"
|
||||
- takeScreenshot: compose-02-attach-sheet
|
||||
- evalScript: ${output.pickPhoto = ORIENTATION != 'landscape'}
|
||||
# The system photo picker runs in another process: XCTest sees none of its
|
||||
|
||||
@@ -11,8 +11,12 @@ tags:
|
||||
direction: DOWN
|
||||
centerElement: true
|
||||
timeout: 20000
|
||||
# Swipe the row itself: the avatar checkbox's label has the subject too, and
|
||||
# a swipe that starts on it only selects the row (#91).
|
||||
- swipe:
|
||||
from: ".*Grüße aus München.*"
|
||||
from:
|
||||
id: email-row
|
||||
text: ".*Grüße aus München.*"
|
||||
direction: LEFT
|
||||
duration: 400
|
||||
# The undo bar shows for 5 s; when it goes, the Compose button slides into
|
||||
@@ -27,14 +31,18 @@ tags:
|
||||
- takeScreenshot: swipe-02-undone
|
||||
|
||||
- swipe:
|
||||
from: ".*Grüße aus München.*"
|
||||
from:
|
||||
id: email-row
|
||||
text: ".*Grüße aus München.*"
|
||||
direction: RIGHT
|
||||
duration: 400
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- takeScreenshot: swipe-03-read-toggled
|
||||
- swipe:
|
||||
from: ".*Grüße aus München.*"
|
||||
from:
|
||||
id: email-row
|
||||
text: ".*Grüße aus München.*"
|
||||
direction: RIGHT
|
||||
duration: 400
|
||||
- waitForAnimationToEnd:
|
||||
|
||||
@@ -7,7 +7,7 @@ tags:
|
||||
---
|
||||
- runFlow: ../subflows/signed-in.yaml
|
||||
|
||||
# Move via More → Move to...
|
||||
# Move via More → Move to
|
||||
- runFlow:
|
||||
file: ../subflows/open-message.yaml
|
||||
env:
|
||||
@@ -17,9 +17,11 @@ tags:
|
||||
visible: "More actions"
|
||||
timeout: 10000
|
||||
- takeScreenshot: actions-01-more
|
||||
- tapOn: "Move to..."
|
||||
# Both the menu row and the sheet title read "Move to" (webmail catalog),
|
||||
# so wait for the folder list instead of the title.
|
||||
- tapOn: "Move to"
|
||||
- extendedWaitUntil:
|
||||
visible: "Move to..."
|
||||
visible: "Projects"
|
||||
timeout: 10000
|
||||
- tapOn:
|
||||
text: "Projects"
|
||||
@@ -63,12 +65,21 @@ tags:
|
||||
direction: DOWN
|
||||
centerElement: true
|
||||
timeout: 20000
|
||||
- longPressOn: ".*Grüße aus München.*"
|
||||
# The row, not its avatar checkbox (whose label has the subject too).
|
||||
- longPressOn:
|
||||
id: email-row
|
||||
text: ".*Grüße aus München.*"
|
||||
- extendedWaitUntil:
|
||||
visible: "Clear selection"
|
||||
timeout: 10000
|
||||
- takeScreenshot: actions-04-selected
|
||||
# Star (or Unstar, when an earlier run left it starred), then toggle back.
|
||||
# On a tablet the list column is narrow and Star sits in the More menu.
|
||||
- runFlow:
|
||||
when:
|
||||
notVisible: "Star|Unstar"
|
||||
commands:
|
||||
- tapOn: "More actions.*"
|
||||
- tapOn: "Star|Unstar"
|
||||
# A bulk action ends selection mode by itself.
|
||||
- runFlow:
|
||||
@@ -76,7 +87,14 @@ tags:
|
||||
visible: "Clear selection"
|
||||
commands:
|
||||
- tapOn: "Clear selection"
|
||||
- longPressOn: ".*Grüße aus München.*"
|
||||
- longPressOn:
|
||||
id: email-row
|
||||
text: ".*Grüße aus München.*"
|
||||
- runFlow:
|
||||
when:
|
||||
notVisible: "Unstar|Star"
|
||||
commands:
|
||||
- tapOn: "More actions.*"
|
||||
- tapOn: "Unstar|Star"
|
||||
- runFlow:
|
||||
when:
|
||||
|
||||
@@ -48,9 +48,11 @@ tags:
|
||||
- extendedWaitUntil:
|
||||
visible: "Keep scheduled"
|
||||
timeout: 10000
|
||||
# The dialog's button (below its message), not its title or the button
|
||||
# behind it. Cancelling now moves the message back to Drafts.
|
||||
- tapOn:
|
||||
text: "Cancel send"
|
||||
below: "Keep scheduled|.*will not be delivered.*"
|
||||
below: ".*will not be (sent|delivered).*"
|
||||
- extendedWaitUntil:
|
||||
notVisible: ".*${output.subject}.*"
|
||||
timeout: 30000
|
||||
|
||||
@@ -34,7 +34,12 @@ tags:
|
||||
timeout: 5000
|
||||
- takeScreenshot: calendar-04-week
|
||||
|
||||
# Create
|
||||
# Create. The button starts the event on the selected day: make that today
|
||||
# (a stray tap, such as one through the notification prompt, can select
|
||||
# another day), so the schedule below finds it after Go to today.
|
||||
- tapOn: "Go to today"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- tapOn: "New event"
|
||||
- extendedWaitUntil:
|
||||
visible: "Save"
|
||||
@@ -46,6 +51,19 @@ tags:
|
||||
- pressKey: Enter
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
# A new event starts on the calendar's default reminders; Customize turns
|
||||
# them into the event's own list, which has Add reminder.
|
||||
- scrollUntilVisible:
|
||||
element: "Customize|Add reminder"
|
||||
direction: DOWN
|
||||
timeout: 10000
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Customize"
|
||||
commands:
|
||||
- tapOn: "Customize"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- scrollUntilVisible:
|
||||
element: "Add reminder"
|
||||
direction: DOWN
|
||||
|
||||
@@ -44,20 +44,22 @@ tags:
|
||||
timeout: 20000
|
||||
- takeScreenshot: files-04-folder-created
|
||||
- longPressOn: ".*${output.folder}.*"
|
||||
# Delete moves it to the trash at once, with Undo and no question (like the
|
||||
# webmail). A tap while the actions sheet still slides in can miss: tap
|
||||
# again while Delete is still there.
|
||||
- extendedWaitUntil:
|
||||
visible: "Delete"
|
||||
timeout: 10000
|
||||
- repeat:
|
||||
while:
|
||||
notVisible: "Delete this item.*"
|
||||
visible: "Delete"
|
||||
times: 3
|
||||
commands:
|
||||
- tapOn: "Delete"
|
||||
- tapOn:
|
||||
text: "Delete"
|
||||
optional: true
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- extendedWaitUntil:
|
||||
visible: "Delete this item.*"
|
||||
timeout: 10000
|
||||
- tapOn:
|
||||
text: "Delete"
|
||||
below: "Delete this item.*"
|
||||
- extendedWaitUntil:
|
||||
notVisible: ".*${output.folder}.*"
|
||||
timeout: 20000
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# Every settings page opens and shows its content.
|
||||
# Every settings page opens and shows its content, first half (13-settings-more
|
||||
# has the rest: one flow for all 24 pages ran into the 15-minute flow limit on
|
||||
# the iPad simulator).
|
||||
appId: org.bulwarkmail.mobile
|
||||
name: Settings pages
|
||||
tags:
|
||||
@@ -11,6 +13,11 @@ tags:
|
||||
visible: "Search settings"
|
||||
timeout: 20000
|
||||
- takeScreenshot: settings-00-list
|
||||
# The search also lists the settings inside each page under it, so a common
|
||||
# word ("Folders", "Calendar") pushes the page's row below the keyboard.
|
||||
# Each Q matches its page near the top. A PROOF the search lists under the
|
||||
# row would pass on the iPad without the page, so those differ. The Themes
|
||||
# cards are one element each, named by the theme: check the page's text.
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: account, ROW: ".*@example.org, .*", PROOF: "Logged-in accounts|Display Name"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: language, ROW: "Language, Region & Time, .*", Q: "Language", PROOF: "Date Format"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: notifications, ROW: "Notifications, .*", Q: "Notifications", PROOF: "Email Notifications"}}
|
||||
@@ -22,17 +29,5 @@ tags:
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: vacation, ROW: "Out of Office, .*", Q: "Office", PROOF: "Reply automatically"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: filters, ROW: "Filters, .*", Q: "Filters", PROOF: "Email Filters"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: templates, ROW: "Templates, .*", Q: "Templates", PROOF: "Email Templates"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: folders, ROW: "Folders, .*", Q: "Folders", PROOF: ".*Inbox.*"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: tags, ROW: "Tags, .*", Q: "Tags", PROOF: "Email Tags"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: downloads, ROW: "Downloads, .*", Q: "Downloads", PROOF: "Email .*eml.* filename"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: security, ROW: "Security, .*", Q: "Security", PROOF: "Account Security|Detecting server capabilities.*"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: content, ROW: "Content & Senders, .*", Q: "Content", PROOF: "External Content"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: calendar, ROW: "Calendar, .*", Q: "Calendar", PROOF: "Default view"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: contacts, ROW: "Contacts, .*", Q: "Contacts", PROOF: "Group by first letter"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: files, ROW: "Files, .*", Q: "Files", PROOF: "Default View"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: sidebar-apps, ROW: "Sidebar Apps, .*", Q: "Sidebar", PROOF: "Keep Apps Loaded"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: themes, ROW: "Themes, .*", Q: "Themes", PROOF: "The default light and dark palettes."}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: storage, ROW: "Storage & backup, .*", Q: "Storage", PROOF: "Offline mail.*"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: about, ROW: "About, .*", Q: "About", PROOF: "This device"}}
|
||||
- tapOn:
|
||||
id: tab-mail
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Every settings page opens and shows its content, second half (see 12-settings).
|
||||
appId: org.bulwarkmail.mobile
|
||||
name: Settings pages, continued
|
||||
tags:
|
||||
- features
|
||||
---
|
||||
- runFlow: ../subflows/signed-in.yaml
|
||||
- tapOn:
|
||||
id: tab-settings
|
||||
- extendedWaitUntil:
|
||||
visible: "Search settings"
|
||||
timeout: 20000
|
||||
- takeScreenshot: settings-00-list-more
|
||||
# The search also lists the settings inside each page under it, so a common
|
||||
# word ("Folders", "Calendar") pushes the page's row below the keyboard.
|
||||
# Each Q matches its page near the top. A PROOF the search lists under the
|
||||
# row would pass on the iPad without the page, so those differ. The Themes
|
||||
# cards are one element each, named by the theme: check the page's text.
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: folders, ROW: "Folders, .*", Q: "New folder", PROOF: ".*Inbox.*"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: tags, ROW: "Tags, .*", Q: "Tags", PROOF: "Email Tags"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: downloads, ROW: "Downloads, .*", Q: "Downloads", PROOF: "Email .*eml.* filename"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: security, ROW: "Security, .*", Q: "Security", PROOF: "Account Security|Detecting server capabilities.*"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: content, ROW: "Content & Senders, .*", Q: "Content &", PROOF: "External Content"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: calendar, ROW: "Calendar, .*", Q: "Week start", PROOF: "Default view"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: contacts, ROW: "Contacts, .*", Q: "Group by", PROOF: "Sort by last name"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: files, ROW: "Files, .*", Q: "Files", PROOF: "Default View"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: sidebar-apps, ROW: "Sidebar Apps, .*", Q: "Sidebar", PROOF: "Keep Apps Loaded"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: themes, ROW: "Themes, .*", Q: "Themes", PROOF: "Pick a colou?r theme.*"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: storage, ROW: "Storage & backup, .*", Q: "Storage", PROOF: "Offline mail.*"}}
|
||||
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: about, ROW: "About, .*", Q: "About", PROOF: "This device"}}
|
||||
- tapOn:
|
||||
id: tab-mail
|
||||
@@ -10,8 +10,11 @@ tags:
|
||||
env:
|
||||
CAMERA: deny
|
||||
- tapOn: "Scan a sign-in code.*"
|
||||
# Denied for good: "Camera access is disabled..."; not asked yet (the
|
||||
# simulator doesn't always apply the denial up front): "Bulwark Mail needs
|
||||
# camera access..." (English catalog) or the built-in fallback wording.
|
||||
- extendedWaitUntil:
|
||||
visible: "Camera access is.*|Scanning a sign-in QR code uses the camera.*"
|
||||
visible: "Camera access is.*|.*needs camera access.*|Scanning a sign-in QR code uses the camera.*"
|
||||
timeout: 15000
|
||||
- takeScreenshot: scanner-01-denied
|
||||
- tapOn: "Close"
|
||||
|
||||
@@ -11,9 +11,13 @@ appId: org.bulwarkmail.mobile
|
||||
- setOrientation: PORTRAIT
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
# Maestro repeats a tap that seems to change nothing; here the repeat
|
||||
# landed on the app once the alert was gone (a day in the calendar's
|
||||
# month grid, which opened that day). Tap once.
|
||||
- tapOn:
|
||||
text: "Allow"
|
||||
optional: true
|
||||
retryTapIfNoChange: false
|
||||
- runFlow:
|
||||
when:
|
||||
true: ${ORIENTATION == 'landscape'}
|
||||
|
||||
@@ -18,3 +18,14 @@ appId: org.bulwarkmail.mobile
|
||||
- tapOn:
|
||||
text: "Continue"
|
||||
below: "Sign in with a link."
|
||||
# A tap while the alert is still animating in can go nowhere: tap again if
|
||||
# it is still up.
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Sign in with a link."
|
||||
commands:
|
||||
- tapOn:
|
||||
text: "Continue"
|
||||
below: "Sign in with a link."
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
# covers it (the tap then opens another tab): on the phone, scroll it to the
|
||||
# middle. The iPad's list is a column Maestro's scroll (in the middle of the
|
||||
# screen) doesn't reach; its rows fit on screen.
|
||||
# The avatar's "Select <sender>: <subject>" checkbox mentions the subject
|
||||
# too, and a tap on it selects the row instead of opening it (#91): match
|
||||
# the row itself by its id.
|
||||
appId: org.bulwarkmail.mobile
|
||||
---
|
||||
- runFlow:
|
||||
@@ -10,7 +13,9 @@ appId: org.bulwarkmail.mobile
|
||||
true: ${DEVICE != 'tablet'}
|
||||
commands:
|
||||
- scrollUntilVisible:
|
||||
element: ".*${SUBJECT}.*"
|
||||
element:
|
||||
id: email-row
|
||||
text: ".*${SUBJECT}.*"
|
||||
direction: DOWN
|
||||
centerElement: true
|
||||
visibilityPercentage: 100
|
||||
@@ -20,13 +25,17 @@ appId: org.bulwarkmail.mobile
|
||||
true: ${DEVICE == 'tablet'}
|
||||
commands:
|
||||
- scrollUntilVisible:
|
||||
element: ".*${SUBJECT}.*"
|
||||
element:
|
||||
id: email-row
|
||||
text: ".*${SUBJECT}.*"
|
||||
direction: DOWN
|
||||
timeout: 30000
|
||||
# A tap while the list still moves lands on another row.
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 5000
|
||||
- tapOn: ".*${SUBJECT}.*"
|
||||
- tapOn:
|
||||
id: email-row
|
||||
text: ".*${SUBJECT}.*"
|
||||
# The message toolbar's "more" button: "Reply All" shows on the phone, but
|
||||
# the iPad pane has a quick reply box instead for mail to one recipient.
|
||||
- extendedWaitUntil:
|
||||
|
||||
@@ -18,10 +18,12 @@ appId: org.bulwarkmail.mobile
|
||||
- inputText: ${Q}
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
# Now and then a typed character goes missing: type it again.
|
||||
- runFlow:
|
||||
when:
|
||||
# Now and then the simulator drops or garbles a typed character
|
||||
# (seen: "Storagew,"): type it again, a few times if need be.
|
||||
- repeat:
|
||||
while:
|
||||
notVisible: ${ROW}
|
||||
times: 3
|
||||
commands:
|
||||
- tapOn: "Search settings"
|
||||
- eraseText: 40
|
||||
|
||||
@@ -25,6 +25,14 @@ appId: org.bulwarkmail.mobile
|
||||
element: "Sign out"
|
||||
direction: DOWN
|
||||
timeout: 30000
|
||||
# The scroll can stop with the button half under the tab bar, where
|
||||
# a tap reaches the tab bar instead: scroll on to the end of the list.
|
||||
- swipe:
|
||||
start: 50%,70%
|
||||
end: 50%,30%
|
||||
duration: 400
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- tapOn: "Sign out"
|
||||
- extendedWaitUntil:
|
||||
visible: "The account is removed from this device.*"
|
||||
|
||||
@@ -26,3 +26,13 @@ appId: org.bulwarkmail.mobile
|
||||
- tapOn:
|
||||
text: "Sign in"
|
||||
index: 1
|
||||
# Stalwart 1.0.1 asks for consent after Sign in, once per client grant;
|
||||
# 0.16 and repeat sign-ins go straight back to the app.
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 5000
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Allow"
|
||||
commands:
|
||||
- takeScreenshot: stalwart-consent-page
|
||||
- tapOn: "Allow"
|
||||
|
||||
@@ -4,10 +4,15 @@
|
||||
# field and type again. The caret is still at the end of what was just
|
||||
# typed, so eraseText (which deletes before the caret) empties it; selecting
|
||||
# the text with a long press didn't work and appended instead. SECRET=yes
|
||||
# skips the check (a password field shows dots).
|
||||
# skips the check (a password field shows dots). TAP=no types into the
|
||||
# field that has the focus already.
|
||||
appId: org.bulwarkmail.mobile
|
||||
---
|
||||
- tapOn: ${HINT}
|
||||
- runFlow:
|
||||
when:
|
||||
true: ${TAP != 'no'}
|
||||
commands:
|
||||
- tapOn: ${HINT}
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- inputText: ${TEXT}
|
||||
|
||||
@@ -11,12 +11,25 @@ appId: org.bulwarkmail.mobile
|
||||
env:
|
||||
HINT: "user@example.com"
|
||||
TEXT: ${LOGIN}
|
||||
# On the iPhone, Safari's keyboard toolbar (Previous, Next, Done) can sit
|
||||
# over the password field, and a tap there leaves the focus in Email (the
|
||||
# password then went into Email). Its Next moves the focus to the password.
|
||||
- evalScript: ${output.webNext = false}
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Next"
|
||||
commands:
|
||||
- tapOn: "Next"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- evalScript: ${output.webNext = true}
|
||||
- runFlow:
|
||||
file: type-web.yaml
|
||||
env:
|
||||
HINT: "Enter your password"
|
||||
TEXT: ${PASSWORD}
|
||||
SECRET: "yes"
|
||||
TAP: "${output.webNext ? 'no' : 'yes'}"
|
||||
- tapOn: "Sign in"
|
||||
- runFlow:
|
||||
when:
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Mail, Calendar, BookUser, HardDrive, Settings } from './src/components/
|
||||
|
||||
import { startLiveUpdates, type LiveUpdatesHandle } from './src/api/push-stream';
|
||||
import { jmapClient } from './src/api/jmap-client';
|
||||
import { pruneEmailSubmissions } from './src/api/submissions';
|
||||
import type { StateChange } from './src/api/types';
|
||||
import { dispatchStateChange } from './src/lib/state-change-bus';
|
||||
import {
|
||||
@@ -34,6 +35,7 @@ import {
|
||||
getInitialNotificationTap,
|
||||
getStoredRelayBaseUrl,
|
||||
notificationTapJmapAccountId,
|
||||
renewOtherPushAccounts,
|
||||
resyncPushNotifications,
|
||||
teardownPushNotificationsForAccount,
|
||||
type NotificationTapPayload,
|
||||
@@ -43,20 +45,14 @@ import { addUnifiedPushEndpointListener } from './src/lib/unified-push';
|
||||
// Pause between a mute change and the push-filter update it sends (#78).
|
||||
const MUTES_RESYNC_DELAY_MS = 1_000;
|
||||
import type { MainTabsParamList, RootStackParamList } from './src/navigation/types';
|
||||
import ComposeScreen from './src/screens/ComposeScreen';
|
||||
import EmailThreadScreen from './src/screens/EmailThreadScreen';
|
||||
import EmailSourceScreen from './src/screens/EmailSourceScreen';
|
||||
import LoginScreen from './src/screens/LoginScreen';
|
||||
import MailTab from './src/screens/MailTab';
|
||||
import FilesScreen from './src/screens/FilesScreen';
|
||||
import CalendarScreen from './src/screens/CalendarScreen';
|
||||
import ContactsTab from './src/screens/ContactsTab';
|
||||
import ContactDetailScreen from './src/screens/ContactDetailScreen';
|
||||
import ContactFormScreen from './src/screens/ContactFormScreen';
|
||||
import GroupDetailScreen from './src/screens/GroupDetailScreen';
|
||||
import SettingsScreen from './src/screens/SettingsScreen';
|
||||
import ScheduledScreen from './src/screens/ScheduledScreen';
|
||||
import UnifiedInboxScreen from './src/screens/UnifiedInboxScreen';
|
||||
// Screens that aren't on the way to the inbox load on first visit
|
||||
// (getComponent below), so their modules stay out of the startup work.
|
||||
import { useAccountStore } from './src/stores/account-store';
|
||||
import { useAuthStore } from './src/stores/auth-store';
|
||||
import { useCalendarStore } from './src/stores/calendar-store';
|
||||
@@ -65,6 +61,8 @@ import { useEmailStore } from './src/stores/email-store';
|
||||
import { loadDetail, prefetchMessage } from './src/lib/email-detail-cache';
|
||||
import { useHasCalendar, useHasContacts, useHasFiles } from './src/lib/capabilities';
|
||||
import { useSettingsStore } from './src/stores/settings-store';
|
||||
import { startPolicySync, usePolicyFeature } from './src/stores/policy-store';
|
||||
import { startScreenPrivacySync } from './src/lib/screen-privacy';
|
||||
import { useLocaleStore } from './src/stores/locale-store';
|
||||
import { useLayout } from './src/lib/use-layout';
|
||||
import { toast } from './src/stores/toast-store';
|
||||
@@ -80,10 +78,12 @@ import { signOutWidgets, startWidgetSync } from './src/widgets/sync';
|
||||
import { draftContextFromEmail } from './src/lib/draft-context';
|
||||
import {
|
||||
acceptSignInLink,
|
||||
enterLinkAccount,
|
||||
handleDeepLink,
|
||||
parseDeepLink,
|
||||
routeParkedSignInLink,
|
||||
shareToDeepLink,
|
||||
type AccountSwitchResult,
|
||||
type DeepLink,
|
||||
} from './src/navigation/linking';
|
||||
import { usePendingSignInLinkStore } from './src/navigation/pending-sign-in-link';
|
||||
@@ -105,20 +105,26 @@ const navigationRef = createNavigationContainerRef<RootStackParamList>();
|
||||
// on every getInitialURL(), and a sign-in link must not run twice.
|
||||
let initialSignInLinkRead = false;
|
||||
|
||||
// Move to the account a notification, reminder or link names. An account
|
||||
// that was signed out meanwhile has nothing safe to open.
|
||||
async function switchToAccount(accountId: string): Promise<AccountSwitchResult> {
|
||||
const auth = useAuthStore.getState();
|
||||
if (auth.activeAccountId === accountId) return true;
|
||||
if (!useAccountStore.getState().getAccountById(accountId)) return false;
|
||||
await auth.switchAccount(accountId);
|
||||
return useAuthStore.getState().activeAccountId === accountId ? 'switched' : false;
|
||||
}
|
||||
|
||||
async function navigateToNotificationTap(payload: NotificationTapPayload): Promise<void> {
|
||||
if (!navigationRef.isReady()) return;
|
||||
|
||||
// The notification carries the account it was generated for. If the user
|
||||
// has since switched to a different account (or had a different one active
|
||||
// when the notification arrived), opening EmailThread under the active
|
||||
// account would fetch the email from the wrong server and fail.
|
||||
const auth = useAuthStore.getState();
|
||||
if (payload.accountId && payload.accountId !== auth.activeAccountId) {
|
||||
const account = useAccountStore.getState().getAccountById(payload.accountId);
|
||||
if (!account) return; // account was logged out — nothing safe to open.
|
||||
await auth.switchAccount(payload.accountId);
|
||||
if (useAuthStore.getState().activeAccountId !== payload.accountId) return;
|
||||
}
|
||||
// account would fetch the email from the wrong server and fail. After a
|
||||
// switch the stack starts over on that account's inbox, so Back from the
|
||||
// message lands there instead of on the old account's screens.
|
||||
if (!(await enterLinkAccount(navigationRef, payload.accountId, switchToAccount))) return;
|
||||
|
||||
prefetchMessage({ id: payload.emailId, threadId: payload.threadId });
|
||||
navigationRef.navigate('EmailThread', {
|
||||
@@ -136,12 +142,7 @@ async function navigateToNotificationTap(payload: NotificationTapPayload): Promi
|
||||
// under the account it was scheduled for. The target is parked for
|
||||
// CalendarScreen, which also picks it up when it mounts after a cold start.
|
||||
async function openCalendarReminder(target: CalendarReminderTarget): Promise<void> {
|
||||
const auth = useAuthStore.getState();
|
||||
if (target.appAccountId && target.appAccountId !== auth.activeAccountId) {
|
||||
if (!useAccountStore.getState().getAccountById(target.appAccountId)) return;
|
||||
await auth.switchAccount(target.appAccountId);
|
||||
if (useAuthStore.getState().activeAccountId !== target.appAccountId) return;
|
||||
}
|
||||
if (!(await enterLinkAccount(navigationRef, target.appAccountId, switchToAccount))) return;
|
||||
setPendingCalendarOpen(target);
|
||||
// On a cold start the navigator mounts right after the auth gate flips.
|
||||
for (let attempt = 0; attempt < 50 && !navigationRef.isReady(); attempt++) {
|
||||
@@ -176,13 +177,7 @@ async function openDeepLink(link: DeepLink): Promise<void> {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
switchAccount: async (accountId) => {
|
||||
const auth = useAuthStore.getState();
|
||||
if (auth.activeAccountId === accountId) return true;
|
||||
if (!useAccountStore.getState().getAccountById(accountId)) return false;
|
||||
await auth.switchAccount(accountId);
|
||||
return useAuthStore.getState().activeAccountId === accountId;
|
||||
},
|
||||
switchAccount: switchToAccount,
|
||||
});
|
||||
// A widget or a link elsewhere brought the app up for this; say it failed
|
||||
// rather than leave the user wondering why the app just opened.
|
||||
@@ -238,6 +233,12 @@ function MainTabsNavigator({ navigation }: NativeStackScreenProps<RootStackParam
|
||||
const hasContacts = useHasContacts();
|
||||
const hasFiles = useHasFiles();
|
||||
const disabledTabStyle = { opacity: 0.4 } as const;
|
||||
// The organisation policy can turn a whole area off (webmail feature
|
||||
// gates); its tab goes away rather than showing as unavailable.
|
||||
const calendarAllowed = usePolicyFeature('calendarEnabled');
|
||||
const contactsAllowed = usePolicyFeature('contactsEnabled');
|
||||
const filesAllowed = usePolicyFeature('filesEnabled');
|
||||
const hiddenTab = { tabBarItemStyle: { display: 'none' as const }, tabBarButton: () => null };
|
||||
const t = useLocaleStore((state) => state.t);
|
||||
// The webmail's navigation names; a tab the server lacks says so.
|
||||
const tabLabels = {
|
||||
@@ -323,49 +324,52 @@ function MainTabsNavigator({ navigation }: NativeStackScreenProps<RootStackParam
|
||||
</Tab.Screen>
|
||||
<Tab.Screen
|
||||
name="Calendar"
|
||||
component={CalendarScreen}
|
||||
getComponent={() => require('./src/screens/CalendarScreen').default}
|
||||
options={{
|
||||
title: tabLabels.calendar,
|
||||
tabBarButtonTestID: 'tab-calendar',
|
||||
tabBarIcon: ({ color, size }) => <Calendar size={size} color={color} />,
|
||||
tabBarItemStyle: hasCalendar ? undefined : disabledTabStyle,
|
||||
tabBarAccessibilityLabel: hasCalendar ? tabLabels.calendar : unavailable(tabLabels.calendar),
|
||||
...(calendarAllowed ? null : hiddenTab),
|
||||
}}
|
||||
listeners={{
|
||||
tabPress: (e) => {
|
||||
if (!hasCalendar) e.preventDefault();
|
||||
if (!hasCalendar || !calendarAllowed) e.preventDefault();
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Contacts"
|
||||
component={ContactsTab}
|
||||
getComponent={() => require('./src/screens/ContactsTab').default}
|
||||
options={{
|
||||
title: tabLabels.contacts,
|
||||
tabBarButtonTestID: 'tab-contacts',
|
||||
tabBarIcon: ({ color, size }) => <BookUser size={size} color={color} />,
|
||||
tabBarItemStyle: hasContacts ? undefined : disabledTabStyle,
|
||||
tabBarAccessibilityLabel: hasContacts ? tabLabels.contacts : unavailable(tabLabels.contacts),
|
||||
...(contactsAllowed ? null : hiddenTab),
|
||||
}}
|
||||
listeners={{
|
||||
tabPress: (e) => {
|
||||
if (!hasContacts) e.preventDefault();
|
||||
if (!hasContacts || !contactsAllowed) e.preventDefault();
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Files"
|
||||
component={FilesScreen}
|
||||
getComponent={() => require('./src/screens/FilesScreen').default}
|
||||
options={{
|
||||
title: tabLabels.files,
|
||||
tabBarButtonTestID: 'tab-files',
|
||||
tabBarIcon: ({ color, size }) => <HardDrive size={size} color={color} />,
|
||||
tabBarItemStyle: hasFiles ? undefined : disabledTabStyle,
|
||||
tabBarAccessibilityLabel: hasFiles ? tabLabels.files : unavailable(tabLabels.files),
|
||||
...(filesAllowed ? null : hiddenTab),
|
||||
}}
|
||||
listeners={{
|
||||
tabPress: (e) => {
|
||||
if (!hasFiles) e.preventDefault();
|
||||
if (!hasFiles || !filesAllowed) e.preventDefault();
|
||||
},
|
||||
}}
|
||||
/>
|
||||
@@ -428,7 +432,17 @@ export default function App() {
|
||||
// Device sync (Android, #34): live changes, the app's own edits and the
|
||||
// foreground ask Android to sync; accounts are checked against Android's.
|
||||
startDeviceSyncTriggers();
|
||||
return useNetworkStore.getState().init();
|
||||
// The organisation policy of the active account's webmail: the cached
|
||||
// one at once, a fresh one on start and on every account switch.
|
||||
const stopPolicySync = startPolicySync();
|
||||
// Settings > Security > Block screenshots.
|
||||
const stopScreenPrivacy = startScreenPrivacySync();
|
||||
const stopNetwork = useNetworkStore.getState().init();
|
||||
return () => {
|
||||
stopPolicySync();
|
||||
stopScreenPrivacy();
|
||||
stopNetwork?.();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Home-screen widgets follow the signed-in data; once the last account is
|
||||
@@ -641,18 +655,39 @@ export default function App() {
|
||||
return () => { cancelled = true; };
|
||||
}, [addAccountForLink]);
|
||||
|
||||
// The server refused the stored sign-in of an account (auth store
|
||||
// keepForSignIn): open the sign-in to it, filled in, over what is cached.
|
||||
const reauthTarget = useAuthStore((s) => s.reauth);
|
||||
React.useEffect(() => {
|
||||
if (!isAuthenticated || !reauthTarget) return;
|
||||
let cancelled = false;
|
||||
void (async () => {
|
||||
for (let attempt = 0; attempt < 50 && !navigationRef.isReady(); attempt++) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
if (cancelled) return;
|
||||
}
|
||||
if (cancelled || !navigationRef.isReady()) return;
|
||||
if (navigationRef.getCurrentRoute()?.name === 'AddAccount') return;
|
||||
navigationRef.navigate('AddAccount');
|
||||
})();
|
||||
return () => { cancelled = true; };
|
||||
}, [isAuthenticated, reauthTarget]);
|
||||
|
||||
// Re-register the device with the configured relay once authenticated,
|
||||
// and whenever the FCM token rotates. Honours the user's notification
|
||||
// preference - flipping it off tears down THIS account's subscription so
|
||||
// notifications stop arriving for it. Other logged-in accounts keep their
|
||||
// setups intact. An account the user turned push off for, or whose device
|
||||
// registration was revoked, stays off (see resyncPushNotifications).
|
||||
// setups intact. An account the user turned push off for on this device
|
||||
// stays off (see resyncPushNotifications).
|
||||
const emailNotificationsEnabled = useSettingsStore(
|
||||
(s) => s.emailNotificationsEnabled,
|
||||
);
|
||||
const activeAccountId = useAuthStore((s) => s.activeAccountId);
|
||||
React.useEffect(() => {
|
||||
if (!isAuthenticated || !client) return;
|
||||
// The setup asks the server through the client: it waits for the
|
||||
// session, and runs again once one comes up (an account added or
|
||||
// switched to, a cold start that began offline).
|
||||
if (!isAuthenticated || !client || !haveLiveSession) return;
|
||||
|
||||
let cancelled = false;
|
||||
const doSetup = async () => {
|
||||
@@ -671,13 +706,16 @@ export default function App() {
|
||||
relayBaseUrl,
|
||||
accountLabel: client.username ?? undefined,
|
||||
});
|
||||
if (cancelled) return;
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
'[push] relay setup failed:',
|
||||
error instanceof Error ? error.message : error,
|
||||
);
|
||||
}
|
||||
if (cancelled) return;
|
||||
// The other signed-in accounts' subscriptions expire too (webmail #1054); each
|
||||
// is renewed through a client of its own.
|
||||
await renewOtherPushAccounts().catch(() => undefined);
|
||||
};
|
||||
|
||||
void doSetup();
|
||||
@@ -695,7 +733,7 @@ export default function App() {
|
||||
unsubscribe();
|
||||
unsubscribeUp();
|
||||
};
|
||||
}, [client, isAuthenticated, emailNotificationsEnabled, activeAccountId]);
|
||||
}, [client, isAuthenticated, emailNotificationsEnabled, activeAccountId, haveLiveSession]);
|
||||
|
||||
// Muting a folder or account changes the server-side push filter (#78),
|
||||
// which the setup above would only send at the next launch. Send it now,
|
||||
@@ -703,8 +741,10 @@ export default function App() {
|
||||
// content, since hydration hands back an equal but new object; the value
|
||||
// seen first after hydration is the baseline, not a change.
|
||||
const notificationMutes = useSettingsStore((s) => s.notificationMutes);
|
||||
// The Inbox-only switch changes the same filter (webmail #983).
|
||||
const pushInboxOnly = useSettingsStore((s) => s.pushNotifyInboxOnly);
|
||||
const settingsHydrated = useSettingsStore((s) => s.hydrated);
|
||||
const mutesKey = JSON.stringify(notificationMutes);
|
||||
const mutesKey = JSON.stringify([notificationMutes, pushInboxOnly]);
|
||||
const lastMutesKey = React.useRef<string | null>(null);
|
||||
React.useEffect(() => {
|
||||
if (!settingsHydrated) return;
|
||||
@@ -787,6 +827,7 @@ export default function App() {
|
||||
void email.fetchMailboxes();
|
||||
if (email.currentMailboxId) void email.refreshEmails();
|
||||
void useOutboxStore.getState().flush();
|
||||
void pruneEmailSubmissions();
|
||||
};
|
||||
|
||||
// Foreground liveness for the per-account connection dot: the open
|
||||
@@ -807,6 +848,11 @@ export default function App() {
|
||||
});
|
||||
|
||||
void start();
|
||||
// Stalwart 1.0 stops accepting sends once an account holds
|
||||
// `maxSubmissions` submission records, so remove the ones that can no
|
||||
// longer be cancelled (throttled per account; a resume or a send runs it
|
||||
// again).
|
||||
void pruneEmailSubmissions();
|
||||
|
||||
const subscription = AppState.addEventListener('change', (state) => {
|
||||
const nowActive = state === 'active';
|
||||
@@ -873,10 +919,10 @@ export default function App() {
|
||||
<Stack.Navigator screenOptions={{ headerShown: false, freezeOnBlur: true }} screenLayout={withHosts}>
|
||||
<Stack.Screen name="MainTabs" component={MainTabsNavigator} />
|
||||
<Stack.Screen name="EmailThread" component={EmailThreadScreen} />
|
||||
<Stack.Screen name="EmailSource" component={EmailSourceScreen} />
|
||||
<Stack.Screen name="EmailSource" getComponent={() => require('./src/screens/EmailSourceScreen').default} />
|
||||
<Stack.Screen
|
||||
name="Compose"
|
||||
component={ComposeScreen}
|
||||
getComponent={() => require('./src/screens/ComposeScreen').default}
|
||||
// A tablet shows the composer as a panel over the screen it came
|
||||
// from, so that screen stays drawn behind the dimmed backdrop.
|
||||
options={isSplit
|
||||
@@ -886,15 +932,15 @@ export default function App() {
|
||||
<Stack.Screen name="ContactDetail" component={ContactDetailScreen} />
|
||||
<Stack.Screen
|
||||
name="ContactForm"
|
||||
component={ContactFormScreen}
|
||||
getComponent={() => require('./src/screens/ContactFormScreen').default}
|
||||
options={{
|
||||
presentation: 'modal',
|
||||
animation: 'slide_from_bottom',
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen name="GroupDetail" component={GroupDetailScreen} />
|
||||
<Stack.Screen name="Scheduled" component={ScheduledScreen} />
|
||||
<Stack.Screen name="UnifiedInbox" component={UnifiedInboxScreen} />
|
||||
<Stack.Screen name="Scheduled" getComponent={() => require('./src/screens/ScheduledScreen').default} />
|
||||
<Stack.Screen name="UnifiedInbox" getComponent={() => require('./src/screens/UnifiedInboxScreen').default} />
|
||||
<Stack.Screen
|
||||
name="AddAccount"
|
||||
options={{
|
||||
|
||||
+4
-2
@@ -98,9 +98,11 @@ the audit's "Fix pass, 2026-09-24" section.
|
||||
| 07 | Filters (Sieve), vacation responder, Files | [docs/parity/07-filters-vacation-files.md](docs/parity/07-filters-vacation-files.md) | 32 | 29 | 3 | 3 | 8 | 21 |
|
||||
| 08 | Settings, sync, push, i18n, themes, updates, misc UI | [docs/parity/08-settings-push-i18n-ui.md](docs/parity/08-settings-push-i18n-ui.md) | 46 | 38 | 8 | 0 | 14 | 32 |
|
||||
| 09 | JMAP client core, live sync, offline, security, S/MIME | [docs/parity/09-jmap-core-sync-security.md](docs/parity/09-jmap-core-sync-security.md) | 56 | 54 | 2 | 7 | 23 | 26 |
|
||||
| | **Total** | | **415** | **380** | **35** | **32** | **138** | **244** |
|
||||
| 10 | Webmail 1.10.0 to 1.18.0 and Unreleased (all areas) | [docs/parity/10-webmail-1.10-to-1.18.md](docs/parity/10-webmail-1.10-to-1.18.md) | 112 | 52 | 60 | 10 | 40 | 62 |
|
||||
| | **Total** | | **527** | **432** | **95** | **42** | **178** | **306** |
|
||||
|
||||
Counts are of the `- [ ]` and `- [x]` items per file as of 2026-09-24. Done and
|
||||
Counts are of the `- [ ]` and `- [x]` items per file as of 2026-09-24 (row 10:
|
||||
2026-10-29). Done and
|
||||
Open split them by tick; the P columns count the priority tags on those items
|
||||
(one item carries none).
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" tools:node="remove"/>
|
||||
<!-- expo-camera declares the microphone for video; QR scanning never records audio. -->
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove"/>
|
||||
<!-- expo-screen-capture registers its screenshot listener on Android 14+ as soon
|
||||
as it loads; without this permission the app crashes at launch. -->
|
||||
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<!-- Device sync (#34): contacts and calendars in Android's providers, driven by the sync framework. -->
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
@@ -47,6 +50,13 @@
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="bulwarkmail"/>
|
||||
</intent-filter>
|
||||
<!-- Browser sign-in callback of the client the app registers at Stalwart 1.0 (org.bulwarkmail.mobile:/auth/callback). -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="org.bulwarkmail.mobile"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.intent.action.SENDTO"/>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,107 @@
|
||||
The reading fonts in this folder (Settings > Accessibility > Font) are
|
||||
licensed under the SIL Open Font License, Version 1.1, copied below.
|
||||
|
||||
AtkinsonHyperlegibleNext*.ttf:
|
||||
Copyright 2020-2024 The Atkinson Hyperlegible Next Project Authors
|
||||
(https://github.com/googlefonts/atkinson-hyperlegible-next)
|
||||
|
||||
Lexend*.ttf:
|
||||
Copyright 2018 The Lexend Project Authors (https://github.com/googlefonts/lexend),
|
||||
with Reserved Font Name "RevReading Lexend".
|
||||
|
||||
OpenDyslexic*.ttf:
|
||||
Copyright (c) 2019-07-29, Abbie Gonzalez (https://abbiecod.es|support@abbiecod.es),
|
||||
with Reserved Font Name OpenDyslexic.
|
||||
Copyright (c) 12/2012 - 2019
|
||||
|
||||
The files are built by scripts/build-reading-fonts.py from the woff2 files
|
||||
the webmail bundles (from Fontsource, https://fontsource.org): the variable
|
||||
Atkinson Hyperlegible Next and Lexend fonts as static regular and bold
|
||||
instances, OpenDyslexic unchanged apart from the woff2 decompression.
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
@@ -77,6 +77,10 @@ class BulwarkFcmModule(reactContext: ReactApplicationContext)
|
||||
val markReadLabel = options.takeIf { it.hasKey("markReadLabel") }?.getString("markReadLabel") ?: "Mark as read"
|
||||
val deleteLabel = options.takeIf { it.hasKey("deleteLabel") }?.getString("deleteLabel") ?: "Delete"
|
||||
val replyLabel = options.takeIf { it.hasKey("replyLabel") }?.getString("replyLabel") ?: "Reply"
|
||||
// Delete moves the mail to Trash where there is one: no unlock needed
|
||||
// for that (#85). Without a Trash it destroys the mail, so it asks.
|
||||
// Unlock stays the default for a caller that doesn't say.
|
||||
val deleteNeedsUnlock = !options.hasKey("deleteNeedsUnlock") || options.getBoolean("deleteNeedsUnlock")
|
||||
|
||||
// Bitmap fetch + draw off the bridge thread so the caller doesn't
|
||||
// block waiting for the favicon request.
|
||||
@@ -86,7 +90,7 @@ class BulwarkFcmModule(reactContext: ReactApplicationContext)
|
||||
postNotification(
|
||||
notificationId, title, body, preview, largeIcon, bgColorHex,
|
||||
emailId, threadId, subject, accountId, jmapAccountId, groupKey,
|
||||
groupTitle, markReadLabel, deleteLabel, replyLabel,
|
||||
groupTitle, markReadLabel, deleteLabel, replyLabel, deleteNeedsUnlock,
|
||||
)
|
||||
if (groupKey != null) postGroupSummary(groupKey, groupTitle, bgColorHex, accountId)
|
||||
promise.resolve(null)
|
||||
@@ -122,6 +126,7 @@ class BulwarkFcmModule(reactContext: ReactApplicationContext)
|
||||
markReadLabel: String,
|
||||
deleteLabel: String,
|
||||
replyLabel: String,
|
||||
deleteNeedsUnlock: Boolean,
|
||||
) {
|
||||
val ctx = reactApplicationContext
|
||||
val intent = Intent(ctx, MainActivity::class.java).apply {
|
||||
@@ -148,8 +153,10 @@ class BulwarkFcmModule(reactContext: ReactApplicationContext)
|
||||
}
|
||||
}
|
||||
|
||||
val builder = NotificationCompat.Builder(ctx, BulwarkMessagingService.CHANNEL_ID)
|
||||
val builder = NotificationCompat.Builder(ctx, BulwarkMessagingService.channelId(ctx))
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
// Android 7 has no channels: the notification names its sound.
|
||||
.setSound(BulwarkMessagingService.soundUri(ctx))
|
||||
.setLargeIcon(largeIcon)
|
||||
.setContentTitle(title)
|
||||
// body is the subject with the "(No subject)" fallback; an empty
|
||||
@@ -209,8 +216,8 @@ class BulwarkFcmModule(reactContext: ReactApplicationContext)
|
||||
)
|
||||
builder.addAction(
|
||||
NotificationCompat.Action.Builder(0, deleteLabel, deletePending)
|
||||
// Android 12+: unlock first, so a locked phone can't delete mail.
|
||||
.setAuthenticationRequired(true)
|
||||
// Android 12+: unlock first, so a locked phone can't destroy mail.
|
||||
.setAuthenticationRequired(deleteNeedsUnlock)
|
||||
.build()
|
||||
)
|
||||
|
||||
@@ -279,7 +286,7 @@ class BulwarkFcmModule(reactContext: ReactApplicationContext)
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||
)
|
||||
val summary = NotificationCompat.Builder(ctx, BulwarkMessagingService.CHANNEL_ID)
|
||||
val summary = NotificationCompat.Builder(ctx, BulwarkMessagingService.channelId(ctx))
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentTitle(groupTitle)
|
||||
.setContentText(if (count == 1) "1 new message" else "$count new messages")
|
||||
|
||||
@@ -6,6 +6,8 @@ import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.AudioAttributes
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
@@ -49,7 +51,10 @@ class BulwarkMessagingService : FirebaseMessagingService() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "bulwark_mail"
|
||||
// Plays the Involved sound; see channelId().
|
||||
const val CHANNEL_ID = "bulwark_mail_involved"
|
||||
// The first mail channel, with the system's default sound.
|
||||
const val LEGACY_CHANNEL_ID = "bulwark_mail"
|
||||
private const val TAG = "BulwarkPush"
|
||||
private const val START_WAKE_LOCK_MS = 10_000L
|
||||
|
||||
@@ -103,10 +108,39 @@ class BulwarkMessagingService : FirebaseMessagingService() {
|
||||
startWakeLock = null
|
||||
}
|
||||
|
||||
/**
|
||||
* The channel new-mail notifications go to. A channel's sound can't
|
||||
* change after it is created, so the Involved sound (the webmail's
|
||||
* default, lib/notification-sound.ts) needs a channel of its own; the
|
||||
* old one stays as the user left it. Someone who turned mail
|
||||
* notifications off in the old channel keeps them off: their
|
||||
* notifications keep going there.
|
||||
*/
|
||||
fun channelId(context: Context): String {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return CHANNEL_ID
|
||||
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val legacy = manager.getNotificationChannel(LEGACY_CHANNEL_ID)
|
||||
return if (legacy != null && legacy.importance == NotificationManager.IMPORTANCE_NONE) {
|
||||
LEGACY_CHANNEL_ID
|
||||
} else {
|
||||
CHANNEL_ID
|
||||
}
|
||||
}
|
||||
|
||||
/** The Involved sound (res/raw, kept from the shrinker by res/raw/keep.xml). */
|
||||
fun soundUri(context: Context): Uri =
|
||||
Uri.parse("android.resource://${context.packageName}/raw/involved_notification")
|
||||
|
||||
fun ensureChannel(context: Context) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
if (channelId(context) != CHANNEL_ID) return
|
||||
if (manager.getNotificationChannel(CHANNEL_ID) != null) return
|
||||
val sound = soundUri(context)
|
||||
val attributes = AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.build()
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Mail notifications",
|
||||
@@ -114,6 +148,7 @@ class BulwarkMessagingService : FirebaseMessagingService() {
|
||||
).apply {
|
||||
description = "Incoming email alerts"
|
||||
enableVibration(true)
|
||||
setSound(sound, attributes)
|
||||
}
|
||||
manager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- The calendar reminder channel (src/lib/calendar-notifications.ts) and the
|
||||
mail channel (BulwarkMessagingService.kt) name their sound by resource
|
||||
name, which the release resource shrinker can't see. -->
|
||||
<resources xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:keep="@raw/involved_notification" />
|
||||
+45
-5
@@ -35,8 +35,10 @@ module.exports = {
|
||||
slug: 'bulwark-mobile',
|
||||
// mailto: lets Android/iOS offer the app for mail links in other apps;
|
||||
// bulwarkmail: is the webmail's sign-in link (bulwarkmail://pair?...).
|
||||
// org.bulwarkmail.mobile: is where Stalwart 1.0 sends the browser sign-in
|
||||
// back: it registers public clients only with a scheme that has a dot.
|
||||
// Android's manifest is maintained by hand and lists the same schemes.
|
||||
scheme: ['bulwarkmobile', 'bulwarkmail', 'mailto'],
|
||||
scheme: ['bulwarkmobile', 'bulwarkmail', 'mailto', 'org.bulwarkmail.mobile'],
|
||||
version: VERSION,
|
||||
orientation: 'default',
|
||||
icon: './assets/icon.png',
|
||||
@@ -56,10 +58,10 @@ module.exports = {
|
||||
bundleIdentifier: 'org.bulwarkmail.mobile',
|
||||
buildNumber: IOS_BUILD_NUMBER,
|
||||
config: {
|
||||
// The app only speaks HTTPS/TLS and uses platform crypto, which is
|
||||
// exempt. Declaring it here skips the manual export-compliance
|
||||
// questionnaire that otherwise blocks every TestFlight build.
|
||||
usesNonExemptEncryption: false,
|
||||
// PGP and S/MIME (#58) add end-to-end encryption beyond HTTPS/TLS,
|
||||
// so the app declares non-exempt encryption; App Store Connect asks
|
||||
// for the export compliance documentation on upload.
|
||||
usesNonExemptEncryption: true,
|
||||
},
|
||||
},
|
||||
android: {
|
||||
@@ -81,6 +83,10 @@ module.exports = {
|
||||
},
|
||||
plugins: [
|
||||
'expo-secure-store',
|
||||
// WebCrypto for PGP and S/MIME (#58). On iOS it raises the deployment
|
||||
// target (app and pods) to 16.4, the minimum Nitro modules build for;
|
||||
// Android has nothing to set.
|
||||
'react-native-quick-crypto',
|
||||
'@react-native-community/datetimepicker',
|
||||
'expo-localization',
|
||||
[
|
||||
@@ -104,6 +110,40 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
['./plugins/ios-push', { apsEnvironment: APS_ENVIRONMENT, teamId: APPLE_TEAM_ID }],
|
||||
// TLS client certificates (#3): the iOS side of src/lib/client-cert.ts.
|
||||
'./plugins/ios-client-cert',
|
||||
// The iOS inbox widget (WidgetKit); src/widgets/ios.ts feeds it.
|
||||
['./plugins/ios-widget', { teamId: APPLE_TEAM_ID }],
|
||||
// The reading fonts (Settings > Accessibility > Font) for the app's
|
||||
// text on iOS, found by the family names in the files
|
||||
// (src/theme/reading-fonts.ts). iOS only: Android reads the same files
|
||||
// from its hand-maintained assets/fonts, where the plugin would copy
|
||||
// them onto themselves.
|
||||
[
|
||||
'expo-font',
|
||||
{
|
||||
ios: {
|
||||
fonts: [
|
||||
'AtkinsonHyperlegibleNext.ttf',
|
||||
'AtkinsonHyperlegibleNext_bold.ttf',
|
||||
'Lexend.ttf',
|
||||
'Lexend_bold.ttf',
|
||||
'OpenDyslexic.ttf',
|
||||
'OpenDyslexic_bold.ttf',
|
||||
].map((file) => `./android/app/src/main/assets/fonts/${file}`),
|
||||
},
|
||||
},
|
||||
],
|
||||
// Bundles the Involved sound calendar reminders (src/lib/calendar-
|
||||
// notifications.ts) and new mail (plugins/ios-push) play on iOS.
|
||||
// Android has it in res/raw.
|
||||
[
|
||||
'expo-notifications',
|
||||
{
|
||||
mode: APS_ENVIRONMENT,
|
||||
sounds: ['./assets/sounds/involved_notification.wav'],
|
||||
},
|
||||
],
|
||||
],
|
||||
extra: {
|
||||
commit: COMMIT,
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,187 @@
|
||||
# Native sync with webmail 1.18.0 and Stalwart 1.0.0 (2026-10-29)
|
||||
|
||||
Branch `feat/stalwart-1.0`: native main (f358d605) merged in, then the 1.0.0
|
||||
release check, the webmail 1.10-1.18 parity fixes and the open native issues.
|
||||
It is 200 commits ahead of main (181 without merges) and 0 behind. Nothing is
|
||||
pushed.
|
||||
|
||||
At 595df26e (the last code change; only docs follow): `tsc` clean, vitest 323 files / 4911 tests pass
|
||||
(113 skipped are the opt-in live suites), `i18n:check` passes. The app has no
|
||||
lint script.
|
||||
|
||||
## What landed
|
||||
|
||||
Merge of main into the branch: f1db6b48 (19 conflicted files). Main's later
|
||||
work won (iOS push and its notification extension, SafeAreaModal, file
|
||||
preview, restyle and Tabler icons, token sign-in, notification mutes, device
|
||||
sync); the branch's per-account routing, session epochs and refusal handling
|
||||
were carried onto it. Notable calls: jmap-client keeps the branch's
|
||||
epoch-safe credential writes plus main's shared-keychain writes and
|
||||
`adoptStoredTokens`; auth-store keeps the branch's `signIn` helper and routes
|
||||
main's token sign-in through it; push keeps the branch's SetError-only
|
||||
narrowing plus main's device-sync types and mutes.
|
||||
|
||||
### Stalwart 1.0.0 (step 1)
|
||||
|
||||
- Sign-in: password + TOTP on 1.0 (96ef8515: one `/api/auth` body for both
|
||||
versions, RFC 7591 public client registered per server, redirect
|
||||
`org.bulwarkmail.mobile:/auth/callback` because 1.0 wants a dot in the
|
||||
scheme); browser sign-in with the registered client and Stalwart's data
|
||||
scopes (6ffd0473, a2caa4cc); refresh-token rotation (1456e15d, 28d928f6,
|
||||
cfd81737); no secret follows a redirect to another host (2c05e7bd,
|
||||
06d68d73, fe4f9211, 61654d7c); account security explains a limited client
|
||||
(c6eb02d4).
|
||||
- Files: draft-18 `@type`/`mediaType` (174c3fc8), listing past
|
||||
maxObjectsInGet #1385 (7d8e0735), shared-folder writes per account
|
||||
(18b8aa09, 7c495d03).
|
||||
- Mail: inline images that 1.0 lists only in the body parts (d2bb479a),
|
||||
quoted inline images in replies and forwards (913b3766), part names that
|
||||
are paths (4d545f8f), scheduled mail of own and group accounts with equal
|
||||
ids (15171637), bulk delete with partial refusals (547d9e59).
|
||||
- Calendar #89: the branch's split CalendarEvent/get (28ade775) holds on the
|
||||
release; plus mail-only shares (f3e11d4c), 0.16 loads at DST changes
|
||||
(7c775571), account-switch races (1f190c94, e993540a, 41e8f005, 98dc13fe).
|
||||
- Push: device-sync types retry only on a type refusal (20be1105), refused
|
||||
non-public relay explained and rolled back (4ec2ccac, 90e250d3), Android
|
||||
extends subscriptions in the background because both servers clamp expiry
|
||||
to 7 days (9804b499, ce47c73f), subscription id reuse (77327ea8), renewal
|
||||
for every account (d92a2ca8), logins sharing a JMAP account id
|
||||
(9ccb6228, b352e470), removed-account teardown (152f3089).
|
||||
|
||||
### Webmail parity 1.10-1.18 (step 2)
|
||||
|
||||
The inventory is docs/parity/10-webmail-1.10-to-1.18.md (ec7e8e05): 112 rows,
|
||||
10 P1, 40 P2. 52 are ticked with their commits (fe33dfc5); every P1 and every
|
||||
P2 that doesn't need the org policy is done. Highlights:
|
||||
|
||||
- Security (P1): HTML sanitizer rebuilt on a tokenizer, reader CSP
|
||||
`script-src 'none'`, composer script under a nonce (f5e997ba, 07679cbf,
|
||||
47a1d249, 440a2e7a, 4e3da668); forged DMARC/DKIM chips (ef9561f1,
|
||||
8adef1e1); mailto unsubscribe (942f4517, 918571c3); winmail.dat freeze
|
||||
(855d304b).
|
||||
- Wrong-account writes (P1): composer pinned to its login (9daa3441,
|
||||
388f46b5, 4e57b191), undo/Send now per login (e81f0071), calendar and
|
||||
contacts switch guards (see above, 6b9552fc).
|
||||
- Sieve interop (P1): webmail metadata v2, address/domain rules, elsif
|
||||
chains (2167a9d7, fc9c164d), plus forwarding, audience and periods
|
||||
(df1a5ab4).
|
||||
- P2 features: scheduled message view #84 (14d009de), group identity send
|
||||
(31d8e4a4, 893f40a2), reschedule cancel-first #1308 (22f2ac5b,
|
||||
fdf8eece), unconfirmed send #1019 (3eac700a), Files trash (7c914183 and
|
||||
be04fcb1..946304ab), attach from and save to Files (8e07b7d8, 39e82f99),
|
||||
templates in account metadata and team templates (fac318dc), shared tags
|
||||
(b7b9c7e0), verification-code chip (b1550f14), unverified sender
|
||||
(cc21317b), search without Spam/Trash (a412b516, 2b5257bf), calendar
|
||||
search (b50270d8) and default reminders (9b87100a), feed changes reach
|
||||
imported events (8a71495e, 47f29718), directory browser (0fd14a83),
|
||||
rules from a message (d546d509), contact links and vCard (32f70c10,
|
||||
07fd03e2, 3c156673), placeholder warning (a8e7a3f4).
|
||||
|
||||
### Issues (step 3)
|
||||
|
||||
#80 96c7c045, #81 01983a38, #82 7a1031a9, #83 6f475c0c, #84 14d009de,
|
||||
#86 65f00640, #87 e47c05c5, #89 (branch 28ade775, checked on the release).
|
||||
#85 needs a decision (below). No issue was commented on or closed.
|
||||
|
||||
Every group got a separate review agent; review findings were fixed in a
|
||||
follow-up round on each branch (two HIGHs in the composer, two pre-existing
|
||||
HIGHs in the HTML handling, one in calendar feed sync).
|
||||
|
||||
## Verified live
|
||||
|
||||
Opt-in suites in src/api/__tests__/live-stalwart*.test.ts, run at the tip
|
||||
against Stalwart 1.0.0 (stw-rel, :8120) and 0.16.27 (:8016), each with its
|
||||
own e2e account. All pass on both: base 17, mail 14, compose 10, files 16,
|
||||
push 11, contacts 9, calendar 22, auth 14 (with a TOTP account). The base
|
||||
suite now imports its own mail, so it runs on any account.
|
||||
|
||||
Android emulator (Pixel 7 API 34, release build of the tip):
|
||||
|
||||
- 1.0.0: browser sign-in (consent page, registered client, new redirect
|
||||
scheme) as usera; reader with the new CSP (height, links open in the
|
||||
browser); reply composer loads, draft saved; push enabled through the
|
||||
hosted relay, a mail sent by userb arrived as a notification; calendar
|
||||
loads with series (#89); Files shows the trash.
|
||||
- 1.0.0 and 0.16: password + TOTP sign-in for e2e-totp-phone (the 1.0
|
||||
`invalid_client` bug is gone); send from the 1.0 TOTP account.
|
||||
- Unified inbox across the logins.
|
||||
|
||||
Not covered on the emulator: series edits, contact photos and
|
||||
reschedule/undo (covered by the live suites only), and anything iOS.
|
||||
|
||||
## Open
|
||||
|
||||
- iOS, needs a CI run: reader CSP and composer nonce assume injected scripts
|
||||
ignore page CSP and the editor loads as about:blank; the notification
|
||||
extension's label check (b352e470, Swift); `target=_blank` link taps;
|
||||
#81 keyboard. iOS push still lapses after 7 days without opening the app
|
||||
(the extension can't extend it).
|
||||
- Blocked on the webmail: org policy (locked settings, session length,
|
||||
external sender label and recipient confirm, org signature and
|
||||
disclaimer, template library). The policy route only takes the webmail
|
||||
session cookie, and the app doesn't keep the paired webmail URL. Also the
|
||||
hand-off still returns the password in the `bulwarkmobile://` fragment.
|
||||
- Not done: the 60 P3 rows in the parity file; P3 bugs in Sieve (forward
|
||||
limit, vacation length, editor losing text); device DST in other-zone
|
||||
daily expansion keeps a wrong recurrenceId for that one day; a login
|
||||
switch from a deep link doesn't reset the navigation stack, so a thread
|
||||
left below can act through the new login.
|
||||
- New strings are English-only except where the webmail already had
|
||||
translations.
|
||||
- The `C:\ns` junction (to the worktree, used for the emulator build) is
|
||||
still there; removing it was blocked by the permission settings.
|
||||
|
||||
## Bugs that also live in the webmail
|
||||
|
||||
Not fixed there in this run:
|
||||
|
||||
- Inline images of drafts and sent mail on 1.0.0 come only from
|
||||
`attachments` (components/email/email-viewer.tsx:2518, email-composer.tsx
|
||||
:905, :1391, :2647).
|
||||
- Save to Files on 0.16 stores the whole message (save-to-files-dialog.tsx
|
||||
:79-84); Blob/copy without the blob capability (lib/jmap/client.ts:9985).
|
||||
- Reschedule after a lost answer: cancel (client.ts:11619) and create
|
||||
(:11643) can strand or double-send.
|
||||
- Default alert ids `alert1…` reused across calendars and lists; 1.0
|
||||
refuses them (lib/calendar-alerts.ts:206, calendar-management-settings.tsx
|
||||
:200).
|
||||
- Sieve parser drops `elsif`/`else` and trailing comments, which breaks the
|
||||
category-tabs section (lib/sieve/parser.ts:251); generator falls back to
|
||||
`header :contains` (lib/sieve/generator.ts:126).
|
||||
- Vacation turned on with a failed filter re-include only logs
|
||||
(stores/vacation-store.ts:210).
|
||||
- Feed sync: no END:VCALENDAR check, no mass-deletion guard
|
||||
(stores/calendar-store.ts:1978); invitation trust lets a failing DKIM
|
||||
signature beat dmarc=pass (lib/calendar-invitation.ts:219); a HELO SPF
|
||||
pass counts as aligned (lib/email-headers.ts:64).
|
||||
- Files trash: purge without a fresh read (stores/file-store.ts:1947),
|
||||
restore with its folder lands at the root (:1914), no `.Trash` name check.
|
||||
- Push: no device client id check (lib/web-push.ts:786), cap path destroys
|
||||
first (:852), refused relay URL shown raw (notification-settings.tsx:173).
|
||||
- Probably: decoded CSS put back into STYLE (lib/email-sanitization.ts:733),
|
||||
untested; Lite refresh follows redirects (lib/auth/lite-tokens.ts:533).
|
||||
|
||||
## Questions
|
||||
|
||||
1. #85: the notification Delete action requires unlock (#68), which is why
|
||||
Android closes the shade. Drop the requirement (Delete then works from the
|
||||
lock screen, it only moves to Trash) or keep it?
|
||||
2. Org policy on the phone: add a webmail route that accepts a JMAP bearer
|
||||
token, and keep the paired webmail URL in the app?
|
||||
3. Full account security on 1.0 needs a first-party client for the app. Add
|
||||
an admin-registered `bulwark-mobile` client to the OAuth setup?
|
||||
4. MIG-02 on native: after a 0.16 to 1.0 migration every token account fails
|
||||
to refresh and is removed with "Your session has expired". Keep it as
|
||||
"sign in again" instead? A pasted OAuth access token also drops after
|
||||
about an hour (no refresh token).
|
||||
5. A password change from a TOTP account ends its grant on 0.16, so the app
|
||||
asks to sign in again. Fine?
|
||||
6. Translate the new English-only strings into the other 26 locales now?
|
||||
7. Reset the navigation stack when a deep link or notification switches the
|
||||
login?
|
||||
8. Small ones: the feed mass-removal guard (keeps events when more than half
|
||||
of 10+ would go, no override); contact re-import marks same-UID cards as
|
||||
"update existing", unselected; team templates read-only; Delete
|
||||
permanently and Restore on stale trash rows refresh silently.
|
||||
9. The account switcher shows `10.10.10.62` for both servers (port dropped),
|
||||
so two logins of the same user look identical. Show the port?
|
||||
@@ -0,0 +1,719 @@
|
||||
# Native catch-up with webmail 1.19.1 and Stalwart 1.0.1 (2026-11)
|
||||
|
||||
Branch `catch-up-2026-11` from native main 7a42a15f: 327 commits (300
|
||||
without merges, 27 merges of `cu-*` topic branches). Main on GitHub was
|
||||
fast-forwarded to it so CI could run (decision 17), so origin/main is the
|
||||
tip, 63357420. The version is still 0.1.87; no release is cut.
|
||||
|
||||
The run brought webmail 1.19.1 and Stalwart 1.0.1 into the app, closed most
|
||||
leftovers of the earlier parity files, built the large features that had
|
||||
waited for their own project (org policy, PGP and S/MIME, offline Outbox,
|
||||
iOS widget and client certificates, global search, folder sharing, dark mode
|
||||
recolouring, Jalali, free/busy) and translated every app string into the 28
|
||||
languages besides English. CI (full vitest suite) is green at 63357420:
|
||||
https://github.com/bulwarkmail/native/actions/runs/41226998363
|
||||
|
||||
Left: the results of the last full iOS e2e run and of the emulator recheck
|
||||
(RESULT PENDING below), some policy and accessibility pieces (see Open), and
|
||||
the webmail and website commits, which are merged locally but not pushed.
|
||||
|
||||
The inventory from the start of the run and the decisions follow the report.
|
||||
The rows the run closed are ticked there and in docs/parity.
|
||||
|
||||
## What landed
|
||||
|
||||
### Wave 1: inventory fixes
|
||||
|
||||
- Reader and list (merge f643df81): forged senders get a warning shield
|
||||
instead of a photo 0b0fc021, favicons off by default 8e10e73e, blockquote
|
||||
freeze 3243714d, direction-override characters in attachment names
|
||||
a930d177, plain-text reading by setting or per message 7dc45778, body
|
||||
follows the text size 7b551fc3, reduce motion and paused GIFs 6d701c4b,
|
||||
plain-text mail in the theme's colours 43973608, select by avatar tap #91
|
||||
6715df7f, undo after list actions da1da0ec, sort button 1cbe28b1, search
|
||||
folder tree e5c9433a, and the file 10 mail list rows (#1241, #1156, #1219,
|
||||
#1136, #1138, #1235, #1370, via badge, cold start, folder links).
|
||||
- Composer (0e69c86c to 73a6adf2): quick reply runs the composer's checks
|
||||
db8eb731, signature swap 60d0e98f, text part 0647cbf1, missing subject and
|
||||
Discard 1cc4ccca, schedule presets #1390 8fe5798b, reply caret 8bb7b607,
|
||||
recipient splitter ae2eb46b, offline message a43b55ec, reports left out of
|
||||
forwards 482e9a20, quoted history folded 9fd80993, template manager
|
||||
2c1f266f, alt text prompt 303dea01, confirm before send 0e69c86c.
|
||||
- Calendar, contacts, files and filters (merge d9a759b7): new calendar keeps
|
||||
its name and colour a2344ea0, cancelled meeting removal a19afe9b, editors
|
||||
ask before discarding 10e9f81d, DST recurrence id 6e697baa, forward limit
|
||||
and vacation length dcbfc907, Sieve editor text 6c3046c8, custom colours
|
||||
#90 d3f6d6ae, save without invitations 9b5692a8, shared task moves #1204
|
||||
b6995bb6, contact filters 18c6b03f, folder duplicate a0f586f9.
|
||||
- Platform (merge 470f12d2): cancel a hung sign-in (U-133) 55d008a2,
|
||||
accessibility page with presets and text sizes up to 200% c9b3bdfe, 4.5:1
|
||||
muted text 11522995, field names ff5a8c91, announcements on iOS d812caf0,
|
||||
consentRequired 1bef4719, IDN sign-in f5875f87, quota soft limit #1242
|
||||
af86339b, push fallback notification #1041 f6e6f8f5, Inbox-only push
|
||||
1821fda7, Thai and Estonian e8bc54eb, formal German 25f9b810.
|
||||
|
||||
### Wave 2
|
||||
|
||||
- Booking, parcel and order cards with calendar add and update, and Apple
|
||||
Wallet passes (merge 30e06315, mounted in 90e4e230).
|
||||
- Search snippets 1342e2a0 and size filter aac8a403, live checks 12b798c0.
|
||||
- Recover deleted mail #1319 113e96bd and delegates 8a5bb728 (merge
|
||||
3048525d).
|
||||
- Follow-ups (merges cec68f0d, a4e5f81f, 7139390a): directory and Sent
|
||||
recipient suggestions a7a37573, undo after deleting rules, tags and
|
||||
trusted senders 8ac19971, IDN sending 4932764a, quick reply switch
|
||||
bb45198f, trash leads to recovery 7467764e, address book sharing 96b163a1,
|
||||
starred folders and recent files cad9296a, files next and previous
|
||||
e3fc67e7, calendar, contact and files links fe455370 ecefd1cb d684b00e,
|
||||
light event colours 01578ac2, week all-day area #1122 90ce7c1b.
|
||||
|
||||
### Emulator walkthrough fixes
|
||||
|
||||
A release APK at 7139390a was walked through on a phone and a tablet
|
||||
emulator. Fixes:
|
||||
|
||||
- Calendar and sharing (merge 47b922ca): typed participant added once
|
||||
9ec58be4, all-day events stay all-day on 0.16 75513969, hex colour input
|
||||
826d5ad2, slot marker cleared 11fa0365, share sheet looks up a typed
|
||||
address 72b41eab, no principal ids shown as addresses 236e9117, widget tap
|
||||
opens the app without a chooser fe678174.
|
||||
- Sheets and settings (merge 73823292): sheet buttons and fields above the
|
||||
taskbar and keyboard 426f7403 98024346, localized folder names in the rule
|
||||
folder picker 56c8b98b, scheduled out of office shown as scheduled
|
||||
9781b304, screen reader names and selected states e9816809 e7fd665b
|
||||
d6b9b342.
|
||||
- Mail (merge f606866d): undo-send countdown d1c44f52, star undo ecf814b6,
|
||||
tag undo 9763036c, sender on search hits from Sent ff878fc6, Android reply
|
||||
keyboard 461e4da0, read state kept 9bc7b4a5, mail deleted elsewhere
|
||||
f2513de0, reader header scales with the text size ce21f273, tablet pane
|
||||
width e69bfc50.
|
||||
- Direct fixes: sheets above the taskbar 9b0bba7c, TalkBack busy state
|
||||
350b0e8e, moved count 58c80543, shared address book names 423e2780,
|
||||
delegate server id b803da5d, unified views across ports 268b1105, id token
|
||||
kept on refresh ce950430, unified inbox row label 172d0f63, Saturday week
|
||||
start kept after a restart 10fb7ab7.
|
||||
|
||||
### Decided behaviour (merge 14ec671c)
|
||||
|
||||
- Two accounts on one host kept apart by port 688e8bef, also next to a kept
|
||||
host-only account 7b249ab9.
|
||||
- MIG-02: an account whose sign-in stops working stays and asks to sign in
|
||||
again d0060549; a password change on a 0.16 TOTP account says so 149e5bce.
|
||||
- A notification or link for another account starts the stack over on that
|
||||
account's inbox f9a36b28.
|
||||
- #85: the notification Delete skips the unlock when the mail goes to Trash
|
||||
2fe83669, Android side 9669e07e.
|
||||
- Sign-out ends the identity provider session 5a301d62.
|
||||
- No crash when switching to an account left in Trash db717dcd.
|
||||
|
||||
### PR #66 and ios-perf
|
||||
|
||||
- PR #66 (lucletoffe): unified views for a single account, merged as
|
||||
5ecf6f32 (45b4feeb).
|
||||
- ios-perf: modules and screens load on first use, and the cached inbox
|
||||
shows before the session arrives; merged as 36cc49b7 (0c8b8179, d13769a9,
|
||||
3424bcf2). Fast start kept (decision 19).
|
||||
|
||||
### Org policy and hand-off
|
||||
|
||||
- The app redeems a one-time code from the webmail instead of reading the
|
||||
sign-in from the link ab2e5a60.
|
||||
- The app keeps the webmail it signed in through and applies its policy
|
||||
9d977773: locked and hidden settings f27e2ceb, hidden pages 1d654bbe,
|
||||
colour mode b7b3dd4c, themes a69bc071, external content 83981e8b,
|
||||
settings export df1e2117, tasks 8ba2f27a, archiving off efb92593
|
||||
13dcbdc0, delegates and recovery bc47c6a1, presets respect locks
|
||||
c66e6209, no third-party traffic and custom From d254d7b5, and the
|
||||
operator's help links in About.
|
||||
- Org signature and disclaimer on new mail, templates hidden when off
|
||||
fe3f860e.
|
||||
- BIMI logos from the webmail 83ddd137, shown in the list, the unified inbox
|
||||
and the reader 6b7e752c.
|
||||
- @ mentions can be turned off, with a choice of where the contact goes
|
||||
ab226b03.
|
||||
|
||||
### Webmail and website changes
|
||||
|
||||
Merged into webmail main, not pushed (decision 3):
|
||||
|
||||
- fb3ba0275 feat: hand the mobile app a one-time code instead of the
|
||||
sign-in in its link
|
||||
- f74ff49f5 feat: give the mobile app its organisation policy for its
|
||||
access token
|
||||
- 3bdbdb136 docs: move the mobile hand-off contract to the website
|
||||
|
||||
Website repo (repos/website), local only: 7292593 documents the hand-off
|
||||
code and the policy token in docs/development/mobile-handoff.md.
|
||||
|
||||
### Calendar extras (merge 66b4305e)
|
||||
|
||||
- Free/busy of invited participants in the event editor e6a89ad0.
|
||||
- Invitations, changes and cancellations from CalendarEventNotification
|
||||
d663a5e2.
|
||||
- Jalali months, days and dates for Persian f1297b6b.
|
||||
- Involved sound for reminders b4f10829.
|
||||
|
||||
### Reading and accessibility (merge 6e5980e4)
|
||||
|
||||
- Dark mode recolours HTML mail instead of inverting it 23b2bf97.
|
||||
- Reading fonts (Atkinson Hyperlegible Next, Lexend, OpenDyslexic) for the
|
||||
app and plain-text mail 4724c28f, on iOS too cfca6b9c.
|
||||
- Colour-blind-safe palette for tags c6501ec8 and calendars 21dd5a24.
|
||||
- Plain language in English and German 97b71e22.
|
||||
|
||||
### Native iOS and Android (merge 62a6c22f)
|
||||
|
||||
- Involved sound for new mail 6a27c596.
|
||||
- iOS push renews itself when the app isn't opened for a week c704c7e6.
|
||||
- TLS client certificates on iOS #3 ba150060, described the iOS way
|
||||
48a0590a.
|
||||
- iOS inbox widget fbe54576.
|
||||
|
||||
### Compose extras (merge 609d36b0)
|
||||
|
||||
- Priority, delivery notifications (DSN) and REQUIRETLS 649d3adc, kept when
|
||||
a scheduled send moves 409ada94.
|
||||
- @ suggestions in the message body 8389db8f.
|
||||
- Full-screen recipient picker in phone landscape (U-069) e1ad5c09.
|
||||
- Outbox for mail sent offline, sent once back online 0ec809eb.
|
||||
|
||||
### Tags, search and sharing (merge 0865c1d6)
|
||||
|
||||
- Nested tags and a choice of which tags the sidebar shows 585c70a5.
|
||||
- Share a mail folder from its menu 417f1843.
|
||||
- Global search over mail, contacts, events and files of every account
|
||||
f15b7bb3.
|
||||
|
||||
### Dependencies (decisions 10, 15 and 20)
|
||||
|
||||
- postal-mime 4.2 against crafted .eml headers 368c90ce.
|
||||
- React Navigation 7.5, which drops its vulnerable URL and id helpers
|
||||
38257f81.
|
||||
- zustand, jszip and vitest patch releases b2cd83b1; SDK 54 patch line and
|
||||
lockfile f9a3b761.
|
||||
- Screenshot and recent-apps protection with expo-screen-capture 0ae33f3a.
|
||||
- openpgp and pkijs 051f6bf2; react-native-quick-crypto provides WebCrypto
|
||||
instead of webcrypto-liner 4c818d64.
|
||||
|
||||
### PGP and S/MIME #58 (merge e5027c45)
|
||||
|
||||
- Read PGP and S/MIME mail and manage keys under Settings, Encryption
|
||||
8aa1d8e0.
|
||||
- Sign and encrypt from the composer options b0997c0a.
|
||||
- Live test between two accounts be98acdc; signed mail lists its
|
||||
attachments once 7a06ab03.
|
||||
- Export compliance declared 5ead7a81 (usesNonExemptEncryption true).
|
||||
|
||||
### Translations (merge 3c0d7ad5)
|
||||
|
||||
German by hand 8345be43, the other 27 locales in six commits (cef88b93 to
|
||||
a5b40d5a). A test catches the informal German "passe an" 2e90f431. The key
|
||||
offer and already-sent messages were translated last 63357420.
|
||||
|
||||
### End-to-end flows (merge d9d7a466)
|
||||
|
||||
11 Maestro flows now follow the app's intended changes: open and select by
|
||||
row, swipe target, compose options, cancel send, default reminders,
|
||||
notification prompt, files delete, settings search, sign out above the tab
|
||||
bar, camera pre-prompt, Safari's Next key. The consent page step came
|
||||
earlier in 3769b24b.
|
||||
|
||||
### Fixes after the merges
|
||||
|
||||
- iOS pod install: the widget and the notification extension get their own
|
||||
copy of the shared keychain code 05bc0f56 (prebuild is idempotent again).
|
||||
- Android 14 and later crashed at launch, because expo-screen-capture needs
|
||||
DETECT_SCREEN_CAPTURE and the manifest removed it d2d44e0d.
|
||||
- CI: test updates 77914b0a 22893d32 e4295dab, account id check threw for
|
||||
saved accounts without a server b43472dd, share sheet loading state hidden
|
||||
from screen readers 7e60d324, duplicate import 9192311d, PKCS#1 v1.5 on
|
||||
newer Node 43bf8da7.
|
||||
- Review fixes (merge b188c2db): PGP key planting 93c63070, no-Drafts send
|
||||
looked sent e2d4915e, notice after three unconfirmed attempts 1c33f8da,
|
||||
old favicon default on upgrade b1a0eaf8, Reply-To with a comma 1aba571a,
|
||||
Move to Drafts after send ac875ffe, retry during a flush 75213f6a, retry
|
||||
during a cached start 2c5d9518, iOS renewal ignored notUpdated 1194bf28.
|
||||
|
||||
## Testing
|
||||
|
||||
- CI (ci.yml, full vitest suite): green at 63357420. The failures on the way
|
||||
came from tests that read source and from Node differences, plus two real
|
||||
bugs (b43472dd, 7e60d324); all are fixed and listed above.
|
||||
- Live suites at 7139390a: all 9 files pass on 1.0.1, and on 0.16.27 except
|
||||
one compose case on a dirty account, which passes on a clean one. Live
|
||||
cases added later and run on both servers: end session, all-day on 0.16,
|
||||
delegation with an unknown principal and share lookup, free/busy, event
|
||||
notifications, send options and moved scheduled send, send queue (also
|
||||
without Drafts), global search, mailbox share, PGP and S/MIME both ways.
|
||||
- iOS e2e (e2e-ios.yml):
|
||||
- 41206960084 at 36cc49b7: build ok, signin-tablet pass, 6 legs failed,
|
||||
all from outdated flows or flaky; flows fixed in d9d7a466.
|
||||
- 41215412383 at 22893d32: pod install failed ("no parent for object
|
||||
SharedKeychain.swift"); fixed in 05bc0f56.
|
||||
- 41216860380 at 05bc0f56: build and signin-tablet pass, the first compile
|
||||
of the new Swift (push renewal, client certificates, widget).
|
||||
- 41220976434 at 43bf8da7, all legs, after a reset and seed of stw-v016:
|
||||
RESULT PENDING.
|
||||
- Android release builds (x86_64, debug-signed) at 43bf8da7 and d2d44e0d:
|
||||
success. The emulator recheck found the Android 14 launch crash
|
||||
(d2d44e0d). Emulator recheck results: RESULT PENDING.
|
||||
- quick-crypto on a real Hermes runtime, through the in-app self-test on the
|
||||
emulator: RESULT PENDING.
|
||||
- Emulator walkthrough of 7139390a on a phone and a tablet: its bugs are
|
||||
under "Emulator walkthrough fixes".
|
||||
- Code review (code-review skill, high) of 7a42a15f..43bf8da7: 10 findings,
|
||||
9 fixed (review fixes above). Not fixed: the org signature details are
|
||||
always empty (needs settings sync).
|
||||
|
||||
## Webmail bugs
|
||||
|
||||
Carried from the last report, still open in the webmail:
|
||||
|
||||
- Inline images of drafts and sent mail on 1.0 only from `attachments`
|
||||
(components/email/email-viewer.tsx:2508; email-composer.tsx:953, :969,
|
||||
:1445, :2669, :2703).
|
||||
- Save to Files on 0.16 stores the whole message
|
||||
(save-to-files-dialog.tsx:78-88); Blob/copy without the blob capability
|
||||
(lib/jmap/client.ts:10047).
|
||||
- Reschedule after a lost answer can strand or double-send
|
||||
(client.ts:11686, :11706, :11721).
|
||||
- Alerts the settings rows don't show keep their old ids
|
||||
(lib/calendar-alerts.ts:206).
|
||||
- Sieve parser drops `elsif`/`else` and trailing comments
|
||||
(lib/sieve/parser.ts:251-263); the generator falls back to
|
||||
`header :contains` (lib/sieve/generator.ts:105, :126).
|
||||
- Vacation with a failed filter re-include only logs
|
||||
(stores/vacation-store.ts:206-210).
|
||||
- Feed sync without END:VCALENDAR check or mass-deletion guard
|
||||
(stores/calendar-store.ts:1978).
|
||||
- A HELO SPF pass counts as aligned (lib/email-headers.ts:64).
|
||||
- Files trash: purge without a fresh read (stores/file-store.ts:1947),
|
||||
restore with its folder lands at the root (:1914), no `.Trash` name check
|
||||
(lib/file-name-rules.ts:78).
|
||||
- Push: no device client id check (lib/web-push.ts:788), the cap path
|
||||
destroys first (:853), a refused relay URL is shown raw
|
||||
(components/settings/notification-settings.tsx:180).
|
||||
- Probably: decoded CSS put back into STYLE
|
||||
(lib/email-sanitization.ts:613-740); Lite refresh follows redirects
|
||||
(lib/auth/lite-tokens.ts:536, also :376, :399, :467, :504).
|
||||
|
||||
New in this run:
|
||||
|
||||
- `consentRequired` reads as a wrong password
|
||||
(lib/auth/stalwart-login-request.ts).
|
||||
- isValidEmail rejects Unicode domains.
|
||||
- A bare /calendar/<date> link switches to the month view.
|
||||
- Recent files are shared across accounts.
|
||||
- Participant input: groups have an empty email, and onBlur adds the typed
|
||||
address (harmless on the web; it caused the app's double add).
|
||||
- 49 keys were dropped in the settings reorg.
|
||||
- A stale comment says Stalwart takes limit 0 as "no limit".
|
||||
- lib/account-utils.ts generateAccountId drops the port, so two servers on
|
||||
one host collide (fixed in the app).
|
||||
- hydrateExpandedOccurrences (lib/jmap/client.ts) and
|
||||
lib/recurrence-instances.ts skip one-off synthetic ids without a
|
||||
recurrenceId; on 0.16 one-off all-day events probably show as timed.
|
||||
- components/settings/delegates/delegates-settings.tsx:182 shows the
|
||||
principal id as the "Unknown person" detail line.
|
||||
- components/settings/share-collection-dialog.tsx has no lookup for a typed
|
||||
exact address.
|
||||
- context_menu.items_selected has no singular.
|
||||
- The filter-rule-modal.tsx folder picker shows server folder names, not
|
||||
the localized role names.
|
||||
- thread-list-item.tsx listMailboxRole picks sender or recipient by the
|
||||
selected folder's role, so a search from Sent shows the recipient on Inbox
|
||||
hits (also the email-list.tsx column header).
|
||||
- The components/search/search-chips.tsx size chip reads "Larger than (KB):
|
||||
1 KB".
|
||||
- The mail-app.tsx undo-send toast doesn't count down (maybe intended).
|
||||
- Jalali: the week view keeps Gregorian day numbers
|
||||
(calendar-week-view.tsx:498), and month navigation uses Gregorian
|
||||
addMonths (calendar-app.tsx:576-592).
|
||||
- On 0.16 the event notification toast always says "Untitled event" (0.16
|
||||
doesn't return `event`).
|
||||
- free-busy.ts says Stalwart 1.0 sets mayGetAvailability false with
|
||||
directory queries off; on 1.0.1 it's true.
|
||||
- Reschedule and Send now (lib/jmap/client.ts ~11624-11640) drop the DSN and
|
||||
REQUIRETLS envelope parameters.
|
||||
- composing-settings.tsx never passes `locked` for plainTextMode,
|
||||
autoSelectReplyIdentity and replyIdentityMatch; defaultReplyMode has no
|
||||
locked UI.
|
||||
- The externalContentEnabled gate only hides the setting; the user's stored
|
||||
choice keeps applying (maybe intended).
|
||||
- In the standard layout, Enter in the global search palette only saves the
|
||||
recent search and closes.
|
||||
- Plugins (repos/plugins): smime extractKeyUsage reads ku.digitalSignature,
|
||||
but pkijs gives a BIT STRING, so every certificate with keyUsage ends up
|
||||
canSign and canEncrypt false; smime importPkcs12 takes the first
|
||||
certificate bag as the leaf; bulwark-gpg signerEmailFor compares primary
|
||||
key ids (subkey signatures show no signer), renderInnerMime drops the first
|
||||
paragraph of non-MIME inline PGP, and detect() searches the raw body, so it
|
||||
misses armor in QP or base64 parts.
|
||||
- Catalog: mn uses "Харилцах утас" for Contacts and "Тэмдэгтүүд" for
|
||||
Identities and icons; ru has "Отменить изменения" where discard is meant.
|
||||
|
||||
Stalwart, not the webmail: on 0.16, ContactCard/query `{text}` doesn't find
|
||||
a card whose name has only `full` set (1.0 does). With directory queries
|
||||
off, 1.0 can't name a sharee or share by exact address over JMAP (DAV can).
|
||||
The app explains this, and it goes to Stalwart (decision 18).
|
||||
|
||||
## Open
|
||||
|
||||
- Org signature details (title, department, phone, mobile) are always
|
||||
empty: the app has no form for them and settings sync is blocked.
|
||||
- Group-identity policy scopes (webmail policyForSender) aren't ported; the
|
||||
app uses the active account's policy.
|
||||
- The composer still offers the custom From sheet when customFromEnabled is
|
||||
off (the webmail hides it).
|
||||
- No-third-party mode counts as off until /api/config is read (the webmail
|
||||
treats it as on until then).
|
||||
- The Android home-screen widget still shows Archive when the organisation
|
||||
turns archiving off (src/widgets/layouts/mail.tsx, src/widgets/actions.ts).
|
||||
- Mailbox sharing: the admin's sharingRights don't hide the write presets
|
||||
yet.
|
||||
- Policy pieces not built: idle sign-out and session length, the external
|
||||
sender chip, the external recipient confirm.
|
||||
- PGP: Argon2-protected keys get a clear error (no WebAssembly on Hermes).
|
||||
S/MIME RC2, DES, Camellia and AES-CCM content and legacy RC2 .p12 files
|
||||
get a banner or an error. No WKD (the plugin has none). Drafts stay
|
||||
unencrypted on the server, as in the webmail.
|
||||
- iOS: the Swift for client certificates, push renewal and the widget
|
||||
compiled in CI but never ran on a device; reading fonts on iOS are
|
||||
untested. The widget needs the App ID org.bulwarkmail.mobile.Widget and the
|
||||
secret IOS_WIDGET_PROVISIONING_PROFILE_BASE64 in release-ios.yml; until
|
||||
then signed releases leave it out. The iOS minimum is now 16.4
|
||||
(quick-crypto and Nitro).
|
||||
- The APK is about 10 MB bigger per ABI (libQuickCrypto).
|
||||
- Nested tags: renaming or moving a tag with its messages, and reordering,
|
||||
aren't ported.
|
||||
- Global search: other logins' file listing is capped at about 500 items;
|
||||
the Pro search tab and saved searches aren't ported.
|
||||
- Jalali: Android's native date pickers and the widgets stay Gregorian.
|
||||
- Accessibility rows done only in part: no text spacing, more contrast or
|
||||
underlined links; no names next to colours; no explanations for technical
|
||||
terms; no default compose font; the text size still reaches only some
|
||||
screens.
|
||||
- Composer extras not built: numeric date placeholders, font size, pasted
|
||||
lists. BIMI shows no verified-mark check.
|
||||
- An expired session still drops an open composer without saving it.
|
||||
- Still open from earlier: run a rule on a folder, one ring per message
|
||||
(#1141), settings sync on 0.16, background reminder refresh without device
|
||||
sync, the real APNs path, QR scan with the camera, SSO pairing, discovery
|
||||
success.
|
||||
|
||||
## Test rig changes (VM 106)
|
||||
|
||||
- servers.conf: stw-rel on stalwartlabs/stalwart:v1.0.1 (backup
|
||||
servers.conf.bak-2026-10-31), reset and seeded with rel_setup.sh: 93 ok,
|
||||
0 failed.
|
||||
- seed.py (backup seed.py.bak-2026-10-31): FileNode creates retry in the
|
||||
draft-18 shape (`@type`, `mediaType`); the TOTP login registers a public
|
||||
client with the data scopes and sends `query` and consent.
|
||||
- register-mobile-client.py registers the first-party bulwark-mobile OAuth
|
||||
client on a 1.0 server (run on stw-rel).
|
||||
- rotate-user.py rotates one test user's password on all servers and in the
|
||||
secrets. userc was rotated on 2026-10-31 after an emulator agent exposed
|
||||
it once (it was typed into the visible username field), and the vault item
|
||||
is updated. Secrets backup: secrets-bak-2026-10-31.tgz (without the
|
||||
root-owned pushtest.json).
|
||||
- stw-v016 (0.16.27) was reset and seeded twice, before each full e2e run.
|
||||
The 0.16 admin password is regenerated each time and kept in the vault
|
||||
item "Stalwart test – admin (0.16)".
|
||||
|
||||
## New questions
|
||||
|
||||
1. Dutch: the overlay mixes "je" (catalog) and "u" (older app strings).
|
||||
Sweep it to one form, and which one?
|
||||
2. English source strings the translator flagged. Fix them in one pass?
|
||||
Title Case strings (files.settings_*, settings.vacation.date_range.title,
|
||||
settings.vacation.message.title, widgets.vacation.title "Vacation
|
||||
Responder" vs "Out of office"), a UK/US spelling mix, `one` branches
|
||||
without # (contacts.import.updated_count,
|
||||
files.trash_unavailable_message), "{count} days" without a plural
|
||||
(calendar.views.n_days, settings.offline.keep_days), fragments
|
||||
(settings.offline.keep_from, use_at_most), a {service} case problem
|
||||
(settings.privacy.push), a bare {count}
|
||||
(settings.keywords.discover.found), identical one/other branches
|
||||
(widgets.calendar.more_this_week, unit_minutes,
|
||||
widgets.draft.edited_minutes), misnamed keys
|
||||
(email_composer.read_receipt_off, widgets.mail.in_outbox), stale "Android
|
||||
only" push texts (settings.notifications.push.err_android_only,
|
||||
status_android_only), the licences_desc wording, desktop words ("Click",
|
||||
"the drawer"), and " - " next to "—".
|
||||
3. ios-perf: re-measure the start time on the bench rig now that it's
|
||||
merged?
|
||||
4. Push the webmail commits (fb3ba0275, f74ff49f5, 3bdbdb136) and the
|
||||
website commit (7292593)? The app's hand-off code and policy need the
|
||||
webmail ones on the server.
|
||||
5. iOS widget: create the App ID org.bulwarkmail.mobile.Widget and add the
|
||||
IOS_WIDGET_PROVISIONING_PROFILE_BASE64 secret?
|
||||
6. Export compliance: with usesNonExemptEncryption true, App Store Connect
|
||||
asks for documentation on the next upload. Who files it?
|
||||
|
||||
## Leftovers on disk
|
||||
|
||||
- Junctions the permission check wouldn't let agents remove: C:\cn
|
||||
(dangling), C:\cd, C:\cp; scratch folder C:\cn-iosfix.
|
||||
- Folders Windows wouldn't delete (already unregistered from git):
|
||||
C:\Users\info\worktrees\react-native-cu-deps and react-native-cu-pgp
|
||||
("Filename too long", each with its own partial node_modules), and the
|
||||
empty locked folders react-native-cu-calcon, -cu-calendar2, -cu-followup,
|
||||
-cu-platform and -cu-compx.
|
||||
- Kept on purpose: C:\ns and C:\Users\info\worktrees\native-sync (decision
|
||||
14), C:\Users\info\worktrees\react-native-cu-build (the build worktree
|
||||
with local-only patches, removed at the end of the run) and the C:\cu
|
||||
junction to it.
|
||||
|
||||
## Inventory (Phase 0, 2026-10-31)
|
||||
|
||||
Written before the fixes; rows closed since are ticked with their commits.
|
||||
Sources, all checked against native main 7a42a15f and webmail main
|
||||
4887ed457:
|
||||
|
||||
- Webmail 1.19.0, 1.19.1 and the 237 commits since 2026-10-29:
|
||||
docs/parity/11-webmail-1.19.md (42 rows, 11 P2, 31 P3). 1.19.1 is the
|
||||
latest webmail release; the changelog has no Unreleased section.
|
||||
- Stalwart 1.0.1 (2026-10-28, the latest release): below.
|
||||
- Leftovers: the unticked rows of docs/parity/01-10, the "Open" and webmail
|
||||
bug lists of docs/native-sync-2026-10-29.md, the deferred items of
|
||||
docs/ux-audit-2026-10.md and docs/ios-test-2026-10.md, open issues and PRs:
|
||||
below.
|
||||
- Dependencies: below.
|
||||
|
||||
### Stalwart 1.0.1
|
||||
|
||||
v1.0.0...v1.0.1 is one squashed commit. Two release-note lines read the wrong
|
||||
way round: the "Fixed" JMAP lines describe the old bug. 1.0.0 answered a
|
||||
Mailbox, AddressBook, ContactCard or Identity create with only `{id}`; 1.0.1
|
||||
adds the server-set and defaulted properties the client left out (plus
|
||||
myRights, mayDelete, isDefault). Calendar adds description, color and
|
||||
timeZone as null and an empty shareWith when omitted; CalendarEvent adds
|
||||
isDraft, useDefaultAlerts, mayInviteSelf, mayInviteOthers and hideAttendees.
|
||||
Every native create caller reads only the id, re-reads, or merges the reply
|
||||
over what it sent (createMailbox src/api/email.ts:284, createAddressBook
|
||||
src/api/contacts.ts:352, createContact :263-275, createIdentity
|
||||
src/api/identity.ts:39-41, createEvent src/api/calendar.ts:1080, device sync
|
||||
src/device-sync/engine/item-sync.ts:1287-1312), except createCalendar.
|
||||
|
||||
Moving stw-rel to 1.0.1 surfaced two seed failures that looked like 1.0.1
|
||||
changes. Neither is: both behave the same on 1.0.0, and the app sends what
|
||||
both versions want.
|
||||
|
||||
- FileNode creates need `@type` (`File`/`Directory`/`SymLink`) and
|
||||
`mediaType` on the released 1.0.x; `nodeType` and `type` are refused. The
|
||||
app sends that shape whenever the account capability lists
|
||||
`supportedNodeTypes` (src/api/files.ts:67-72, :420-428, every create goes
|
||||
through createFileNodeIn :441-460). The seed script's fallback was written
|
||||
for the pre-release stw-v1 build, which still takes the old shape.
|
||||
- `/api/auth` answered 400 "JSON deserialization failed" to the seed's 0.16
|
||||
body: 1.0.x requires `query`. The app's body (src/lib/totp-login.ts:296-321)
|
||||
carries `query` and `consent: true`; probed on 1.0.1 it deserializes and
|
||||
gets as far as the client check.
|
||||
|
||||
Findings:
|
||||
|
||||
- [x] **A new calendar lands in the store without its name and colour** — `P2` — `bug` (also on 1.0.0) — fixed in a2344ea0
|
||||
- What Stalwart does: the Calendar/set create answer never echoes properties the client sent (crates/jmap/src/calendar/set.rs:1307-1367).
|
||||
- What RN does: createCalendar returns `created` alone (src/api/calendar.ts:1269) and the store inserts it (src/stores/calendar-store.ts:1415-1423), so the calendar shows nameless and colourless until the next fetch.
|
||||
- Fix hint: `{ ...props, ...created }`, as createEvent does. Live test in live-stalwart-calendar.
|
||||
|
||||
- [x] **iOS Maestro OAuth flows have no step for the consent page** — `P2` — `test` — fixed in 3769b24b
|
||||
- What Stalwart does: on 1.0.1 the consent page comes after Sign in, once per client grant ("Allow"/"Deny", login.html:462-463), for every client that isn't a Stalwart-installed Application, `bulwark-mobile` included.
|
||||
- What RN does: .maestro/subflows/stalwart-login-page.yaml types and taps Sign in, then inbox.yaml waits for the tab bar. The app doesn't depend on the order (oauth-native.ts never sends `prompt=consent`). The iOS e2e server (`vars.E2E_SERVER`) is the 0.16 server, so CI isn't affected today.
|
||||
- Fix hint: optional `runFlow when visible "Allow"` tap at the end of the subflow.
|
||||
|
||||
- [x] **A `consentRequired` answer reads as a wrong password** — `P3` — `bug` — fixed in 1bef4719
|
||||
- What Stalwart does: new answer `{type:'consentRequired', token}` and request `{type:'authConsent', token, query, consent}` in 1.0.1.
|
||||
- What RN does: readStalwartLoginResponse maps it to invalid credentials (src/lib/totp-login.ts:344-380). Not reachable while `consent: true` is sent. The webmail has the same gap (lib/auth/stalwart-login-request.ts).
|
||||
- Fix hint: answer it with `authConsent` and the same query, or a clear error.
|
||||
|
||||
- [x] **The encrypt-on-upload copy no longer matches the server** — `P3` — `docs` — fixed in 853cb5d0 (comment; the live compose check is not written)
|
||||
- What Stalwart does: 1.0.1 stores JMAP `$draft` messages and the Sent copy unencrypted (crates/email/src/message/ingest.rs:608-611).
|
||||
- What RN does: no workaround for encrypted drafts; the comment at src/api/account-security.ts:249-254 says sent mail is encrypted too.
|
||||
- Fix hint: fix the comment; a live check in live-stalwart-compose that a draft and the Sent copy read back as text with encryption on.
|
||||
|
||||
No action: registry listings now return only the requested account (an admin
|
||||
login no longer sees other accounts' keys, src/api/account-security.ts:299-307);
|
||||
the iTIP PRIORITY fix (RSVP is a participationStatus patch); the
|
||||
FORGED_RCVD_TRAIL fix lowers spam scores the app shows; `denyLocation` left
|
||||
GET /api/auth/client (the app reads only `error`); telemetry, ACME, RocksDB,
|
||||
WebUI and installer changes.
|
||||
|
||||
### Leftovers
|
||||
|
||||
#### Older parity files (01-10)
|
||||
|
||||
95 unticked rows: 77 still open, 3 done but unticked, 7 blocked, 8 not to
|
||||
do. About 15 of the 24 open rows in 01-09 duplicate a file 10 row. The four
|
||||
commits after file 10 close none.
|
||||
|
||||
- Done, to tick: templates sync on Stalwart 1.0 (04, fac318dc; 0.16 still
|
||||
needs settings sync), calendar strings (05, 84fef142), iOS push (08,
|
||||
f62849d1).
|
||||
- Not to do, reasons kept from the rows: identity visibility refresh (04),
|
||||
S/MIME send stub (04, see #58), component set on new calendars #760 (05),
|
||||
push without CalendarEvent types (05), legacy flat-name migration (07),
|
||||
stability banner (07), status bar theming (08), sharing before 0.16.6 (10,
|
||||
below the app's minimum).
|
||||
- Blocked on the webmail: hand-off password in the fragment, org policy,
|
||||
session limits, external sender chip, external recipient confirm, org
|
||||
signature and disclaimer (10), settings sync (08).
|
||||
- Priority changes: up to P2 the DuckDuckGo favicons (leak every sender
|
||||
domain and the device IP, on by default, src/lib/avatar-utils.ts:149,
|
||||
src/stores/settings-store.ts:312), the blockquote freeze, direction-override
|
||||
characters in attachment names, and no notification when the push lookup
|
||||
fails (#1041). Down to P3 the 09 offline-send row (same item as 04 and 10).
|
||||
- New: the move sheet still picks icons by folder name
|
||||
(src/components/MoveSheet.tsx:20-28), the #1235 bug 96c7c045 fixed in the
|
||||
sidebar. The recipient splitter row also covers pasting into the composer.
|
||||
- Status at the end of the run: the four P2 re-ratings are fixed (8e10e73e,
|
||||
3243714d, a930d177, f6e6f8f5), the move sheet icons too (a4d38911), and
|
||||
the S/MIME send stub is replaced by real sending (b0997c0a). Of the
|
||||
webmail-blocked rows, the hand-off code and the org policy, signature and
|
||||
disclaimer are built; session limits, the external sender chip and the
|
||||
external recipient confirm are not.
|
||||
|
||||
The policy route doesn't take a JMAP bearer token (app/api/admin/policy/route.ts:15,
|
||||
lib/auth/instance-details.ts:23). Without a session it still answers the
|
||||
global restrictions, feature gates, defaults and theme policy, and strips only
|
||||
organisation data, scopes, plugins and push relays (route.ts:19-37). If the
|
||||
app kept the webmail URL it gets at pairing or hand-off (src/lib/oauth.ts:292
|
||||
parses it, nothing stores it), the global locks need no webmail change.
|
||||
Built since: the app keeps the webmail URL, and the webmail takes the app's
|
||||
token for the organisation policy (f74ff49f5, not pushed).
|
||||
|
||||
#### Report "Open" (2026-10-29)
|
||||
|
||||
- [ ] **iOS: reader CSP, composer nonce, `target=_blank` taps, #81 keyboard** — `P2` — `partial` — needs an iOS run (e2e-ios.yml in testing step 4) — partly: the iOS build and the tablet sign-in leg pass at 05bc0f56 (run 41216860380); the full run 41220976434 is RESULT PENDING
|
||||
- [x] **iOS push lapses after 7 days without opening the app** — `P2` — `bug` — the extension has no expiry handling (plugins/ios-push/NotificationService.swift), no BGTask; needs a device to verify — fixed in c704c7e6 (compiled in CI, not run on a device)
|
||||
- [x] **Login switch from a deep link or notification keeps the old navigation stack** — `P2` — `bug` — App.tsx:118-122, :141-145, :181-186 switch and navigate on top; question 7 — fixed in f9a36b28
|
||||
- [x] **Sieve editor loses typed text (#1162)** — `P3` — `bug` — src/components/filters/SieveEditorSheet.tsx:35-41 re-seeds on every content change — fixed in 6c3046c8
|
||||
- [x] **No forward limit (#1155, #1208), no vacation length warning** — `P3` — `missing` — FilterRuleModal.tsx:443-459, VacationSettings.tsx:140-142 — fixed in dcbfc907
|
||||
- [x] **Other-zone daily series keeps a wrong recurrenceId on the device's DST day** — `P3` — `bug` — src/lib/recurrence-expansion.ts:526-536 builds candidates from device-local fields — fixed in 6e697baa
|
||||
- [x] **New strings English-only** — `P3` — `partial` — 331 overlay keys missing in 25 locales, 86 in de; question 6 — fixed in 3c0d7ad5 (German by hand in 8345be43, the other locales in six commits after it)
|
||||
- [ ] **Org policy, hand-off password** — blocked on the webmail (above) — partly: hand-off code in ab2e5a60, organisation policy in 9d977773; group-identity scopes, session limits, the external sender chip and recipient confirm, and the org signature's user details are not done
|
||||
- [ ] **`C:\ns` junction and the native-sync worktree still exist** — chore — C:\ns points at C:\Users\info\worktrees\native-sync (e6e5e521, on main) — kept on purpose (decision 14)
|
||||
|
||||
#### Webmail bugs from the 2026-10-29 report
|
||||
|
||||
All 19 are fixed in the app. In the webmail, 17 are still open at current
|
||||
lines, one is fixed (invitation trust: failing DKIM beat dmarc=pass,
|
||||
e1b43c894, dropped) and one is fixed except alerts the settings rows don't
|
||||
show keep their old ids (lib/calendar-alerts.ts:206). The carried list with
|
||||
current lines is in "Webmail bugs" above.
|
||||
|
||||
#### UX audit deferred (docs/ux-audit-2026-10.md)
|
||||
|
||||
- [x] **U-133 no way out of a hung "Signing in"** — `P2` — `missing` — src/screens/LoginScreen.tsx:218 swallows Back while busy, SigningInStep has no cancel; abortable sign-in, M — fixed in 55d008a2
|
||||
- [x] **U-069 recipient suggestions hidden in phone landscape** — `P2` — `missing` — needs a full-screen picker, M (feature) — fixed in e1ad5c09
|
||||
- [x] **U-045 German Prev/Next read "Zurück"/"Weiter"** — `P3` — `bug` — override in locales/rn/de.json — fixed in 25f9b810
|
||||
- [x] **Plain-text mail dark background fixed at #0a0a0a** — `P3` — `partial` — src/lib/email-html.ts:963 ignores the theme — fixed in 43973608
|
||||
- [ ] **U-017 system alert vs Dialog, U-136 empty all-day strip, U-124 language order** — product calls, kept deferred unless you say otherwise — kept (decision 12)
|
||||
- U-016 scrim token: won't fix (no visible change). U-018 font scale resets navigation: React Native limitation. U-131: done (776447dd).
|
||||
|
||||
#### iOS test deferred (docs/ios-test-2026-10.md)
|
||||
|
||||
- [ ] **Sign-in and features matrices empty; the last three release runs of e2e-ios failed** — `P2` — most failures miss seeded mail, so a reset and seed of stw-v016 comes first (testing step 4) — partly: stw-v016 reset and seeded, 11 outdated flows fixed in d9d7a466, pod install fixed in 05bc0f56; the full run is RESULT PENDING
|
||||
- [x] **ios-perf branch never merged** (d13769a9, measured -18% to first inbox) — not part of this run unless you want it — fixed in 36cc49b7
|
||||
- Blocked on a device or setup: real APNs path through the extension, QR scan with the camera, SSO pairing (needs an external IdP), discovery success (needs DNS records), iOS widgets (WidgetKit extension, L). The iOS widget is built since (fbe54576), compiled in CI only.
|
||||
- Done: the six app bugs from the test (on main), the webmail hand-off fix 310f910a6.
|
||||
|
||||
#### GitHub (bulwarkmail/native)
|
||||
|
||||
- Fixed on main, unreleased (close after 0.1.87 ships): #80, #82, #83, #84, #86, #87, #89. #81 is fixed in code (01983a38), unchecked on iOS.
|
||||
- [x] **#91 multi-select without long press** — `P3` — `missing` — EmailListScreen.tsx:190-218; tap on the avatar toggles selection — fixed in 6715df7f
|
||||
- [x] **#90 custom calendar colours** — `P3` — `missing` — CalendarEditSheet.tsx:222 offers the palette only — fixed in d3f6d6ae
|
||||
- [x] **#85 keep the shade open on Delete** — question 1 — fixed in 2fe83669 (the Android side in 9669e07e)
|
||||
- [x] **#58 PGP / S/MIME** — `P3` — L, not in this run — fixed in e5027c45 (reading 8aa1d8e0, sending b0997c0a; no Argon2 keys, WKD or legacy S/MIME ciphers)
|
||||
- [x] **#3 TLS client certificates** — Android done in 0.1.83; iOS has none (L) — fixed in ba150060 (compiled in CI, not run on a device)
|
||||
- [x] **PR #66 unified views for a single account** (lucletoffe) — conflicts, no review, the author's question unanswered — fixed in 5ecf6f32 (the author's 45b4feeb)
|
||||
|
||||
### Dependencies
|
||||
|
||||
Expo SDK 54 (expo 54.0.33, React Native 0.81.5, React 19.1.0). Newest stable
|
||||
is SDK 58 (React Native 0.88, React 19.3), four behind; SDK 54 still gets
|
||||
patch releases.
|
||||
|
||||
Done in this run: every "On SDK 54 now" upgrade below (368c90ce, 38257f81,
|
||||
b2cd83b1, f9a3b761), plus expo-screen-capture, openpgp, pkijs and
|
||||
react-native-quick-crypto. npm audit on 2026-10-31 at the tip: 30 findings
|
||||
(2 high, 27 moderate, 1 low, no critical), down from 53. The two highs are
|
||||
postcss and image-size, both pulled in by Expo's build packages; the
|
||||
postal-mime and React Navigation chains are gone.
|
||||
|
||||
npm audit: 53 findings (2 critical, 17 high, 32 moderate, 2 low). Three chains
|
||||
ship in the app:
|
||||
|
||||
- postal-mime 3.0.0, high, reachable: quadratic CPU on RFC 2047 headers; the
|
||||
app parses .eml attachments and embedded messages with it
|
||||
(src/components/email/use-body-override.ts). Fixed in 4.0.8+; the webmail
|
||||
moved to postal-mime 4 on 2026-10-29 (4aea0976a). The local patch
|
||||
(Blob from typed arrays) has to be redone for 4.2.0.
|
||||
- query-string/decode-uri-component and nanoid via React Navigation, high,
|
||||
not reachable (no `linking` prop; default nanoid size). React Navigation
|
||||
native 7.5.1 drops them.
|
||||
|
||||
The rest (both criticals included: tar, shell-quote) sit in Expo CLI, Metro,
|
||||
the dev server or vitest and run only on dev machines and CI; a lockfile
|
||||
refresh within current ranges clears most.
|
||||
|
||||
Upgrades:
|
||||
|
||||
- On SDK 54 now: postal-mime 4.2.0 (redo the patch; fixes the advisory, risk:
|
||||
the RN Blob path, covered by src/lib/__tests__/postal-mime-rn.test.ts),
|
||||
@react-navigation native 7.5.1 with native-stack and bottom-tabs 7.21.0
|
||||
(drops two advisories, risk: navigation regressions, needs the emulator
|
||||
walk), zustand 5.0.16, jszip 3.10.2, vitest 4.1.12, the SDK 54 patch line
|
||||
(expo 54.0.38, expo-file-system 19.0.24, expo-image-picker 17.0.11,
|
||||
expo-localization 17.0.9), lockfile refresh.
|
||||
- With an SDK bump (its own project): SDK 58 means Gradle 9 and AGP 9 hand
|
||||
edits in android/, removing newArchEnabled/edgeToEdgeEnabled, Node 22 in
|
||||
six workflow lines, iOS 16.4 minimum, Expo's fetch replacing the global
|
||||
fetch (SDK 56), async File.write (StorageSettings.tsx:105,
|
||||
use-file-preview.ts:75), foreground notifications shown by default
|
||||
(calendar-notifications.ts), react-native-webview 14 (mail body and
|
||||
composer), react-native-android-widget 0.26 (the #52 patch can probably
|
||||
go). SDK 57 first keeps AGP 9 as its own step.
|
||||
- Skip: AsyncStorage 3, TypeScript 7, vitest 5.
|
||||
|
||||
## Decisions
|
||||
|
||||
Taken on 2026-10-31, before the fixes:
|
||||
|
||||
1. #85: Delete from the notification skips the unlock only when the account
|
||||
has a Trash; without one the unlock stays.
|
||||
2. Org policy: store the webmail URL from pairing or hand-off and apply the
|
||||
global policy now.
|
||||
3. Webmail changes: yes. The hand-off one-time code and a policy route that
|
||||
takes the app's token get built in the webmail repo too.
|
||||
4. MIG-02: keep the account and ask to sign in again; same for an expired
|
||||
pasted token.
|
||||
5. TOTP password change on 0.16: show a clear "sign in again" message.
|
||||
6. Translations: German by hand, the other 25 locales by an agent.
|
||||
7. Notification or deep link switching the account resets the navigation.
|
||||
8. The four small behaviours stay as they are.
|
||||
9. Port in the account switcher: always when it isn't the default.
|
||||
10. Dependencies: the SDK 54 updates now (postal-mime 4.2, React Navigation
|
||||
7.5.1, patch updates, SDK 54 patch line, lockfile). SDK 58 is separate.
|
||||
11. The large items formerly under "Not in this run": all of them, by agents,
|
||||
on this branch after the fixes.
|
||||
12. U-017, U-124, U-136, U-018 stay as they are.
|
||||
13. PR #66 and the ios-perf branch: take both.
|
||||
14. C:\ns and the native-sync worktree stay on disk.
|
||||
15. Screenshot protection: yes, with the dependency install.
|
||||
16. End the identity provider session at sign-out: yes.
|
||||
17. CI: push to main.
|
||||
|
||||
Taken during the run:
|
||||
|
||||
18. Stalwart 1.0 sharing with directory lookups off: explain it in the
|
||||
app and report it to Stalwart; no DAV path.
|
||||
19. ios-perf fast start kept. Risk: an action in the first round trip
|
||||
after launch on a just-replaced server can go out with old ids.
|
||||
20. Dependency install approved, then `npm ci` in the main checkout (twice:
|
||||
after the dependency updates and after quick-crypto).
|
||||
21. PGP and S/MIME crypto through react-native-quick-crypto;
|
||||
webcrypto-liner removed (it couldn't do the job and pulled an elliptic
|
||||
fork over git+ssh with a critical audit finding).
|
||||
22. iOS minimum 16.4, usesNonExemptEncryption true and the bigger APK
|
||||
accepted.
|
||||
@@ -258,7 +258,7 @@ RN covers the core single-folder loop well (folder tree incl. shared/group accou
|
||||
|
||||
### Tags / keywords
|
||||
|
||||
- [ ] **Tag definition features: visibility, nesting/parent, reorder, rename with migration, unknown tags** — `P3` — `partial` — partial in 3194b5e: unknown `$label:` ids listed/removable in TagSheet; deferred: visibility, nesting, reorder, rename migration (device-local tag model, see native issue #1)
|
||||
- [ ] **Tag definition features: visibility, nesting/parent, reorder, rename with migration, unknown tags** — `P3` — `partial` — partial in 3194b5e: unknown `$label:` ids listed/removable in TagSheet; deferred: visibility, nesting, reorder, rename migration (device-local tag model, see native issue #1) — partly: nesting and sidebar visibility in 585c70a5; reorder and rename with migration still missing
|
||||
- What WEB does: `visibility: show|unread|hide` (`stores/settings-store.ts:180-190`, `components/settings/keyword-settings.tsx:98-132`), nested ids `parent/child` (`lib/keyword-nesting.ts`, `keyword-settings.tsx:213-243`), drag reorder (atomic, changelog 1.9.0), rename keyword migrates messages (`renameKeyword`/`client.migrateKeyword` `lib/jmap/client.ts:1963`), tag picker lists unknown ids present on a message so they can be removed (`components/email/tag-picker.tsx:484-491`).
|
||||
- What RN does: `{ id, label, color }` only (`src/stores/keywords-store.ts:9-13`); `TagSheet` lists defined tags only (`src/components/TagSheet.tsx:76-95`), so a `$label:work/clients` set by WEB can neither be seen nor removed on RN. Tag definitions are device-local (see native issue #1).
|
||||
- Fix hint: show unknown `$label:*` ids found on the selected emails at the bottom of `TagSheet`; add `visibility` when the drawer tag section lands; nesting/reorder are lower priority.
|
||||
|
||||
@@ -71,7 +71,7 @@ Legend for refs: WEB paths are relative to `the webmail repo`, RN paths to `the
|
||||
- What RN does: `sendEmail` sets no `messageId`, relying on the server; RN's `src/lib/uuid.ts:6-20` already has a guarded UUID generator (Hermes fallback) so the crash class of the web fix does not apply.
|
||||
- Fix hint: optional — set `messageId: [`${generateUUID()}@${domain}`]` in `sendEmail` so the id is known before the response (useful once drafts/threading exist).
|
||||
|
||||
- [ ] **Sending from shared/group accounts unsupported** — `P3` — `missing` (partly N/A) — deferred: RN is single-active-account; envelope/identity routing for shared accounts not implemented
|
||||
- [x] **Sending from shared/group accounts unsupported** — `P3` — `missing` (partly N/A) — deferred: RN is single-active-account; envelope/identity routing for shared accounts not implemented — fixed in 31d8e4a4, 893f40a2
|
||||
- What WEB does: routes the submission to the identity's owning account (#461, `email-composer.tsx:2258-2274`, `lib/jmap/client.ts:3131-3134` targetAccountId) and preselects the shared folder's identity (`lib/reply-identity.ts:114-128`).
|
||||
- What RN does: `ComposeScreen.tsx:267-272` explicitly always uses the user's own Sent ("composing on behalf of a shared account isn't supported"), `getIdentities` (`src/api/identity.ts:5-12`) only queries the primary account. Replying from a shared-folder message sends as the reaching login.
|
||||
- Fix hint: low priority; if implemented, query `Identity/get` for the shared `accountId`, submit with that `accountId`, and file into that account's Sent.
|
||||
@@ -260,7 +260,7 @@ Legend for refs: WEB paths are relative to `the webmail repo`, RN paths to `the
|
||||
|
||||
### Security / crypto
|
||||
|
||||
- [ ] **S/MIME sign/encrypt on send is a stub** — `P3` — `missing` — deferred: no raw-MIME send path in RN; certificates are stored only
|
||||
- [x] **S/MIME sign/encrypt on send is a stub** — `P3` — `missing` — deferred: no raw-MIME send path in RN; certificates are stored only — fixed in b0997c0a
|
||||
- What WEB does: crypto plugins hook `onComposeSend` (`email-composer.tsx:2180-2216`) and the store has `sendRawEmail` (`stores/email-store.ts:1724`).
|
||||
- What RN does: `src/components/settings/SmimeSettings.tsx` lists imported certificates ("Your Certificates", "Recipient Certificates") but `ComposeScreen.tsx` has no sign/encrypt control (`grep -i smime|sign|encrypt` → none) and `sendEmail` has no raw-MIME path.
|
||||
- Fix hint: out of scope for parity right now; document as a known gap in the settings pane ("certificates are stored but not yet used for sending").
|
||||
@@ -272,7 +272,7 @@ Legend for refs: WEB paths are relative to `the webmail repo`, RN paths to `the
|
||||
- What RN does: `settings-store.ts:105-106` has `trustedSenders`, but `ComposeScreen.tsx` never touches it (`grep -i trusted` → none).
|
||||
- Fix hint: after a successful reply push `finalTo`/`finalCc` emails into `trustedSenders` (dedupe).
|
||||
|
||||
- [ ] **Offline send is not queued** — `P3` — `missing` (RN-only opportunity) — deferred: needs a new outbox op kind carrying the whole Email/set + submission payload; drafts now autosave to the server instead
|
||||
- [x] **Offline send is not queued** — `P3` — `missing` (RN-only opportunity) — deferred: needs a new outbox op kind carrying the whole Email/set + submission payload; drafts now autosave to the server instead — the offline message (says offline, the text stays) is in a43b55ec. One verdict for the 04, 09 and 10 rows — fixed in 0ec809eb
|
||||
- What RN does: `outbox-store.ts:40-43` only knows `keywords|mailboxes|destroy` ops; `performSend` fails with a network error when offline (`ComposeScreen.tsx:975-979`) and the composed text is lost unless the user keeps the screen open. WEB has no offline outbox either, so this is not a parity item, but once drafts exist the natural mobile behaviour is "save draft locally, submit when online".
|
||||
- Fix hint: add a `send` op kind carrying the Email/set create + submission payload, flushed by `outbox-store.flush`.
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ RN has a solid read path (month/week/agenda, shared-calendar namespacing, per-vi
|
||||
- What RN does: fixed preset list, multiple allowed, non-offset alerts are dropped from the UI but kept until save (`src/components/calendar/EventModal.tsx:299-312`, `src/lib/calendar-alerts.ts:51-70`). Note the "kept until save" comment is wrong: `remindersToAlerts` rebuilds the map from presets only, so an absolute-trigger alert is lost on the next save.
|
||||
- Fix hint: add a custom value+unit row; when rebuilding alerts, carry over alerts whose trigger has no `offset`.
|
||||
|
||||
- [ ] **Duplicate event, export .ics, copy meeting link / title, add note** — `P3` — `missing` — duplicate (+1 day, opens editor), share as .ics (`eventToICS` port + expo-sharing) and copy meeting link done in 5fe8e40; deferred: copy title, append-a-note
|
||||
- [x] **Duplicate event, export .ics, copy meeting link / title, add note** — `P3` — `missing` — duplicate (+1 day, opens editor), share as .ics (`eventToICS` port + expo-sharing) and copy meeting link done in 5fe8e40; deferred: copy title, append-a-note — fixed in baa3ff59 (copy title, add note)
|
||||
- What WEB does: popover/context-menu actions duplicate (+1 day, opens editor), export via `downloadEventICS`, copy meeting link, copy title, append a timestamped note to the description (ref `components/calendar/calendar-app.tsx:1017-1125`, `lib/calendar-ics-export.ts:96, 176`).
|
||||
- What RN does: `EventDetailSheet` has an `onDuplicate` prop that `CalendarScreen` never passes (`src/components/calendar/EventDetailSheet.tsx:59, 336-342`); no export/copy/note.
|
||||
- Fix hint: wire `onDuplicate` (clone like WEB `handleDuplicateFromDetail`), add "Share .ics" via `expo-sharing` + a port of `eventToICS`, add "Copy link" with `Clipboard`.
|
||||
@@ -169,12 +169,12 @@ RN has a solid read path (month/week/agenda, shared-calendar namespacing, per-vi
|
||||
|
||||
### Views / navigation / locale
|
||||
|
||||
- [ ] **Entire calendar UI is hard-coded English although RN has i18n** — `P2` — `missing` — partial in 0f80e98: screen, month/week/agenda views, event card/sheet/modal, drawer, scope dialog, settings, banner and calendar/share sheets use `t()` + date-fns locales; TasksSheet (09c24fa), `formatReminder` (2abea97) and ICalImportSheet (b7f7110) localized too; deferred: ICalSubscriptionSheet and RecurrenceEditor still hard-code English
|
||||
- [x] **Entire calendar UI is hard-coded English although RN has i18n** — `P2` — `missing` — partial in 0f80e98: screen, month/week/agenda views, event card/sheet/modal, drawer, scope dialog, settings, banner and calendar/share sheets use `t()` + date-fns locales; TasksSheet (09c24fa), `formatReminder` (2abea97) and ICalImportSheet (b7f7110) localized too; deferred: ICalSubscriptionSheet and RecurrenceEditor still hard-code English — fixed in 84fef142
|
||||
- What WEB does: everything through `next-intl` (`t('calendar.*')`), month/day names and popover dates localized (changelog 1.6.x "Localize event start date in detail popover and event modal").
|
||||
- What RN does: `src/i18n/index.ts` + `useLocaleStore` are used by 18 other files, but none of `src/components/calendar/*`, `src/screens/CalendarScreen.tsx` or `CalendarSettings.tsx` import it; all labels ("Today", "No events", "Does not repeat", "Going?", RSVP labels, drawer titles, etc.) and `date-fns` `format()` calls without a `locale` are English (`CalendarScreen.tsx:408, 448, 504, 640-641`, `EventModal.tsx:60-67, 326, ...`, `MonthView.tsx:25-26`, `AgendaView.tsx:33-37`).
|
||||
- Fix hint: add a `calendar.*` namespace to `locales/*.json` (WEB's keys can be copied) and pass a date-fns locale from the locale store into `format()`.
|
||||
|
||||
- [ ] **First day of week limited to Monday/Sunday; no Saturday, no Jalali (#490)** — `P3` — `missing` — Saturday (0|1|6) done in 0f80e98; deferred: Jalali month grid needs a port of `lib/jalali-utils.ts` + `useCalendarLocale`
|
||||
- [x] **First day of week limited to Monday/Sunday; no Saturday, no Jalali (#490)** — `P3` — `missing` — Saturday (0|1|6) done in 0f80e98; deferred: Jalali month grid needs a port of `lib/jalali-utils.ts` + `useCalendarLocale` — fixed in f1297b6b (Saturday kept after a restart since 10fb7ab7; Android date pickers and widgets stay Gregorian)
|
||||
- What WEB does: `firstDayOfWeek` 0/1/6 with Saturday default for Persian and a full Jalali month grid / headers (ref `hooks/use-calendar-locale.ts:35-120`, `lib/jalali-utils.ts:161-169`, changelog 1.8.0 #490).
|
||||
- What RN does: `FirstDayOfWeek = 0 | 1` (`src/stores/settings-store.ts:76`), `MonthView`/`WeekView` typed `0 | 1` (`MonthView.tsx:34`, `WeekView.tsx:40`), no Jalali.
|
||||
- Fix hint: widen the type to `0|1|6`, add Saturday to `CalendarSettings`; Jalali is a larger port of `jalali-utils.ts` + a `useCalendarLocale` equivalent (only if the fa locale is added to RN).
|
||||
@@ -214,7 +214,7 @@ RN has a solid read path (month/week/agenda, shared-calendar namespacing, per-vi
|
||||
- What RN does: `formatRange` uses `eventTimeRange` -> `getEventEndDate` (exclusive), so a single-day all-day event on Mar 1 prints "Mar 1 – Mar 2" and a two-day one "Mar 1 – Mar 3" (`src/components/calendar/EventDetailSheet.tsx:63-74`, `src/lib/calendar-utils.ts:84-90`).
|
||||
- Fix hint: for `allDay` use `getEventDisplayEndDate(event)` before comparing/formatting.
|
||||
|
||||
- [ ] **Deep links to calendar/event** — `P3` — `missing` — partly: incoming links are parsed since 5802cae (`src/navigation/linking.ts`), and an event link opens the event since 9282ee6; a date link only opens the Calendar tab
|
||||
- [x] **Deep links to calendar/event** — `P3` — `missing` — incoming links are parsed since 5802cae (`src/navigation/linking.ts`), an event link opens the event since 9282ee6, a date link opens its view and day since fe455370 (same verdict as the 10 row)
|
||||
- What WEB does: `parseCalendarPath`/`buildCalendarPath` handle `/calendar/<view>/<date>?event=` (ref `lib/deep-links.ts:272-300`, changelog 1.8.3 "Deep links for mail, calendar, contacts, files").
|
||||
- What RN does: `Calendar: undefined` route params (`src/navigation/types.ts:33`); `handleDeepLink` hands an event to the Calendar tab through `pending-calendar-open`, the same path a tapped reminder takes, but ignores the date.
|
||||
- Fix hint: accept `{ date?, eventId? }` params on the Calendar route and open the detail sheet.
|
||||
@@ -246,7 +246,7 @@ RN has a solid read path (month/week/agenda, shared-calendar namespacing, per-vi
|
||||
- What RN does: `createEvent` merges the `/set` echo over the payload (`src/api/calendar.ts:272-274`) and appends without expansion (`src/stores/calendar-store.ts:286-304`).
|
||||
- Fix hint: after create, `CalendarEvent/get` the id (RN `getEvents([id])`) and, if `recurrenceRules` is set, run `expandRecurringEvents` for `loadedRange` or call `refresh()`.
|
||||
|
||||
- [ ] **Push: FCM subscription excludes `CalendarEvent`/`Calendar` types** — `P3` — `partial` — deferred: local reminders (8e1d03c) are resynced at launch, on `Calendar`/`CalendarEvent` state changes and on return to the foreground (4b903ed), but nothing refreshes them in the background; adding the calendar types to the push subscription lives in `src/lib/push-notifications.ts` (push area) and would need a background refresh + reschedule hook
|
||||
- [ ] **Push: FCM subscription excludes `CalendarEvent`/`Calendar` types** — `P3` — `partial` — deferred: local reminders (8e1d03c) are resynced at launch, on `Calendar`/`CalendarEvent` state changes and on return to the foreground (4b903ed), but nothing refreshes them in the background; left in the second calendar pass: background reminder refresh without device sync: left, it needs native work: the push subscription would have to carry `CalendarEvent`/`Calendar` for every account (src/lib/push-notifications.ts), PushRouting.kt would have to start a headless JS task for a calendar-only push (today it starts nothing without device sync), and that task would need a reminder scheduler that runs without the restored session and stores the in-app one (src/lib/calendar-notifications.ts) depends on; Android only, and not testable without a device. While the app runs, reminders are rescheduled on every calendar change already (10 row)
|
||||
- What WEB does: push subscription covers calendar types so the sidebar refreshes on external changes (FEATURES.md "JMAP push keeps everything in sync").
|
||||
- What RN does: foreground EventSource/polling dispatches to `useCalendarStore.handleStateChange` (`App.tsx:390-397`, `src/stores/calendar-store.ts:261-284`) — at parity while the app is open; the background push subscription carries only `['EmailDelivery']` since 00faceb (`src/lib/push-notifications.ts`), so calendar changes never wake the app.
|
||||
- Fix hint: leave as is unless background calendar refresh becomes needed.
|
||||
@@ -267,7 +267,7 @@ RN has a solid read path (month/week/agenda, shared-calendar namespacing, per-vi
|
||||
|
||||
### Settings
|
||||
|
||||
- [ ] **No custom time-zone setting (#755); no birthday-calendar colour; no "Day" view on mobile (see above)** — `P3` — `missing` — time-zone setting (`calendarTimeZone`, used for saved events and JMAP queries) done in 58a6f45 + 0f80e98; the editors and views work in calendar-zone wall time since a8e6f6e and 8af163a, and floating task dues since 1a651a9; deferred: birthday-calendar colour setting
|
||||
- [x] **No custom time-zone setting (#755); no birthday-calendar colour; no "Day" view on mobile (see above)** — `P3` — `missing` — time-zone setting (`calendarTimeZone`, used for saved events and JMAP queries) done in 58a6f45 + 0f80e98; the editors and views work in calendar-zone wall time since a8e6f6e and 8af163a, and floating task dues since 1a651a9; deferred: birthday-calendar colour setting — fixed in 574db426 (birthday colour; the time zone and the Day view were done before)
|
||||
- What WEB does: `timeZone: 'auto' | IANA` overriding browser detection, used for display (`displayNow`, `toDisplayDate`) and for the `timeZone` on saved events (ref `lib/timezone.ts:29-75`, `stores/settings-store.ts:317, 553`, changelog 1.9.2 #755); `birthdayCalendarColor` (`settings-store.ts:377, 609`).
|
||||
- What RN does: device zone only (`src/api/calendar.ts:15-21`); birthday colour fixed (`src/lib/birthday-calendar.ts:7`).
|
||||
- Fix hint: add `timeZone` to the settings store (synced with WEB's key so it round-trips through settings sync), pass it as the JMAP `timeZone` arg and into saved events; convert display via `Intl` like WEB's `getWallClock`.
|
||||
|
||||
@@ -73,7 +73,7 @@ RN covers the visible surface reasonably well (list with alphabetical index, det
|
||||
- What RN does: shows the trash icon whenever `books.length > 1 && mayDelete !== false` (`src/components/settings/ContactsSettings.tsx:209`); the server then rejects and the user sees a raw error.
|
||||
- Fix hint: add `!book.isDefault` to the guard.
|
||||
|
||||
- [ ] **Address book share / visibility (subscription) management missing** — `P3` — `missing`
|
||||
- [x] **Address book share / visibility (subscription) management missing** — `P3` — `missing` — sharing fixed in 96b163a1 (same verdict as the 10 row); visibility (subscription): the webmail has no subscription switch for address books to port
|
||||
- deferred: `shareWith`/`myRights` are now requested explicitly (580e010) and typed on `AddressBook`; no sharing sheet on mobile yet (reuse the calendar-sharing sheet when it exists).
|
||||
- What WEB does: `shareAddressBook` via `shareWith/<principal>` patch + ShareCollectionDialog (`stores/contact-store.ts:1064-1085`, `components/contacts/contacts-app.tsx:1142-1158`); sidebar shows a shared indicator (`contacts-sidebar.tsx:879-881`).
|
||||
- What RN does: nothing; `getAddressBooks` requests no `properties` so `shareWith` is whatever the server defaults to (`src/api/contacts.ts:17-24`; WEB requests `ADDRESS_BOOK_PROPERTIES` incl. `shareWith`, `lib/jmap/client.ts:252-261`, #257).
|
||||
@@ -146,7 +146,7 @@ RN covers the visible surface reasonably well (list with alphabetical index, det
|
||||
- What RN does: filter = contacts with no `addressBookIds` (`src/stores/contacts-store.ts:439-440`; `src/screens/ContactsScreen.tsx:146-149`), drawer row passes `count={0}` (`src/components/contacts/ContactsSidebarDrawer.tsx:128-136`) so the count never shows; on Stalwart every card has a book so the view is always empty.
|
||||
- Fix hint: redefine as "no keywords", move the row under the Tags section, compute the count.
|
||||
|
||||
- [ ] **Contact list filters (org / title / location / domain / birthday month / has email-phone-photo) missing** — `P3` — `missing`
|
||||
- [x] **Contact list filters (org / title / location / domain / birthday month / has email-phone-photo) missing** — `P3` — `missing` — fixed in 18c6b03f
|
||||
- deferred: optional; free-text search only.
|
||||
- What WEB does: filter drawer with tri-state chips (`components/contacts/contact-list.tsx:18-76, 154-228, 350-456`; changelog 1.5.x "Revamp contact detail view with filters").
|
||||
- What RN does: free-text search only (`src/lib/contact-utils.ts:216-241`).
|
||||
@@ -189,7 +189,7 @@ RN covers the visible surface reasonably well (list with alphabetical index, det
|
||||
|
||||
### Composer autocomplete
|
||||
|
||||
- [ ] **Autocomplete lacks groups, recent recipients (Sent), on-demand server search and directory principals** — `P2` — `missing`
|
||||
- [x] **Autocomplete lacks groups, recent recipients (Sent), on-demand server search and directory principals** — `P2` — `missing` — fixed in a7a37573 (the composer's on-demand Sent search and directory people; event participants got groups and recent recipients in e988dc29)
|
||||
- store-side done in 26a34d5 (`getAutocomplete` with group entries + recent recipients, `loadRecentRecipients`/`searchRecipients` fed by `src/api/recent-recipients.ts`, `getGroupRecipients`, #672 normalisation); the composer uses `getAutocomplete` and the recent Sent recipients since 5b72c4d. Still open: the on-demand server search (`searchRecipients` has no caller), directory principals, and the calendar's `ParticipantInput`, which still offers contacts only.
|
||||
- What WEB does: `getAutocomplete` merges contacts, group entries (as one chip with `memberCount`), RFC 9670 directory principals and recent Sent recipients, deduped and sanitised (#672) (`stores/contact-store.ts:622-707, 23-49`; `loadRecentRecipients` `:1118-1142` fed from `mail-app.tsx:423`; `searchRecipients` `:1144-1154` used by `components/email/email-composer.tsx:1175-1212`; group chip insertion `:1218-1233`; changelogs 1.6.x "Recipient autocomplete from Sent, with on-demand server search", 1.7.x group chips).
|
||||
- What RN does: suggestions come only from `individuals` matched with `matchesContactSearch` (`src/screens/ComposeScreen.tsx:367-392`); groups are explicitly filtered out and nothing else is consulted. `ParticipantInput` (calendar) likewise flattens contacts only (`src/components/calendar/ParticipantInput.tsx:34-51`).
|
||||
@@ -209,7 +209,7 @@ RN covers the visible surface reasonably well (list with alphabetical index, det
|
||||
- What RN does: none (see previous item).
|
||||
- Fix hint: same sheet as above; show the matching card's phone/org and an "Open" that navigates to `ContactDetail`.
|
||||
|
||||
- [ ] **Contact deep links (`/contacts/<id>[/edit]`, `/contacts/new`)** — `P3` — `missing`
|
||||
- [x] **Contact deep links (`/contacts/<id>[/edit]`, `/contacts/new`)** — `P3` — `missing` — fixed in ecefd1cb (same verdict as the 10 row)
|
||||
- partly: `/contacts/<id>` opens the contact since 5802cae; `/contacts/new` and `/contacts/<id>/edit` only open the Contacts tab.
|
||||
- What WEB does: `parseContactsPath`/`buildContactsPath` (`lib/deep-links.ts:323-329`), applied once on mount (`components/contacts/contacts-app.tsx:182-224`).
|
||||
- What RN does: `src/navigation/linking.ts` (5802cae) maps `/contacts/<id>` to `ContactDetail { contactId }` and every other contacts path to the tab; it never opens `ContactForm`.
|
||||
|
||||
@@ -143,7 +143,7 @@ Filters: RN carries a byte-for-byte port of WEB's Sieve parser/generator/tests a
|
||||
- What WEB does: on first listing, reparents nodes named with `/`, `∕`, `⁄`, `/` separators and replaces blob-backed "dir marker" files with real folders, with a progress overlay (`files-app.tsx:245-263`, `710-734`).
|
||||
- What RN does: reads only the real hierarchy (`src/api/files.ts:6-14` comment acknowledges WEB migrates). A user who never opens WEB sees legacy files as flat names at the root. Acceptable if WEB is always used at least once; otherwise port `migrateLegacyFlatNodes`.
|
||||
|
||||
- [ ] **Deep link `/files/<folder>` and `?preview=` not handled** — `P3` — `missing` (changelog 1.8.1 "Deep links for mail, calendar, contacts, files") — partly: `/files` links open the Files tab since 5802cae; folder paths and `?preview=` are ignored
|
||||
- [x] **Deep link `/files/<folder>` and `?preview=` not handled** — `P3` — `missing` (changelog 1.8.1 "Deep links for mail, calendar, contacts, files") — `/files` links open the Files tab since 5802cae; folder paths, `?preview=` and `?shared=` since d684b00e (same verdict as the 10 row)
|
||||
- What WEB does: `parseFilesPath`/`buildFilesPath` walk the drive to the folder and open the preview (`components/files/files-app.tsx:190-278`; `lib/deep-links.ts:381`).
|
||||
- What RN does: `src/navigation/linking.ts` (5802cae) turns any `/files/...` link into `{ kind: 'files' }` and opens the tab; `Files: undefined` in `src/navigation/types.ts:35`.
|
||||
- Fix hint: parse the rest of the path in `linking.ts`, pass it to `Files` as a `path` param and walk `allNodes` by names.
|
||||
|
||||
@@ -171,7 +171,7 @@ RN toggles that are stored but never read (fix: either wire them or remove the c
|
||||
- What RN does: `setupPushNotificationsInner` calls `native.getToken()` directly (not the guarded `getFcmToken`) so a Firebase rejection (`fcm_token_failed`, typical on de-Googled devices or right after `deleteToken()` during Disable) bubbles up raw; `registerWithRelay` throws `Relay register failed: <status>` without the body (`RN: src/lib/push-notifications.ts:312-313,215-217`); `pollVerificationCode` times out after 75 s with the same message for every cause (`:268`). The UI shows `error.message` verbatim (`NotificationSettings.tsx:95-99`). Disable then immediately Enable also races: `teardownPushNotificationsForAccount` deletes the FCM token when it was the last account (`:477-481`) and the next `getToken()` may be rejected until Firebase re-registers.
|
||||
- Fix hint: read the relay response body into the error, distinguish "no Google Play services" (map to the UnifiedPush finding), skip `deleteToken()` unless the user disables push for all accounts, and log each phase (`token`, `relay`, `jmap create`, `verify`) so the settings pane can show which step failed. Needs a repro on the reporter's device to confirm the exact failure.
|
||||
|
||||
- [ ] **iOS push missing entirely** — `P2` — `missing` — deferred: needs an APNs transport in the relay + iOS token module; the settings pane now states Android-only
|
||||
- [x] **iOS push missing entirely** — `P2` — `missing` — deferred: needs an APNs transport in the relay + iOS token module; the settings pane now states Android-only — fixed in f62849d1
|
||||
- What WEB does: Web Push works on iOS 16.4+ once installed to the home screen (`lib/web-push.ts:157-164,394-398`).
|
||||
- What RN does: `getNative()` returns `null` off Android and setup throws "Push notifications require Android" (`RN: src/lib/push-notifications.ts:125-128,303-304`); the relay has only FCM and Web Push transports (`repos/relay/README.md` endpoints).
|
||||
- Fix hint: needs an APNs transport in the relay (`POST /api/push/register/apns` storing a device token, HTTP/2 to `api.push.apple.com` with a `.p8` key) plus an iOS native module (or `expo-notifications`) for the token and a Notification Service Extension to fetch sender/subject over JMAP. Content-blind payload stays the same.
|
||||
|
||||
@@ -181,7 +181,7 @@ RN's JMAP client (`src/api/jmap-client.ts`, 615 lines) is a thin transport: sess
|
||||
- What RN does: `android:allowBackup="true"` with `@xml/secure_store_backup_rules` from the expo-secure-store plugin, which only excludes SecureStore's own prefs (`android/app/src/main/AndroidManifest.xml:16`); the AsyncStorage SQLite DB (full cached bodies `webmail:offline-cache:entry:v2:*`, outbox, settings, account registry) is backed up to the user's Google account.
|
||||
- Fix hint: add an app-owned `fullBackupContent`/`dataExtractionRules` excluding `databases/RKStorage` (AsyncStorage) or set `allowBackup=false` via an Expo config plugin.
|
||||
|
||||
- [ ] **Send has no offline path and no draft fallback** — `P2` — `missing` — partly: drafts autosave to the server since 5b72c4d, and since 92b42e4 the close dialog no longer drops the text when the draft can't be saved; an offline send is still not queued, and a draft can't be saved offline
|
||||
- [x] **Send has no offline path and no draft fallback** — `P2` — `missing` — partly: drafts autosave to the server since 5b72c4d, and since 92b42e4 the close dialog no longer drops the text when the draft can't be saved; a draft can't be saved offline — the offline message (says offline, the text stays) is in a43b55ec. One verdict for the 04, 09 and 10 rows — fixed in 0ec809eb
|
||||
- What WEB does: no offline queue either, but send failures leave the composer open with the draft autosaved (composer agent's area); send itself has a deadline (#702).
|
||||
- What RN does: `ComposeScreen` shows `Alert('Send failed')` and keeps the modal (`src/screens/ComposeScreen.tsx:975-979`); there is no draft API in `src/api/email.ts` (`grep -n '[Dd]raft' src/api/email.ts` only finds the send create id), so the body is lost if the user leaves, and an offline send is not queued although the outbox exists.
|
||||
- Fix hint: either add an `outbox` `send` op kind (blob uploads must already be done) or at minimum persist the composer state locally on failure.
|
||||
@@ -231,7 +231,7 @@ RN's JMAP client (`src/api/jmap-client.ts`, 615 lines) is a thin transport: sess
|
||||
- What RN does: `onShouldStartLoadWithRequest` returns `true` for any `data:` URL (`src/components/EmailBodyView.tsx:715-718`), so `<a href="data:text/html,...">` replaces the message with attacker HTML rendered inside the app chrome (phishing form); the WebView is otherwise well hardened (`originWhitelist=['about:blank']`, file access off, `mixedContentMode="never"`, `setSupportMultipleWindows={false}`, `incognito`, `domStorageEnabled={false}`, `EmailBodyView.tsx:675-712`).
|
||||
- Fix hint: allow `data:` only for `request.isTopFrame === false` or block it entirely (the body is loaded via `source={{html}}`, not a data: URL).
|
||||
|
||||
- [ ] **No screenshot / recent-apps protection option** — `P3` — `missing`
|
||||
- [x] **No screenshot / recent-apps protection option** — `P3` — `missing` — fixed in 0ae33f3a (the Android 14 launch crash it caused in d2d44e0d)
|
||||
- What WEB does: N/A.
|
||||
- What RN does: no `FLAG_SECURE`/`expo-screen-capture` anywhere; mail content appears in the Android recents thumbnail.
|
||||
- Fix hint: optional "Hide content in app switcher" setting using `expo-screen-capture` `preventScreenCaptureAsync`.
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
# Webmail 1.10.0 to 1.18.0 (and Unreleased)
|
||||
|
||||
## Summary
|
||||
The last parity pass was against webmail 1.9.2. This file walks every changelog entry from 1.10.0 to 1.18.0, the Unreleased section and the commits after the 1.18.0 release (b0d1a97bb..main, 2026-10-29), checked against native `feat/stalwart-1.0` with main merged in (f1db6b48). Web paths are relative to the webmail root, native paths to this repo.
|
||||
|
||||
What stands out:
|
||||
- Several webmail security fixes never reached the app: the HTML sanitizer lets `<img src=x/onerror=…>` through, a sender can fake a DMARC pass in the auth chips, mailto unsubscribe sends to every address, a crafted winmail.dat freezes the JS thread, and the webmail hand-off still puts the password in the `bulwarkmobile://` fragment.
|
||||
- Sieve interop broke in both directions: the app reads webmail 1.13+ scripts (metadata v2) as raw Sieve and its "Reset to visual builder" wipes them, and a save on the phone broadens "is the address" rules into substring matches.
|
||||
- Account switches still leak: the composer follows a switch with an open draft (and its autosave can destroy another account's message), calendar, task and contact loads from the previous account land after a switch, Undo/Send now of a delayed send goes through the active login, and push renewal only reaches the active account.
|
||||
- Everything the organisation policy drives (locked settings, session length, external recipient confirm and label, org signature and disclaimer) has no source on the phone: the policy is served by the webmail at `/api/admin/policy` behind its session cookie (`lib/auth/instance-details.ts:23`), and the app forgets which webmail it was paired with (`src/lib/oauth.ts:292`). These need a webmail change first (see "Needs a decision").
|
||||
- Features that live on the JMAP server and only need porting: shared tags and team templates (JMAP Metadata `org.bulwarkmail/*`), recover deleted items (`x:ArchivedItem`), delegates (`shareWith`), Files trash, save attachment to Files, scheduled message view.
|
||||
|
||||
Classification: `bug` = fixed in the webmail, still live in the app; `missing`; `partial`. Not applicable on mobile and left out: admin panel, plugins and marketplace, command palette, keyboard shortcuts, print, Bulwark Lite, CSP/third-party switch, Mountain View, desktop-only layout (drag and drop, hover, multi-pane), server routes the app doesn't use, Docker, docs and translation-only entries.
|
||||
|
||||
Stalwart 1.0.0 items found in the same pass (1.0 SSO scopes, Files on 1.0, #1385) are tracked with the Stalwart 1.0 work and not repeated here.
|
||||
|
||||
## Findings
|
||||
|
||||
### Security
|
||||
|
||||
- [x] **HTML sanitizer lets `on*` handlers through after a slash** — `P1` — `bug` — fixed in f5e997ba, 07679cbf, 47a1d249, 440a2e7a
|
||||
- What WEB does: DOMPurify for mail and drafts (`lib/email-sanitization.ts`, GHSA-g35q-3gpc-x2rr).
|
||||
- What RN does: the handler regex needs whitespace before `on*` (`src/lib/email-html.ts:142-145`), so `<img src=x/onerror=…>` survives. The viewer WebView (`src/components/EmailBodyView.tsx:793`) and the editor (`src/lib/editor-html.ts:37`, seeded at `src/screens/ComposeScreen.tsx:557`) allow inline script; a received mail can navigate without a tap (`EmailBodyView.tsx:841-843`).
|
||||
- Fix hint: drop every `on*` attribute whatever precedes it (or parse), and stop allowing inline handlers.
|
||||
|
||||
- [x] **A sender can fake a DMARC/DKIM pass in the auth chips** — `P1` — `bug` — fixed in ef9561f1, 8adef1e1
|
||||
- What WEB does: splits Authentication-Results on `;` outside quotes and comments, reads the method at the start of each part, takes DKIM/DMARC from the topmost header only (`lib/email-headers.ts:231`, 8f7124c87).
|
||||
- What RN does: matches `dkim=`/`dmarc=` anywhere in all headers joined (`src/lib/email-headers.ts:143`, `:152`, `:265`); this also feeds invitation trust.
|
||||
- Fix hint: port the web parser.
|
||||
|
||||
- [x] **mailto unsubscribe sends to every address and the confirm hides it** — `P1` — `bug` — fixed in 942f4517, 918571c3
|
||||
- What WEB does: one address, ignores `to=`/`cc=`, caps subject and body, shows them in the confirm (`lib/validation.ts:180`, `components/email/unsubscribe-banner.tsx:42`, f0aa0eeda).
|
||||
- What RN does: sends to every to/cc and `to=` address with the sender's subject and body (`src/lib/unsubscribe.ts:95-113`); the confirm only says an email will be sent (`src/components/email/UnsubscribeBanner.tsx:139-147`).
|
||||
- Fix hint: port parseUnsubscribeMailto and the confirm text.
|
||||
|
||||
- [x] **A crafted winmail.dat freezes the JS thread** — `P1` — `bug` — fixed in 855d304b
|
||||
- What WEB does: clamps the MAPI value count and stops on a read that doesn't advance (`lib/tnef.ts:173`, 012fe6eda).
|
||||
- What RN does: trusts the count (up to 2^32) and loops on non-advancing reads (`src/lib/tnef.ts:201`, `:211`).
|
||||
- Fix hint: clamp to remaining/4, break on a null or non-advancing read.
|
||||
|
||||
- [x] **Webmail hand-off returns the password in the `bulwarkmobile://` fragment** — `P2` — `bug` (cross-repo) — fixed in ab2e5a60 (webmail side fb3ba0275)
|
||||
- What WEB does: `app/(main)/[locale]/login/page.tsx:426`, `lib/auth/mobile-handoff.ts:31` put the password in the redirect.
|
||||
- What RN does: reads it from the fragment (`src/lib/oauth.ts:247`); any app can claim a custom scheme.
|
||||
- Fix hint: hand back a one-time code and redeem it like QR pairing (`src/lib/oauth.ts:435`, `/api/auth/pair/redeem`). Needs the webmail side first.
|
||||
|
||||
- [x] **Sender favicons are fetched from icons.duckduckgo.com on the device** — `P3` — `partial` (privacy) — fixed in 8e10e73e (off by default; the push background task keeps its own default, see the reader report)
|
||||
- What WEB does: proxies favicons on the server (`app/api/favicon/route.ts:484`).
|
||||
- What RN does: fetches directly, on by default (`src/lib/avatar-utils.ts:149`, `src/stores/settings-store.ts:312`), leaking sender domains and the device IP.
|
||||
- Fix hint: default off, or use the paired webmail's proxy.
|
||||
|
||||
### Accounts and sign-in
|
||||
|
||||
- [ ] **Org user policy and "Set by your organisation"** — `P2` — `missing` (blocked) — partly: applied in 9d977773, locked and hidden settings in f27e2ceb, colour mode in b7b3dd4c, archiving off in efb92593 and 13dcbdc0, delegates and recovery in bc47c6a1; not yet: share rights for mail folder presets, group-identity scopes, Archive on the Android widget
|
||||
- What WEB does: locks or hides settings per domain or group (`stores/policy-store.ts:110`, `hooks/use-restriction.ts:22`, `components/settings/org-locked-note.tsx:14`), incl. colour mode #1280, archiving off #1257, tags above shared mailboxes #1334, share rights, hiding delegates or recovery.
|
||||
- What RN does: nothing (`src/stores/settings-store.ts:453`); the paired webmail is forgotten (`src/lib/oauth.ts:292`).
|
||||
- Fix hint: keep the webmail URL at hand-off/pairing and fetch the policy with a bearer token. Needs a webmail route that accepts a JMAP bearer token. See "Needs a decision".
|
||||
|
||||
- [ ] **Admin-set idle sign-out and maximum session length** — `P2` — `missing` (blocked)
|
||||
- What WEB does: `components/session/session-timeout-guard.tsx:94`, `lib/session-policy/limits.ts:36`, refuses to restore over-age sessions (6e5d8faec), saves drafts first (79c697a93).
|
||||
- What RN does: stays signed in for good (`src/stores/auth-store.ts:929`, `App.tsx:818`).
|
||||
- Fix hint: needs the policy; then an AppState idle timer and a max-age check in restoreSession, drafts flushed first.
|
||||
|
||||
- [x] **With TOTP on, changing the password and turning TOTP off fail** — `P2` — `bug` — fixed in 99a42128
|
||||
- What WEB does: asks for the current code and sends it (`components/settings/account-security-settings.tsx:52`).
|
||||
- What RN does: sends neither (`src/api/account-security.ts:346-353`, `:387-395`; `src/components/settings/AccountSecuritySettings.tsx:292`); Stalwart answers "Current OTP code is required".
|
||||
- Fix hint: ask for the code and patch `otpAuth/otpCode` (not the whole object).
|
||||
|
||||
- [x] **A full sign-out keeps the search history** — `P2` — `bug` — fixed in 986216f4, 639aa4aa
|
||||
- What WEB does: `stores/auth-store.ts:1301`, `lib/sign-out-cleanup.ts:22`.
|
||||
- What RN does: clearAllFeatureStores (`src/stores/auth-store.ts:126-134`) never clears `src/stores/search-history-store.ts:44`.
|
||||
- Fix hint: clear it, and check the other persisted stores in the same pass.
|
||||
|
||||
- [x] **Internationalized domains** — `P3` — `missing` — fixed in f5875f87 (sign-in, discovery, account rows), 4932764a (composer recipients sent in ASCII form; own-domain senders shown as written in the composer and reader), b35d15ca (mail list rows), ecf1f3ec (auto-reply forward address in ASCII form); unit tests only, the test servers have no IDN domain
|
||||
- What WEB does: `lib/idn.ts:25` (#1100). What RN does: ASCII-only checks (`src/lib/recipients.ts:31`, `src/lib/server-discovery.ts:78`).
|
||||
- Fix hint: toASCII on the wire, Unicode on screen.
|
||||
|
||||
- [x] **Sign-out doesn't end the identity provider session** — `P3` — `missing` — fixed in 5a301d62
|
||||
- What WEB does: `app/api/auth/token/route.ts:90` (#905). What RN does: `src/lib/oauth.ts:219`, `src/lib/oauth-native.ts:158`.
|
||||
- Fix hint: open end_session_endpoint, or use an ephemeral browser session.
|
||||
|
||||
- [x] **Explain a refused token exchange on TOTP sign-in** — `P3` — `partial` — fixed in 37e72c12
|
||||
- What WEB does: `stores/auth-store.ts:153`, `:1432`. What RN does: `src/lib/totp-login.ts:148`, `:156` throw token_exchange_failed; `src/lib/login-errors.ts:129` has no copy.
|
||||
|
||||
- [ ] **Shared account settings scope for calendar and contacts** — `P3` — `missing` (old 01 item)
|
||||
- What WEB does: `components/settings/account-settings.tsx:32`. What RN does: `src/lib/capabilities.ts:86` covers filters and vacation only.
|
||||
|
||||
### Mail list and folders
|
||||
|
||||
- [x] **The list stops loading older mail when the server sends no total (#1364)** — `P2` — `bug` — fixed in 58bc4aca
|
||||
- What WEB does: keeps paging while full pages come back (`stores/email-store.ts:6508-6515`).
|
||||
- What RN does: a missing total becomes 0 (`src/api/email.ts:529`, `:757`) and paging stops (`src/stores/email-store.ts:1113`).
|
||||
|
||||
- [x] **View scheduled messages; Cancel send puts them back in Drafts (native #84)** — `P2` — `partial` — fixed in 14d009de, 51b3208b, c817f978
|
||||
- What WEB does: opens scheduled mail with a banner (Send now, Reschedule, Cancel) (`components/mail/mail-app.tsx:2523`, `components/email/email-viewer.tsx:773`, f7adbf1ef, 780b64461, 831a3e6b8).
|
||||
- What RN does: a row tap only opens the action sheet (`src/screens/ScheduledScreen.tsx:278`); Cancel leaves the copy in Sent (`:129-132`, `src/api/email.ts:2079`).
|
||||
- Fix hint: open the row in the thread screen with a scheduled banner; make Cancel do the cancel-to-Drafts that Edit already does.
|
||||
|
||||
- [x] **Shared tags in group mailboxes (#1310, #470)** — `P2` — `missing` — fixed in b7b9c7e0, ba672050 (read-only; editing stays in the webmail)
|
||||
- What WEB does: reads and writes `org.bulwarkmail/tags` via JMAP Metadata on the group account (`lib/shared-tags.ts:185`, `components/settings/shared-tag-settings.tsx`, `lib/jmap/bulwark-metadata.ts`).
|
||||
- What RN does: local tag names only (`src/stores/keywords-store.ts:5`, `src/components/settings/KeywordSettings.tsx`).
|
||||
- Fix hint: read the group's tags through Metadata (Stalwart 1.0 implements it; the 0.16 guard from 42751a6b stays). Locking needs the policy.
|
||||
|
||||
- [x] **Search leaves out Spam and Trash by default** — `P2` — `missing` — fixed in a412b516, 2b5257bf
|
||||
- What WEB does: `inMailboxOtherThan` trash/junk unless "All folders" is picked (`lib/unified-mailbox.ts:98`, 763732fdc, 3904af2cb).
|
||||
- What RN does: no mailbox filter (`src/stores/email-store.ts:268-271`, `:492`).
|
||||
|
||||
- [x] **Unified "All mail" search leaves out Sent, Archive and Drafts** — `P2` — `bug` — fixed in 484cac58
|
||||
- What WEB does: with a query, excludes only trash and junk (`lib/unified-mailbox.ts:510`).
|
||||
- What RN does: applies the view exclusions also with a query (`src/api/unified-inbox.ts:353`, `:35`).
|
||||
|
||||
- [ ] **Undo after a move to another account and in the unified inbox** — `P3` — `partial`
|
||||
- What WEB does: `stores/email-store.ts:5177` (ed9fa2de4), `lib/email-action-toast.ts` (6c5aead20).
|
||||
- What RN does: Undo exists within an account and in spanning lists, not after crossAccountMove (`src/stores/email-store.ts:1540-1550`, `:1786-1796`) or for `src/screens/UnifiedInboxScreen.tsx:246-270`.
|
||||
|
||||
- [ ] **Archive without an Archive folder; unified archive modes (#1176, #1178)** — `P3` — `missing`
|
||||
- What WEB does: creates Archive and reuses year/month folders (`stores/email-store.ts:1332`, `:2561`).
|
||||
- What RN does: returns silently (`src/stores/email-store.ts:1594`, `:1729`) or throws (`src/api/unified-inbox.ts:546`).
|
||||
|
||||
- [x] **A server's own "all" mailbox is included in All mail/Unread (#1241)** — `P3` — `bug` — fixed in 43bb5e47
|
||||
- What WEB does: role `all` excluded (`lib/jmap/types.ts:1158`). What RN does: `src/api/unified-inbox.ts:34`, `:354`.
|
||||
|
||||
- [x] **Tag views and counts include Trash and Spam (#1156)** — `P3` — `bug` — fixed in eb6bf6d3
|
||||
- What WEB does: `lib/unified-mailbox.ts:104`, `:577`. What RN does: `src/api/tag-counts.ts:37-48`, `src/stores/email-store.ts:455`.
|
||||
|
||||
- [x] **User folders named like system folders get system icons (#1235, native #80)** — `P3` — `bug` — fixed in 96c7c045
|
||||
- What WEB does: icons by role only (`components/layout/sidebar.tsx:168`). What RN does: name matching fallback (`src/components/SidebarDrawer.tsx:92`).
|
||||
|
||||
- [x] **Sort by "Date sent" falls back to received date in merged lists (#1219)** — `P3` — `bug` — fixed in 04a962a6
|
||||
- What WEB does: lists fetch sentAt (`lib/jmap/client.ts:443`). What RN does: EMAIL_LIST_PROPERTIES lacks it (`src/api/email.ts:22`; comparator `src/lib/message-list-order.ts:225`).
|
||||
|
||||
- [x] **Previews start with a style sheet (#1136)** — `P3` — `bug` — fixed in 219aef04 (list, unified list, conversation cards; notifications: platform)
|
||||
- What WEB does: cleanPreview (`lib/utils.ts:320`, `:363`). What RN does: `src/screens/EmailListScreen.tsx:270`, `src/lib/push-background-task.ts:513`.
|
||||
|
||||
- [x] **Paperclip on messages with only inline images** — `P3` — `bug` — fixed in 4470a4ba (folder list)
|
||||
- What WEB does: `components/email/attachment-chips.tsx:165`. What RN does: `src/screens/EmailListScreen.tsx:237`, `src/screens/UnifiedInboxScreen.tsx:358` use hasAttachment.
|
||||
|
||||
- [x] **List row grows when its attachment chips arrive** — `P3` — `bug` — fixed in 4470a4ba
|
||||
- What WEB does: reserves the row (7b25d3a95). What RN does: `src/components/email/ListAttachmentChips.tsx:89`.
|
||||
|
||||
- [x] **Cold start reopens the last folder or search instead of the inbox** — `P3` — `bug` — fixed in 9067908f
|
||||
- What WEB does: 64b39c504. What RN does: `src/stores/email-store.ts:2195`, `:829`.
|
||||
|
||||
- [x] **List keeps its scroll position on folder, tag or search switch (#1138)** — `P3` — `bug` (from the code, not seen on a device) — fixed in aa25d08d
|
||||
- What RN does: `src/screens/EmailListScreen.tsx:1590`.
|
||||
|
||||
- [ ] **Unified inbox live updates for other logins (#1350)** — `P3` — `partial`
|
||||
- What RN does: `src/screens/UnifiedInboxScreen.tsx:154-167` listens to the active login only.
|
||||
|
||||
- [ ] **Copy to another account (#1150); move several folders (#1173); move menu order in a shared mailbox (#1149)** — `P3` — `missing`
|
||||
- What WEB does: `hooks/use-message-file-actions.ts:94`, `components/settings/folder-settings.tsx:202`. What RN does: `src/components/MoveSheet.tsx:31`, `src/components/settings/FolderSettings.tsx:137-173`, `src/lib/mailbox-tree.ts:162-205`.
|
||||
|
||||
- [ ] **Hide shared mail accounts (#1228); remove from this folder (#615); folder name on all-folder results (#1172); own folders section (#1205); per-folder counter (#982); hide account dot (#912)** — `P3` — `missing`
|
||||
- What WEB does: `lib/shared-mail-visibility.ts:19`, `hooks/use-remove-from-folder.ts:15`, `stores/settings-store.ts:695`. What RN does: `src/components/SidebarDrawer.tsx:124-150`, `:672`; `src/screens/EmailListScreen.tsx:103`.
|
||||
|
||||
- [x] **Search folder picker is flat and capped at 12** — `P3` — `partial` — fixed in e5c9433a
|
||||
- What WEB does: `lib/search-scope-folders.ts`. What RN does: `src/screens/EmailListScreen.tsx:1729-1740` (`slice(0, 12)`).
|
||||
|
||||
- [ ] **Tags without a definition are grey; no account tint (#1052); tag visibility, nesting, order** — `P3` — `partial` — partly: nesting and visibility in 585c70a5; grey undefined tags, account tint and order still open
|
||||
- What WEB does: `hooks/use-keyword-format.ts`, `stores/settings-store.ts:391`. What RN does: `src/screens/EmailListScreen.tsx:164`, `src/stores/keywords-store.ts:9`.
|
||||
|
||||
- [ ] **Today/yesterday follow the time zone setting (#1259); .eml import and cross-account moves keep the date (#891)** — `P3` — `partial`
|
||||
- What RN does: `src/lib/date-format.ts:111-115` (device zone); `src/api/email.ts:1059-1072` sends no receivedAt.
|
||||
|
||||
- [ ] **Reorder folders; custom folder icons** — `P3` — `missing` (old 02 items)
|
||||
- What WEB does: `stores/email-store.ts:7502`, `components/settings/folder-settings.tsx:261`, `:701`. What RN does: `src/components/settings/FolderSettings.tsx:350`, `:32` (`src/api/email.ts:297` already takes sortOrder).
|
||||
|
||||
- [ ] **RTL swipe sides** — `P3` — `partial` (old 08 item; web doesn't swap either)
|
||||
- What RN does: `src/components/SwipeableRow.tsx:353`.
|
||||
|
||||
- [x] **`/mail/folder/<ref>` deep link opens only the tab** — `P3` — `bug` — fixed in ed490b71 (folder of the active account; `?account=` switches first as before)
|
||||
- What RN does: `src/navigation/linking.ts:134` parses it, `:285-287` drops it.
|
||||
|
||||
### Reading mail
|
||||
|
||||
- [x] **Unverified-sender warning, badge, no "Always trust" for forged mail** — `P2` — `partial` — fixed in cc21317b
|
||||
- What WEB does: getSenderVerification (`lib/email-headers.ts:112`), banner and badge (`components/email/email-viewer.tsx:4037`).
|
||||
- What RN does: a spoof line in the collapsed details only, first DKIM signature only, trust still offered (`src/components/email/MessageHeader.tsx:108`, `:273`; `src/lib/email-headers.ts:99`; `src/components/EmailBodyView.tsx:771`).
|
||||
|
||||
- [ ] **External sender chip** — `P2` — `missing` (blocked on the policy)
|
||||
- What WEB does: `lib/external-mail.ts:119`, `components/email/external-sender-chip.tsx`, `components/email/email-viewer.tsx:903`.
|
||||
- What RN does: `src/components/email/MessageHeader.tsx:159`, nothing.
|
||||
- Fix hint: internal and partner domains come from the policy; identity domains would only be a fallback.
|
||||
|
||||
- [x] **Invitation trust uses unaligned SPF/DKIM and flags colleagues (#1374)** — `P2` — `bug` — fixed in 7a95d43f, 1bce37bb, 6133bcd9
|
||||
- What WEB does: alignment check and own-domain exemption, REPLY/COUNTER checked against the answering attendee (`lib/calendar-invitation.ts:210-216`, `:400-422`).
|
||||
- What RN does: `src/lib/calendar-invitation.ts:325-328`, `:375-376`.
|
||||
|
||||
- [x] **Copy chip for sign-in and verification codes** — `P2` — `missing` — fixed in b1550f14
|
||||
- What WEB does: `lib/verification-code.ts:275`, `components/email/email-viewer.tsx:1378`, setting at `stores/settings-store.ts:540`.
|
||||
- What RN does: nothing (`src/components/email/MessageHeader.tsx`, `src/screens/EmailListScreen.tsx`).
|
||||
|
||||
- [x] **A mail full of blockquotes freezes the reader** — `P3` — `bug` — fixed in 3243714d
|
||||
- What WEB does: single walk (`lib/quote-collapse.ts:92`). What RN does: quadratic walk (`src/lib/quote-collapse.ts:95-112`).
|
||||
|
||||
- [x] **Attachment names keep direction-override characters** — `P3` — `bug` — fixed in a930d177
|
||||
- What WEB does: `lib/bidi.ts:10`. What RN does: `src/lib/attachment-display.ts:34`, `src/lib/email-export.ts:56`.
|
||||
|
||||
- [x] **Part names that are paths (#1371, #1373)** — `P3` — `bug` — fixed in 4d545f8f
|
||||
- What WEB does: `lib/jmap/download-name.ts:11`. What RN does: `src/api/jmap-client.ts:1350` puts `%2F` into the download URL.
|
||||
|
||||
- [x] **Dark mode recolours instead of inverting** — `P3` — `missing` — fixed in 23b2bf97
|
||||
- What WEB does: `lib/email-dark-mode.ts:48`, `:71`. What RN does: `filter: invert(1) hue-rotate(180deg)` (`src/lib/email-html.ts:500`); mail's own dark styles follow the system scheme (`:640`).
|
||||
|
||||
- [x] **"via" badge repeats the sender's address** — `P3` — `bug` — fixed in fac54b41
|
||||
- What WEB does: `components/email/email-identity-badge.tsx:69`. What RN does: `src/components/email/MessageHeader.tsx:116-117`, `:223`.
|
||||
|
||||
- [ ] **Fixed-width tables don't reflow (#1020)** — `P3` — `partial`
|
||||
- What WEB does: `lib/email-fit-width.ts:111`. What RN does: shrinks the whole mail (`src/components/EmailBodyView.tsx:277-290`).
|
||||
|
||||
- [ ] **Invitation banner: View in calendar, collapse, counter-proposal review** — `P3` — `missing` (old 03 item)
|
||||
- What WEB does: `components/email/calendar-invitation-banner.tsx:727`, `:463`, `:245`. What RN does: `src/components/email/CalendarInvitationBanner.tsx:153`.
|
||||
|
||||
- [x] **Quick reply can be turned off (#1370)** — `P3` — `missing` — fixed in 56e4a1e0 (setting `quickReplyEnabled`; the switch is the platform agent's)
|
||||
- What WEB does: `stores/settings-store.ts:560`. What RN does: always rendered (`src/screens/EmailThreadScreen.tsx:1233`).
|
||||
|
||||
### Composer and sending
|
||||
|
||||
- [x] **An open draft follows an account switch; its autosave can destroy another account's message** — `P1` — `bug` — fixed in 9daa3441, 388f46b5, 4e57b191
|
||||
- What WEB does: pins the account when the composer opens (`components/email/email-composer.tsx:506`, d58344329).
|
||||
- What RN does: reads Drafts/Sent, identities and `jmapClient.accountId` live (`src/screens/ComposeScreen.tsx:443-455`, `:778`, `:1355`; `src/api/email.ts:1899`, `:1928-1930`). A notification tap or deep link switches with the composer open (`App.tsx:117-120`); the next autosave creates the draft in the other account and destroys `previousDraftId` there.
|
||||
- Fix hint: pin account, identities and mailboxes at open; pass the account to saveDraftVersion, sendEmail and uploads.
|
||||
|
||||
- [x] **Undo or Send now of a delayed send goes through the active login** — `P1` — `bug` — fixed in e81f0071, 893f40a2
|
||||
- What WEB does: GHSA-8x23 (`components/mail/mail-app.tsx:2549`).
|
||||
- What RN does: the pending send has no login (`src/stores/send-undo-store.ts:89`, `:109-118`; `src/screens/ComposeScreen.tsx:2202`); after a switch the cancel hits the same submission id on another server.
|
||||
- Fix hint: store the login with the pending send; refuse or clear on a switch.
|
||||
|
||||
- [x] **Reschedule and Send now create the replacement before cancelling (#1308)** — `P2` — `bug` — fixed in 22f2ac5b, fdf8eece
|
||||
- What WEB does: cancels first, restores on failure (`lib/jmap/client.ts:11532-11664`).
|
||||
- What RN does: creates first (`src/api/email.ts:2189`).
|
||||
|
||||
- [x] **A send the server doesn't confirm is reported as sent (#1019)** — `P2` — `bug` — fixed in 3eac700a
|
||||
- What WEB does: `lib/jmap/client.ts:994`, `:5115`.
|
||||
- What RN does: returns success without a submission id (`src/api/email.ts:1857`), drops the draft and closes (`src/screens/ComposeScreen.tsx:2158`).
|
||||
|
||||
- [x] **Send and save drafts as a group or shared identity (#1090)** — `P2` — `missing` (old 04 item too) — fixed in 31d8e4a4, 6414b0d6, 893f40a2
|
||||
- What WEB does: resolveSendAccountId (`lib/jmap/client.ts:6257`), identities of every account (`components/email/email-composer.tsx:214`, `:271`).
|
||||
- What RN does: primary account's identities only (`src/api/identity.ts:17`, `src/screens/ComposeScreen.tsx:778`); sendEmail has no account (`:2151`).
|
||||
|
||||
- [x] **Team templates, personal templates in the account, org template library** — `P2` — `missing` — fixed in fac318dc (personal + team read-only; org library needs the policy)
|
||||
- What WEB does: templates in JMAP Metadata with tombstones (`lib/templates/personal-templates.ts:141`, `lib/templates/team-templates.ts:57`, `lib/templates/org-library.ts:13`); on 1.0 the webmail moves them into the account.
|
||||
- What RN does: local only (`src/stores/templates-store.ts:30`, `src/components/TemplateSheet.tsx`).
|
||||
- Fix hint: read and write `org.bulwarkmail` templates via Metadata. The org library and locks need the policy.
|
||||
|
||||
- [x] **Attach from Files (#1179)** — `P2` — `missing` — fixed in 8e07b7d8
|
||||
- What WEB does: `components/email/email-composer.tsx:4574`. What RN does: device picker only (`src/screens/ComposeScreen.tsx:1686-1711`).
|
||||
|
||||
- [ ] **External recipient confirm (composer, quick reply, invitations)** — `P2` — `missing` (blocked on the policy)
|
||||
- What WEB does: `lib/outgoing/external-recipients.ts:53`, `:107`, `components/email/external-recipient-confirm.tsx`.
|
||||
- What RN does: nothing (`src/screens/ComposeScreen.tsx:2151`, `src/components/email/QuickReplyBox.tsx`, `src/components/calendar/EventModal.tsx`).
|
||||
|
||||
- [ ] **Org signature with user details; legal disclaimer on every sent message** — `P2` — `missing` (blocked on the policy) — partly: org signature and disclaimer in fe3f860e; the user details (title, department, phone) stay empty without settings sync
|
||||
- What WEB does: added in the client before sending (`lib/org/outgoing.ts:73`, `:99`, `lib/org-signature.ts:311`, 0154e24ff).
|
||||
- What RN does: mail from the phone goes out without the disclaimer (`src/screens/ComposeScreen.tsx:2151`, `src/lib/signature-utils.ts`).
|
||||
|
||||
- [x] **Unfilled placeholder warning** — `P3` — `missing` — fixed in a8e7a3f4, 7594e459
|
||||
- What WEB does: findUnfilledPlaceholders with per-locale words and a confirm (`lib/placeholder-check.ts:115`, `components/email/email-composer.tsx:3261`), setting placeholderWarningEnabled.
|
||||
- What RN does: nothing (`src/screens/ComposeScreen.tsx:2151`).
|
||||
|
||||
- [x] **Signature swap when changing From** — `P3` — `bug` — fixed in 60d0e98f
|
||||
- What WEB does: `components/email/email-composer.tsx:1148`, `components/email/compose-format.ts:71`.
|
||||
- What RN does: an identity without a signature keeps the primary's (`src/lib/signature-utils.ts:83-91`, `src/screens/ComposeScreen.tsx:975`); typing under the signature breaks the swap (`src/lib/signature-utils.ts:245`).
|
||||
|
||||
- [x] **Plain-text part of sent mail** — `P3` — `bug` — fixed in 0647cbf1
|
||||
- What WEB does: `lib/html-to-text.ts:73`, `:169`. What RN does: "-- " trimmed to "--", "> " dropped, ordered lists bulleted (`src/lib/compose-html.ts:109`, `:112`, `:125`).
|
||||
|
||||
- [x] **Composer prompts: missing-subject asks again, autosaved draft says "unsaved", no Discard for a reopened draft** — `P3` — `bug` — fixed in 1cc4ccca
|
||||
- What WEB does: `components/email/email-composer.tsx:3251`, `:3770`, `:3758`. What RN does: `src/screens/ComposeScreen.tsx:1983`, `:2135`, `:1501`, `:1454-1471`.
|
||||
|
||||
- [x] **Offline send shows a raw network error; no offline send queue** — `P3` — `partial` (old 04/09 item) — the offline message (says offline, the text stays) is in a43b55ec. One verdict for the 04, 09 and 10 rows — fixed in 0ec809eb
|
||||
- What WEB does: says offline and keeps the message (`components/email/email-composer.tsx:3661`). What RN does: `src/screens/ComposeScreen.tsx:2236`, `src/lib/send-failure-message.ts:57`, `src/stores/outbox-store.ts:53`.
|
||||
|
||||
- [x] **Scheduled time shows a minute early; presets (tomorrow 13:00, next Monday 08:00)** — `P3` — `bug`/`partial` — fixed in 8fe5798b
|
||||
- What WEB does: `lib/scheduled-send-label.ts:11`, `lib/schedule-presets.ts:20`. What RN does: `src/screens/ScheduledScreen.tsx:53`, `:222`; `src/screens/ComposeScreen.tsx:2046`.
|
||||
|
||||
- [x] **Reply puts no cursor in the body; autofill offered in address fields** — `P3` — `missing` — fixed in 8bb7b607 (composer; ParticipantInput in the event editor is the calendar pass)
|
||||
- What WEB does: `components/email/email-composer.tsx:2885`. What RN does: `src/screens/ComposeScreen.tsx:2466`, `:2316`, `:2946`, `src/components/calendar/ParticipantInput.tsx:119`.
|
||||
|
||||
- [ ] **Composer extras: priority (#970), numeric date placeholders (#1284), @mentions (#1134), directory lookup (#1253), font size, pasted lists, DSN/REQUIRETLS** — `P3` — `missing` — partly: priority and DSN/REQUIRETLS in 649d3adc, @mentions in 8389db8f, directory lookup in a7a37573; numeric date placeholders, font size and pasted lists still missing
|
||||
- What WEB does: `lib/recipient-mentions.ts`, `components/email/plain-text-paste.ts`, `components/email/rich-text-editor.tsx:445`, `lib/jmap/client.ts:1392-1413`. What RN does: `src/lib/template-utils.ts:9-15`, `src/components/RichTextEditor.tsx:26`, `src/lib/editor-html.ts`.
|
||||
|
||||
- [x] **The recipient splitter treats an escaped quote as closing** — `P3` — `bug` — fixed in ae2eb46b
|
||||
- What WEB does: `lib/email-composer-utils.ts:276`. What RN does: `src/lib/recipients.ts:57-90` (reached only from IdentitySettings).
|
||||
|
||||
### Calendar
|
||||
|
||||
- [x] **Calendar and task data from the previous account lands after a switch** — `P1` — `bug` — fixed in 1f190c94, e993540a, 41e8f005, 98dc13fe
|
||||
- What WEB does: epoch check (`stores/calendar-store.ts:956`).
|
||||
- What RN does: in-flight fetchEvents and `tasksInFlight` survive reset (`src/stores/calendar-store.ts:742`, `:750`, `:786`; reset at `src/stores/auth-store.ts:830`); a tick or delete then writes to the new account by the old one's id.
|
||||
|
||||
- [x] **Task reminders relative to the start use the due date (native #83, webmail #1328)** — `P2` — `bug` — fixed in 6f475c0c
|
||||
- What WEB does: `lib/calendar-alerts.ts:343`. What RN does: `src/lib/calendar-alert-scheduler.ts:86`.
|
||||
|
||||
- [x] **Daily series stop at a DST gap** — `P2` — `bug` — fixed in 4de85692
|
||||
- What WEB does: candidates from the start's wall clock (`lib/recurrence-expansion.ts:420`, 34fb9d247).
|
||||
- What RN does: `src/lib/recurrence-expansion.ts:526-531`, `:396-398`, `:709`.
|
||||
|
||||
- [x] **Subscribed feed changes never reach imported events** — `P2` — `bug` — fixed in 8a71495e, 47f29718
|
||||
- What WEB does: calendar-feed-diff (`stores/calendar-store.ts:2012`). What RN does: known UIDs skipped (`src/stores/calendar-subscriptions-store.ts:222`, `src/stores/calendar-store.ts:1158`).
|
||||
|
||||
- [x] **Invitation time in the wrong zone (#1252)** — `P2` — `bug` — fixed in 0fb7c8cd
|
||||
- What WEB does: `lib/calendar-invitation.ts:701`. What RN does: `src/components/email/CalendarInvitationBanner.tsx:175`.
|
||||
|
||||
- [x] **Default reminders per calendar and email reminders (#1209)** — `P2` — `partial` — fixed in 9b87100a, b767df9f
|
||||
- What WEB does: `components/settings/calendar-management-settings.tsx:184`, `components/calendar/event-modal.tsx:448`, `lib/calendar-alerts.ts:175`.
|
||||
- What RN does: fires a calendar's defaults (`src/lib/calendar-alert-scheduler.ts:96`) but can't set them and new events don't start with them (`src/components/calendar/EventModal.tsx:266`).
|
||||
|
||||
- [x] **Search events** — `P2` — `missing` — fixed in b50270d8
|
||||
- What WEB does: `components/calendar/calendar-search.tsx:82`. What RN does: `src/screens/CalendarScreen.tsx`.
|
||||
|
||||
- [x] **Zero-length events run to 23:59 (#837)** — `P3` — `bug` — fixed in acbd085a
|
||||
- What RN does: `src/stores/calendar-store.ts:144`, `src/lib/calendar-utils.ts:116`. Fix: `duration || 'PT0S'`.
|
||||
|
||||
- [x] **.ics export breaks emoji and colon names** — `P3` — `bug` — fixed in f83284d7
|
||||
- What RN does: `src/lib/calendar-ics-export.ts:9`, `:160`, `:167`. Fix: port foldLine and parameter quoting.
|
||||
|
||||
- [x] **Blank participant names are written (#748)** — `P3` — `bug` — fixed in 59c32782
|
||||
- What WEB does: `lib/calendar-participants.ts:368`. What RN does: `src/lib/calendar-participants.ts:360`, `:385`, `:484`.
|
||||
|
||||
- [x] **Offer to save without invitations when the server refuses them** — `P3` — `partial` — fixed in 9b5692a8
|
||||
- What WEB does: `lib/jmap/scheduling-error.ts:7`. What RN does: `src/components/calendar/EventModal.tsx:385-390`.
|
||||
|
||||
- [x] **Moving a task into or within a shared calendar (#1204)** — `P3` — `partial` — fixed in b6995bb6
|
||||
- What WEB does: `lib/jmap/client.ts:9473`. What RN does: updateTask sends unmapped calendarIds (`src/stores/calendar-store.ts:1311-1315`).
|
||||
|
||||
- [x] **Working hours (#1164); week all-day area (#1122); light tinted colours; Jalali; birthday colour; copy title/append note; component set on new calendars (#760); date deep links; background reminder refresh without device sync** — `P3` — `missing`/`partial` — birthday colour fixed in 574db426, copy title/append note in baa3ff59 (duplicate and .ics export were there), week all-day area in 90ce7c1b (sized from the days in view, tasks under their day's events, three rows with +N), light tinted colours in 01578ac2 (Settings > Calendar "Light event colors"), date deep links in fe455370 (`/calendar/<view>/<date>`, a bare date keeps the view, `tasks` opens the task list); left: background reminder refresh without device sync: left, it needs native work: the push subscription would have to carry `CalendarEvent`/`Calendar` for every account (src/lib/push-notifications.ts), PushRouting.kt would have to start a headless JS task for a calendar-only push (today it starts nothing without device sync), and that task would need a reminder scheduler that runs without the restored session and stores the in-app one (src/lib/calendar-notifications.ts) depends on; Android only, and not testable without a device. While the app runs, reminders are rescheduled on every calendar change already; Jalali in f1297b6b; working hours: own feature, not in this run; component set (#760): needs a CalDAV client (05 row)
|
||||
- What RN does: `src/components/calendar/WeekView.tsx`, `src/components/calendar/TimeGridScrollView.tsx:177-185`, `src/lib/birthday-calendar.ts:6`, `src/components/calendar/EventDetailSheet.tsx:422`, `src/api/calendar.ts:1150`, `src/navigation/linking.ts:151`, `:288-299`, `src/lib/push-notifications.ts:243`.
|
||||
|
||||
- [ ] **Free/busy, CalendarEventNotification, organizer from ParticipantIdentity (1.10.0)** — `P3` — `missing` — partly: free/busy in e6a89ad0, CalendarEventNotification in d663a5e2; the organizer from ParticipantIdentity is not ported
|
||||
- What WEB does: `lib/jmap/client.ts:1198`.
|
||||
|
||||
### Contacts
|
||||
|
||||
- [x] **Contacts from the previous account land after a switch** — `P1` — `bug` — fixed in 6b9552fc
|
||||
- What WEB does: epoch guard (`stores/contact-store.ts:597`). What RN does: `src/stores/contacts-store.ts:357`, `:373`; reset (`:873`) keeps contactsPromise.
|
||||
|
||||
- [x] **Contacts with a calendar, scheduling or free/busy link don't save** — `P2` — `bug` — fixed in 32f70c10
|
||||
- What WEB does: maps them to `calendars`/`schedulingAddresses` (`lib/jmap/contact-wire.ts:63-87`).
|
||||
- What RN does: writes `calendarUri`, `schedulingUri`, `freeBusyUri` as card properties (`src/lib/contact-form.ts:412-414`, `src/api/contacts.ts:97`); Stalwart refuses the card.
|
||||
|
||||
- [x] **vCard import sends fields Stalwart rejects** — `P2` — `bug` — fixed in 32f70c10, 3c156673
|
||||
- What WEB does: `lib/jmap/contact-wire.ts`. What RN does: flat ADR and `source` (`src/lib/vcard.ts:652-672`, `:989-990`).
|
||||
|
||||
- [x] **Company directory in Contacts and the composer (#1253)** — `P2` — `missing` — fixed in 0fd14a83 (Contacts; the composer lookup is exported, not wired)
|
||||
- What WEB does: `components/contacts/directory-browser.tsx:61`, `stores/contact-store.ts:924`. What RN does: `src/screens/ContactsScreen.tsx`, `src/stores/contacts-store.ts:728`; listPrincipals exists (`src/api/files.ts:593`).
|
||||
|
||||
- [x] **Deleting an address book that still holds contacts fails** — `P3` — `bug` — fixed in 32f70c10
|
||||
- What WEB does: `onDestroyRemoveContents` (`lib/jmap/client.ts:6937`). What RN does: `src/api/contacts.ts:404`.
|
||||
|
||||
- [x] **vCard 4.0 export (#1407)** — `P3` — `bug` — fixed in 07fd03e2
|
||||
- What RN does: dashes in dates, MOBILE instead of CELL (`src/lib/vcard.ts:21`, `:24`, `:1264`).
|
||||
|
||||
- [x] **Delete contact categories; address book sharing; list filters; deep links new/edit** — `P3` — `missing`/`partial` — category delete with Undo fixed in 65ffef71, list filters in 18c6b03f, address book sharing in 96b163a1 (Settings > Contacts, own books the user may share; one share sheet with the calendars), deep links new/edit in ecefd1cb (`/contacts/new?email=&name=`, `/contacts/<id>/edit`, the older `?contactId=`/`?addEmail=` form)
|
||||
- What WEB does: `components/settings/address-book-management-settings.tsx:84`, `:435`, `components/contacts/contact-list.tsx:30`, `lib/deep-links.ts:394`. What RN does: `src/components/contacts/ContactsSidebarDrawer.tsx:144`, `src/components/settings/ContactsSettings.tsx:33`, `src/screens/ContactsScreen.tsx:163`, `src/navigation/linking.ts:155`.
|
||||
|
||||
### Files
|
||||
|
||||
- [x] **Files trash** — `P2` — `missing` — fixed in 7c914183 and be04fcb1..946304ab
|
||||
- What WEB does: `lib/files-trash.ts:20`, `components/files/files-app.tsx:419`.
|
||||
- What RN does: deletes for good (`src/api/files.ts:316`, `src/screens/FilesScreen.tsx:673`) and lists the webmail's `/.Trash` as an ordinary folder.
|
||||
|
||||
- [x] **Save an attachment to Files (incl. shared and group mail)** — `P2` — `missing` — fixed in 8e07b7d8, 39e82f99
|
||||
- What WEB does: `components/email/save-to-files-dialog.tsx:43`, `lib/jmap/client.ts:9981`. What RN does: `src/components/email/AttachmentChips.tsx:183`; `src/api/files.ts:411` has createFileNodeFromBlob.
|
||||
|
||||
- [x] **Copy folders, numbered new folder names, next/previous in preview, starred and recent, deep links** — `P3` — `partial`/`missing` — copy folders fixed in a0f586f9; numbered new folder names: no webmail behaviour to port (its New folder dialog also takes the typed name, file-browser.tsx:1389); next/previous in preview in e3fc67e7; starred and recent in cad9296a (kept on the device per account like the webmail's localStorage, listed above the root's files, Star in a folder's actions); deep links in d684b00e (`/files/<path>`, `?preview=`, `?shared=`)
|
||||
- What WEB does: `lib/jmap/client.ts:10155`, `stores/file-store.ts:640`, `lib/deep-links.ts:444`. What RN does: `src/api/files.ts:511-519`, `:328-345`, `src/components/files/FilePreviewModal.tsx`, `src/navigation/linking.ts:158`, `:307`.
|
||||
|
||||
- [ ] **Sharing fails on Stalwart before 0.16.6** — `P3` — `bug`
|
||||
- What WEB does: LegacyFileNodeRights (`lib/jmap/client.ts:66-70`). What RN does: `src/components/files/ShareSheet.tsx:33-41`.
|
||||
|
||||
### Filters and vacation
|
||||
|
||||
- [x] **Filter scripts saved by webmail 1.13+ open as raw Sieve; reset wipes them (#1152)** — `P1` — `bug` — fixed in 2167a9d7, fc9c164d
|
||||
- What WEB does: accepts metadata v1 and v2 (`lib/sieve/parser.ts:855`); writes v2 for rule periods, vacation forwarding or audience (`lib/jmap/sieve-types.ts:146`).
|
||||
- What RN does: `if (metadata.version !== 1) return OPAQUE` (`src/lib/sieve/parser.ts:804`); "Reset to visual builder" (`src/components/settings/FilterSettings.tsx:290-318`) wipes the rules and forwarding.
|
||||
- Fix hint: accept v2 and carry activeFrom/activeUntil, vacationForward, vacationAudience through parse and save.
|
||||
|
||||
- [x] **Webmail "is the address" / "has the domain" rules are broadened on a phone save** — `P1` — `bug` — fixed in 2167a9d7
|
||||
- What WEB does: `address :is` / `address :domain :is` (`lib/sieve/generator.ts:106`, `lib/sieve/parser.ts:485`).
|
||||
- What RN does: no such comparators (`src/lib/sieve/types.ts:27`); the generator's default branch writes `header :contains` (`src/lib/sieve/generator.ts:130-131`).
|
||||
|
||||
- [x] **"All messages" condition (#1154)** — `P2` — `missing` — fixed in 2167a9d7, df1a5ab4
|
||||
- What WEB does: `lib/sieve/generator.ts:61`. What RN does: throws on save (`src/lib/sieve/generator.ts:113`), so every save fails once a web rule uses it.
|
||||
|
||||
- [x] **"Stop processing" after Delete silently or Reject (#1153)** — `P2` — `bug` — fixed in 2167a9d7
|
||||
- What WEB does: `lib/sieve/generator.ts:487`. What RN does: skips `stop;` (`src/lib/sieve/generator.ts:396`).
|
||||
|
||||
- [x] **Rule names with double spaces get a duplicate (#1153)** — `P2` — `bug` — fixed in 2167a9d7
|
||||
- What WEB does: collapses whitespace (`lib/sieve/generator.ts:455`, `lib/sieve/parser.ts:904`). What RN does: `src/lib/sieve/parser.ts:841`, `src/lib/sieve/generator.ts:39`.
|
||||
|
||||
- [x] **Out of Office forwarding, reply audience and rule periods (#1152)** — `P2` — `missing` — fixed in df1a5ab4
|
||||
- What WEB does: `components/settings/vacation-settings.tsx:135`, `components/filters/filter-rule-modal.tsx:188`, `:299`, `lib/sieve/period.ts`. What RN does: `src/components/settings/VacationSettings.tsx:209`, `src/lib/sieve/types.ts:66-108`.
|
||||
|
||||
- [x] **Create rules from a message** — `P2` — `missing` — fixed in d546d509, 7b7236f0
|
||||
- What WEB does: `components/email/rules-menu.tsx`, `lib/filters/quick-rules.ts:187`. What RN does: `src/components/email/ActionSheet.tsx`, `src/components/filters/FilterRuleModal.tsx`.
|
||||
|
||||
- [ ] **Forward limit (#1155, #1208); vacation length warning; run a rule on a folder (#1166); Sieve editor loses typed text (#1162); size value raw into Sieve; undo after deleting tags/rules/trusted senders** — `P3` — `missing`/`bug` — forward limit and vacation length fixed in dcbfc907, Sieve editor text in 6c3046c8, size value in 2167a9d7 (before this pass); undo after deleting rules (store side b95f2c29), tags and trusted senders, and the forward limit notes in the rule list, in 8ac19971; not done: run a rule on a folder, which needs a server-side run and is its own feature
|
||||
- What WEB does: `lib/filters/forward-limit.ts:16`, `components/settings/vacation-settings.tsx:253`, `lib/filters/quick-rule-flow.ts:221`, `lib/sieve/generator.ts:63`. What RN does: `src/components/filters/FilterRuleModal.tsx:63`, `:413`, `src/components/settings/VacationSettings.tsx:106`, `src/components/filters/SieveEditorSheet.tsx:35-41`, `src/lib/sieve/generator.ts:69-74`, `src/components/settings/KeywordSettings.tsx:50-56`.
|
||||
|
||||
### Push, settings and core
|
||||
|
||||
- [x] **Push is renewed for the active account only; settings show only that account (#1054)** — `P2` — `bug` — fixed in d92a2ca8
|
||||
- What WEB does: `components/push-notification-prompt.tsx:173`, `:219`, `components/settings/multi-account-push.tsx:33`.
|
||||
- What RN does: resyncPushNotifications works on the active client (`App.tsx:671`, `:723`; `src/lib/push-notifications.ts:1592-1602`), so other accounts' subscriptions expire.
|
||||
|
||||
- [x] **Push with two logins that share a JMAP account id** — `P2` — `bug` — fixed in 9ccb6228, b352e470
|
||||
- What WEB does: 07e6ecce1.
|
||||
- What RN does: the iOS extension's credentials are keyed by JMAP id (`src/lib/ios-push-shared.ts:54`); a tap opens the first match (`src/lib/push-notifications.ts:1873`); the background task checks every login with that id (`src/lib/push-background-task.ts:201`).
|
||||
|
||||
- [x] **Writes other than sends are replayed after a dropped connection** — `P2` — `partial` — fixed in d57bdfaa
|
||||
- What WEB does: retries read-only batches only (`lib/jmap/client.ts:245`).
|
||||
- What RN does: blocks the retry only for EmailSubmission/set, Email/import, Blob/upload (`src/api/jmap-client.ts:102-109`, `:853-857`).
|
||||
|
||||
- [x] **Recover deleted items (#1319)** — `P3` — `missing` — fixed in 113e96bd (Settings page only; the admin day limit and hiding wait for the org policy and plug in at `src/lib/recovery/policy.ts`; the Trash folder's "Recover deleted items" link is not ported, the store's `checkRecent` is ready for it) — Trash link in 7467764e
|
||||
- What WEB does: `x:ArchivedItem/query` (`lib/recovery/archived-items.ts:139`, `lib/jmap/client.ts:12119`, `components/settings/recover-deleted-settings.tsx`).
|
||||
- What RN does: nothing (`src/screens/SettingsScreen.tsx:112`). The day limit and hiding need the policy.
|
||||
|
||||
- [x] **Delegates** — `P3` — `missing` — fixed in 8a5bb728 (Settings > Sharing; the admin hiding, rights limit and app gates wait for the org policy and plug in at `src/lib/delegation/policy.ts`)
|
||||
- What WEB does: Mailbox/AddressBook/Calendar shareWith in one batch (`lib/delegation/delegation.ts:96`).
|
||||
- What RN does: no mailbox sharing (`src/components/settings/FolderSettings.tsx`).
|
||||
|
||||
- [x] **Shared accounts treated as capable when their capabilities say otherwise (#1239)** — `P3` — `bug` — fixed in 67732348 (filters and auto-reply), 7a15c0c6 (contacts and files)
|
||||
- What WEB does: `lib/jmap/client.ts:6431`. What RN does: `src/api/contacts.ts:76`, `src/api/files.ts:98`, `src/api/sieve.ts:43`, `src/api/vacation.ts:77`.
|
||||
|
||||
- [x] **Storage quota ignores softLimit (#1242)** — `P3` — `bug` — fixed in af86339b
|
||||
- What WEB does: `lib/quota.ts:32`. What RN does: `src/api/quota.ts:50`.
|
||||
|
||||
- [ ] **Push extras: Inbox-only switch (#983), one ring per message (#1141), generic notification when the lookup fails (#1041), re-check on return (#1148)** — `P3` — `missing`/`partial` — Inbox-only done in 1821fda7, generic notification in f6e6f8f5, re-check on return in 327b2cae; not done: one ring per message (#1141) needs a change in the Android notification builder (BulwarkFcmModule.kt) and a device
|
||||
- What RN does: `src/lib/notification-mutes.ts`, `src/lib/push-background-task.ts:468-488`, `:518`, `src/components/settings/NotificationSettings.tsx:115-134`.
|
||||
|
||||
- [ ] **Settings extras: language search, date locale override, font size everywhere, nav-bar icon style, sidebar apps never rendered, screenshot protection, Flat fields theme** — `P3` — `missing`/`partial` (old 08/09 items) — screenshot protection in 0ae33f3a; text size widened in c9b3bdfe
|
||||
- What RN does: `src/components/settings/LanguageSettings.tsx:87`, `src/theme/dynamic.ts:48`, `src/lib/sidebar-apps.ts:10`, `src/theme/builtin-themes.ts:18`.
|
||||
|
||||
- [x] **Search snippets, size filter, mail folder sharing, global search (1.10.0)** — `P3` — `missing` — search snippets fixed in 1342e2a0, size filter in aac8a403 (live checks on 0.16 and 1.0 in 12b798c0); mail folder sharing in 417f1843 (the admin's share rights don't hide the write presets yet), global search in f15b7bb3 (other logins' file listing capped at about 500 items; the Pro search tab and saved searches not ported)
|
||||
|
||||
## Old open items (docs/parity 01-09, audit-2026-09)
|
||||
Of 42 open boxes and "Missing vs webmail" items, 8 are done (84fef142 calendar UI strings, f62849d1 iOS push, 75299244 Tabler icons, 26a34d59 autocomplete groups, 5802cae contact deep link, status bar theming, birthday time zone, folder colours 548b647), 9 are no longer relevant (identity and template sync, settings sync, S/MIME is a plugin, #379 migration, stability banner, admin relay list, Android badge is iOS-only by design), and 25 are still open; those are folded into the rows above.
|
||||
|
||||
## Needs a decision
|
||||
- Organisation policy on the phone: the app needs the webmail it was paired with and a webmail route that accepts a JMAP bearer token. Until then locked settings, session limits, external recipient confirm and label, the org signature and disclaimer, template locks and recovery limits can't be enforced on the phone.
|
||||
- Webmail hand-off: replacing the password in the redirect with a one-time code needs the webmail side first.
|
||||
@@ -0,0 +1,251 @@
|
||||
# Webmail 1.19.0 and 1.19.1
|
||||
|
||||
## Summary
|
||||
The last pass (file 10) covered webmail 1.10.0 to 1.18.0 and the commits after the 1.18.0 release up to early 2026-10-29 (b0d1a97bb..4aea0976a). This file walks the changelog sections 1.19.0 (2026-10-29) and 1.19.1 (2026-10-31) and every webmail commit since (`git log --since=2026-10-29`, 237 commits; `4aea0976a..4887ed457` gives 238, the extra one is the plugin page fix #1358). There is no Unreleased section; 1.19.1 is the latest release. Checked against native main at 7a42a15f. Web paths are relative to the webmail root, native paths to this repo.
|
||||
|
||||
Many 1.19.0 entries were in "Unreleased" when file 10 was written and already have rows there (organisation policy, delegates, recover deleted items, shared tags and team templates, Files trash). They are not repeated; the unticked file 10 rows they touch are listed at the end.
|
||||
|
||||
Counts: 42 rows, P1 0, P2 11, P3 31.
|
||||
|
||||
What stands out:
|
||||
- A forged From still gets the real sender's favicon or contact photo. The webmail shows a warning shield in its place now.
|
||||
- The 1.19.1 booking, parcel and order cards are missing. The webmail's `lib/structured-mail` is plain TypeScript and takes the HTML parser as a parameter, so it can be ported.
|
||||
- The 1.19.0 accessibility work has little counterpart on the phone: form fields without names, toasts and Undo not read out on iOS, grey text at 2.56:1 in the light theme, a mail body fixed at 14 px, and a "Plain Text Only" switch that promises plain-text reading but doesn't change it.
|
||||
- Calendar: a cancellation mail can't remove the meeting, and the event and task editors throw typed changes away.
|
||||
|
||||
Classification: `bug` = fixed in the webmail, still live in the app; `missing`; `partial`. Not applicable on mobile and left out (same list as file 10): admin panel, plugins and marketplace, command palette, keyboard shortcuts and keyboard navigation, print, Bulwark Lite, CSP/third-party switch as a server feature, Mountain View, desktop-only layout (drag and drop, hover, multi-pane), server routes the app doesn't use, Docker, docs, demo mode and translation-only entries.
|
||||
|
||||
## Findings
|
||||
|
||||
### Reading mail
|
||||
|
||||
- [x] **A forged From keeps the real sender's favicon and contact photo** — `P2` — `bug` — fixed in 0b0fc021
|
||||
- What WEB does: when the sender check fails or is unverified, the avatar shows a warning shield instead of a photo, logo or initials, in the reader, the conversation and the list (`components/ui/avatar.tsx:323`, `hooks/use-sender-warning.ts:13`, `components/email/email-viewer.tsx:721`). The list fetch asks for `header:Authentication-Results:all` (`lib/jmap/client.ts:461`). 605a3983b (#1439).
|
||||
- What RN does: MessageHeader works out the verdict (`src/components/email/MessageHeader.tsx:118`) but renders SenderAvatar with the claimed address (`:175`); the same in thread cards (`src/components/email/ThreadMessageCard.tsx:50`) and list rows (`src/screens/EmailListScreen.tsx:213`). SenderAvatar picks a contact photo or the domain favicon (`src/components/SenderAvatar.tsx:62-65`). List rows have no auth data (`src/api/email.ts:23-26`).
|
||||
- Fix hint: pass a sender warning from getSenderVerification into SenderAvatar and draw a shield; for list rows add `header:Authentication-Results:all` to the list properties.
|
||||
|
||||
- [x] **No booking, parcel or order cards from schema.org markup** — `P2` — `missing` — fixed in 5f2ef4c1, a6066a09, cd677e77, 2055df8c, 982a30ef, c90a35c4, mounted in 90e4e230
|
||||
- What WEB does: reads JSON-LD/microdata booking markup and attached Wallet passes and shows a card above the message (flight, train, bus, hotel, rental car, event, restaurant, parcel tracking, order status) with copy buttons and links that show where they lead. Bookings go into a calendar the user picks with stable UIDs, so a later mail updates the event and a cancellation removes it. Gate: the receiving server verified the From domain, not a forward, not a personal mailbox provider, not turned off by the admin (`lib/structured-mail/index.ts:36`, `trust.ts:104`, `pkpass.ts`, `calendar.ts:147`, `components/email/structured-mail/use-booking-calendar.ts:115`, `lib/structured-mail-gate.ts:7`). a529e6e57..859d368a1.
|
||||
- What RN does: nothing; the only banners above the body are unsubscribe and invitation (`src/components/email/MessageContent.tsx:104`, `:112`).
|
||||
- Fix hint: port `lib/structured-mail/` with an RN-side HTML parser passed through `parseHtml` (`lib/structured-mail/index.ts:4-5`, `:21`) and build RN cards. The admin switch needs the org policy, so default on.
|
||||
|
||||
- [x] **"Plain Text Only" says it also changes reading, but the reader ignores it** — `P2` — `bug` — fixed in 7dc45778
|
||||
- What WEB does: reads HTML mail as plain text by default or per message (`lib/html-to-readable-text.ts:39`, `components/email/email-viewer.tsx`; 1e929b0a9).
|
||||
- What RN does: the switch reads "Compose and read in plain text only" (`src/components/settings/ReadingSettings.tsx:208-210`), but only the composer and the placeholder check read plainTextMode (`src/lib/compose-html.ts:175`, `src/lib/placeholder-check.ts:157`); EmailBodyView never does (`src/components/EmailBodyView.tsx:529-537`).
|
||||
- Fix hint: render the text part (or the HTML turned into text) when the setting is on, and add a per-message "Show as plain text".
|
||||
|
||||
- [x] **Mail body ignores the app's text size and iOS Dynamic Type** — `P2` — `missing` — fixed in 7b551fc3, a3650ebf
|
||||
- What WEB does: bodies follow the chosen text size, with a separate mail text size and a minimum font size (`lib/mail-reading.ts:32`, `:42`; c07ba1a37).
|
||||
- What RN does: the body is fixed at 14 px for HTML and plain text (`src/lib/email-html.ts:695`, `:979`); the app's text size scales only the app's own UI (`src/theme/dynamic.ts:48-69`). Android's WebView may follow the system font size (no textZoom set; unverified), iOS doesn't. Pinch zoom exists (`src/components/EmailBodyView.tsx:408`).
|
||||
- Fix hint: scale the body's base font size by the app setting and `PixelRatio.getFontScale()`.
|
||||
|
||||
- [ ] **Reading aids: paused animated images, reduce motion in mail, reader view, read aloud, reading width** — `P3` — `missing` — reduce motion and paused animated images fixed in 6d701c4b; not done: reader view, read aloud and reading width are left to the OS
|
||||
- What WEB does: animations in mail stop with reduce motion (24a13cc1f), animated images pause behind a play button (bb7869798), plus reader view, read aloud, reading mask and width (caed2d313, af3019ea2, a982c3f23, 8cc0f8f25, dcc0d600f).
|
||||
- What RN does: none. The app reads reduce motion (`src/theme/dynamic.ts:92`) but doesn't pass it into mail.
|
||||
- Fix hint: pass reduce motion into the body CSS (animation off, GIFs paused) first; read aloud and reader view are left to the OS.
|
||||
|
||||
- [ ] **No BIMI sender logos or verified-mark check** — `P3` — `missing` — partly: BIMI logos from the webmail in 83ddd137 (list, unified inbox and reader in 6b7e752c); no check mark for a verified mark
|
||||
- What WEB does: when DMARC passes for the exact From domain the avatar shows the domain's BIMI logo before the favicon, with a check mark when a VMC/CMC backs it; DNS and certificate checks run on the webmail server (`lib/email-headers.ts:175`, `components/ui/avatar.tsx:292`, `app/api/bimi/route.ts:49`). cf0eb03c4.
|
||||
- What RN does: contact photo, DuckDuckGo favicon, initials (`src/components/SenderAvatar.tsx:38`, `:62-65`; `src/lib/avatar-utils.ts:149`).
|
||||
- Fix hint: use the paired webmail's `/api/bimi` once the app keeps the webmail URL (same blocker as the org policy, file 10).
|
||||
|
||||
- [x] **A DKIM pass reported only by `header.i` doesn't count (#1420)** — `P3` — `bug` — fixed in 49d43729
|
||||
- What WEB does: takes the signer domain from `header.i` when `header.d` is missing (`lib/email-headers.ts:282`, `:346`). f8f2e93cd.
|
||||
- What RN does: reads `header.d` only (`src/lib/email-headers.ts:397`); such mail shows as unverified and its invitations lose trust.
|
||||
- Fix hint: port identityDomain.
|
||||
|
||||
- [x] **Reply and quick reply are offered on drafts and scheduled messages in a conversation** — `P3` — `bug` — fixed in 35297379
|
||||
- What WEB does: hides Reply and Reply all on a conversation's drafts and scheduled messages (`components/email/email-viewer.tsx:881`). 8b29254a6 (#1404).
|
||||
- What RN does: every card is replyable (`src/screens/EmailThreadScreen.tsx:1212`, `src/components/email/ThreadMessageCard.tsx:68-85`); quick reply targets the last member, which can be the user's own draft (`src/screens/EmailThreadScreen.tsx:1124`).
|
||||
- Fix hint: `replyable={false}` for `$draft` and scheduled members; point quick reply at the newest message that is neither.
|
||||
|
||||
- [x] **Bounce and receipt report parts show as list chips and go along on forward** — `P3` — `bug` — fixed in 7772da84 (list chips, global- types), 482e9a20 (forward, compose)
|
||||
- What WEB does: leaves `message/delivery-status`, `message/disposition-notification` and their `global-` forms out of list chips and forwards (`lib/attachment-visibility.ts:73`, `components/email/email-composer.tsx:985`). 8a09ba573.
|
||||
- What RN does: the reader hides them (`src/lib/attachment-display.ts:56`, `:130`), list chips and forwards don't (`src/lib/list-attachments.ts:21`, `src/lib/reply-compose.ts:14-19`); isReportPart misses the `global-` types.
|
||||
- Fix hint: filter isReportPart in both places and add the two global types.
|
||||
|
||||
- [x] **Attachment icons in the reader aren't coloured by file type** — `P3` — `partial` — fixed in 4849cf23
|
||||
- What WEB does: reader chips use the list's per-type icon and colour (`components/email/attachment-chips.tsx`). a78d03a18.
|
||||
- What RN does: list chips are typed (`src/components/email/ListAttachmentChips.tsx:96`), reader chips use a plain paperclip (`src/components/email/AttachmentChips.tsx:244`).
|
||||
- Fix hint: reuse attachmentKind in AttachmentChips.
|
||||
|
||||
- [x] **Avatar initials are always white, even on light colours** — `P3` — `bug` — fixed in 6f66fa06
|
||||
- What WEB does: picks black or white initials by contrast (`components/ui/avatar.tsx:174-182`; 2e13accad).
|
||||
- What RN does: always `#ffffff` (`src/components/SenderAvatar.tsx:130-133`); several palette colours give 3.1 to 3.7:1 (`src/lib/avatar-utils.ts:19-20`, `:47-48`).
|
||||
- Fix hint: port initialsColor.
|
||||
|
||||
### Mail list and search
|
||||
|
||||
- [x] **Search results can't be moved into the open folder (#1452)** — `P3` — `bug` — fixed in 9a4515f9
|
||||
- What WEB does: a search across folders keeps the open folder selected, and hits from other folders can move into it (`lib/move-targets.ts:71`). dafdda534.
|
||||
- What RN does: the move sheet greys out the current folder (`src/components/MoveSheet.tsx:110-115`) and the single-row pick returns on it (`src/screens/EmailListScreen.tsx:1904-1918`), even for rows from other folders.
|
||||
- Fix hint: in search mode mark a folder current only when the rows are already in it.
|
||||
|
||||
- [x] **No Undo for read/unread, star and tag changes** — `P3` — `missing` — fixed in da1da0ec
|
||||
- What WEB does: the toast offers Undo and puts back each message's previous keywords (`lib/email-action-toast.ts:86`). ee17c0dba.
|
||||
- What RN does: UndoEntry covers archive, delete, move and spam only (`src/stores/email-store.ts:322`).
|
||||
- Fix hint: a `keywords` undo kind with the original keywords per item.
|
||||
|
||||
- [x] **No Sort menu in the list header** — `P3` — `partial` — fixed in 1cbe28b1, beff8744
|
||||
- What WEB does: Sort menu with the order presets in the list header (`components/email/mail-list-menus.tsx`). e3f1ccce7.
|
||||
- What RN does: the header only flips the direction (`src/screens/EmailListScreen.tsx:1193`); presets live in Settings > Layout (`src/components/settings/LayoutSettings.tsx:88-104`).
|
||||
- Fix hint: a sort button that opens the presets.
|
||||
|
||||
- [x] **Tag counts don't use Keyword/get where the server counts keywords (#1399)** — `P3` — `missing` — fixed in eb6bf6d3
|
||||
- What WEB does: one Keyword/get when the capability has `supportsCounts`, else two Email/query per tag (`lib/jmap/client.ts:2721`, `:2737`).
|
||||
- What RN does: always two queries per tag (`src/api/tag-counts.ts:22-40`).
|
||||
- Fix hint: same check and fallback. Stalwart doesn't advertise counts, so low value.
|
||||
|
||||
- [x] **The folder list doesn't say which folder is open, and the unread count reads "Show unread"** — `P3` — `bug` — fixed in e3a34eb1
|
||||
- What WEB does: says which folder is open and how many are unread (7c282f1f4, 80a2a212a).
|
||||
- What RN does: folder rows have no selected state (`src/components/SidebarDrawer.tsx:170-180`); the count is a button labelled "Show unread" (`:126-133`).
|
||||
- Fix hint: `accessibilityState.selected` and a label like "Inbox, 3 unread".
|
||||
|
||||
### Composer and sending
|
||||
|
||||
- [x] **Quick reply skips the placeholder, forgotten-attachment and confirm checks** — `P3` — `bug` — fixed in db8eb731
|
||||
- What WEB does: quick reply runs the composer's checks (`lib/outgoing/quick-reply.ts:34`). 534049952.
|
||||
- What RN does: the composer has them (`src/screens/ComposeScreen.tsx:2078`, `:2137`), QuickReplyBox sends straight away (`src/components/email/QuickReplyBox.tsx:78-140`).
|
||||
- Fix hint: run unfilledPlaceholdersInMessage and the attachment keyword check before send.
|
||||
|
||||
- [x] **Quoted reply history isn't collapsed in the composer** — `P3` — `missing` — fixed in 9fd80993
|
||||
- What WEB does: collapses the quote behind an ellipsis button (`components/email/reply-quote-collapse.ts:13`). 9e0089604 (#1405).
|
||||
- What RN does: seeds the full quote into the editor (`src/screens/ComposeScreen.tsx:562-615`).
|
||||
- Fix hint: hide the quoted block in the editor behind a toggle, keep it in the sent body.
|
||||
|
||||
- [x] **Tag picker can't make a tag; template picker can't open the template manager** — `P3` — `missing` — fixed in e09cd395 (new tag), 2c1f266f (template manager, compose)
|
||||
- What WEB does: "New tag" in the tag picker (`components/email/tag-picker.tsx:214`), "Manage" in the template picker (`components/templates/template-picker.tsx:227`). 19676a14f.
|
||||
- What RN does: tag sheets list existing tags only (`src/screens/EmailThreadScreen.tsx:1489`, `src/components/TagSheet.tsx`); the template sheet shows "No templates yet" with no way on (`src/components/TemplateSheet.tsx:148-150`).
|
||||
- Fix hint: a "New tag" row and a "Manage templates" link.
|
||||
|
||||
- [x] **No optional confirm before sending, deleting or archiving** — `P3` — `missing` — fixed in db8eb731 (send, composer and quick reply), 9794dc03 (delete and archive)
|
||||
- What WEB does: `sendConfirmation`, `confirmDelete`, `confirmArchive` (`components/email/email-composer.tsx:501`, `lib/mail-removal-confirm.ts:24`, `lib/accessibility-settings.ts:93-94`). 4d91d9b29, 374ae9c8d.
|
||||
- What RN does: asks only before a permanent delete (`src/lib/delete-confirm.ts:27`).
|
||||
- Fix hint: three settings and an Alert in the send, delete and archive paths, swipes and quick reply included.
|
||||
|
||||
- [ ] **No alt-text prompt for images, no default compose font** — `P3` — `missing` — alt-text prompt fixed in 303dea01; not done: default compose font and size, which needs a font setting with switches in the settings pages
|
||||
- What WEB does: asks for alt text when an image goes in (`components/email/image-alt-dialog.tsx`; 53cb49d3c), starts new mail in a chosen font and size (1b24c1980), offers an accessible example template (420bf8f9a).
|
||||
- What RN does: alt is the file name (`src/screens/ComposeScreen.tsx:1857`, `src/lib/editor-html.ts:413-419`); the editor font is fixed (`src/lib/editor-html.ts:160`).
|
||||
- Fix hint: the alt prompt first.
|
||||
|
||||
### Accessibility
|
||||
|
||||
- [x] **No Accessibility page or presets** — `P2` — `missing` — fixed in c9b3bdfe
|
||||
- What WEB does: Settings > Accessibility with presets (screen reader, low vision, dyslexia, focus, motor, motion, hearing) and text size 87.5% to 200% (`components/settings/accessibility-settings.tsx`, `lib/accessibility-presets.ts:23`, `lib/accessibility-settings.ts:13-22`; 8008393e5, 0731d24f6).
|
||||
- What RN does: no page (`src/screens/SettingsScreen.tsx:150-174`); text size 0.92/1/1.12 (`src/theme/dynamic.ts:14-18`). Text follows the system font scale; reduce motion follows the system (`src/theme/dynamic.ts:88-120`).
|
||||
- Fix hint: a page that groups the existing switches, widens the text size range and takes the presets that make sense on a phone.
|
||||
|
||||
- [x] **Form fields have no name for screen readers** — `P2` — `bug` — fixed in ff5a8c91 (settings and sign-in), 98b962a8 (composer, quick reply, folder name prompt, template search), 3c3274e4 (mail search, filter panel, unified search), 7ba00be6 (contact form, event editor, calendar, contacts and files sheets, auto-reply)
|
||||
- What WEB does: every field has a label tied to it (`components/ui/field.tsx:42`; ef13556a0, 0e2a05e0e, dcc21acd0, 4e2affe40, f73643bbb).
|
||||
- What RN does: the shared Input draws its label as separate Text (`src/components/Input.tsx:40-55`); the Security password and code fields have no label at all (`src/components/settings/AccountSecuritySettings.tsx:181`, `:191`, `:198`); contact fields rely on placeholders (`src/screens/ContactFormScreen.tsx:823-834`); the reminder field's only name is "30" (`src/components/calendar/EventModal.tsx:772`, `:817`).
|
||||
- Fix hint: Input passes its label as accessibilityLabel; every bare TextInput gets one.
|
||||
|
||||
- [x] **Toasts and Undo are not read out on iOS; the Undo bar isn't read out anywhere** — `P2` — `bug` — fixed in d812caf0, 01dd7461
|
||||
- What WEB does: live regions and announcements for toasts, loads, new mail, saved drafts and offline (`lib/announce.ts:124`, `components/ui/toast.tsx:471`; 36d13b76b, e2fc2c750, 23813be9b, d713d31af, 4c1f24947).
|
||||
- What RN does: no `announceForAccessibility` call in src; ToastHost and OfflineBanner use accessibilityLiveRegion, which only Android honours (`src/components/ToastHost.tsx:194`, `src/components/OfflineBanner.tsx:33`); the Undo bar has neither (`src/components/UndoSnackbar.tsx:129-160`).
|
||||
- Fix hint: announce through AccessibilityInfo on iOS, live region on the Undo bar.
|
||||
|
||||
- [x] **Grey text is below 4.5:1 contrast** — `P2` — `bug` — fixed in 11522995
|
||||
- What WEB does: every built-in theme at 4.5:1 or more, with a contrast test (`lib/builtin-themes.ts:16`, `lib/__tests__/theme-contrast.test.ts`; 9f6c7b99c).
|
||||
- What RN does: textMuted is `#94a3b8` on white in the light theme (2.56:1) and `#737373` in dark (4.18:1 on the background, 3.19:1 on surfaces) (`src/theme/tokens.ts:236`, `:140`); used in 456 places, also as placeholder colour.
|
||||
- Fix hint: adjust both, add a contrast test over tokens.ts and builtin-themes.ts.
|
||||
|
||||
- [x] **The calendar grid reads poorly with a screen reader** — `P2` — `bug` — fixed in 294023e2
|
||||
- What WEB does: events sit inside their day cell and day cells have full date names (0026abba4, 19e0acde9).
|
||||
- What RN does: month day cells read only the number and the dots are silent (`src/components/calendar/MonthView.tsx:199-240`); event blocks have no role, label or date (`src/components/calendar/EventBlock.tsx:72-104`).
|
||||
- Fix hint: label day cells with date and event count; event blocks get a button role and a label with title, date and time.
|
||||
|
||||
- [ ] **No reading fonts, text spacing, more contrast or underlined links** — `P3` — `missing` — partly: reading fonts in 4724c28f (iOS in cfca6b9c, untested on a device); text spacing, more contrast and underlined links not done
|
||||
- What WEB does: Atkinson Hyperlegible Next, Lexend, OpenDyslexic, spacing settings, more contrast, underlined links (`lib/accessibility-settings.ts:53-68`; 8008393e5, da215b8c0).
|
||||
- What RN does: none of these settings (`src/stores/settings-store.ts`).
|
||||
- Fix hint: the dyslexia font and line spacing first.
|
||||
|
||||
- [ ] **No colour-blind palette and no names next to colours** — `P3` — `missing` — partly: colour-blind-safe palette for tags in c6501ec8 and calendars in 21dd5a24; no names next to colours
|
||||
- What WEB does: a colour-blind-safe palette, a tag's letter in its dot, the calendar's name on events (cf4d02b49, d5d7d39ef, 396290003).
|
||||
- What RN does: colours only (`src/components/calendar/EventBlock.tsx:72-104`, `src/components/calendar/MonthView.tsx:233-240`).
|
||||
|
||||
- [x] **Toast and Undo times are fixed and short** — `P3` — `missing` — fixed in c9b3bdfe
|
||||
- What WEB does: one duration setting, 5 to 60 s or until closed (`lib/toast-duration.ts:7-15`; 8d2ebccf0, be5e2dbc5).
|
||||
- What RN does: 5 s, errors 10 s (`src/stores/toast-store.ts:44`), Undo 5 s (`src/components/UndoSnackbar.tsx:15`).
|
||||
- Fix hint: a duration setting and a longer default while a screen reader is on.
|
||||
|
||||
- [ ] **No plain language and no explanations for technical terms** — `P3` — `missing` — partly: plain language in 97b71e22; no explanations for technical terms
|
||||
- What WEB does: simpler English and German wording (`i18n/plain-language.ts`; bb739c744, 9861af6be, 5ffecf692) and term explanations (`components/ui/term.tsx`; 724c45d6e).
|
||||
- What RN does: none (`src/i18n/index.ts:79-140`).
|
||||
|
||||
- [x] **Accessibility statement, help and sign-language links aren't shown** — `P3` — `missing` — fixed in 9d977773
|
||||
- What WEB does: shows the operator's links from the public `/api/config` (`app/api/config/route.ts:104`; 315e29270, 623c9e179).
|
||||
- What RN does: About has only the app's own links (`src/lib/about-links.ts:5-28`); the webmail URL isn't kept (`src/lib/oauth-native.ts:93-108`).
|
||||
- Fix hint: keep the paired webmail URL (also needed for the org policy) and read `/api/config`.
|
||||
|
||||
### Accounts and sign-in
|
||||
|
||||
- [x] **The two-factor prompt shows as a red error (#1422)** — `P3` — `bug` — fixed in b205c13a
|
||||
- What WEB does: an info box with role status, no shake (`app/(main)/[locale]/login/page.tsx:1393`; 141190708).
|
||||
- What RN does: TotpRequiredError sets a notice (`src/screens/LoginScreen.tsx:389-391`) shown in the error tone (`src/screens/login/PasswordStep.tsx:151`, `src/screens/login/LoginNotice.tsx:15-24`).
|
||||
- Fix hint: `tone="info"` for the TOTP step.
|
||||
|
||||
- [x] **Two accounts with the same name look alike in the switcher** — `P3` — `bug` — fixed in 505fbc59 (Settings > Account and the shared accountRowText) and 97173d4a (the switcher rows in SidebarDrawer); the port waits for question 9
|
||||
- What WEB does: the address becomes the title when another account shares the name (`lib/account-utils.ts:159`; 3bb9f7df8).
|
||||
- What RN does: name as title, address below (`src/components/SidebarDrawer.tsx:884-891`, `src/components/settings/AccountSettings.tsx:186`). Report question 9 (port in the label) is the same area.
|
||||
- Fix hint: port accountRowText.
|
||||
|
||||
- [ ] **An expired session drops the open composer without saving** — `P3` — `partial`
|
||||
- What WEB does: open composers save their drafts before the session ends and the login page says if one couldn't be saved (`stores/auth-store.ts` saveDraftsBeforeExpiry; 842c369fa).
|
||||
- What RN does: on a dead refresh the account is evicted with no draft flush (`src/stores/auth-store.ts:1138-1153`, `:1170-1180`). Read from the code, not tried on a device.
|
||||
- Fix hint: flush the open composer's autosave before eviction.
|
||||
|
||||
### Settings
|
||||
|
||||
- [x] **The density sample shows previews while "Show preview text" is off** — `P3` — `bug` — fixed in c8f417dd
|
||||
- What WEB does: c166651d1 (`components/settings/appearance-settings.tsx:14-16`).
|
||||
- What RN does: showPreview comes from the density only (`src/components/settings/AppearanceSettings.tsx:11-15`, `:24-27`).
|
||||
|
||||
- [x] **The identity editor closes without asking about unsaved changes** — `P3` — `bug` — fixed in 7ecac4ab
|
||||
- What WEB does: identity, rule and Sieve editors ask (`components/settings/use-discard-changes.tsx`; 9d3ae2203).
|
||||
- What RN does: Close, Cancel and Back call closeEditor (`src/components/settings/IdentitySettings.tsx:122`, `:304`, `:311`, `:389`). FilterRuleModal already asks.
|
||||
|
||||
### Calendar
|
||||
|
||||
- [x] **A cancellation mail can't remove the meeting from the calendar (#1425)** — `P2` — `missing` — fixed in a19afe9b
|
||||
- What WEB does: offers "Remove from calendar" for a trusted cancellation from the organizer, for the series or one occurrence, and says when it's outdated (`components/email/calendar-invitation-banner.tsx:627-635`, `:831`, `:1229`, `lib/calendar-cancellation.ts:49`; 357b6af3c).
|
||||
- What RN does: only "This event was cancelled" (`src/components/email/CalendarInvitationBanner.tsx:201`, `:270-272`); it already looks up the server copy (`:122`).
|
||||
- Fix hint: port planCancellationRemoval behind the banner's trust check.
|
||||
|
||||
- [x] **The event and task editors throw away typed changes on Close or Back** — `P2` — `bug` — fixed in 10e9f81d
|
||||
- What WEB does: asks before closing with unsaved changes (`components/calendar/event-modal.tsx:530-547`, `components/calendar/task-modal.tsx`; 13f9f27c4).
|
||||
- What RN does: close and Back call onClose (`src/components/calendar/EventModal.tsx:495`, `:500`); the task editor resets on the next open (`src/components/calendar/TasksSheet.tsx:135-146`, `:238-253`).
|
||||
- Fix hint: compare with the initial form and ask, as ContactFormScreen does (`src/screens/ContactFormScreen.tsx:639`).
|
||||
|
||||
- [x] **A shared calendar's server colour is replaced by a random one (#1414)** — `P3` — `bug` — fixed in 64eb97f4
|
||||
- What WEB does: auto-colours only shared calendars with neither a server colour nor an override (`lib/shared-calendar-colors.ts:79`; 2dbe5d024).
|
||||
- What RN does: colours every shared calendar without an override (`src/screens/CalendarScreen.tsx:315-333`).
|
||||
- Fix hint: skip calendars with `cal.color`.
|
||||
|
||||
### Notifications
|
||||
|
||||
- [x] **New mail can't play the Involved sound** — `P3` — `missing` — fixed in 6a27c596 (calendar reminders in b4f10829)
|
||||
- What WEB does: Involved by default (`stores/settings-store.ts:949`, `lib/notification-sound.ts:11`). 5eabdd890.
|
||||
- What RN does: the OS channel owns the sound (`src/stores/settings-store.ts:266-267`).
|
||||
- Fix hint: optional; needs a new Android channel id and a bundled iOS sound.
|
||||
|
||||
### i18n
|
||||
|
||||
- [x] **No Thai or Estonian, and the vendored catalogs are stale** — `P3` — `missing` — fixed in d23af55e, e8bc54eb
|
||||
- What WEB does: 29 languages, Thai new in 1.19.0 (#1379, be52744a5; `i18n/routing.ts:15`).
|
||||
- What RN does: 27 (`src/i18n/index.ts:25-53`); `locales/et/common.json` is vendored but not registered; no `th`. German lacks 1283 of the webmail's keys (last sync 7c914183).
|
||||
- Fix hint: `node scripts/sync-locales.mjs`, register th and et, add overlays.
|
||||
|
||||
- [x] **Some German phone strings still say "du"** — `P3` — `bug` — fixed in 25f9b810
|
||||
- What WEB does: formal everywhere (7b720dd22).
|
||||
- What RN does: 12 overlay strings (`locales/rn/de.json`, e.g. settings.privacy.*, settings.backup.description); 18 more in the stale vendored catalog, which the sync fixes.
|
||||
- Fix hint: rewrite with "Sie".
|
||||
|
||||
## Already done in native
|
||||
Return to the list after archive/move/spam (#1458), shared mailbox identity (#1450), Bulwark Mobile client (#1459, 7a42a15f), security settings after a TOTP sign-in (#1432), unique default alert ids (#1435), keep-alive while push is live (#1401), vCard colons and 4.0 export (#1438 9d547b9d, #1407), chip moves between To/Cc/Bcc, placeholder warning, send waits for autosave (#1394), clearing filters keeps the folder (#1430), translated folder names in search (#1429), invitation passing DMARC (#1403), attachment download errors (#1426, #1428), partly failed bulk delete (#1384), .eml preview attachments (#1395), Files move/zip/delete undo, Contacts move and long-press menus, no Edit/Delete on read-only events, reduce motion. Details with file:line are in the inventory drafts summarised in docs/native-sync-2026-11.md.
|
||||
|
||||
## Unticked file 10 rows touched by 1.19
|
||||
Schedule presets (1.19.0 adds "This morning"/"This afternoon", #1390), undo after a cross-account move and in the unified inbox, search snippets and size filter, fixed-width tables (#1020), recover deleted items (#1319), delegates, external sender chip, external recipient confirm, org signature and disclaimer, org policy and session limits (the last five blocked on the policy route).
|
||||
|
||||
## Needs a decision
|
||||
- BIMI and the operator's help links need the app to keep the paired webmail's URL, the same thing the org policy needs.
|
||||
@@ -364,7 +364,7 @@ carries the commit that fixed it.
|
||||
- [x] **U-044 · P2 · PP (German, 1.3) · The toolbar overflows and pushes More off screen.** *(fixed in 975ed08)*
|
||||
- "Stern hinzufügen" is long (`d_de_thread`).
|
||||
- Fix: drop the toolbar labels when they don't fit; keep the a11y labels.
|
||||
- [ ] **U-045 · P3 · German · Prev/Next read "Zurück"/"Weiter".** *(deferred: the strings come from the vendored webmail catalog (locales/de/common.json); the fix belongs in the webmail's German translation)*
|
||||
- [x] **U-045 · P3 · German · Prev/Next read "Zurück"/"Weiter".** *(deferred: the strings come from the vendored webmail catalog (locales/de/common.json); the fix belongs in the webmail's German translation)* — fixed in 25f9b810
|
||||
- "Zurück" reads as Back. "Allen antworten" wraps in the bottom bar.
|
||||
- Fix: "Vorherige"/"Nächste" (locale), and a single line with shrinking text.
|
||||
- [x] **U-046 · P2 · all · The tag sheet doesn't scroll.** *(fixed in b59d00f)*
|
||||
@@ -407,7 +407,7 @@ carries the commit that fixed it.
|
||||
with the usual "-- " delimiter and the composer adds its own separator above it (screenshot
|
||||
`t_compose_panel`).
|
||||
- Fix: don't add a separator to a signature that already starts with one.
|
||||
- [ ] **U-069 · P2 · PL · Recipient suggestions can't show in phone landscape.** *(deferred: with the keyboard up a phone in landscape has about 100 dp left; suggestions need a different design (a full-screen picker), which is a feature)* With the keyboard
|
||||
- [x] **U-069 · P2 · PL · Recipient suggestions can't show in phone landscape.** *(deferred: with the keyboard up a phone in landscape has about 100 dp left; suggestions need a different design (a full-screen picker), which is a feature)* With the keyboard — fixed in e1ad5c09
|
||||
up, about 50 dp are left under the To field, so the suggestion list is hidden behind the
|
||||
keyboard. Typing a full address still works.
|
||||
### Calendar
|
||||
@@ -510,7 +510,7 @@ carries the commit that fixed it.
|
||||
- [x] **U-132 · P2 · all · The password step's return keys don't move on.** *(fixed in ae1ef5b)*
|
||||
- Username has no `next`. With TOTP on, the password field shows Next and does nothing.
|
||||
- Where: `PasswordStep.tsx:56,78`.
|
||||
- [ ] **U-133 · P2 · all · There is no way out while "Signing in" hangs.** *(deferred: a cancel needs an abortable sign-in in the auth flow, which is more than a UI fix; see product questions)* Hardware back is
|
||||
- [x] **U-133 · P2 · all · There is no way out while "Signing in" hangs.** — fixed in 55d008a2 *(deferred: a cancel needs an abortable sign-in in the auth flow, which is more than a UI fix; see product questions)* Hardware back is
|
||||
swallowed. Where: `SigningInStep.tsx`, `LoginScreen.tsx:165`.
|
||||
|
||||
### Found in the re-walk
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// First: wraps react-native's Text for the reading font before any module
|
||||
// copies it (src/theme/reading-font-text).
|
||||
import './src/theme/reading-font-install';
|
||||
// WebCrypto for PGP and S/MIME (#58), before openpgp or pkijs can load.
|
||||
// Loads nothing native until first used.
|
||||
import './src/lib/crypto/install-webcrypto';
|
||||
import { createElement } from 'react';
|
||||
import { AppRegistry } from 'react-native';
|
||||
import { registerRootComponent } from 'expo';
|
||||
|
||||
+1745
-203
File diff suppressed because it is too large
Load Diff
+1746
-204
File diff suppressed because it is too large
Load Diff
+1743
-201
File diff suppressed because it is too large
Load Diff
+1753
-211
File diff suppressed because it is too large
Load Diff
+1763
-221
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,785 @@
|
||||
{
|
||||
"login": {
|
||||
"jmap_endpoint_cors_hint": "Der Mailserver muss Anmeldungen von dieser Website erlauben. Ihre Administration kann das einrichten.",
|
||||
"reconnecting": "Die Verbindung ist weg. Wir versuchen, wieder zu verbinden…",
|
||||
"error": {
|
||||
"invalid_credentials": "Die E-Mail-Adresse oder das Passwort ist falsch. Prüfen Sie beides und versuchen Sie es noch einmal.",
|
||||
"invalid_token": "Der Server hat dieses Zugriffstoken nicht angenommen. Vielleicht ist es nicht vollständig oder nicht mehr gültig. Kopieren Sie es noch einmal oder erstellen Sie ein neues.",
|
||||
"connection_failed": "Wir erreichen den Mailserver nicht. Prüfen Sie Ihre Internetverbindung und versuchen Sie es noch einmal.",
|
||||
"cors_blocked": "Der Mailserver läuft, erlaubt aber keine Anmeldung von dieser Website. Bitten Sie Ihre Administration, diese Website auf dem Server zu erlauben.",
|
||||
"server_error": "Der Mailserver hat gerade ein Problem. Versuchen Sie es in ein paar Minuten noch einmal.",
|
||||
"generic": "Etwas ist schiefgelaufen. Versuchen Sie es noch einmal. Wenn das öfter passiert, fragen Sie Ihre Administration.",
|
||||
"totp_required": "Ihr Konto braucht zur Anmeldung einen zweiten Code. Öffnen Sie Ihre Authenticator-App und geben Sie unten den 6-stelligen Code ein.",
|
||||
"totp_invalid": "Dieser Code ist falsch oder zu alt. Geben Sie den Code ein, den Ihre Authenticator-App gerade anzeigt.",
|
||||
"oauth_discovery_failed": "Die Anmeldung über Ihre Organisation funktioniert gerade nicht. Versuchen Sie es später noch einmal oder fragen Sie Ihre Administration.",
|
||||
"token_exchange_failed": "Passwort und Code sind richtig, aber der Mailserver hat diese App nicht hereingelassen. Bitten Sie Ihre Administration, die Anmelde-Einstellungen für diese App zu prüfen."
|
||||
},
|
||||
"totp_toggle": "Ich habe einen Code aus meiner Authenticator-App",
|
||||
"token_hint": "Manche Mailanbieter geben Ihnen ein Token statt eines Passworts. Erstellen Sie es in den Einstellungen Ihres Mailanbieters, kopieren Sie es und fügen Sie es hier ein.",
|
||||
"config_error": {
|
||||
"title": "Das Webmail ist nicht richtig eingerichtet",
|
||||
"fetch_failed": "Das Webmail konnte seine Einstellungen nicht laden. Versuchen Sie es in ein paar Minuten noch einmal.",
|
||||
"server_not_configured": "Es ist noch kein Mailserver eingerichtet. Bitten Sie Ihre Administration, einen einzurichten."
|
||||
},
|
||||
"session_expired": "Sie wurden abgemeldet. Melden Sie sich wieder an, um weiterzumachen.",
|
||||
"oauth_error": {
|
||||
"title": "Die Anmeldung hat nicht geklappt",
|
||||
"invalid_state": "Die Anmeldung wurde unterbrochen. Fangen Sie noch einmal an.",
|
||||
"missing_params": "Es fehlen Anmeldedaten. Fangen Sie noch einmal an.",
|
||||
"token_exchange_failed": "Wir konnten die Anmeldung nicht abschließen. Versuchen Sie es noch einmal.",
|
||||
"access_denied": "Sie dürfen sich hier nicht anmelden. Bitten Sie Ihre Administration um Zugang."
|
||||
}
|
||||
},
|
||||
"session_limits": {
|
||||
"idle_message": "Sie haben eine Weile nichts getan. In {seconds, plural, one {# Sekunde} other {# Sekunden}} werden Sie abgemeldet. E-Mails, die Sie gerade schreiben, werden als Entwurf gespeichert.",
|
||||
"max_message": "Ihre Organisation meldet Sie {hours, plural, one {# Stunde} other {# Stunden}} nach der Anmeldung ab. In {seconds, plural, one {# Sekunde} other {# Sekunden}} werden Sie abgemeldet. E-Mails, die Sie gerade schreiben, werden als Entwurf gespeichert.",
|
||||
"reason_max": "Sie wurden abgemeldet, weil Sie {hours, plural, one {# Stunde} other {# Stunden}} angemeldet waren. Länger erlaubt es Ihre Organisation nicht.",
|
||||
"drafts_not_saved": "Einige E-Mails, die Sie geschrieben haben, konnten nicht als Entwurf gespeichert werden."
|
||||
},
|
||||
"totp_reauth": {
|
||||
"title": "Neuen Code eingeben",
|
||||
"description": "Ihr letzter Code gilt nicht mehr. Öffnen Sie Ihre Authenticator-App und geben Sie den neuen Code ein, um weiterzumachen.",
|
||||
"oauth_hint": "Diese Abfrage kommt immer wieder. Ihre Administration kann das abstellen, indem sie eine andere Anmeldeart (OAuth) einschaltet.",
|
||||
"code_label": "Code aus Ihrer Authenticator-App"
|
||||
},
|
||||
"email_list": {
|
||||
"permanent_delete_confirm_message": "Diese E-Mail wird endgültig gelöscht. Sie können sie nicht zurückholen.",
|
||||
"permanent_delete_confirm_batch_message": "{count, plural, one {Diese E-Mail wird} other {Diese # E-Mails werden}} endgültig gelöscht. Sie können sie nicht zurückholen.",
|
||||
"empty_folder": {
|
||||
"confirm_message": "Alle E-Mails in diesem Ordner werden endgültig gelöscht. Sie können sie nicht zurückholen."
|
||||
}
|
||||
},
|
||||
"email_viewer": {
|
||||
"read_receipt": {
|
||||
"send_failed": "Die Lesebestätigung konnte nicht gesendet werden"
|
||||
},
|
||||
"archive_mailbox_not_found": "Es gibt keinen Archiv-Ordner, deshalb kann die E-Mail nicht archiviert werden",
|
||||
"source_load_failed": "Die Rohdaten der E-Mail konnten nicht geladen werden. Versuchen Sie es noch einmal oder speichern Sie die E-Mail als Datei (.eml).",
|
||||
"spam": {
|
||||
"error": "Die E-Mail konnte nicht als Spam markiert werden. Versuchen Sie es noch einmal.",
|
||||
"error_not_spam": "Die E-Mail konnte nicht zurückgeholt werden. Versuchen Sie es noch einmal."
|
||||
},
|
||||
"unsubscribe_banner": {
|
||||
"error": "Das Abbestellen hat nicht geklappt. Versuchen Sie es später noch einmal."
|
||||
},
|
||||
"calendar_invitation": {
|
||||
"parse_error": "Diese Einladung kann nicht gelesen werden",
|
||||
"action_failed": "Das hat in Ihrem Kalender nicht geklappt. Versuchen Sie es noch einmal."
|
||||
},
|
||||
"undo_send_failed": "Das Senden konnte nicht gestoppt werden. Die E-Mail ist vielleicht schon gesendet.",
|
||||
"undo_send_signed_out": "Das Senden konnte nicht gestoppt werden, weil Sie bei dem Konto, das sie sendet, nicht mehr angemeldet sind.",
|
||||
"send_cancelled_restore_failed": "Das Senden wurde gestoppt. Die E-Mail konnte aber nicht zurück in „Entwürfe“ gelegt werden. Sie liegt noch in „Gesendet“.",
|
||||
"send_cancelled_remove_failed": "Das Senden wurde gestoppt. Die E-Mail konnte aber nicht aus „Gesendet“ entfernt werden.",
|
||||
"cancel_scheduled_failed": "Das geplante Senden konnte nicht gestoppt werden. Versuchen Sie es noch einmal.",
|
||||
"save_to_files": {
|
||||
"failed": "Die Datei konnte nicht in „Dateien“ gespeichert werden. Versuchen Sie es noch einmal."
|
||||
}
|
||||
},
|
||||
"email_composer": {
|
||||
"files_picker": {
|
||||
"load_failed": "Ihre Dateien konnten nicht geladen werden. Versuchen Sie es noch einmal."
|
||||
},
|
||||
"save_failed": "Der Entwurf konnte nicht gespeichert werden. Versuchen Sie es noch einmal.",
|
||||
"validation": {
|
||||
"invalid_recipients": "Nicht gesendet. Diese Adressen sind nicht richtig: {addresses}",
|
||||
"attachment_upload_failed": "Nicht gesendet. Ein Anhang konnte nicht hochgeladen werden. Entfernen Sie ihn und versuchen Sie es noch einmal."
|
||||
},
|
||||
"upload_failed": "{filename} konnte nicht hochgeladen werden. Versuchen Sie es noch einmal.",
|
||||
"send_failed": "Ihre E-Mail wurde nicht gesendet. Versuchen Sie es noch einmal.",
|
||||
"send_offline": "Sie sind nicht mit dem Internet verbunden. Ihre E-Mail ist noch da. Senden Sie sie noch einmal, wenn Sie wieder online sind.",
|
||||
"send_timeout": "Der Server hat nicht mehr geantwortet. Vielleicht wurde Ihre E-Mail trotzdem gesendet. Schauen Sie in Ihren Ordner „Gesendet“, bevor Sie sie noch einmal senden.",
|
||||
"submission_limit": "Der Server hat die E-Mail nicht gesendet, weil Sie eine Grenze erreicht haben: {reason}",
|
||||
"send_plugin_failed": "Nicht gesendet. Eine Erweiterung, die die E-Mail sendet, hat nicht funktioniert oder zu lange gebraucht. Schauen Sie in Ihren Ordner „Gesendet“, bevor Sie sie noch einmal senden.",
|
||||
"schedule_send_invalid": "Geben Sie ein Datum und eine Uhrzeit ein.",
|
||||
"schedule_send_cleanup_warning": "Die E-Mail wird zur gewählten Zeit gesendet. Der Entwurf konnte aber nicht entfernt werden und liegt noch in „Entwürfe“.",
|
||||
"invalid_recipient": "Das ist keine richtige E-Mail-Adresse",
|
||||
"send_filing_warning": "Gesendet. Ein alter Entwurf dieser E-Mail liegt aber vielleicht noch in „Entwürfe“.",
|
||||
"send_guard_failed": "Nicht gesendet. Eine Erweiterung, die E-Mails vor dem Senden prüft, hat nicht funktioniert oder nicht geantwortet."
|
||||
},
|
||||
"common": {
|
||||
"reconnecting": "Die Verbindung ist weg. Wir versuchen, wieder zu verbinden…"
|
||||
},
|
||||
"notifications": {
|
||||
"error_archiving": "Die E-Mail konnte nicht archiviert werden. Versuchen Sie es noch einmal.",
|
||||
"error_updating": "Die E-Mail konnte nicht geändert werden. Versuchen Sie es noch einmal.",
|
||||
"error_sending": "Ihre E-Mail wurde nicht gesendet. Versuchen Sie es noch einmal.",
|
||||
"error_deleting": "Die E-Mail konnte nicht gelöscht werden. Versuchen Sie es noch einmal.",
|
||||
"error_loading": "Ihre E-Mails konnten nicht geladen werden. Versuchen Sie es noch einmal.",
|
||||
"move_failed": "Verschieben hat nicht geklappt. Versuchen Sie es noch einmal.",
|
||||
"undo_failed": "Rückgängig machen hat nicht geklappt. Versuchen Sie es noch einmal.",
|
||||
"copy_failed": "Kopieren hat nicht geklappt. Versuchen Sie es noch einmal.",
|
||||
"move_error": "Die E-Mails konnten nicht in diesen Ordner verschoben werden. Versuchen Sie es noch einmal.",
|
||||
"tag_failed": "Das Label konnte nicht hinzugefügt werden. Versuchen Sie es noch einmal.",
|
||||
"identity_create_failed": "Die Absenderadresse konnte nicht hinzugefügt werden: {error}",
|
||||
"identity_update_failed": "Die Absenderadresse konnte nicht geändert werden: {error}",
|
||||
"identity_delete_failed": "Die Absenderadresse konnte nicht gelöscht werden: {error}",
|
||||
"identity_unauthorized": "Sie dürfen von dieser E-Mail-Adresse nicht senden. Fragen Sie Ihre Administration, wenn Sie sie brauchen.",
|
||||
"vacation_save_failed": "Ihre automatische Antwort konnte nicht gespeichert werden. Versuchen Sie es noch einmal.",
|
||||
"filters_save_failed": "Ihre Filterregeln konnten nicht gespeichert werden. Versuchen Sie es noch einmal.",
|
||||
"export_email_error": "Die E-Mail konnte nicht als Datei gespeichert werden. Versuchen Sie es noch einmal.",
|
||||
"import_email_error": "Die E-Mail konnte nicht aus der Datei hinzugefügt werden. Prüfen Sie die Datei und versuchen Sie es noch einmal.",
|
||||
"rule_apply_failed": "Die Regel konnte nicht auf die E-Mails angewendet werden, die schon da sind",
|
||||
"rule_folder_failed": "Der Ordner konnte nicht erstellt werden. Versuchen Sie es noch einmal.",
|
||||
"vacation_filters_save_failed": "Ihre automatische Antwort ist gespeichert. Die Weiterleitung und die Antwortliste konnten aber nicht gespeichert werden. Versuchen Sie es noch einmal."
|
||||
},
|
||||
"settings": {
|
||||
"reset_confirm": "Alle Einstellungen auf den Anfangszustand zurücksetzen?",
|
||||
"themes": {
|
||||
"description": "Wählen Sie ein Aussehen. Ihre Administration kann weitere Designs hinzufügen.",
|
||||
"locked": "Ihre Administration hat das Design gewählt. Sie können es nicht ändern.",
|
||||
"forced": "Ihre Administration hat das Design „{name}“ gewählt. Sie können es nicht ändern."
|
||||
},
|
||||
"appearance": {
|
||||
"message_list_order": {
|
||||
"description": "Die Reihenfolge der E-Mails in der Liste. Der Server sortiert den ganzen Ordner, nicht nur die E-Mails, die Sie sehen.",
|
||||
"tiebreak_note": "Sie können bis zu drei Stufen wählen. Eine spätere Stufe zählt nur, wenn die früheren gleich sind. Am Ende kommen immer die neuesten E-Mails zuerst.",
|
||||
"scope": {
|
||||
"description": "Ist das aus, gilt die Reihenfolge nur im Posteingang. Andere Ordner zeigen die neuesten E-Mails zuerst."
|
||||
},
|
||||
"unsupported_note": "Ihr Mailserver kann nicht nach gelesen oder ungelesen, Stern oder Label sortieren. Diese Stufen fallen weg, und die neuesten E-Mails kommen zuerst."
|
||||
},
|
||||
"description": "Ändern Sie, wie das Webmail aussieht",
|
||||
"theme": {
|
||||
"description": "Wählen Sie helle oder dunkle Farben"
|
||||
},
|
||||
"font_size": {
|
||||
"description": "Machen Sie den Text größer oder kleiner"
|
||||
},
|
||||
"list_density": {
|
||||
"description": "Wie viel Platz zwischen den Einträgen ist"
|
||||
},
|
||||
"animations": {
|
||||
"description": "Dinge beim Öffnen und Schließen sanft bewegen"
|
||||
},
|
||||
"toolbar_position": {
|
||||
"description": "Wo die Knöpfe für eine E-Mail stehen, etwa Antworten, Archivieren oder Löschen"
|
||||
},
|
||||
"toolbar_labels": {
|
||||
"description": "Wörter neben den Symbolen der Knöpfe zeigen. Schalten Sie das aus, um Platz zu sparen, wenn Sie die Symbole kennen."
|
||||
},
|
||||
"hide_account_switcher": {
|
||||
"description": "Die Kontoauswahl oben in der Ordnerliste ausblenden. Sie können das Konto weiterhin unten in der linken Leiste wechseln."
|
||||
},
|
||||
"show_rail_account_list": {
|
||||
"label": "Ihre Konten in der linken Leiste zeigen",
|
||||
"description": "Unten in der linken Leiste für jedes Konto einen Kreis zeigen, damit Sie schnell wechseln können. Darunter ist ein Knopf zum Abmelden."
|
||||
},
|
||||
"unified_mailbox": {
|
||||
"description": "Ordner zeigen, die die E-Mails mehrerer Konten zusammenfassen, etwa einen Posteingang für alle. Dafür brauchen Sie mehr als ein Konto (oder ein Gruppenpostfach unten).",
|
||||
"cross_account": {
|
||||
"description": "Die E-Mails aller Ihrer Konten zusammenfassen, nicht nur des Kontos, das Sie gerade nutzen."
|
||||
},
|
||||
"include_group": {
|
||||
"description": "Auch die Postfächer hinzufügen, die Sie mit einer Gruppe teilen."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"folders_description": "Wählen Sie, welche Ordner dieses Kontos zu den Listen „Alle Mails“, „Ungelesen“ und „Markiert“ gehören."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"description": "Ordner- und Label-Symbole farbig zeigen (blauer Posteingang, roter Spam, grünes Gesendet und so weiter). Schalten Sie das aus für graue Symbole."
|
||||
},
|
||||
"tint_list_rows": {
|
||||
"description": "Jede E-Mail in der Liste in der Farbe ihres ersten Labels einfärben. Ist das aus, zeigen die kleinen Punkte die Label-Farbe trotzdem."
|
||||
},
|
||||
"show_folder_total_count": {
|
||||
"description": "Zeigen, wie viele E-Mails ein Ordner oder Label insgesamt hat, neben der Zahl der ungelesenen."
|
||||
},
|
||||
"favicon_unread_badge": {
|
||||
"description": "Die Zahl der ungelesenen E-Mails auf dem Browser-Tab und dem App-Symbol zeigen. So sehen Sie neue E-Mails, auch wenn Sie gerade einen anderen Tab nutzen."
|
||||
},
|
||||
"show_shared_mail_accounts": {
|
||||
"description": "Die Ordner von Postfächern, die andere mit Ihnen teilen, in der Ordnerliste unter „Geteilt“ zeigen. Schalten Sie das aus, um alle auszublenden, oder blenden Sie unten einzelne Konten aus. Kalender und Kontakte bleiben."
|
||||
},
|
||||
"pro_interface": {
|
||||
"description": "Eine Ansicht für große Bildschirme mit vielen Tabs und mehreren Konten gleichzeitig. Sie können jederzeit zur normalen Ansicht zurück."
|
||||
},
|
||||
"account_marker": {
|
||||
"description": "In den zusammengefassten Ordnern jede E-Mail mit der Farbe ihres Kontos markieren."
|
||||
}
|
||||
},
|
||||
"keywords": {
|
||||
"description": "Erstellen Sie Labels, um Ihre E-Mails zu ordnen. Die Labels werden mit Ihren E-Mails auf dem Server gespeichert.",
|
||||
"migration_error": "Das Label konnte bei Ihren E-Mails nicht geändert werden. Versuchen Sie es noch einmal.",
|
||||
"filters_migration_error": "Manche Filterregeln nutzen noch das alte Label. Ändern Sie sie selbst.",
|
||||
"nesting": {
|
||||
"description": "Labels in andere Labels legen, wie Ordner. Die Seitenleiste zeigt sie als Baum."
|
||||
},
|
||||
"has_children_locked": "In diesem Label liegen andere Labels. Deshalb können Sie es nicht umbenennen oder verschieben. Verschieben oder löschen Sie zuerst die Labels darin.",
|
||||
"has_children_delete": "Löschen Sie zuerst die Labels in diesem Label",
|
||||
"unrecognized": {
|
||||
"description": "Dieser Browser speichert die Namen und Farben Ihrer Labels. Die Labels selbst sind an Ihren E-Mails gespeichert. Suchen Sie in Ihren E-Mails nach Labels, die hier fehlen, und fügen Sie sie wieder hinzu.",
|
||||
"partial": "Nur Ihre {scanned} neuesten von {total} E-Mails wurden durchsucht. Labels, die nur an älteren E-Mails hängen, fehlen vielleicht.",
|
||||
"error": "Ihre E-Mails konnten nicht nach Labels durchsucht werden. Versuchen Sie es noch einmal."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"push": {
|
||||
"description": "Erhalten Sie eine Meldung auf Ihrem Bildschirm, wenn neue E-Mails ankommen, auch wenn diese Website geschlossen ist. Der Dienst, der die Meldung weitergibt, sieht Ihre E-Mails nie.",
|
||||
"relay_label": "Benachrichtigungsdienst",
|
||||
"relay_desc": "Der Dienst, der Benachrichtigungen an Ihr Gerät weitergibt. Ihre Administration legt fest, welche Sie wählen können.",
|
||||
"relay_locked_desc": "Ihre Administration hat den Benachrichtigungsdienst gewählt. Sie können ihn nicht ändern.",
|
||||
"status_unsupported": "Dieser Browser kann keine Benachrichtigungen zeigen, wenn die Website geschlossen ist",
|
||||
"status_lite": "Diese Version des Webmails kann keine Benachrichtigungen senden, wenn sie geschlossen ist.",
|
||||
"reenable": "Wieder einschalten",
|
||||
"ios_hint": "Auf iPhone und iPad fügen Sie diese Website zuerst zum Home-Bildschirm hinzu. Safari sendet Benachrichtigungen nur für Websites auf dem Home-Bildschirm.",
|
||||
"devices_title": "Geräte, die Benachrichtigungen bekommen",
|
||||
"devices_desc": "Alle Geräte, die Benachrichtigungen für dieses Konto bekommen. Wenn Sie ein Gerät entfernen, bekommt es sofort keine mehr.",
|
||||
"devices_error": "Die Liste der Geräte konnte nicht geladen werden. Versuchen Sie es noch einmal.",
|
||||
"device_status_unknown": "Der Benachrichtigungsdienst kennt dieses Gerät nicht",
|
||||
"revoke": "Entfernen",
|
||||
"confirm_revoke_title": "Dieses Gerät entfernen?",
|
||||
"confirm_revoke_message": "Es bekommt sofort keine Benachrichtigungen mehr. Um sie wieder zu bekommen, schalten Sie die Benachrichtigungen auf diesem Gerät wieder ein.",
|
||||
"confirm_revoke_message_this": "Dieses Gerät bekommt sofort keine Benachrichtigungen mehr.",
|
||||
"shared_mailboxes_no_push": "Für Postfächer, die andere mit Ihnen teilen, bekommen Sie keine Benachrichtigungen.",
|
||||
"error_forbidden": "Der Mailserver erlaubt für dieses Konto keine Benachrichtigungen.",
|
||||
"error_over_quota": "Zu viele Geräte bekommen Benachrichtigungen für dieses Konto (meist sind höchstens 15 erlaubt). Entfernen Sie Geräte, die Sie nicht mehr nutzen, und versuchen Sie es dann noch einmal."
|
||||
},
|
||||
"push_all": {
|
||||
"description": "Benachrichtigungen für mehrere Konten auf einmal ein- oder ausschalten.",
|
||||
"result_partial": "Für {enabled} Konten eingeschaltet. Bei {failed} Konten hat es nicht geklappt."
|
||||
},
|
||||
"sound_selection": {
|
||||
"choose_desc": "Wählen Sie einen Ton. Klicken Sie auf den Lautsprecher, um ihn anzuhören."
|
||||
},
|
||||
"email": {
|
||||
"description": "Wählen Sie, wie Sie von neuen E-Mails erfahren",
|
||||
"inbox_only_desc": "Nur bei E-Mails im Posteingang melden, nicht bei E-Mails, die ein Filter in einen anderen Ordner legt"
|
||||
},
|
||||
"calendar": {
|
||||
"description": "Wählen Sie, wie Sie an Ihre Termine erinnert werden"
|
||||
}
|
||||
},
|
||||
"language_region": {
|
||||
"description": "Sprache, wie Datum und Uhrzeit aussehen, und andere Dinge für Ihr Land",
|
||||
"date_locale": {
|
||||
"description": "Die Reihenfolge von Tag, Monat und Jahr im Datum"
|
||||
},
|
||||
"time_format": {
|
||||
"description": "Uhrzeiten wie 3:00 PM (12 Stunden) oder wie 15:00 (24 Stunden) zeigen"
|
||||
},
|
||||
"time_zone": {
|
||||
"description": "Die Zeiten von E-Mails und Terminen in dieser Zeitzone zeigen. Wenn Sie keine wählen, gilt die Zeitzone Ihres Browsers."
|
||||
}
|
||||
},
|
||||
"email_behavior": {
|
||||
"inbox_type": {
|
||||
"description": "Bei „Ungelesene zuerst“ stehen Ihre 25 neuesten ungelesenen E-Mails oben im Posteingang"
|
||||
},
|
||||
"clear_search_on_folder_change": {
|
||||
"description": "Wenn Sie einen anderen Ordner öffnen, endet Ihre Suche und Sie sehen den ganzen Ordner. Ist das aus, sucht die Suche im neuen Ordner weiter."
|
||||
},
|
||||
"request_read_receipt": {
|
||||
"label": "Immer eine Lesebestätigung anfordern",
|
||||
"description": "Neue E-Mails bitten den Empfänger, Ihnen zu melden, wenn er die E-Mail geöffnet hat. Sie können das bei jeder E-Mail ausschalten."
|
||||
},
|
||||
"read_receipt_response": {
|
||||
"label": "Wenn jemand eine Lesebestätigung möchte",
|
||||
"description": "Manche Absender möchten erfahren, wann Sie ihre E-Mail öffnen. Wählen Sie, was dann passiert."
|
||||
},
|
||||
"description": "Legen Sie fest, was mit Ihren E-Mails passiert",
|
||||
"mark_read": {
|
||||
"description": "Wann eine geöffnete E-Mail als gelesen gilt"
|
||||
},
|
||||
"delete_action": {
|
||||
"warning": "Gelöschte E-Mails sind dann endgültig weg. Sie können sie nicht zurückholen."
|
||||
},
|
||||
"message_spacing": {
|
||||
"description": "Der Abstand um den Text einer geöffneten E-Mail"
|
||||
},
|
||||
"archive_mode": {
|
||||
"description": "Wie archivierte E-Mails in Ordner sortiert werden"
|
||||
},
|
||||
"permanently_delete_junk": {
|
||||
"label": "Spam endgültig löschen",
|
||||
"description": "Wenn Sie eine E-Mail im Spam-Ordner löschen, ist sie sofort endgültig weg. Sie kommt nicht erst in den Papierkorb."
|
||||
},
|
||||
"confirm_bulk_delete": {
|
||||
"description": "Fragen, bevor mehrere ausgewählte E-Mails in den Papierkorb kommen. Beim endgültigen Löschen wird immer gefragt."
|
||||
},
|
||||
"mail_layout": {
|
||||
"description": "Wählen Sie, wie die E-Mail-Liste und die geöffnete E-Mail auf dem Bildschirm stehen.",
|
||||
"split_description": "Die E-Mail-Liste und die geöffnete E-Mail stehen nebeneinander.",
|
||||
"focus_description": "Jede E-Mail hat eine Zeile. Eine geöffnete E-Mail nutzt die ganze Breite. Die Ordnerliste bleibt sichtbar.",
|
||||
"horizontal_description": "Die E-Mail-Liste steht oben. Die geöffnete E-Mail steht darunter."
|
||||
},
|
||||
"list_columns": {
|
||||
"description": "Die Liste als Tabelle mit den Spalten Von, Betreff, Datum und Größe zeigen."
|
||||
},
|
||||
"show_verification_codes": {
|
||||
"description": "Wenn eine E-Mail einen Anmelde- oder Bestätigungscode enthält, gibt es einen Knopf zum Kopieren. In der Liste bleibt der Knopf einen Tag lang."
|
||||
},
|
||||
"plain_text_mode": {
|
||||
"description": "Alle E-Mails als reinen Text schreiben, ohne Fettdruck, Farben oder Bilder. Das gilt auch für Antworten und Weiterleitungen."
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"description": "Wenn Sie antworten, geht die Antwort von der Adresse aus, an die die E-Mail ging. Das klappt mit all Ihren eigenen Adressen. Weiter unten wählen Sie, ob auch andere Adressen Ihrer Domain zählen."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Welche Adressen als Ihre gelten",
|
||||
"description": "„Nur exakte Adresse“ nutzt nur die Adressen, die Sie eingerichtet haben. „Jede Adresse auf meinen Domains“ antwortet auch von anderen Adressen Ihrer Domains. Wählen Sie die exakte Adresse, wenn diese anderen Adressen Verteilerlisten sind."
|
||||
},
|
||||
"signature_position": {
|
||||
"description": "Wo Ihre Signatur steht, wenn Sie antworten oder weiterleiten. Über dem zitierten Text schließt sie Ihre Antwort ab. Darunter bleibt die alte E-Mail an einem Stück."
|
||||
},
|
||||
"signature_separator": {
|
||||
"label": "Linie vor der Signatur",
|
||||
"description": "Die übliche Zeile „-- “ vor Ihre Signatur setzen. Viele Mail-Apps erkennen die Signatur daran."
|
||||
},
|
||||
"sub_address_delimiter": {
|
||||
"label": "Zeichen vor dem Adress-Stichwort",
|
||||
"description": "Das Zeichen zwischen Ihrem Namen und dem Stichwort in einer Adresse, etwa user{delimiter}tag@domain.com. Nehmen Sie dasselbe Zeichen wie Ihr Mailserver."
|
||||
},
|
||||
"attachment_click_action": {
|
||||
"label": "Wenn Sie auf einen Anhang klicken",
|
||||
"description": "Wählen Sie, ob ein Klick auf einen Anhang ihn anzeigt oder auf Ihrem Gerät speichert"
|
||||
},
|
||||
"attachment_position": {
|
||||
"description": "Wo Anhänge in einer geöffneten E-Mail stehen"
|
||||
},
|
||||
"always_light_mode": {
|
||||
"description": "E-Mails mit hellem Hintergrund zeigen, auch wenn die App dunkel ist. Manche E-Mails sehen falsch aus, wenn sie dunkel dargestellt werden."
|
||||
},
|
||||
"external_content": {
|
||||
"label": "Bilder aus dem Internet",
|
||||
"description": "Wählen Sie, ob Bilder gezeigt werden, die eine E-Mail aus dem Internet lädt. Das Laden kann dem Absender verraten, dass Sie die E-Mail geöffnet haben."
|
||||
},
|
||||
"trusted_senders": {
|
||||
"description": "Absender, deren Bilder immer gezeigt werden",
|
||||
"empty_description": "Wenn eine E-Mail blockierte Bilder hat, klicken Sie auf „Diesem Absender immer vertrauen“, um den Absender hier hinzuzufügen.",
|
||||
"save_error": "Die Liste konnte nicht gespeichert werden. Versuchen Sie es noch einmal.",
|
||||
"use_address_book_label": "Liste im Adressbuch speichern",
|
||||
"use_address_book_description": "Vertrauenswürdige Absender in einem eigenen Adressbuch speichern. Dann ist die Liste auf allen Ihren Geräten gleich."
|
||||
},
|
||||
"hover_actions": {
|
||||
"description": "Die Knöpfe, die Sie sehen, wenn Sie mit der Maus über eine E-Mail in der Liste fahren"
|
||||
},
|
||||
"default_mail_program": {
|
||||
"description": "E-Mail-Links auf Websites (mailto:-Links) in {appName} öffnen",
|
||||
"error": "Ihr Browser kann das nicht"
|
||||
},
|
||||
"attachment_reminder": {
|
||||
"description": "Wenn Ihre E-Mail von einem Anhang spricht, aber nichts angehängt ist, werden Sie vor dem Senden gewarnt",
|
||||
"keywords_label": "Wörter, bei denen die Erinnerung kommt",
|
||||
"keywords_description": "Wenn Ihre E-Mail eines dieser Wörter enthält, kommt die Erinnerung"
|
||||
},
|
||||
"placeholder_warning": {
|
||||
"description": "Vor dem Senden fragen, wenn die E-Mail noch Lücken hat, etwa [TIME] oder [Ihr Name], leere Vorlagenfelder oder Notizen wie TODO."
|
||||
},
|
||||
"recipient_mentions": {
|
||||
"description": "Tippen Sie @ in Ihrer E-Mail, um einen Empfänger auszuwählen. Sein Vorname kommt in den Text. Mit Enter oder Tab wählen Sie den markierten Namen."
|
||||
},
|
||||
"mention_adds_recipient": {
|
||||
"description": "Wenn Sie @ tippen, können Sie auch Personen aus Ihren Kontakten wählen. Sind sie noch keine Empfänger, werden sie zur E-Mail hinzugefügt."
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Bilder im Text nicht als Anhang zeigen",
|
||||
"description": "Bilder, die zum Text der E-Mail gehören, stehen nicht noch einmal in der Liste der Anhänge"
|
||||
},
|
||||
"attachment_image_previews": {
|
||||
"description": "Kleine Vorschaubilder von angehängten Bildern statt eines Datei-Symbols zeigen"
|
||||
},
|
||||
"send_delay": {
|
||||
"description": "Ihre E-Mails warten ein paar Sekunden, bevor sie gesendet werden. In dieser Zeit können Sie sie noch stoppen.",
|
||||
"unsupported": "Der Mailserver dieses Kontos kann mit dem Senden nicht warten. Die Einstellung wird trotzdem für Ihre anderen Konten gespeichert."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"description": "Auf einem Touchscreen: was passiert, wenn Sie eine E-Mail nach rechts wischen."
|
||||
},
|
||||
"swipe_left_action": {
|
||||
"description": "Auf einem Touchscreen: was passiert, wenn Sie eine E-Mail nach links wischen."
|
||||
},
|
||||
"rtl_editing": {
|
||||
"label": "Schreiben von rechts nach links",
|
||||
"description": "Fügt dem Editor einen Knopf hinzu. Damit schreiben Sie einen Absatz von links nach rechts oder von rechts nach links."
|
||||
}
|
||||
},
|
||||
"account": {
|
||||
"description": "Sehen Sie die Angaben zu Ihrem Konto",
|
||||
"auth_method_label": "So melden Sie sich an",
|
||||
"auth_method_oauth": "Anmeldeseite Ihrer Organisation",
|
||||
"server": {
|
||||
"label": "Adresse des Mailservers"
|
||||
},
|
||||
"accounts": {
|
||||
"description": "Ziehen Sie die Konten, um ihre Reihenfolge in der Kontoliste zu ändern"
|
||||
},
|
||||
"backup": {
|
||||
"description": "Speichern Sie die Liste Ihrer Konten auf dem Server und holen Sie sie auf einem anderen Gerät zurück. Ein Passwort, das nur Sie kennen, schützt die Liste. Die Passwörter der Postfächer werden nicht gespeichert. Sie melden sich also bei jedem Konto neu an.",
|
||||
"description_passwords": "Speichern Sie die Liste Ihrer Konten auf dem Server und holen Sie sie auf einem anderen Gerät zurück. Ein Passwort, das nur Sie kennen, schützt die Liste. Sie können auch die Passwörter der Postfächer speichern. Dann melden sich die Konten von selbst an.",
|
||||
"password_hint": "Mindestens 10 Zeichen. Niemand kann dieses Passwort zurücksetzen. Wenn Sie es vergessen, speichern Sie die Liste noch einmal mit einem neuen Passwort.",
|
||||
"restore_hint": "Das Passwort, das Sie beim Speichern der Liste gewählt haben.",
|
||||
"include_passwords_hint": "Wenn Sie die Liste zurückholen, melden sich diese Konten von selbst an. Aber Vorsicht: Wer das Passwort der Sicherung kennt, kann jedes Postfach in der Liste öffnen.",
|
||||
"skipped": "{count, plural, one {# Konto wurde} other {# Konten wurden}} nicht hinzugefügt, weil Sie schon die höchste erlaubte Zahl an Konten haben.",
|
||||
"sign_in_failed": "{count, plural, one {# gespeichertes Passwort hat nicht funktioniert} other {# gespeicherte Passwörter haben nicht funktioniert}}. Melden Sie sich in der Kontoliste selbst bei diesen Konten an.",
|
||||
"errors": {
|
||||
"unlock_failed": "Das Passwort ist falsch, oder diese Liste gehört zu einem anderen Konto.",
|
||||
"owner_signin_required": "Melden Sie sich noch einmal bei diesem Konto an. Versuchen Sie es dann erneut.",
|
||||
"https_required": "Das geht nur über eine sichere Verbindung (die Adresse beginnt mit https).",
|
||||
"storage_failed": "Der Server konnte die Liste nicht speichern oder öffnen. Versuchen Sie es später noch einmal."
|
||||
}
|
||||
},
|
||||
"shared_accounts": {
|
||||
"description": "Konten, die Sie mit anderen teilen und verwalten dürfen. Wählen Sie eines aus, um seine Filter, automatischen Antworten, Kalender und Kontakte zu ändern."
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"description": "Ändern Sie Ihr Passwort und schützen Sie Ihr Konto",
|
||||
"not_available": "Die Sicherheitseinstellungen für diesen Mailserver können Sie hier nicht ändern. Vielleicht erlaubt Ihr Server das nicht. Mehr dazu steht in der <a href=\"https://bulwarkmail.org/docs/guides/account-security\" class=\"underline hover:opacity-80\" target=\"_blank\">Dokumentation</a>.",
|
||||
"password": {
|
||||
"error_title": "Ihr Passwort wurde nicht geändert",
|
||||
"error_mismatch": "Die beiden neuen Passwörter sind nicht gleich. Geben Sie sie noch einmal ein.",
|
||||
"error_min_length": "Ihr Passwort braucht mindestens 8 Zeichen",
|
||||
"error_generic": "Ihr Passwort konnte nicht geändert werden. Versuchen Sie es noch einmal."
|
||||
},
|
||||
"display_name": {
|
||||
"description": "Der Name, den der Mailserver für Sie anzeigt",
|
||||
"error": "Ihr Name konnte nicht geändert werden. Versuchen Sie es noch einmal."
|
||||
},
|
||||
"totp": {
|
||||
"label": "Code aus einer Authenticator-App",
|
||||
"description": "Schützen Sie Ihr Konto besser: Bei der Anmeldung geben Sie zusätzlich einen Code aus einer App auf Ihrem Handy ein",
|
||||
"enable_error": "Die Zwei-Faktor-Authentifizierung konnte nicht eingeschaltet werden. Versuchen Sie es noch einmal.",
|
||||
"disable_error": "Die Zwei-Faktor-Authentifizierung konnte nicht ausgeschaltet werden. Versuchen Sie es noch einmal.",
|
||||
"setup_instructions": "Öffnen Sie Ihre Authenticator-App (zum Beispiel Google Authenticator oder Authy) und fügen Sie diesen Link dort hinzu:",
|
||||
"verification_code": "Code aus der App",
|
||||
"disable_confirm_prompt": "Um die Zwei-Faktor-Authentifizierung auszuschalten, geben Sie Ihr Passwort ein.",
|
||||
"password_required": "Geben Sie Ihr Passwort ein",
|
||||
"code_required": "Geben Sie den Code aus der App ein",
|
||||
"code_invalid": "Dieser Code ist falsch oder zu alt. Geben Sie den Code ein, den die App gerade anzeigt."
|
||||
},
|
||||
"app_passwords": {
|
||||
"description": "Zusätzliche Passwörter für Mail-Apps, die keinen zweiten Code abfragen können, etwa manche Mail-Apps auf dem Handy",
|
||||
"allowed_ips_label": "Nur von diesen Internetadressen (optional)",
|
||||
"allowed_ips_hint": "Trennen Sie die Adressen mit Komma oder Leerzeichen. Lassen Sie das Feld leer, um alle Adressen zu erlauben.",
|
||||
"copy_now_warning": "Kopieren Sie dieses Passwort jetzt. Sie sehen es nicht noch einmal.",
|
||||
"add_error": "Das App-Passwort konnte nicht erstellt werden. Versuchen Sie es noch einmal.",
|
||||
"remove_error": "Das App-Passwort konnte nicht gelöscht werden. Versuchen Sie es noch einmal.",
|
||||
"delete_message": "Apps, die {name} nutzen, können sich dann nicht mehr anmelden."
|
||||
},
|
||||
"api_keys": {
|
||||
"description": "Schlüssel für Programme und Skripte, die ohne Sie mit Ihrem Postfach arbeiten, zum Beispiel eine Sicherung",
|
||||
"copy_now_warning": "Kopieren Sie diesen Schlüssel jetzt. Sie sehen ihn nicht noch einmal.",
|
||||
"add_error": "Der Schlüssel konnte nicht erstellt werden. Versuchen Sie es noch einmal.",
|
||||
"remove_error": "Der Schlüssel konnte nicht gelöscht werden. Versuchen Sie es noch einmal.",
|
||||
"delete_message": "Programme und Skripte, die {name} nutzen, funktionieren dann nicht mehr."
|
||||
},
|
||||
"encryption": {
|
||||
"label": "E-Mails verschlüsselt speichern",
|
||||
"description": "Der Server speichert Ihre E-Mails verschlüsselt. Nur Ihr Schlüssel kann sie wieder lesbar machen.",
|
||||
"error": "Die Verschlüsselung konnte nicht geändert werden. Versuchen Sie es noch einmal.",
|
||||
"algorithm_label": "Art der Verschlüsselung",
|
||||
"encrypt_on_append": "Auch E-Mails verschlüsseln, die Apps in Ihr Postfach legen",
|
||||
"allow_spam_training": "Den Spamfilter aus neuen E-Mails lernen lassen, bevor sie verschlüsselt werden",
|
||||
"disable_message": "Ab jetzt speichert der Server neue E-Mails ohne Verschlüsselung."
|
||||
},
|
||||
"public_keys": {
|
||||
"key_label": "Öffentlicher Schlüssel (S/MIME oder PGP, als Text)",
|
||||
"add_error": "Der öffentliche Schlüssel konnte nicht hinzugefügt werden. Prüfen Sie, ob Sie ihn ganz eingefügt haben.",
|
||||
"remove_error": "Der öffentliche Schlüssel konnte nicht gelöscht werden. Versuchen Sie es noch einmal."
|
||||
},
|
||||
"email_client": {
|
||||
"description": "Mit diesen Angaben richten Sie E-Mail auf Ihrem Computer oder Handy ein, zum Beispiel in Thunderbird, Apple Mail oder Outlook",
|
||||
"jmap_username_label": "Benutzername für Mail-Apps",
|
||||
"password_instructions": "Nutzen Sie in Ihrer Mail-App den Benutzernamen oben und ein App-Passwort. Ein App-Passwort erstellen Sie weiter unten auf dieser Seite."
|
||||
},
|
||||
"link_device": {
|
||||
"description": "Melden Sie sich in der Bulwark-Mail-App auf Ihrem Handy an, ohne Ihr Passwort einzutippen. Ihr Handy bekommt eine eigene Anmeldung. Wenn Sie sich hier abmelden, bleibt Ihr Handy angemeldet.",
|
||||
"error": "Der Code konnte nicht erstellt werden. Versuchen Sie es noch einmal.",
|
||||
"password_prompt": "Geben Sie Ihr Passwort ein, damit wir wissen, dass Sie es sind. Ihr Passwort wird nicht an das Handy geschickt.",
|
||||
"error_not_signed_in": "Sie wurden abgemeldet. Melden Sie sich wieder an und verbinden Sie dann Ihr Handy.",
|
||||
"error_impersonation": "Sie sehen gerade das Postfach einer anderen Person. Von hier aus können Sie kein Handy verbinden.",
|
||||
"error_too_many_attempts": "Sie haben es zu oft versucht. Warten Sie kurz und versuchen Sie es dann noch einmal.",
|
||||
"error_session_secret": "Das Verbinden eines Handys ist auf diesem Server nicht eingerichtet. Fragen Sie Ihre Administration.",
|
||||
"error_server_unreachable": "Wir erreichen Ihren Mailserver nicht. Versuchen Sie es noch einmal.",
|
||||
"error_pairing_unavailable": "Ihr Mailserver erlaubt das Verbinden eines Handys auf diese Weise nicht. Fragen Sie Ihre Administration.",
|
||||
"error_insecure_server": "Die Handy-App braucht eine sichere Verbindung zum Mailserver (https). Dieser Server hat keine."
|
||||
},
|
||||
"sections": {
|
||||
"devices_description": "Lesen Sie Ihre E-Mails in anderen Apps und auf anderen Geräten."
|
||||
}
|
||||
},
|
||||
"identities": {
|
||||
"description": "Die E-Mail-Adressen, von denen Sie senden können",
|
||||
"identities_count": {
|
||||
"label": "Ihre Absenderadressen",
|
||||
"description": "Adressen, von denen Sie E-Mails senden können"
|
||||
},
|
||||
"sub_addressing": {
|
||||
"label": "Adressen mit Stichwort",
|
||||
"description": "Hängen Sie ein Stichwort an Ihre Adresse, etwa user+shop@domain.com. E-Mails daran kommen trotzdem bei Ihnen an, und Sie können sie nach dem Stichwort sortieren."
|
||||
}
|
||||
},
|
||||
"vacation": {
|
||||
"description": "Neue E-Mails automatisch beantworten, während Sie weg sind",
|
||||
"not_supported": "Ihr Mailserver kann keine automatischen Antworten senden.",
|
||||
"fetch_error": "Ihre automatische Antwort konnte nicht geladen werden. Versuchen Sie es noch einmal.",
|
||||
"status": {
|
||||
"description": "Alle, die Ihnen schreiben, bekommen eine automatische Antwort"
|
||||
},
|
||||
"date_range": {
|
||||
"start_description": "Leer lassen, um sofort zu beginnen"
|
||||
},
|
||||
"message": {
|
||||
"subject_description": "Der Betreff der automatischen Antwort",
|
||||
"html_description": "Fügen Sie eine Version mit Links und Formatierung hinzu. Wer sie in seiner Mail-App nicht sehen kann, bekommt den einfachen Text oben."
|
||||
},
|
||||
"warnings": {
|
||||
"invalid_date": "Geben Sie ein Start- und Enddatum mit Uhrzeit ein oder lassen Sie das Feld leer",
|
||||
"empty_body": "Die Nachricht ist leer. Die Leute würden eine Antwort ohne Text bekommen.",
|
||||
"subject_too_long": "Der Betreff ist zu lang für den Server. Kürzen Sie ihn (höchstens {max} Byte).",
|
||||
"body_too_long": "Die Nachricht ist zu lang für den Server. Kürzen Sie sie (höchstens {max} Byte, mit Formatierung).",
|
||||
"forward_address": "Geben Sie die E-Mail-Adresse ein, an die weitergeleitet wird",
|
||||
"not_running": "Weiterleitung und Antwortliste sind gespeichert, aber gerade aus. Klicken Sie auf Speichern, um sie wieder einzuschalten."
|
||||
},
|
||||
"forward": {
|
||||
"description": "Neue E-Mails in der Zeit oben an eine andere Adresse weiterschicken, mit oder ohne automatische Antwort. Ohne Enddatum läuft das, bis Sie es ausschalten. Spam wird nicht weitergeleitet.",
|
||||
"keep_description": "Ist das aus, wird die E-Mail nur weitergeleitet. Sie bleibt nicht in diesem Postfach, und Ihre Filterregeln laufen nicht darauf."
|
||||
}
|
||||
},
|
||||
"folders": {
|
||||
"description": "Verwalten Sie Ihre Ordner und legen Sie fest, wofür welcher Ordner dient",
|
||||
"folder_list_description": "Klicken Sie auf das Symbol eines Ordners, um es zu ändern",
|
||||
"standard_roles_description": "Legen Sie fest, welcher Ordner Ihr Posteingang, Gesendet, Papierkorb und so weiter ist.",
|
||||
"reorder": "Ziehen Sie nach oben oder unten, um die Reihenfolge zu ändern. Ziehen Sie nach links oder rechts, um den Ordner in einen anderen Ordner zu legen.",
|
||||
"move_partial_error": "{count, plural, one {# Ordner konnte} other {# Ordner konnten}} nicht verschoben werden. Versuchen Sie es noch einmal.",
|
||||
"confirm_delete": "„{name}“ löschen? Die E-Mails in diesem Ordner kommen in den Papierkorb.",
|
||||
"cannot_delete_role": "Dieser Ordner hat eine besondere Aufgabe, etwa Posteingang oder Papierkorb. Entfernen Sie zuerst die Aufgabe, dann können Sie ihn löschen.",
|
||||
"error_delete_has_children": "Der Ordner wurde nicht gelöscht, weil andere Ordner darin liegen. Löschen oder verschieben Sie diese zuerst.",
|
||||
"error_delete_has_email": "Der Ordner wurde nicht gelöscht, weil noch E-Mails darin liegen. Verschieben oder löschen Sie diese zuerst.",
|
||||
"error_role": "Die Aufgabe des Ordners konnte nicht geändert werden. Versuchen Sie es noch einmal."
|
||||
},
|
||||
"advanced": {
|
||||
"debug_mode": {
|
||||
"description": "Mehr Einzelheiten darüber aufzeichnen, was die App tut. Das hilft, Fehler zu finden."
|
||||
},
|
||||
"settings_sync": {
|
||||
"label": "Gleiche Einstellungen auf allen Geräten",
|
||||
"description": "Ihre Einstellungen auf dem Server speichern, damit alle Ihre Browser und Geräte sie nutzen"
|
||||
},
|
||||
"sender_favicons": {
|
||||
"description": "Bei Firmen das Symbol ihrer Website als Bild des Absenders zeigen",
|
||||
"refresh_description": "Die Website-Symbole neu laden, zum Beispiel wenn eine Firma ein neues hat"
|
||||
},
|
||||
"show_avatars_in_junk": {
|
||||
"label": "Bilder der Absender im Spam-Ordner zeigen",
|
||||
"description": "Das Bild oder Website-Symbol des Absenders auch im Spam-Ordner zeigen. Das ist anfangs aus, weil gefälschte E-Mails mit Bild echter wirken."
|
||||
},
|
||||
"keyboard_shortcuts": {
|
||||
"description": "Alle Tasten ansehen, die Sie statt der Maus nutzen können"
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Daten neu vom Server laden",
|
||||
"description": "Ihre Kontakte, Kalender und Ordner neu vom Server laden. Nutzen Sie das, wenn etwas alt oder falsch aussieht. Sie bleiben angemeldet.",
|
||||
"confirm_title": "Daten neu vom Server laden?"
|
||||
},
|
||||
"reset_settings": {
|
||||
"description": "Alle Einstellungen auf den Anfangszustand zurücksetzen"
|
||||
},
|
||||
"export_settings": {
|
||||
"description": "Ihre Einstellungen als Datei auf Ihrem Gerät speichern"
|
||||
},
|
||||
"import_settings": {
|
||||
"description": "Ihre Einstellungen aus einer früher gespeicherten Datei laden"
|
||||
},
|
||||
"sections": {
|
||||
"backup_description": "Dieselben Einstellungen in anderen Browsern und auf anderen Geräten nutzen.",
|
||||
"fix_description": "Wenn etwas alt oder falsch aussieht oder Sie neu anfangen möchten."
|
||||
}
|
||||
},
|
||||
"sidebar_apps": {
|
||||
"description": "Fügen Sie der linken Leiste eigene Apps und Links hinzu",
|
||||
"keep_loaded_description": "Apps im Hintergrund offen lassen, wenn Sie wechseln, damit sie nicht neu laden müssen",
|
||||
"managed_description": "Ihre Administration hat diese Apps für alle eingerichtet. Sie können sie nicht ändern."
|
||||
},
|
||||
"contacts": {
|
||||
"group_by_letter_description": "In der Kontaktliste einen Buchstaben über jede Gruppe von Namen setzen",
|
||||
"sort_by_last_name_description": "Kontakte nach Nachnamen sortieren, damit Familien nebeneinander stehen",
|
||||
"import_description": "Kontakte aus einer Kontaktdatei (.vcf) hinzufügen",
|
||||
"export_description": "Alle Kontakte in einer Kontaktdatei (.vcf) speichern"
|
||||
},
|
||||
"downloads": {
|
||||
"description": "Legen Sie die Dateinamen für E-Mails und Anhänge fest, die Sie speichern.",
|
||||
"email_template": {
|
||||
"description": "Der Dateiname, wenn Sie eine E-Mail speichern oder in einen Ordner auf Ihrem Computer ziehen. Die Endung .eml wird automatisch angehängt."
|
||||
},
|
||||
"attachment_template": {
|
||||
"description": "Der Dateiname, wenn Sie einen Anhang speichern oder herausziehen. Wenn Sie '{filename}' und '{ext}' weglassen, behält die Datei ihre Endung."
|
||||
},
|
||||
"bundle_template": {
|
||||
"description": "Der Dateiname, wenn Sie mehrere E-Mails zusammen in einer .zip-Datei speichern. Die Endung .zip wird automatisch angehängt."
|
||||
},
|
||||
"spaces": {
|
||||
"description": "Im Dateinamen ein anderes Zeichen statt Leerzeichen nutzen."
|
||||
},
|
||||
"lowercase": {
|
||||
"description": "Den ganzen Dateinamen kleinschreiben."
|
||||
},
|
||||
"strip_diacritics": {
|
||||
"description": "Buchstaben mit Akzenten in einfache Buchstaben ändern (ä → a, é → e). Manche Programme kommen mit Akzenten nicht klar."
|
||||
},
|
||||
"collapse_separators": {
|
||||
"description": "Wenn mehrere Leerzeichen, _ oder - hintereinander stehen, nur eines behalten."
|
||||
},
|
||||
"after_export": {
|
||||
"description": "Sie können die E-Mail in einen anderen Ordner verschieben, nachdem Sie sie als Datei gespeichert haben."
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
"description": "Regeln, die neue E-Mails von selbst sortieren, zum Beispiel in einen Ordner",
|
||||
"no_rules_description": "Erstellen Sie eine Regel, dann werden neue E-Mails von selbst sortiert",
|
||||
"vacation_active_description": "Neue E-Mails bekommen eine automatische Antwort",
|
||||
"delete_confirm": "Diese Regel löschen?",
|
||||
"raw_editor": "Regeln als Code bearbeiten (Sieve)",
|
||||
"raw_editor_warning": "Wenn Sie den Code ändern, können Sie die Regeln hier vielleicht nicht mehr mit dem normalen Editor bearbeiten. Ihre Änderungen im Code ersetzen die normalen Regeln.",
|
||||
"not_supported": "Ihr Mailserver kann keine Filterregeln nutzen.",
|
||||
"match_all": "Alle Bedingungen müssen zutreffen",
|
||||
"match_any": "Eine Bedingung genügt",
|
||||
"stop_processing": "Die Regeln darunter nicht mehr prüfen",
|
||||
"condition_fields": {
|
||||
"header": "Andere Kopfzeile",
|
||||
"body": "Text der E-Mail"
|
||||
},
|
||||
"comparators": {
|
||||
"matches": "passt zum Muster (* = beliebiger Text)"
|
||||
},
|
||||
"forward_limit": "Dieser Server leitet eine E-Mail an höchstens {count} Adressen weiter. Weitere Weiterleitungen fallen weg.",
|
||||
"forward_unavailable": "Dieser Server erlaubt keine Weiterleitung. Regeln, die E-Mails weiterleiten, tun nichts.",
|
||||
"header_name": "Name der Kopfzeile",
|
||||
"opaque_warning": "Diese Regeln wurden in einer anderen App geändert. Sie können sie hier nur als Code bearbeiten.",
|
||||
"open_sieve_editor": "Regeln als Code bearbeiten (Sieve)",
|
||||
"expanded_view_description": "Jede Regel mit allen Bedingungen und Aktionen zeigen",
|
||||
"reset_warning": "Damit werden alle Ihre jetzigen Regeln gelöscht und Sie fangen ohne Regeln neu an.",
|
||||
"validation_empty_conditions": "Fügen Sie mindestens eine Bedingung hinzu und füllen Sie ihren Wert aus",
|
||||
"validation_empty_actions": "Fügen Sie mindestens eine Aktion hinzu",
|
||||
"validation_header_name": "Verwenden Sie im Namen der Kopfzeile nur die Buchstaben A bis Z, Ziffern, - und _",
|
||||
"sieve_editor": {
|
||||
"title": "Filterregeln als Code (Sieve)",
|
||||
"warning": "Wenn Sie den Code ändern, können Sie die Regeln hier vielleicht nicht mehr mit dem normalen Editor bearbeiten. Ihre Änderungen im Code ersetzen die normalen Regeln.",
|
||||
"script_content": "Regeln als Code",
|
||||
"valid": "Der Code hat keine Fehler",
|
||||
"invalid": "Der Code hat Fehler",
|
||||
"validating": "Wird geprüft…",
|
||||
"validate": "Prüfen",
|
||||
"overwrite_message": "Dieser Code enthält die Regeln aus dem normalen Editor nicht mehr. Wenn Sie speichern, ersetzt er sie. Das können Sie nicht rückgängig machen.",
|
||||
"validation_failed": "Der Code konnte nicht geprüft werden. Versuchen Sie es noch einmal."
|
||||
},
|
||||
"managed_by_tooltip": "{source} verwaltet das. Ändern Sie es in dieser App oder bearbeiten Sie die Regeln als Code.",
|
||||
"apply_existing_unsupported": "Nur manche Regeln können auch für E-Mails laufen, die schon da sind: Regeln, die Absender, Empfänger, Betreff oder Kopfzeilen prüfen und die verschieben, kopieren, als gelesen markieren, mit Stern markieren oder mit Label versehen.",
|
||||
"period_hint": "Die Regel gilt für E-Mails, die in dieser Zeit ankommen. Lassen Sie ein Feld leer, wenn es keinen Start oder kein Ende gibt.",
|
||||
"validation_period_invalid": "Geben Sie ein Datum mit Uhrzeit ein oder lassen Sie das Feld leer",
|
||||
"apply_existing_period": "Eine Regel mit Zeitraum gilt nur für E-Mails, die in dieser Zeit ankommen. Deshalb kann sie nicht für E-Mails laufen, die schon da sind."
|
||||
},
|
||||
"templates": {
|
||||
"description": "Speichern Sie E-Mails, die Sie oft schreiben, und nutzen Sie sie wieder. Sie können Lücken haben, die Sie jedes Mal ausfüllen.",
|
||||
"export_import_description": "Speichern Sie Ihre Vorlagen als Datei oder bringen Sie sie auf ein anderes Gerät",
|
||||
"validation": {
|
||||
"too_long": "Der Name darf höchstens 200 Zeichen haben"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"sort_direction": {
|
||||
"description": "Von A bis Z oder von Z bis A"
|
||||
},
|
||||
"show_thumbnails": {
|
||||
"description": "Von jedem Bild ein kleines Vorschaubild statt eines Symbols zeigen"
|
||||
},
|
||||
"show_hidden": {
|
||||
"description": "Versteckte Dateien und Ordner zeigen (ihr Name beginnt mit einem Punkt)"
|
||||
}
|
||||
},
|
||||
"reading_page": {
|
||||
"sections": {
|
||||
"images_description": "Bilder aus dem Internet können dem Absender verraten, dass Sie die E-Mail geöffnet haben."
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"page_error_description": "Diese Seite funktioniert gerade nicht. Versuchen Sie es noch einmal oder gehen Sie zurück zum Posteingang.",
|
||||
"sidebar_error": "Ihre Ordner konnten nicht geladen werden",
|
||||
"email_list_error": "Ihre E-Mails konnten nicht geladen werden",
|
||||
"viewer_error_title": "Diese E-Mail kann nicht angezeigt werden",
|
||||
"viewer_error_description": "Etwas in dieser E-Mail verhindert, dass sie hier angezeigt wird. Vielleicht nutzt sie ein Format, das wir nicht unterstützen.",
|
||||
"composer_error": "Das Fenster für neue E-Mails konnte nicht geöffnet werden",
|
||||
"settings_error_title": "Die Einstellungen konnten nicht geöffnet werden",
|
||||
"settings_error_description": "Ihre Einstellungen konnten nicht geladen werden. Was Sie jetzt ändern, wird vielleicht nicht gespeichert."
|
||||
},
|
||||
"identities": {
|
||||
"delete_confirm": "Diese Absenderadresse löschen? Das können Sie nicht rückgängig machen.",
|
||||
"cannot_delete": "Diese Absenderadresse können Sie nicht löschen",
|
||||
"validation_errors": {
|
||||
"invalid_emails": "Diese E-Mail-Adressen sind nicht richtig: {emails}"
|
||||
},
|
||||
"form": {
|
||||
"email_invalid": "Geben Sie eine richtige E-Mail-Adresse ein",
|
||||
"email_immutable": "Die E-Mail-Adresse können Sie nach dem Hinzufügen nicht mehr ändern",
|
||||
"signature_byte_limit_reached": "Die Signatur ist zu lang für den Server"
|
||||
},
|
||||
"sub_address": {
|
||||
"invalid_tag": "Verwenden Sie im Stichwort nur Buchstaben, Ziffern und Bindestriche (-)"
|
||||
}
|
||||
},
|
||||
"calendar": {
|
||||
"import": {
|
||||
"url_description": "Geben Sie die Webadresse einer Kalenderdatei (.ics) ein, um ihre Termine hinzuzufügen."
|
||||
},
|
||||
"management": {
|
||||
"description": "Erstellen Sie neue Kalender, benennen Sie sie um und ändern Sie ihre Farbe. Sie können auch mit der rechten Maustaste auf einen Kalender in der Seitenleiste klicken, um die Farbe zu ändern.",
|
||||
"description_touch": "Erstellen Sie neue Kalender, benennen Sie sie um und ändern Sie ihre Farbe.",
|
||||
"confirm_delete": "„{name}“ löschen? Alle Termine in diesem Kalender werden auch gelöscht.",
|
||||
"confirm_clear": "Alle Termine in „{name}“ löschen? Das können Sie nicht rückgängig machen.",
|
||||
"kind_hint": "Das legt fest, welche Apps auf Ihren anderen Geräten diesen Kalender zeigen. Sie können es später nicht ändern.",
|
||||
"default_alerts_hint": "Neue Termine in diesem Kalender bekommen diese Erinnerungen, außer Sie ändern sie im Termin. Eine Änderung hier ändert keine Termine, die Sie schon haben. Erinnerungen per E-Mail schickt der Mailserver."
|
||||
},
|
||||
"subscription": {
|
||||
"description": "Folgen Sie einem Kalender aus dem Internet, zum Beispiel mit Feiertagen. Seine Termine werden von selbst aktualisiert und in einem eigenen Kalender gezeigt. Die Adresse beginnt mit https:// oder webcal://.",
|
||||
"confirm_delete": "„{name}“ nicht mehr folgen? Der Kalender und alle seine Termine werden entfernt."
|
||||
}
|
||||
},
|
||||
"sharing": {
|
||||
"description": "Erlauben Sie anderen Personen oder Gruppen auf diesem Server den Zugriff. Die Änderung gilt sofort.",
|
||||
"directory_restricted": "Ihr Server zeigt keine Liste aller Benutzer. Deshalb sehen Sie nur Ihre Gruppen und Personen, die etwas mit Ihnen teilen. Ihre Administration kann das ändern.",
|
||||
"directory_not_browsable": "Ihr Server zeigt keine Liste aller Benutzer.",
|
||||
"directory_failed": "Die Liste der Personen und Gruppen konnte nicht geladen werden. Versuchen Sie es noch einmal.",
|
||||
"share_failed": "Die Freigabe konnte nicht geändert werden. Versuchen Sie es noch einmal."
|
||||
},
|
||||
"delegates": {
|
||||
"description": "Erlauben Sie jemandem in einem Schritt den Zugriff auf Ihre E-Mails, Kalender und Kontakte. Sehen Sie, wer worauf Zugriff hat.",
|
||||
"read_failed": "Ein Teil Ihrer Freigaben konnte nicht geladen werden. Sie können Zugriffe ändern oder entfernen, sobald alles geladen ist.",
|
||||
"remove_message": "{name} hat dann keinen Zugriff mehr auf alles, was Sie aus Ihrem Konto geteilt haben: E-Mails, Kalender, Kontakte und Dateien.",
|
||||
"not_found": "Auf diesem Server gibt es niemanden mit der Adresse {address}.",
|
||||
"lookup_failed": "Diese Adresse wurde nicht gefunden. Prüfen Sie sie und versuchen Sie es noch einmal.",
|
||||
"known_only": "Sie sehen nur Ihre Gruppen und Personen, die etwas mit Ihnen geteilt haben.",
|
||||
"directory_off_note": "Um jemand Neues hinzuzufügen, muss Ihre Administration zuerst die Suche nach Benutzern auf dem Server erlauben.",
|
||||
"directory_failed": "Die Liste der Personen und Gruppen konnte nicht geladen werden. Versuchen Sie es noch einmal.",
|
||||
"nothing_to_change": "Es hat sich nichts geändert. Wählen Sie, was die Person bekommen soll, oder klicken Sie auf „Zugriff entfernen“.",
|
||||
"change_note": "Ordner, Kalender und Adressbücher, die Sie nicht auswählen, sind nicht mehr geteilt. Geteilte Dateien bleiben geteilt. „Zugriff entfernen“ nimmt auch diese weg.",
|
||||
"rights_keep_hint": "Was die Person schon hat, bleibt gleich. Alles Neue bekommt dieselben Rechte wie der Rest dieser Art, oder nur Lesen.",
|
||||
"send_note": "Personen mit Zugriff können Ihre E-Mails und Ihren Kalender lesen und ordnen. Sie können keine E-Mails als Sie oder für Sie senden. Wenn mehrere Personen eine Adresse brauchen, fragen Sie Ihre Administration nach einem geteilten Postfach.",
|
||||
"partial_title": "Einiges konnte nicht geändert werden. Alles andere ist erledigt."
|
||||
},
|
||||
"smime": {
|
||||
"your_certificates_desc": "Fügen Sie Ihre Zertifikate hinzu und verwalten Sie sie. Sie brauchen eines, um E-Mails mit S/MIME zu signieren oder zu verschlüsseln.",
|
||||
"recipient_certificates_desc": "Zertifikate anderer Personen. Damit können Sie diesen Personen verschlüsselte E-Mails senden.",
|
||||
"identity_bindings_desc": "Wählen Sie, welches Zertifikat zu welcher Ihrer Absenderadressen gehört",
|
||||
"defaults_desc": "Wählen Sie, ob neue E-Mails von Anfang an signiert oder verschlüsselt werden",
|
||||
"encrypt_by_default_desc": "Eine E-Mail von selbst verschlüsseln, wenn Sie für jeden Empfänger ein Zertifikat haben",
|
||||
"remember_unlocked_desc": "Ihre Schlüssel entsperrt lassen, bis Sie diesen Browser schließen",
|
||||
"p12_passphrase_desc": "Geben Sie das Passwort dieser Zertifikatsdatei ein",
|
||||
"storage_passphrase_desc": "Wählen Sie ein Passwort. Es schützt diesen Schlüssel, solange er in Ihrem Browser gespeichert ist.",
|
||||
"unlock_key_desc": "Geben Sie das Passwort dieses Schlüssels ein, um E-Mails zu signieren oder zu öffnen",
|
||||
"cert_expired": "Dieses Zertifikat ist nicht mehr gültig",
|
||||
"status_encrypted_no_key": "Diese E-Mail ist verschlüsselt, aber Ihnen fehlt der Schlüssel, um sie zu öffnen",
|
||||
"status_encrypted_failed": "Diese E-Mail ist verschlüsselt und konnte nicht entschlüsselt werden. Vielleicht fehlt Ihnen der passende Schlüssel.",
|
||||
"status_signed_invalid": "Die Signatur ist ungültig. Die E-Mail wurde vielleicht verändert.",
|
||||
"status_signed_expired_cert": "Mit einem Zertifikat signiert, das nicht mehr gültig ist",
|
||||
"status_signed_self_signed": "Die Signatur ist gültig. Der Absender hat das Zertifikat aber selbst erstellt, also hat niemand geprüft, wer er ist.",
|
||||
"status_signed_mismatch": "Die Signatur ist gültig, gehört aber zu jemand anderem als dem Absender",
|
||||
"auto_import_signer_certs_desc": "Wenn eine signierte E-Mail geprüft ist, das Zertifikat des Absenders speichern. Dann können Sie ihm später verschlüsselte E-Mails senden.",
|
||||
"export_passphrase_desc": "Wählen Sie ein Passwort, um die gespeicherte Datei zu schützen",
|
||||
"export_storage_desc": "Geben Sie das Passwort dieses Schlüssels ein, um ihn als Datei zu speichern"
|
||||
},
|
||||
"recover_deleted": {
|
||||
"load_failed": "Gelöschte E-Mails konnten nicht geladen werden. Versuchen Sie es noch einmal."
|
||||
}
|
||||
}
|
||||
+2049
-507
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,793 @@
|
||||
{
|
||||
"login": {
|
||||
"jmap_endpoint_cors_hint": "The mail server must allow sign-ins from this website. Your administrator can set this up.",
|
||||
"reconnecting": "The connection is lost. We are trying to connect again…",
|
||||
"error": {
|
||||
"invalid_credentials": "The email address or password is wrong. Check both and try again.",
|
||||
"invalid_token": "The server did not accept this access token. Maybe it is not complete, or it is no longer valid. Copy it again or create a new one.",
|
||||
"connection_failed": "We can't reach the mail server. Check your internet connection and try again.",
|
||||
"cors_blocked": "The mail server works, but it does not allow sign-ins from this website. Ask your administrator to allow this website on the server.",
|
||||
"server_error": "The mail server has a problem right now. Try again in a few minutes.",
|
||||
"generic": "Something went wrong. Try again. If it keeps happening, ask your administrator for help.",
|
||||
"totp_required": "Your account needs a second code to sign in. Open your authenticator app and type the 6-digit code below.",
|
||||
"totp_invalid": "This code is wrong or too old. Type the code your authenticator app shows right now.",
|
||||
"oauth_discovery_failed": "Sign-in through your organisation does not work right now. Try again later or ask your administrator.",
|
||||
"token_exchange_failed": "Your password and code are correct, but the mail server did not let this app in. Ask your administrator to check the sign-in settings for this app."
|
||||
},
|
||||
"totp_toggle": "I have a code from my authenticator app",
|
||||
"token_hint": "Some mail providers give you a token instead of a password. Create one in your mail provider's settings, copy it, and paste it here.",
|
||||
"config_error": {
|
||||
"title": "The webmail is not set up correctly",
|
||||
"fetch_failed": "The webmail could not load its settings. Try again in a few minutes.",
|
||||
"server_not_configured": "No mail server is set up yet. Ask your administrator to set one up."
|
||||
},
|
||||
"session_expired": "You were signed out. Sign in again to continue.",
|
||||
"oauth_error": {
|
||||
"title": "Sign-in did not work",
|
||||
"invalid_state": "The sign-in was interrupted. Start again.",
|
||||
"missing_params": "Some sign-in data is missing. Start again.",
|
||||
"token_exchange_failed": "We could not finish signing you in. Try again.",
|
||||
"access_denied": "You are not allowed to sign in here. Ask your administrator for access."
|
||||
}
|
||||
},
|
||||
"session_limits": {
|
||||
"idle_message": "You haven't done anything for a while. You will be signed out in {seconds, plural, one {# second} other {# seconds}}. Messages you are writing are saved as drafts.",
|
||||
"max_message": "Your organisation signs you out {hours, plural, one {# hour} other {# hours}} after you sign in. You will be signed out in {seconds, plural, one {# second} other {# seconds}}. Messages you are writing are saved as drafts.",
|
||||
"reason_max": "You were signed out because you were signed in for {hours, plural, one {# hour} other {# hours}}. That is the longest time your organisation allows.",
|
||||
"drafts_not_saved": "Some messages you were writing could not be saved as drafts."
|
||||
},
|
||||
"totp_reauth": {
|
||||
"title": "Enter a new code",
|
||||
"description": "Your last code is no longer valid. Open your authenticator app and type the new code to continue.",
|
||||
"oauth_hint": "This question comes back from time to time. Your administrator can stop it by turning on a different way to sign in (OAuth).",
|
||||
"code_label": "Code from your authenticator app"
|
||||
},
|
||||
"email_list": {
|
||||
"permanent_delete_confirm_message": "This message will be deleted for good. You can't get it back.",
|
||||
"permanent_delete_confirm_batch_message": "{count, plural, one {This message} other {These # messages}} will be deleted for good. You can't get {count, plural, one {it} other {them}} back.",
|
||||
"empty_folder": {
|
||||
"confirm_message": "All messages in this folder will be deleted for good. You can't get them back."
|
||||
}
|
||||
},
|
||||
"email_viewer": {
|
||||
"read_receipt": {
|
||||
"send_failed": "The read receipt could not be sent"
|
||||
},
|
||||
"archive_mailbox_not_found": "There is no Archive folder, so the message can't be archived",
|
||||
"source_load_failed": "The raw message could not be loaded. Try again, or save the message as a file (.eml).",
|
||||
"spam": {
|
||||
"error": "The message could not be marked as spam. Try again.",
|
||||
"error_not_spam": "The message could not be moved back. Try again."
|
||||
},
|
||||
"unsubscribe_banner": {
|
||||
"error": "Unsubscribing did not work. Try again later."
|
||||
},
|
||||
"calendar_invitation": {
|
||||
"parse_error": "This invitation can't be read",
|
||||
"action_failed": "That did not work in your calendar. Try again."
|
||||
},
|
||||
"undo_send_failed": "Sending could not be stopped. The message may already be sent.",
|
||||
"undo_send_signed_out": "Sending could not be stopped, because you are no longer signed in to the account that sends it.",
|
||||
"send_cancelled_restore_failed": "Sending was stopped. But the message could not be put back into Drafts. It is still in Sent.",
|
||||
"send_cancelled_remove_failed": "Sending was stopped. But the message could not be removed from Sent.",
|
||||
"cancel_scheduled_failed": "The planned sending could not be stopped. Try again.",
|
||||
"save_to_files": {
|
||||
"failed": "The file could not be saved in Files. Try again."
|
||||
}
|
||||
},
|
||||
"email_composer": {
|
||||
"files_picker": {
|
||||
"load_failed": "Your files could not be loaded. Try again."
|
||||
},
|
||||
"save_failed": "The draft could not be saved. Try again.",
|
||||
"validation": {
|
||||
"invalid_recipients": "Not sent. These addresses are not correct: {addresses}",
|
||||
"attachment_upload_failed": "Not sent. An attachment could not be uploaded. Remove it and try again."
|
||||
},
|
||||
"upload_failed": "{filename} could not be uploaded. Try again.",
|
||||
"send_failed": "Your message was not sent. Try again.",
|
||||
"send_offline": "You are not connected to the internet. Your message is still here. Send it again when you are back online.",
|
||||
"send_timeout": "The server stopped answering. Your message may have been sent anyway. Look in your Sent folder before you send it again.",
|
||||
"submission_limit": "The server did not send the message because you reached a limit: {reason}",
|
||||
"send_plugin_failed": "Not sent. An add-on that sends the message did not work or took too long. Look in your Sent folder before you send it again.",
|
||||
"schedule_send_invalid": "Enter a date and a time.",
|
||||
"schedule_send_cleanup_warning": "The message will be sent at the chosen time. But the draft could not be removed, so it is still in Drafts.",
|
||||
"invalid_recipient": "This is not a correct email address",
|
||||
"send_filing_warning": "Sent. But an old draft of this message may still be in Drafts.",
|
||||
"send_guard_failed": "Not sent. An add-on that checks messages before sending did not work or did not answer.",
|
||||
"attachment_upload_failed": "Could not upload"
|
||||
},
|
||||
"common": {
|
||||
"reconnecting": "The connection is lost. We are trying to connect again…"
|
||||
},
|
||||
"notifications": {
|
||||
"error_archiving": "The message could not be archived. Try again.",
|
||||
"error_updating": "The message could not be changed. Try again.",
|
||||
"error_sending": "Your message was not sent. Try again.",
|
||||
"error_deleting": "The message could not be deleted. Try again.",
|
||||
"error_loading": "Your messages could not be loaded. Try again.",
|
||||
"move_failed": "Could not move. Try again.",
|
||||
"undo_failed": "Could not undo. Try again.",
|
||||
"copy_failed": "Could not copy. Try again.",
|
||||
"move_error": "The messages could not be moved to this folder. Try again.",
|
||||
"tag_failed": "The label could not be added. Try again.",
|
||||
"identity_create_failed": "The sender address could not be added: {error}",
|
||||
"identity_update_failed": "The sender address could not be changed: {error}",
|
||||
"identity_delete_failed": "The sender address could not be deleted: {error}",
|
||||
"identity_unauthorized": "You are not allowed to send from this email address. Ask your administrator if you need it.",
|
||||
"vacation_save_failed": "Your automatic reply could not be saved. Try again.",
|
||||
"filters_save_failed": "Your filter rules could not be saved. Try again.",
|
||||
"export_email_error": "The message could not be saved as a file. Try again.",
|
||||
"import_email_error": "The message could not be added from the file. Check the file and try again.",
|
||||
"rule_apply_failed": "The rule could not be used for the messages that are already here",
|
||||
"rule_folder_failed": "The folder could not be created. Try again.",
|
||||
"vacation_filters_save_failed": "Your automatic reply is saved. But the forwarding and the reply list could not be saved. Try again."
|
||||
},
|
||||
"settings": {
|
||||
"reset_confirm": "Put all settings back to how they were at the start?",
|
||||
"themes": {
|
||||
"description": "Choose a look. Your administrator may add more themes.",
|
||||
"locked": "Your administrator chose the theme. You can't change it.",
|
||||
"forced": "Your administrator chose the theme \"{name}\". You can't change it."
|
||||
},
|
||||
"appearance": {
|
||||
"message_list_order": {
|
||||
"description": "The order of the messages in the list. The server sorts the whole folder, not only the messages you can see.",
|
||||
"tiebreak_note": "You can choose up to three steps. A later step only counts when the earlier steps are the same. At the end, the newest messages always come first.",
|
||||
"scope": {
|
||||
"description": "When this is off, the order is only used in the Inbox. Other folders show the newest messages first."
|
||||
},
|
||||
"unsupported_note": "Your mail server can't sort by read or unread, star or tag. Those steps are left out, and the newest messages come first."
|
||||
},
|
||||
"description": "Change how the webmail looks",
|
||||
"theme": {
|
||||
"description": "Choose light or dark colours"
|
||||
},
|
||||
"font_size": {
|
||||
"description": "Make the text bigger or smaller"
|
||||
},
|
||||
"list_density": {
|
||||
"description": "How much space there is between items"
|
||||
},
|
||||
"animations": {
|
||||
"description": "Let things move softly when they open or close"
|
||||
},
|
||||
"toolbar_position": {
|
||||
"description": "Where the buttons for a message are, like Reply, Archive or Delete"
|
||||
},
|
||||
"toolbar_labels": {
|
||||
"description": "Show words next to the button icons. Turn this off to save space when you know the icons."
|
||||
},
|
||||
"hide_account_switcher": {
|
||||
"description": "Hide the account list at the top of the folder list. You can still change accounts at the bottom of the left bar."
|
||||
},
|
||||
"show_rail_account_list": {
|
||||
"label": "Show your accounts in the left bar",
|
||||
"description": "Show a circle for each account at the bottom of the left bar, so you can change accounts quickly. A button to sign out is below them."
|
||||
},
|
||||
"unified_mailbox": {
|
||||
"description": "Show folders that combine the mail of several accounts, like one Inbox for all. You need more than one account (or a group inbox below) to see them.",
|
||||
"cross_account": {
|
||||
"description": "Combine the mail of all your accounts, not only the account you use now."
|
||||
},
|
||||
"include_group": {
|
||||
"description": "Also add the inboxes you share with a group."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"folders_description": "Choose which folders of this account are part of the lists All mail, Unread and Starred."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"description": "Give folder and tag icons colours (blue Inbox, red Spam, green Sent and so on). Turn this off for grey icons."
|
||||
},
|
||||
"tint_list_rows": {
|
||||
"description": "Colour each message in the list with the colour of its first tag. The tag colour still shows in the small dots when this is off."
|
||||
},
|
||||
"show_folder_total_count": {
|
||||
"description": "Show how many messages a folder or tag has in total, next to the number of unread ones."
|
||||
},
|
||||
"favicon_unread_badge": {
|
||||
"description": "Show the number of unread messages on the browser tab and on the app icon. Then you see new mail even when you use another tab."
|
||||
},
|
||||
"show_shared_mail_accounts": {
|
||||
"description": "Show the folders of mailboxes that others share with you, under Shared in the folder list. Turn this off to hide them all, or hide single accounts below. Their calendars and contacts stay."
|
||||
},
|
||||
"pro_interface": {
|
||||
"description": "A layout for large screens with many tabs and several accounts at once. You can go back to the normal layout at any time."
|
||||
},
|
||||
"account_marker": {
|
||||
"description": "In the combined folders, mark each message with the colour of its account."
|
||||
}
|
||||
},
|
||||
"keywords": {
|
||||
"description": "Make tags to sort your messages. The tags are saved with your messages on the server.",
|
||||
"migration_error": "The tag could not be changed on your messages. Try again.",
|
||||
"filters_migration_error": "Some filter rules still use the old tag. Change them yourself.",
|
||||
"nesting": {
|
||||
"description": "Put tags inside other tags, like folders. The sidebar shows them as a tree."
|
||||
},
|
||||
"has_children_locked": "Other tags are inside this tag. So you can't rename or move it. First move or delete the tags inside it.",
|
||||
"has_children_delete": "First delete the tags inside this tag",
|
||||
"unrecognized": {
|
||||
"description": "This browser keeps the names and colours of your tags. The tags themselves are saved on your messages. Search your mail for tags that are missing here and add them again.",
|
||||
"partial": "Only your {scanned} newest messages out of {total} were searched. Tags that are only on older mail may be missing.",
|
||||
"error": "Your mail could not be searched for tags. Try again."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"push": {
|
||||
"description": "Get a message on your screen about new mail, even when this website is closed. The service that passes on the message never sees your mail.",
|
||||
"relay_label": "Notification service",
|
||||
"relay_desc": "The service that passes notifications on to your device. Your administrator decides which ones you can choose.",
|
||||
"relay_locked_desc": "Your administrator chose the notification service. You can't change it.",
|
||||
"status_unsupported": "This browser can't show notifications when the website is closed",
|
||||
"status_lite": "This version of the webmail can't send notifications when it is closed.",
|
||||
"reenable": "Turn on again",
|
||||
"ios_hint": "On iPhone and iPad, first add this website to your home screen. Safari only sends notifications for websites on the home screen.",
|
||||
"devices_title": "Devices that get notifications",
|
||||
"devices_desc": "All devices that get notifications for this account. If you remove a device, it stops getting them at once.",
|
||||
"devices_error": "The list of devices could not be loaded. Try again.",
|
||||
"device_status_unknown": "The notification service does not know this device",
|
||||
"revoke": "Remove",
|
||||
"confirm_revoke_title": "Remove this device?",
|
||||
"confirm_revoke_message": "It stops getting notifications at once. To get them again, turn notifications on again on that device.",
|
||||
"confirm_revoke_message_this": "This device stops getting notifications at once.",
|
||||
"shared_mailboxes_no_push": "You don't get notifications for mailboxes that others share with you.",
|
||||
"error_forbidden": "The mail server does not allow notifications for this account.",
|
||||
"error_over_quota": "Too many devices get notifications for this account (the limit is usually 15). Remove devices you don't use any more, then try again."
|
||||
},
|
||||
"push_all": {
|
||||
"description": "Turn notifications on or off for several accounts at once.",
|
||||
"result_partial": "Turned on for {enabled} accounts. For {failed} accounts, it did not work."
|
||||
},
|
||||
"sound_selection": {
|
||||
"choose_desc": "Choose a sound. Click the speaker to hear it."
|
||||
},
|
||||
"email": {
|
||||
"description": "Choose how you hear about new mail",
|
||||
"inbox_only_desc": "Only tell me about mail in the Inbox, not about mail that a filter puts into another folder"
|
||||
},
|
||||
"calendar": {
|
||||
"description": "Choose how you hear about your appointments"
|
||||
}
|
||||
},
|
||||
"language_region": {
|
||||
"description": "Language, how dates and times look, and other things for your country",
|
||||
"date_locale": {
|
||||
"description": "The order of day, month and year in dates"
|
||||
},
|
||||
"time_format": {
|
||||
"description": "Show times like 3:00 PM (12 hours) or like 15:00 (24 hours)"
|
||||
},
|
||||
"time_zone": {
|
||||
"description": "Show the times of mail and appointments in this time zone. If you don't choose one, the time zone of your browser is used."
|
||||
}
|
||||
},
|
||||
"email_behavior": {
|
||||
"inbox_type": {
|
||||
"description": "With \"Unread first\", your 25 newest unread messages are shown at the top of the inbox"
|
||||
},
|
||||
"clear_search_on_folder_change": {
|
||||
"description": "When you open another folder, your search ends and you see the whole folder. When this is off, the search continues in the new folder."
|
||||
},
|
||||
"request_read_receipt": {
|
||||
"label": "Always ask for a read receipt",
|
||||
"description": "New messages ask the recipient to tell you when they have opened the message. You can turn this off for each message."
|
||||
},
|
||||
"read_receipt_response": {
|
||||
"label": "When someone asks for a read receipt",
|
||||
"description": "Some senders ask to be told when you open their message. Choose what to do then."
|
||||
},
|
||||
"description": "Choose what happens with your messages",
|
||||
"mark_read": {
|
||||
"description": "When an opened message counts as read"
|
||||
},
|
||||
"delete_action": {
|
||||
"description": "What happens when you delete a message",
|
||||
"warning": "Deleted messages are gone for good. You can't get them back."
|
||||
},
|
||||
"message_spacing": {
|
||||
"description": "The space around the text of an open message"
|
||||
},
|
||||
"archive_mode": {
|
||||
"description": "How archived messages are put into folders"
|
||||
},
|
||||
"permanently_delete_junk": {
|
||||
"label": "Delete spam for good",
|
||||
"description": "When you delete a message in the Spam folder, it is gone for good. It does not go to the Trash first."
|
||||
},
|
||||
"confirm_bulk_delete": {
|
||||
"description": "Ask before several selected messages go to the Trash. Deleting for good always asks."
|
||||
},
|
||||
"mail_layout": {
|
||||
"description": "Choose how the message list and the open message are placed on the screen.",
|
||||
"split_description": "The message list and the open message are side by side.",
|
||||
"focus_description": "Each message takes one line. An open message fills the whole width. The folder list stays visible.",
|
||||
"horizontal_description": "The message list is at the top. The open message is shown below it."
|
||||
},
|
||||
"list_columns": {
|
||||
"description": "Show the list as a table with the columns From, Subject, Date and Size."
|
||||
},
|
||||
"show_verification_codes": {
|
||||
"description": "When a message contains a sign-in or confirmation code, show a button to copy it. In the list, the button stays for one day."
|
||||
},
|
||||
"plain_text_mode": {
|
||||
"description": "Write all messages as plain text, without bold, colours or pictures. This also applies to replies and forwards."
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"description": "When you reply, the reply is sent from the address the message was sent to. This works for all your own addresses. Below, you can choose if other addresses on your domain count too."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Which addresses count as yours",
|
||||
"description": "\"Exact address only\" uses only the addresses you set up. \"Any address on my domains\" also replies from other addresses on your domains. Choose the exact address if those other addresses are group lists."
|
||||
},
|
||||
"signature_position": {
|
||||
"description": "Where your signature goes when you reply or forward. Above the quoted text, it ends your reply. Below, the old message stays in one piece."
|
||||
},
|
||||
"signature_separator": {
|
||||
"label": "Line before the signature",
|
||||
"description": "Put the usual \"-- \" line before your signature. Many mail apps use it to recognise the signature."
|
||||
},
|
||||
"sub_address_delimiter": {
|
||||
"label": "Character before the address tag",
|
||||
"description": "The character between your name and the tag in an address, like user{delimiter}tag@domain.com. Use the same character as your mail server."
|
||||
},
|
||||
"attachment_click_action": {
|
||||
"label": "When you click an attachment",
|
||||
"description": "Choose if a click on an attachment shows it or saves it to your device"
|
||||
},
|
||||
"attachment_position": {
|
||||
"description": "Where attachments are shown in an open message"
|
||||
},
|
||||
"always_light_mode": {
|
||||
"description": "Show messages with a light background even when the app is dark. Some messages look wrong when they are made dark."
|
||||
},
|
||||
"external_content": {
|
||||
"label": "Pictures from the internet",
|
||||
"description": "Choose if pictures that a message loads from the internet are shown. Loading them can tell the sender that you opened the message."
|
||||
},
|
||||
"trusted_senders": {
|
||||
"description": "Senders whose pictures are always shown",
|
||||
"empty_description": "When a message has blocked pictures, click \"Always trust this sender\" to add the sender here.",
|
||||
"save_error": "The list could not be saved. Try again.",
|
||||
"use_address_book_label": "Keep the list in your address book",
|
||||
"use_address_book_description": "Save trusted senders in their own address book. Then the list is the same on all your devices."
|
||||
},
|
||||
"hover_actions": {
|
||||
"description": "The buttons you see when you move the mouse over a message in the list"
|
||||
},
|
||||
"default_mail_program": {
|
||||
"description": "Open email links on websites (mailto: links) in {appName}",
|
||||
"error": "Your browser can't do this"
|
||||
},
|
||||
"attachment_reminder": {
|
||||
"description": "If your message talks about an attachment but nothing is attached, you get a warning before it is sent",
|
||||
"keywords_label": "Words that start the reminder",
|
||||
"keywords_description": "If your message contains one of these words, you get the reminder"
|
||||
},
|
||||
"placeholder_warning": {
|
||||
"description": "Ask before sending if the message still has gaps to fill in, like [TIME] or [Your Name], empty template fields, or notes like TODO."
|
||||
},
|
||||
"recipient_mentions": {
|
||||
"description": "Type @ in your message to pick one of its recipients. Their first name is put into the text. Press Enter or Tab to pick the marked name."
|
||||
},
|
||||
"mention_adds_recipient": {
|
||||
"description": "When you type @, you can also pick people from your contacts. If they are not a recipient yet, they are added to the message."
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Don't list pictures in the text as attachments",
|
||||
"description": "Pictures that are part of the message text are not shown again in the list of attachments"
|
||||
},
|
||||
"attachment_image_previews": {
|
||||
"description": "Show small pictures of attached images instead of a file icon"
|
||||
},
|
||||
"send_delay": {
|
||||
"description": "Your messages wait a few seconds before they are sent. In that time, you can still stop them.",
|
||||
"unsupported": "This account's mail server can't wait before sending. The setting is still saved for your other accounts."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"description": "On a touch screen: what happens when you swipe a message to the right."
|
||||
},
|
||||
"swipe_left_action": {
|
||||
"description": "On a touch screen: what happens when you swipe a message to the left."
|
||||
},
|
||||
"rtl_editing": {
|
||||
"label": "Writing from right to left",
|
||||
"description": "Adds a button to the editor. With it, you can write a paragraph from left to right or from right to left."
|
||||
}
|
||||
},
|
||||
"account": {
|
||||
"description": "See the details of your account",
|
||||
"auth_method_label": "How you sign in",
|
||||
"auth_method_oauth": "Sign-in page of your organisation",
|
||||
"server": {
|
||||
"label": "Mail server address"
|
||||
},
|
||||
"accounts": {
|
||||
"description": "Drag the accounts to change their order in the account list"
|
||||
},
|
||||
"backup": {
|
||||
"description": "Save the list of your accounts on the server and get it back on another device. A password that only you know protects the list. Mailbox passwords are not saved, so you sign in to each account again.",
|
||||
"description_passwords": "Save the list of your accounts on the server and get it back on another device. A password that only you know protects the list. You can also save the mailbox passwords. Then the accounts sign in by themselves.",
|
||||
"password_hint": "Use at least 10 characters. Nobody can reset this password. If you forget it, save the list again with a new password.",
|
||||
"restore_hint": "The password you chose when you saved the list.",
|
||||
"include_passwords_hint": "After you get the list back, these accounts sign in by themselves. But be careful: anyone who knows the backup password can open every mailbox in the list.",
|
||||
"skipped": "{count, plural, one {# account was} other {# accounts were}} not added because you already have the most accounts allowed.",
|
||||
"sign_in_failed": "{count, plural, one {# saved password did not work} other {# saved passwords did not work}}. Sign in to these accounts yourself from the account list.",
|
||||
"errors": {
|
||||
"unlock_failed": "The password is wrong, or this list belongs to a different account.",
|
||||
"owner_signin_required": "Sign in to this account again. Then try once more.",
|
||||
"https_required": "This only works on a secure connection (the address starts with https).",
|
||||
"storage_failed": "The server could not save or open the list. Try again later."
|
||||
}
|
||||
},
|
||||
"shared_accounts": {
|
||||
"description": "Accounts that you share with others and may manage. Choose one to change its filters, its automatic replies, its calendars and its contacts."
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"description": "Change your password and protect your account",
|
||||
"not_available": "You can't change the security settings for this mail server here. Your server may not allow it. The <a href=\"https://bulwarkmail.org/docs/guides/account-security\" class=\"underline hover:opacity-80\" target=\"_blank\">documentation</a> explains more.",
|
||||
"password": {
|
||||
"error_title": "Your password was not changed",
|
||||
"error_mismatch": "The two new passwords are not the same. Type them again.",
|
||||
"error_min_length": "Your password needs at least 8 characters",
|
||||
"error_generic": "Your password could not be changed. Try again."
|
||||
},
|
||||
"display_name": {
|
||||
"description": "The name the mail server shows for you",
|
||||
"error": "Your name could not be changed. Try again."
|
||||
},
|
||||
"totp": {
|
||||
"label": "Code from an authenticator app",
|
||||
"description": "Protect your account better: when you sign in, you also type a code from an app on your phone",
|
||||
"enable_error": "Two-factor authentication could not be turned on. Try again.",
|
||||
"disable_error": "Two-factor authentication could not be turned off. Try again.",
|
||||
"setup_instructions": "Open your authenticator app (for example Google Authenticator or Authy) and add this link to it:",
|
||||
"verification_code": "Code from the app",
|
||||
"disable_confirm_prompt": "To turn off two-factor authentication, type your password.",
|
||||
"password_required": "Type your password",
|
||||
"code_required": "Type the code from the app",
|
||||
"code_invalid": "This code is wrong or too old. Type the code the app shows right now."
|
||||
},
|
||||
"app_passwords": {
|
||||
"description": "Extra passwords for mail apps that can't ask for a second code, like some phone mail apps",
|
||||
"allowed_ips_label": "Only from these internet addresses (optional)",
|
||||
"allowed_ips_hint": "Put a comma or a space between addresses. Leave it empty to allow every address.",
|
||||
"copy_now_warning": "Copy this password now. You won't see it again.",
|
||||
"add_error": "The app password could not be created. Try again.",
|
||||
"remove_error": "The app password could not be deleted. Try again.",
|
||||
"delete_message": "Apps that use {name} will not be able to sign in any more."
|
||||
},
|
||||
"api_keys": {
|
||||
"description": "Keys for programs and scripts that work with your mailbox without you, for example a backup",
|
||||
"copy_now_warning": "Copy this key now. You won't see it again.",
|
||||
"add_error": "The key could not be created. Try again.",
|
||||
"remove_error": "The key could not be deleted. Try again.",
|
||||
"delete_message": "Programs and scripts that use {name} will stop working."
|
||||
},
|
||||
"encryption": {
|
||||
"label": "Store mail encrypted",
|
||||
"description": "The server stores your mail in a locked form. Only your key can unlock it.",
|
||||
"error": "The encryption setting could not be changed. Try again.",
|
||||
"algorithm_label": "Type of encryption",
|
||||
"encrypt_on_append": "Also encrypt mail that apps add to your mailbox",
|
||||
"allow_spam_training": "Let the spam filter learn from new mail before it is encrypted",
|
||||
"disable_message": "From now on, the server stores new mail without encryption."
|
||||
},
|
||||
"public_keys": {
|
||||
"key_label": "Public key (S/MIME or PGP, as text)",
|
||||
"add_error": "The public key could not be added. Check that you pasted all of it.",
|
||||
"remove_error": "The public key could not be deleted. Try again."
|
||||
},
|
||||
"email_client": {
|
||||
"description": "Use these details to set up mail on your computer or phone, for example in Thunderbird, Apple Mail or Outlook",
|
||||
"jmap_username_label": "User name for mail apps",
|
||||
"password_instructions": "In your mail app, use the user name above and an app password. You can create an app password further down on this page."
|
||||
},
|
||||
"link_device": {
|
||||
"description": "Sign in to the Bulwark Mail app on your phone without typing your password. Your phone gets its own sign-in. If you sign out here, your phone stays signed in.",
|
||||
"error": "The code could not be created. Try again.",
|
||||
"password_prompt": "Type your password to show it's you. Your password is not sent to the phone.",
|
||||
"error_not_signed_in": "You were signed out. Sign in again, then link your phone.",
|
||||
"error_impersonation": "You are looking at someone else's mailbox right now. You can't link a phone from here.",
|
||||
"error_too_many_attempts": "You tried too often. Wait a moment, then try again.",
|
||||
"error_session_secret": "Linking a phone is not set up on this server. Ask your administrator.",
|
||||
"error_server_unreachable": "We can't reach your mail server. Try again.",
|
||||
"error_pairing_unavailable": "Your mail server does not allow linking a phone this way. Ask your administrator.",
|
||||
"error_insecure_server": "The phone app needs a secure connection to the mail server (https). This server does not have one."
|
||||
},
|
||||
"sections": {
|
||||
"devices_description": "Read your mail in other apps and on other devices."
|
||||
}
|
||||
},
|
||||
"identities": {
|
||||
"description": "The email addresses you can send from",
|
||||
"identities_count": {
|
||||
"label": "Your sender addresses",
|
||||
"description": "Addresses you can send mail from"
|
||||
},
|
||||
"sub_addressing": {
|
||||
"label": "Addresses with a tag",
|
||||
"description": "Add a tag to your address, like user+shop@domain.com. Mail to it still reaches you, and you can sort it by the tag."
|
||||
}
|
||||
},
|
||||
"vacation": {
|
||||
"description": "Answer new mail by yourself while you are away",
|
||||
"not_supported": "Your mail server can't send automatic replies.",
|
||||
"fetch_error": "Your automatic reply could not be loaded. Try again.",
|
||||
"status": {
|
||||
"description": "Everyone who writes to you gets an automatic reply"
|
||||
},
|
||||
"date_range": {
|
||||
"start_description": "Leave it empty to start right away"
|
||||
},
|
||||
"message": {
|
||||
"subject_description": "The subject of the automatic reply",
|
||||
"html_description": "Add a version with links and formatting. People whose mail app can't show it get the plain text above."
|
||||
},
|
||||
"warnings": {
|
||||
"end_before_start": "The end date must come after the start date",
|
||||
"invalid_date": "Enter a start and end date with a time, or leave the field empty",
|
||||
"empty_body": "The message is empty. People would get a reply with no text.",
|
||||
"subject_too_long": "The subject is too long for the server. Make it shorter (at most {max} bytes).",
|
||||
"body_too_long": "The message is too long for the server. Make it shorter (at most {max} bytes, with formatting).",
|
||||
"forward_address": "Enter the email address to forward to",
|
||||
"not_running": "Forwarding and the reply list are saved, but they are off right now. Click Save to turn them on again."
|
||||
},
|
||||
"forward": {
|
||||
"description": "Send new mail on to another address during the time above, with or without the automatic reply. Without an end date, this goes on until you turn it off. Spam is not forwarded.",
|
||||
"keep_description": "If this is off, the message is only forwarded. It is not kept in this mailbox, and your filter rules don't run on it."
|
||||
}
|
||||
},
|
||||
"folders": {
|
||||
"description": "Manage your folders and choose which folder is used for what",
|
||||
"folder_list_description": "Click the icon of a folder to change it",
|
||||
"standard_roles_description": "Choose which folder is your Inbox, Sent, Trash and so on.",
|
||||
"reorder": "Drag up or down to change the order. Drag left or right to put the folder in another folder.",
|
||||
"move_partial_error": "{count, plural, one {# folder} other {# folders}} could not be moved. Try again.",
|
||||
"confirm_delete": "Delete \"{name}\"? The messages in this folder go to the Trash.",
|
||||
"cannot_delete_role": "This folder has a special job, like Inbox or Trash. Remove the job first, then you can delete it.",
|
||||
"error_delete_has_children": "The folder was not deleted because there are other folders inside it. Delete or move them first.",
|
||||
"error_delete_has_email": "The folder was not deleted because there are still messages in it. Move or delete them first.",
|
||||
"error_role": "The job of the folder could not be changed. Try again."
|
||||
},
|
||||
"advanced": {
|
||||
"debug_mode": {
|
||||
"description": "Record more details about what the app does. This helps to find problems."
|
||||
},
|
||||
"settings_sync": {
|
||||
"label": "Same settings on all devices",
|
||||
"description": "Save your settings on the server, so all your browsers and devices use them"
|
||||
},
|
||||
"sender_favicons": {
|
||||
"description": "For companies, show the icon of their website as the sender's picture",
|
||||
"refresh_description": "Load the website icons again, for example when a company has a new one"
|
||||
},
|
||||
"show_avatars_in_junk": {
|
||||
"label": "Show sender pictures in the Spam folder",
|
||||
"description": "Show the sender's picture or website icon in the Spam folder too. This is off at first, because fake messages can look more real with a picture."
|
||||
},
|
||||
"keyboard_shortcuts": {
|
||||
"description": "See all keys you can use instead of the mouse"
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Load data from the server again",
|
||||
"description": "Load your contacts, calendars and folders from the server again. Use this when something looks old or wrong. You stay signed in.",
|
||||
"confirm_title": "Load data from the server again?"
|
||||
},
|
||||
"reset_settings": {
|
||||
"description": "Put all settings back to how they were at the start"
|
||||
},
|
||||
"export_settings": {
|
||||
"description": "Save your settings as a file on your device"
|
||||
},
|
||||
"import_settings": {
|
||||
"description": "Load your settings from a file you saved before"
|
||||
},
|
||||
"sections": {
|
||||
"backup_description": "Use the same settings in other browsers and on other devices.",
|
||||
"fix_description": "When something looks old or wrong, or you want to start again."
|
||||
}
|
||||
},
|
||||
"sidebar_apps": {
|
||||
"description": "Add your own apps and links to the left bar",
|
||||
"keep_loaded_description": "Keep apps open in the background when you switch, so they don't have to load again",
|
||||
"managed_description": "Your administrator set up these apps for everyone. You can't change them."
|
||||
},
|
||||
"contacts": {
|
||||
"group_by_letter_description": "Put a letter above each group of names in the contact list",
|
||||
"sort_by_last_name_description": "Sort contacts by family name, so families are next to each other",
|
||||
"import_description": "Add contacts from a contacts file (.vcf)",
|
||||
"export_description": "Save all contacts in a contacts file (.vcf)"
|
||||
},
|
||||
"downloads": {
|
||||
"description": "Choose the file names for messages and attachments you save.",
|
||||
"email_template": {
|
||||
"description": "The file name when you save a message or drag it to a folder on your computer. The ending .eml is added for you."
|
||||
},
|
||||
"attachment_template": {
|
||||
"description": "The file name when you save an attachment or drag it out. If you leave out '{filename}' and '{ext}', the file keeps its ending."
|
||||
},
|
||||
"bundle_template": {
|
||||
"description": "The file name when you save several messages together in one .zip file. The ending .zip is added for you."
|
||||
},
|
||||
"spaces": {
|
||||
"description": "Use another character instead of spaces in the file name."
|
||||
},
|
||||
"lowercase": {
|
||||
"description": "Write the whole file name in small letters."
|
||||
},
|
||||
"strip_diacritics": {
|
||||
"description": "Change letters with accents into plain letters (ä → a, é → e). Some programs can't handle letters with accents."
|
||||
},
|
||||
"collapse_separators": {
|
||||
"description": "When there are several spaces, _ or - in a row, keep only one."
|
||||
},
|
||||
"after_export": {
|
||||
"description": "You can move the message to another folder after you save it as a file."
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
"description": "Rules that sort new mail by themselves, for example into a folder",
|
||||
"no_rules_description": "Make a rule, and new mail is sorted by itself",
|
||||
"vacation_active_description": "New mail gets an automatic reply",
|
||||
"delete_confirm": "Delete this rule?",
|
||||
"raw_editor": "Edit rules as code (Sieve)",
|
||||
"raw_editor_warning": "If you change the code, you may not be able to edit the rules here with the normal editor any more. Your changes in the code replace the normal rules.",
|
||||
"not_supported": "Your mail server can't use filter rules.",
|
||||
"match_all": "All conditions must be true",
|
||||
"match_any": "One condition is enough",
|
||||
"stop_processing": "Don't check the rules below this one",
|
||||
"value_placeholder_multi": "Value (put commas between several)",
|
||||
"condition_fields": {
|
||||
"header": "Other header line",
|
||||
"body": "Message text"
|
||||
},
|
||||
"comparators": {
|
||||
"matches": "matches the pattern (* = any text)"
|
||||
},
|
||||
"forward_limit": "This server forwards a message to at most {count} addresses. Other forwards are left out.",
|
||||
"forward_unavailable": "This server does not allow forwarding. Rules that forward mail do nothing.",
|
||||
"header_name": "Name of the header line",
|
||||
"opaque_warning": "These rules were changed in another app. You can only edit them here as code.",
|
||||
"open_sieve_editor": "Edit rules as code (Sieve)",
|
||||
"expanded_view_description": "Show each rule with all its conditions and actions",
|
||||
"reset_warning": "This deletes all your current rules and starts again with none.",
|
||||
"validation_empty_conditions": "Add at least one condition and fill in its value",
|
||||
"validation_empty_actions": "Add at least one action",
|
||||
"validation_header_name": "Use only the letters A to Z, numbers, - and _ in the name of the header line",
|
||||
"sieve_editor": {
|
||||
"title": "Filter rules as code (Sieve)",
|
||||
"warning": "If you change the code, you may not be able to edit the rules here with the normal editor any more. Your changes in the code replace the normal rules.",
|
||||
"script_content": "Rules as code",
|
||||
"valid": "The code has no mistakes",
|
||||
"invalid": "The code has mistakes",
|
||||
"validating": "Checking…",
|
||||
"validate": "Check",
|
||||
"overwrite_message": "This code no longer contains the rules you made with the normal editor. If you save, it replaces them. You can't undo this.",
|
||||
"validation_failed": "The code could not be checked. Try again."
|
||||
},
|
||||
"managed_by_tooltip": "{source} manages this. Change it in that app, or edit the rules as code.",
|
||||
"apply_existing_unsupported": "Only some rules can also run on messages that are already here: rules that check the sender, recipients, subject or header lines, and that move, copy, mark as read, star or tag.",
|
||||
"validation_period_order": "The end date must come after the start date",
|
||||
"period_hint": "The rule works for messages that arrive in this time. Leave a field empty if there is no start or no end.",
|
||||
"validation_period_invalid": "Enter a date with a time, or leave the field empty",
|
||||
"apply_existing_period": "A rule with a time period only works for messages that arrive in that time. So it can't run on messages that are already here."
|
||||
},
|
||||
"templates": {
|
||||
"description": "Save messages you write often and use them again. They can have gaps that you fill in each time.",
|
||||
"export_import_description": "Save your templates as a file, or move them to another device",
|
||||
"validation": {
|
||||
"too_long": "The name can have at most 200 characters"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"sort_direction": {
|
||||
"description": "From A to Z or from Z to A"
|
||||
},
|
||||
"show_thumbnails": {
|
||||
"description": "Show a small picture of each image instead of an icon"
|
||||
},
|
||||
"show_hidden": {
|
||||
"description": "Show hidden files and folders (their names start with a dot)"
|
||||
}
|
||||
},
|
||||
"reading_page": {
|
||||
"sections": {
|
||||
"images_description": "Pictures from the internet can tell the sender that you opened the message."
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"page_error_description": "This page stopped working. Try again, or go back to your inbox.",
|
||||
"sidebar_error": "Your folders could not be loaded",
|
||||
"email_list_error": "Your messages could not be loaded",
|
||||
"viewer_error_title": "This message can't be shown",
|
||||
"viewer_error_description": "Something in this message stops it from being shown here. It may use a format we don't support.",
|
||||
"composer_error": "The editor for new messages could not be opened",
|
||||
"settings_error_title": "Settings could not be opened",
|
||||
"settings_error_description": "Your settings could not be loaded. Changes you make now may not be saved."
|
||||
},
|
||||
"identities": {
|
||||
"delete_confirm": "Delete this sender address? You can't undo this.",
|
||||
"cannot_delete": "You can't delete this sender address",
|
||||
"validation_errors": {
|
||||
"invalid_emails": "These email addresses are not correct: {emails}"
|
||||
},
|
||||
"form": {
|
||||
"email_invalid": "Enter a correct email address",
|
||||
"email_immutable": "You can't change the email address after you add it",
|
||||
"signature_byte_limit_reached": "The signature is too long for the server"
|
||||
},
|
||||
"sub_address": {
|
||||
"invalid_tag": "Use only letters, numbers and dashes (-) in the tag"
|
||||
}
|
||||
},
|
||||
"calendar": {
|
||||
"import": {
|
||||
"url_description": "Enter the web address of a calendar file (.ics) to add its events."
|
||||
},
|
||||
"management": {
|
||||
"description": "Make new calendars, rename them and change their colour. You can also right-click a calendar in the side bar to change its colour.",
|
||||
"description_touch": "Make new calendars, rename them and change their colour.",
|
||||
"confirm_delete": "Delete \"{name}\"? All appointments in this calendar are deleted too.",
|
||||
"confirm_clear": "Delete all appointments in \"{name}\"? You can't undo this.",
|
||||
"kind_hint": "This decides which apps on your other devices show this calendar. You can't change it later.",
|
||||
"default_alerts_hint": "New appointments in this calendar get these reminders, unless you change them in the appointment. A change here does not change appointments you already have. The mail server sends reminders by email."
|
||||
},
|
||||
"subscription": {
|
||||
"description": "Follow a calendar from the internet, for example public holidays. Its appointments are updated by themselves and shown in their own calendar. The address starts with https:// or webcal://.",
|
||||
"confirm_delete": "Stop following \"{name}\"? The calendar and all its appointments are removed."
|
||||
}
|
||||
},
|
||||
"sharing": {
|
||||
"description": "Let other people or groups on this server use this. The change works at once.",
|
||||
"directory_restricted": "Your server does not show a list of all its users. So you only see your groups and people who share something with you. Your administrator can change this.",
|
||||
"directory_not_browsable": "Your server does not show a list of all its users.",
|
||||
"directory_failed": "The list of people and groups could not be loaded. Try again.",
|
||||
"share_failed": "Sharing could not be changed. Try again."
|
||||
},
|
||||
"delegates": {
|
||||
"description": "Let someone use your mail, calendars and contacts, all in one step. See who can use what.",
|
||||
"read_failed": "Some of your sharing could not be loaded. You can change or remove access when it has loaded.",
|
||||
"remove_message": "{name} can no longer use anything you shared from your account: mail, calendars, contacts and files.",
|
||||
"not_found": "There is nobody with the address {address} on this server.",
|
||||
"lookup_failed": "This address could not be found. Check it and try again.",
|
||||
"known_only": "You only see your groups and people who shared something with you.",
|
||||
"directory_off_note": "To add someone new, your administrator first has to allow searching for users on the server.",
|
||||
"directory_failed": "The list of people and groups could not be loaded. Try again.",
|
||||
"nothing_to_change": "Nothing has changed. Choose what the person should get, or click Remove access.",
|
||||
"change_note": "Folders, calendars and address books that you don't choose are no longer shared. Shared files stay shared. Remove access takes them away too.",
|
||||
"rights_keep_hint": "What they already have stays the same. Anything new gets the same rights as the rest of its kind, or read only.",
|
||||
"send_note": "People you give access can read and sort your mail and calendar. They can't send mail as you or for you. If several people need one address, ask your administrator for a shared mailbox.",
|
||||
"partial_title": "Some things could not be changed. Everything else is done."
|
||||
},
|
||||
"smime": {
|
||||
"your_certificates_desc": "Add and manage your certificates. You need one to sign or encrypt mail with S/MIME.",
|
||||
"recipient_certificates_desc": "Certificates of other people. With them, you can send encrypted mail to these people.",
|
||||
"identity_bindings_desc": "Choose which certificate belongs to which of your sender addresses",
|
||||
"defaults_desc": "Choose if new messages are signed or encrypted from the start",
|
||||
"encrypt_by_default_desc": "Encrypt a message by itself when you have a certificate for every recipient",
|
||||
"remember_unlocked_desc": "Keep your keys unlocked until you close this browser",
|
||||
"p12_passphrase_desc": "Enter the password of this certificate file",
|
||||
"storage_passphrase_desc": "Choose a password. It protects this key while it is saved in your browser.",
|
||||
"unlock_key_desc": "Enter the password of this key to sign or open messages",
|
||||
"cert_expired": "This certificate is no longer valid",
|
||||
"status_encrypted_no_key": "This message is encrypted, but you don't have the key to open it",
|
||||
"status_encrypted_failed": "This message is locked, and it could not be unlocked. You may not have the right key.",
|
||||
"status_signed_invalid": "The signature is not valid. The message may have been changed.",
|
||||
"status_signed_expired_cert": "Signed with a certificate that is no longer valid",
|
||||
"status_signed_self_signed": "The signature is valid. But the sender made the certificate themselves, so nobody has checked who they are.",
|
||||
"status_signed_mismatch": "The signature is valid, but it belongs to someone other than the sender",
|
||||
"auto_import_signer_certs_desc": "When a signed message is checked, save the sender's certificate. Then you can send them encrypted mail later.",
|
||||
"export_passphrase_desc": "Choose a password to protect the file you save",
|
||||
"export_storage_desc": "Enter the password of this key to save it as a file"
|
||||
},
|
||||
"recover_deleted": {
|
||||
"load_failed": "Deleted mail could not be loaded. Try again."
|
||||
},
|
||||
"global_search": {
|
||||
"account_error": "{account} could not be searched"
|
||||
}
|
||||
}
|
||||
+1750
-208
File diff suppressed because it is too large
Load Diff
+1738
-196
File diff suppressed because it is too large
Load Diff
+1739
-197
File diff suppressed because it is too large
Load Diff
+1745
-203
File diff suppressed because it is too large
Load Diff
+1743
-201
File diff suppressed because it is too large
Load Diff
+1741
-199
File diff suppressed because it is too large
Load Diff
+1745
-203
File diff suppressed because it is too large
Load Diff
+1734
-192
File diff suppressed because it is too large
Load Diff
+1739
-197
File diff suppressed because it is too large
Load Diff
+1757
-215
File diff suppressed because it is too large
Load Diff
+1744
-202
File diff suppressed because it is too large
Load Diff
+1737
-195
File diff suppressed because it is too large
Load Diff
+1744
-202
File diff suppressed because it is too large
Load Diff
+1746
-204
File diff suppressed because it is too large
Load Diff
+1748
-206
File diff suppressed because it is too large
Load Diff
+769
-35
File diff suppressed because it is too large
Load Diff
+774
-35
File diff suppressed because it is too large
Load Diff
+774
-35
File diff suppressed because it is too large
Load Diff
+771
-35
File diff suppressed because it is too large
Load Diff
+491
-45
@@ -3,7 +3,10 @@
|
||||
"after": "Nach",
|
||||
"before": "Vor",
|
||||
"no_attachment": "Ohne Anhang",
|
||||
"not_starred": "Nicht markiert"
|
||||
"not_starred": "Nicht markiert",
|
||||
"advanced_filters_count": "{name}, {count} aktiv",
|
||||
"larger_than_kb": "Größer als {size} KB",
|
||||
"smaller_than_kb": "Kleiner als {size} KB"
|
||||
},
|
||||
"calendar": {
|
||||
"alerts": {
|
||||
@@ -26,7 +29,8 @@
|
||||
"copy_link_short": "Link",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nichts geplant. Zum Erstellen tippen.",
|
||||
"tap_to_create": "Tippen Sie auf +, um einen zu erstellen"
|
||||
"tap_to_create": "Tippen Sie auf +, um einen zu erstellen",
|
||||
"copy_title_short": "Titel"
|
||||
},
|
||||
"fab": {
|
||||
"event": "Termin",
|
||||
@@ -70,24 +74,40 @@
|
||||
"trust_authentication_missing": "Der Absender konnte nicht verifiziert werden.",
|
||||
"trust_sender_mismatch": "Der Absender weicht vom Organisator des Termins ab.",
|
||||
"trust_sender_mismatch_unverified": "Der Absender stimmt nicht mit dem Organisator überein, und die Nachricht ist nicht authentifiziert.",
|
||||
"trust_verified": "Absender verifiziert"
|
||||
"trust_verified": "Absender verifiziert",
|
||||
"cancel_outdated": "Ihr Kalender enthält eine neuere Version dieses Termins als diese Absage.",
|
||||
"remove_error": "Der Termin konnte nicht entfernt werden",
|
||||
"remove_from_calendar": "Aus Kalender entfernen",
|
||||
"removed_from_calendar": "Aus Ihrem Kalender entfernt."
|
||||
},
|
||||
"management": {
|
||||
"clear_confirm": "Alle entfernen",
|
||||
"clear_description": "Alle Termine in diesem Kalender werden gelöscht. Termine, die auch zu einem anderen Kalender gehören, werden nur aus diesem entfernt.",
|
||||
"clear_title": "Alle Termine entfernen?",
|
||||
"color_invalid": "Geben Sie eine Farbe als sechs Hex-Ziffern ein, mit oder ohne #, etwa #3b82f6.",
|
||||
"delete_description": "Der Kalender und alle seine Termine werden gelöscht. Dies kann nicht rückgängig gemacht werden.",
|
||||
"delete_title": "Kalender löschen?",
|
||||
"description_field": "Beschreibung",
|
||||
"description_placeholder": "Optional",
|
||||
"edit_calendar": "Kalender bearbeiten",
|
||||
"new_calendar": "Neuer Kalender"
|
||||
"new_calendar": "Neuer Kalender",
|
||||
"custom_color": "Eigene Farbe (Hex)",
|
||||
"fewer_colors": "Weniger Farben",
|
||||
"more_colors": "Mehr Farben"
|
||||
},
|
||||
"notifications": {
|
||||
"channel_name": "Kalendererinnerungen",
|
||||
"participant_limit": "Der Server erlaubt höchstens {max, plural, one {# Teilnehmer} other {# Teilnehmer}} pro Termin, den Organisator mitgezählt, und hat die Änderung deshalb abgelehnt. Ihr Administrator kann das Limit erhöhen.",
|
||||
"starts_in_days": "{count, plural, one {Beginnt in # Tag} other {Beginnt in # Tagen}}",
|
||||
"starts_in_hours": "{count, plural, one {Beginnt in # Stunde} other {Beginnt in # Stunden}}",
|
||||
"starts_in_minutes": "{count, plural, one {Beginnt in # Minute} other {Beginnt in # Minuten}}"
|
||||
},
|
||||
"occurrence_not_found_again": "Die Änderung wurde nicht gespeichert: Der Termin konnte auf dem Server nicht wiedergefunden werden. Versuchen Sie es erneut.",
|
||||
"participants": {
|
||||
"limit_warning": "Mehr als {max, plural, one {# Teilnehmer} other {# Teilnehmer}}, den Organisator mitgezählt: Der Server lehnt das Speichern dieses Termins möglicherweise ab. Ihr Administrator kann das Limit erhöhen.",
|
||||
"remove_named": "{name} entfernen",
|
||||
"group_members": "{count, plural, one {# Mitglied} other {# Mitglieder}}"
|
||||
},
|
||||
"recurrence": {
|
||||
"editor_occurrences_count": "{count, plural, one {Termin} other {Terminen}}",
|
||||
"every_count_days": "{count, plural, one {Jeden Tag} other {Alle # Tage}}",
|
||||
@@ -104,7 +124,7 @@
|
||||
"time_zone": "Zeitzone",
|
||||
"time_zone_auto": "Gerätezeitzone",
|
||||
"time_zone_auto_zone": "Gerätezeitzone ({zone})",
|
||||
"time_zone_desc": "Zeitzone für neue Termine und für die Darstellung des Kalenders. \"Gerätezeitzone\" folgt dem Telefon."
|
||||
"time_zone_desc": "Zeitzone für neue Termine und für die Darstellung des Kalenders. \"Gerätezeitzone\" folgt diesem Gerät."
|
||||
},
|
||||
"share": {
|
||||
"add_people": "Personen hinzufügen",
|
||||
@@ -122,7 +142,10 @@
|
||||
"not_a_feed": "Diese URL hat keinen iCalendar-Feed geliefert",
|
||||
"not_synced": "Noch nicht synchronisiert",
|
||||
"url_scheme_invalid": "Die URL muss mit https://, http://, webcal:// oder webcals:// beginnen",
|
||||
"your_subscriptions": "Ihre Abonnements"
|
||||
"your_subscriptions": "Ihre Abonnements",
|
||||
"account_changed": "Das Konto hat gewechselt, während das Abonnement synchronisiert wurde. Es wird erneut in seinem eigenen Konto synchronisiert.",
|
||||
"feed_truncated": "Der Feed wurde vor seinem Ende abgeschnitten. Es wurde nichts geändert.",
|
||||
"mass_removal": "Der Feed hat auf einmal {count} von {total} Terminen weggelassen. Sie wurden behalten, falls der Feed fehlerhaft ist."
|
||||
},
|
||||
"tasks": {
|
||||
"add_placeholder": "Aufgabe hinzufügen…",
|
||||
@@ -138,7 +161,16 @@
|
||||
"n_days": "{count} Tage",
|
||||
"schedule": "Terminübersicht",
|
||||
"three_days": "3 Tage"
|
||||
}
|
||||
},
|
||||
"detail": {
|
||||
"delete_confirm": "Dieses Ereignis löschen?",
|
||||
"location_hint": "Öffnet den Ort; lange drücken zum Kopieren"
|
||||
},
|
||||
"change_month_named": "{month}, Monat wechseln",
|
||||
"a11y": {
|
||||
"event_count": "{count, plural, one {# Termin} other {# Termine}}"
|
||||
},
|
||||
"account_changed": "Das Konto hat gewechselt, während der Kalender geladen oder gespeichert wurde."
|
||||
},
|
||||
"common": {
|
||||
"add": "Hinzufügen",
|
||||
@@ -164,7 +196,8 @@
|
||||
"add_tag": "Kategorie hinzufügen",
|
||||
"delete_confirm_mobile": "{count, plural, one {# Kontakt löschen? Dies kann nicht rückgängig gemacht werden.} other {# Kontakte löschen? Dies kann nicht rückgängig gemacht werden.}}",
|
||||
"move_to_address_book": "In Adressbuch verschieben",
|
||||
"tag_failed": "Kategorie konnte nicht zugewiesen werden"
|
||||
"tag_failed": "Kategorie konnte nicht zugewiesen werden",
|
||||
"delete_confirm_title": "Kontakte löschen"
|
||||
},
|
||||
"contact": "Kontakt",
|
||||
"create_hint_mobile": "Lange drücken, um stattdessen eine Gruppe zu erstellen",
|
||||
@@ -215,6 +248,7 @@
|
||||
"new_organization": "Neue Organisation",
|
||||
"org_name_required": "Geben Sie den Namen der Organisation an.",
|
||||
"photo_hint_mobile": "Tippen Sie auf das Foto, um ein Bild auszuwählen",
|
||||
"photo_media_unread": "Die Fotos des Kontakts konnten nicht vom Server gelesen werden, daher wurde nichts gespeichert. Versuchen Sie es erneut.",
|
||||
"photo_permission_message": "Erlauben Sie den Zugriff auf die Fotomediathek, um ein Kontaktfoto auszuwählen.",
|
||||
"photo_permission_title": "Fotozugriff erforderlich",
|
||||
"pick_date": "Datum auswählen",
|
||||
@@ -247,7 +281,11 @@
|
||||
"into_address_book": "In Adressbuch importieren",
|
||||
"into_named": "In {name} importieren",
|
||||
"no_address_book": "Erstellen Sie ein Adressbuch, bevor Sie Kontakte importieren.",
|
||||
"picker_failed": "Die Dateiauswahl konnte nicht geöffnet werden"
|
||||
"picker_failed": "Die Dateiauswahl konnte nicht geöffnet werden",
|
||||
"replaces": "Ersetzt {name}",
|
||||
"update_existing": "Vorhandenen aktualisieren",
|
||||
"unnamed": "Unbenannter Kontakt",
|
||||
"updated_count": "{count, plural, one {1 Kontakt war bereits vorhanden und wurde aktualisiert.} other {# Kontakte waren bereits vorhanden und wurden aktualisiert.}}"
|
||||
},
|
||||
"no_tags_hint": "Noch keine Kategorien. Geben Sie oben eine ein.",
|
||||
"picker": {
|
||||
@@ -256,11 +294,23 @@
|
||||
},
|
||||
"rename_tag_hint_mobile": "Tippen Sie lange auf eine Kategorie, um sie umzubenennen",
|
||||
"tag_name_placeholder": "Name der neuen Kategorie",
|
||||
"unnamed": "Unbenannt"
|
||||
"unnamed": "Unbenannt",
|
||||
"delete_confirm_title": "Kontakt löschen",
|
||||
"directory": {
|
||||
"restricted_hint": "Ihr Mailserver listet nur die Konten auf, mit denen Sie bereits zu tun haben, etwa Ihre Gruppen und Personen, die etwas mit Ihnen teilen.",
|
||||
"restricted_queries_off": "Ihr Mailserver listet nur die Konten auf, mit denen Sie bereits zu tun haben, etwa Ihre Gruppen und Personen, die etwas mit Ihnen teilen. Ein Administrator kann Verzeichnisabfragen aktivieren, damit alle angezeigt werden."
|
||||
}
|
||||
},
|
||||
"context_menu": {
|
||||
"color_tag": "Label",
|
||||
"remove_color": "Label entfernen"
|
||||
"emails_selected": "{count, plural, one {# E-Mail ausgewählt} other {# E-Mails ausgewählt}}",
|
||||
"remove_color": "Label entfernen",
|
||||
"rules": {
|
||||
"login_changed": "Sie haben das Konto gewechselt, daher wurde die Regel nicht gespeichert"
|
||||
}
|
||||
},
|
||||
"delegates": {
|
||||
"remove_for": "Zugriff für {name} entfernen"
|
||||
},
|
||||
"device_sync": {
|
||||
"auth_problem_text": "Der Server akzeptiert die Anmeldung von {account} nicht mehr, daher werden die Kontakte und Kalender dieses Kontos auf diesem Gerät nicht mehr synchronisiert. Melden Sie sich erneut an, um fortzufahren. Auf diesem Gerät vorgenommene Änderungen bleiben erhalten.",
|
||||
@@ -280,6 +330,11 @@
|
||||
"attachment_reminder_title": "Anhang vergessen?",
|
||||
"attachments_too_large_total": "Die Anhänge würden die {max} überschreiten, die dieser Server pro Nachricht erlaubt.",
|
||||
"editor_unavailable": "Der Nachrichteninhalt konnte nicht gelesen werden. Kopieren Sie Ihren Text, schließen Sie dann den Editor und öffnen Sie ihn erneut.",
|
||||
"files_picker": {
|
||||
"attach": "{count, plural, =0 {Anhängen} one {1 Datei anhängen} other {# Dateien anhängen}}",
|
||||
"empty_folder": "Dieser Ordner ist leer",
|
||||
"load_failed": "Ihre Dateien konnten nicht geladen werden"
|
||||
},
|
||||
"from_options": "Absenderoptionen",
|
||||
"group_chip": "{name} ({count})",
|
||||
"identity_unavailable": "Identität nicht verfügbar",
|
||||
@@ -288,10 +343,38 @@
|
||||
"no_sent_folder": "Kein Gesendet-Ordner gefunden",
|
||||
"permission_camera": "Zum Aufnehmen eines Fotos ist die Kameraberechtigung erforderlich.",
|
||||
"permission_photos": "Zum Anhängen von Bildern ist die Berechtigung für die Fotomediathek erforderlich.",
|
||||
"placeholder_warning": {
|
||||
"title": "Platzhalter prüfen?",
|
||||
"message": "Die Nachricht enthält Platzhaltertext, den die Empfänger sehen. Vor dem Senden bearbeiten?",
|
||||
"found": "Gefundene Platzhalter",
|
||||
"more": "und {count} weitere",
|
||||
"dont_ask_again": "Nicht mehr fragen",
|
||||
"send_anyway": "Trotzdem senden",
|
||||
"back": "Zurück zur Bearbeitung"
|
||||
},
|
||||
"placeholder_words": {
|
||||
"before": [
|
||||
"ihr",
|
||||
"ihre",
|
||||
"ihren",
|
||||
"dein",
|
||||
"deine",
|
||||
"deinen"
|
||||
],
|
||||
"after": [
|
||||
"einfügen",
|
||||
"eintragen",
|
||||
"ergänzen"
|
||||
],
|
||||
"unfinished": []
|
||||
},
|
||||
"recipient_add_contact": "Zu Kontakten hinzufügen",
|
||||
"recipient_copy": "Adresse kopieren",
|
||||
"recipient_move_to": "Verschieben nach {field}",
|
||||
"recipient_remove": "Entfernen",
|
||||
"recipients_partly_refused": "Der Server hat {recipients} abgelehnt. Die anderen Empfänger erhalten die Nachricht.",
|
||||
"recipients_partly_refused_title": "Nicht an alle gesendet",
|
||||
"recipients_refused": "Nicht gesendet: Der Server hat {recipients} abgelehnt.",
|
||||
"remove_attachment": "Anhang entfernen",
|
||||
"save_draft": "Entwurf speichern",
|
||||
"save_failed_no_drafts": "Es gibt keinen Entwürfe-Ordner, in dem dieser Entwurf gespeichert werden kann.",
|
||||
@@ -312,6 +395,8 @@
|
||||
"send_now": "Jetzt senden",
|
||||
"send_timeout_body": "Die Nachricht wurde möglicherweise bereits versendet. Prüfen Sie Ihren Gesendet-Ordner, bevor Sie sie erneut senden.",
|
||||
"send_timeout_title": "Keine Antwort vom Server",
|
||||
"submission_limit": "Der Server hat das Senden abgelehnt, weil ein Limit erreicht ist: {reason}",
|
||||
"submission_quota": "Der Server hat für dieses Konto zu viele Einträge zu gesendeten Nachrichten. Bulwark hat entfernt, was möglich war; wenn das weiterhin vorkommt, bitten Sie Ihren Administrator, das Limit für E-Mail-Übermittlungen zu erhöhen.",
|
||||
"toolbar": {
|
||||
"insert_image": "Bild einfügen",
|
||||
"remove_link": "Link entfernen"
|
||||
@@ -322,7 +407,31 @@
|
||||
"uploading_pct": "Hochladen {pct} %",
|
||||
"validation": {
|
||||
"subject_required": "Betreff hinzufügen"
|
||||
}
|
||||
},
|
||||
"format_plain_text": "Zu Nur-Text wechseln",
|
||||
"format_rich_text": "Zu formatiertem Text (HTML) wechseln",
|
||||
"login_switched_attach_body": "Sie haben diese Nachricht in {account} begonnen. Wechseln Sie zurück zu diesem Konto, um Dateien anzuhängen.",
|
||||
"login_switched_body": "Sie haben diese Nachricht in {account} begonnen. Wechseln Sie zurück zu diesem Konto, um sie zu senden.",
|
||||
"login_switched_save_body": "Sie haben diese Nachricht in {account} begonnen. Wechseln Sie zurück zu diesem Konto, um sie zu speichern.",
|
||||
"login_switched_title": "Konto gewechselt",
|
||||
"option_encrypt": "Verschlüsseln",
|
||||
"option_encrypt_hint": "Nur die Empfänger (und Sie) können sie lesen; jeder Empfänger braucht einen Schlüssel",
|
||||
"option_security": "Signatur und Verschlüsselung",
|
||||
"option_sign": "Signieren",
|
||||
"option_sign_pgp_hint": "Eine OpenPGP-Signatur mit Ihrem Schlüssel hinzufügen",
|
||||
"option_sign_smime_hint": "Eine S/MIME-Signatur mit Ihrem Zertifikat hinzufügen",
|
||||
"read_receipt_off": "Lesebestätigung anfordern",
|
||||
"secure_failed_title": "Signieren oder Verschlüsseln nicht möglich",
|
||||
"secure_missing_keys": "Kein Schlüssel für {recipients}. Importieren Sie einen unter Einstellungen → Verschlüsselung.",
|
||||
"secure_no_key": "Auf diesem Gerät gibt es keinen Schlüssel für {email}. Importieren Sie einen unter Einstellungen → Verschlüsselung.",
|
||||
"secure_offline": "Signierte oder verschlüsselte Nachrichten können nicht im Postausgang warten. Senden Sie sie, wenn Sie online sind.",
|
||||
"secure_unlock_reason": "Ihr Schlüssel signiert diese Nachricht.",
|
||||
"send_unconfirmed_body": "Der Server hat das Senden nicht bestätigt. Prüfen Sie Ihren Gesendet-Ordner, bevor Sie erneut senden. Ihr Entwurf wurde behalten.",
|
||||
"send_unconfirmed_title": "Senden nicht bestätigt",
|
||||
"undo_send_other_login": "Diese Nachricht wurde von einem anderen Konto gesendet",
|
||||
"undo_send_other_login_body": "Wechseln Sie zurück zu {account}, um das Senden rückgängig zu machen oder sofort zu senden. Sie geht raus, wenn die Verzögerung abläuft.",
|
||||
"upload_login_switched": "Nicht hochgeladen: Das Konto wurde gewechselt",
|
||||
"use_template": "Vorlage"
|
||||
},
|
||||
"email_list": {
|
||||
"cross_account_move_offline": "Verschieben zwischen Konten erfordert eine Verbindung",
|
||||
@@ -415,28 +524,46 @@
|
||||
"pdf_external": "PDFs werden in Ihrem PDF-Viewer auf diesem Gerät geöffnet.",
|
||||
"unsupported": "Keine Vorschau für diesen Dateityp.",
|
||||
"media_unsupported": "Diese Datei kann hier nicht abgespielt werden.",
|
||||
"pdf_protected": "Diese PDF ist passwortgeschützt. Öffne sie in einer anderen App, um das Passwort einzugeben.",
|
||||
"pdf_protected": "Diese PDF ist passwortgeschützt. Öffnen Sie sie in einer anderen App, um das Passwort einzugeben.",
|
||||
"pdf_unreadable": "Diese PDF kann hier nicht angezeigt werden.",
|
||||
"archive_files": "{count, plural, one {# Datei} other {# Dateien}}",
|
||||
"archive_first_files": "Die ersten {count} Dateien",
|
||||
"archive_protected": "Dieses Archiv ist passwortgeschützt. Öffne es in einer anderen App, um das Passwort einzugeben.",
|
||||
"archive_protected": "Dieses Archiv ist passwortgeschützt. Öffnen Sie es in einer anderen App, um das Passwort einzugeben.",
|
||||
"archive_unreadable": "Dieses Archiv kann hier nicht geöffnet werden."
|
||||
},
|
||||
"received_via": "an {name}",
|
||||
"remove_color": "Label entfernen",
|
||||
"replied": "Beantwortet",
|
||||
"reschedule_recipients_refused": "Nichts wurde geändert: Der Server hat {recipients} abgelehnt.",
|
||||
"save_to_files": {
|
||||
"title": "In Dateien speichern",
|
||||
"save_here": "Hier speichern",
|
||||
"saved": "In Dateien gespeichert",
|
||||
"saved_to": "In {folder}",
|
||||
"failed": "Konnte nicht in Dateien gespeichert werden",
|
||||
"load_failed": "Ihre Ordner konnten nicht geladen werden. Versuchen Sie es erneut."
|
||||
},
|
||||
"sender_actions": "Absender…",
|
||||
"set_color": "Label setzen",
|
||||
"show_all_count": "Alle anzeigen ({count})",
|
||||
"show_less": "Weniger anzeigen",
|
||||
"smime_unsupported": "Mit S/MIME signiert/verschlüsselt - auf Mobilgeräten nicht unterstützt",
|
||||
"source_too_large": "Die Nachricht ist zu groß, um vollständig angezeigt zu werden. Teilen Sie stattdessen den vollständigen Quelltext.",
|
||||
"still_scheduled": "Die Nachricht ist weiterhin geplant.",
|
||||
"still_scheduled_at": "Die Nachricht ist weiterhin für {date} geplant.",
|
||||
"thread_loading": "Unterhaltung wird geladen...",
|
||||
"undo_send_failed": "Senden konnte nicht rückgängig gemacht werden",
|
||||
"unsubscribe_banner": {
|
||||
"no_identity": "Keine Absenderidentität verfügbar"
|
||||
},
|
||||
"view_dark_mode": "Im dunklen Modus anzeigen",
|
||||
"view_light_mode": "Im hellen Modus anzeigen"
|
||||
"view_light_mode": "Im hellen Modus anzeigen",
|
||||
"not_spam_short": "Kein Spam",
|
||||
"previous": "Vorherige",
|
||||
"next": "Nächste",
|
||||
"quick_reply_login_switched": "Diese Nachricht gehört zu einem anderen Konto. Wechseln Sie zurück zu diesem Konto, um zu antworten.",
|
||||
"quick_reply_unconfirmed": "Der Server hat das Senden nicht bestätigt. Prüfen Sie Ihren Gesendet-Ordner, bevor Sie erneut senden.",
|
||||
"spam_short": "Spam"
|
||||
},
|
||||
"files": {
|
||||
"delete_cascade_hint": "Ordner werden samt ihrem gesamten Inhalt gelöscht. Dies kann nicht rückgängig gemacht werden.",
|
||||
@@ -444,10 +571,17 @@
|
||||
"delete_items_title": "{count, plural, one {# Element löschen?} other {# Elemente löschen?}}",
|
||||
"done": "Fertig",
|
||||
"download_failed_status": "Download fehlgeschlagen (HTTP {status})",
|
||||
"error_forbidden": "Dazu fehlt Ihnen hier die Berechtigung.",
|
||||
"error_invalid_name": "Der Server akzeptiert diesen Namen nicht.",
|
||||
"error_over_quota": "Die Höchstzahl an Dateien oder Ordnern ist erreicht. Löschen Sie einige und versuchen Sie es erneut.",
|
||||
"error_too_deep": "Ordner lassen sich nicht so tief verschachteln.",
|
||||
"error_too_large": "Diese Datei ist für den Server zu groß.",
|
||||
"folder_empty": "Dieser Ordner ist leer",
|
||||
"invalid_name": "Ungültiger Name",
|
||||
"invalid_name_slash": "Namen dürfen kein \"/\" enthalten.",
|
||||
"move_to": "Verschieben nach…",
|
||||
"name_control_chars": "Namen dürfen keine Steuerzeichen enthalten.",
|
||||
"name_too_long": "Dieser Name ist für den Server zu lang.",
|
||||
"open_error": "Die Datei konnte nicht geöffnet werden",
|
||||
"open_with": "Öffnen mit…",
|
||||
"preview_too_large": "Diese Datei ist zu groß für eine Vorschau.",
|
||||
@@ -460,13 +594,41 @@
|
||||
"share_no_users": "Keine anderen Benutzer gefunden",
|
||||
"share_remove": "Zugriff entfernen",
|
||||
"share_remove_confirm": "Zugriff entfernen?",
|
||||
"share_remove_for": "Zugriff für {name} entfernen",
|
||||
"share_role_custom": "Benutzerdefiniert",
|
||||
"share_search_placeholder": "Nach Name oder E-Mail suchen",
|
||||
"share_unavailable": "Teilen ist auf diesem Gerät nicht verfügbar",
|
||||
"shared_with": "Geteilt mit",
|
||||
"sharing_access": "Freigabe & Zugriff",
|
||||
"symlink": "Symbolischer Link",
|
||||
"upload_failed": "{count, plural, one {1 Datei konnte nicht hochgeladen werden} other {# Dateien konnten nicht hochgeladen werden}}",
|
||||
"upload_unavailable": "Die Dateiauswahl ist in diesem Build nicht installiert. Erstellen Sie die App neu, um Uploads zu aktivieren.",
|
||||
"uploading_progress": "Hochladen {current}/{total} · {name}"
|
||||
"uploading_progress": "Hochladen {current}/{total} · {name}",
|
||||
"batch_delete_confirm_message": "Möchten Sie wirklich {count, plural, one {1 Element} other {# Elemente}} löschen? Dies kann nicht rückgängig gemacht werden.",
|
||||
"batch_delete_success": "{count, plural, one {1 Element gelöscht} other {# Elemente gelöscht}}",
|
||||
"delete_confirm_message": "Möchten Sie \"{name}\" wirklich löschen? Dies kann nicht rückgängig gemacht werden.",
|
||||
"delete_confirm_title": "Ressource löschen",
|
||||
"delete_success": "Erfolgreich gelöscht",
|
||||
"name_trash": "„.Trash“ ist der Name des Papierkorb-Ordners auf der obersten Ebene Ihrer Dateien. Wählen Sie einen anderen Namen.",
|
||||
"settings_behavior": "Verhalten",
|
||||
"settings_colored_icons": "Farbige Symbole",
|
||||
"settings_colored_icons_desc": "Farbige statt einfarbige Symbole verwenden",
|
||||
"settings_default_sort": "Standardsortierung",
|
||||
"settings_default_sort_desc": "Wählen Sie die Standardsortierung für Dateien",
|
||||
"settings_default_view": "Standardansicht",
|
||||
"settings_default_view_desc": "Wählen Sie zwischen Raster- und Listenansicht",
|
||||
"settings_display": "Anzeige",
|
||||
"settings_icons": "Symbole",
|
||||
"settings_show_hidden": "Versteckte Dateien anzeigen",
|
||||
"settings_show_hidden_desc": "Dateien und Ordner anzeigen, die mit einem Punkt beginnen",
|
||||
"settings_show_icons": "Dateisymbole anzeigen",
|
||||
"settings_show_icons_desc": "Symbole neben Dateien und Ordnern anzeigen",
|
||||
"settings_show_thumbnails": "Vorschaubilder anzeigen",
|
||||
"settings_show_thumbnails_desc": "Bildvorschauen statt Symbole für Bilddateien anzeigen",
|
||||
"settings_sort_direction": "Sortierrichtung",
|
||||
"settings_sort_direction_desc": "Wählen Sie auf- oder absteigende Reihenfolge",
|
||||
"trash_unavailable_message": "{count, plural, one {Dieses Element kann} other {Diese # Elemente können}} nicht in den Papierkorb verschoben werden. Stattdessen endgültig löschen? Das kann nicht rückgängig gemacht werden.",
|
||||
"upload": "Hochladen"
|
||||
},
|
||||
"identities": {
|
||||
"badge": {
|
||||
@@ -520,7 +682,7 @@
|
||||
"err_pair_connect_detail": "Zeigen Sie auf Ihrem Computer einen neuen Code an und scannen Sie erneut.",
|
||||
"err_pair_connect_title": "Angemeldet, aber keine Verbindung zu {host}",
|
||||
"err_pair_insecure_detail": "{host} möchte eine unverschlüsselte Verbindung verwenden.",
|
||||
"err_pair_network_detail": "Prüfen Sie Ihre Verbindung und ob Ihr Telefon das Webmail erreichen kann, und versuchen Sie es dann erneut.",
|
||||
"err_pair_network_detail": "Prüfen Sie Ihre Verbindung und ob dieses Gerät das Webmail erreichen kann, und versuchen Sie es dann erneut.",
|
||||
"err_pair_rate_detail": "Warten Sie eine Minute und versuchen Sie es dann erneut.",
|
||||
"err_pair_server_detail": "Versuchen Sie es mit einem neuen Code erneut. Wenn es weiterhin fehlschlägt, wenden Sie sich an Ihren Administrator.",
|
||||
"err_pair_server_title": "Das Webmail konnte die Anmeldung nicht abschließen",
|
||||
@@ -589,11 +751,34 @@
|
||||
"unencrypted": "Unverschlüsselte Verbindung",
|
||||
"use_password": "Stattdessen mit Passwort anmelden",
|
||||
"username_label": "E-Mail oder Benutzername",
|
||||
"your_server": "Ihrem Server"
|
||||
"your_server": "Ihrem Server",
|
||||
"cancel_sign_in": "Anmeldung abbrechen",
|
||||
"err_client_rejected_detail": "Der Server hat Bulwark als Anmelde-Client abgelehnt. Bitten Sie Ihren Administrator, ihn zuzulassen, oder melden Sie sich mit einem App-Passwort an.",
|
||||
"err_client_rejected_title": "{host} hat die App nicht akzeptiert",
|
||||
"err_handoff_expired_detail": "Der Browser hat die Anmeldung erst nach ihrem Ablauf zurückgegeben. Melden Sie sich erneut an.",
|
||||
"err_handoff_expired_title": "Die Anmeldung hat zu lange gedauert",
|
||||
"err_handoff_network_detail": "Das Webmail war nach Ihrer Anmeldung nicht erreichbar. Prüfen Sie Ihre Verbindung und melden Sie sich erneut an.",
|
||||
"err_handoff_network_title": "Anmeldung konnte nicht abgeschlossen werden",
|
||||
"err_handoff_rate_detail": "Warten Sie ein paar Minuten und melden Sie sich dann erneut an.",
|
||||
"err_handoff_refused_detail": "Der Anmeldelink wurde bereits verwendet oder stammt nicht aus dieser Anmeldung. Melden Sie sich erneut an.",
|
||||
"err_handoff_refused_title": "Das Webmail hat diese Anmeldung nicht akzeptiert",
|
||||
"err_handoff_server_detail": "Melden Sie sich erneut an. Wenn es weiterhin fehlschlägt, wenden Sie sich an Ihren Administrator.",
|
||||
"err_handoff_server_title": "Das Webmail konnte die Anmeldung nicht abschließen",
|
||||
"err_token_exchange_title": "{host} hat keine Sitzung gestartet",
|
||||
"reauth_detail": "Der Server akzeptiert die gespeicherte Anmeldung für {account} nicht mehr. Melden Sie sich erneut an, um dieses Konto weiter zu nutzen.",
|
||||
"reauth_title": "Erneut anmelden",
|
||||
"server_cert_active": "Anmeldung mit dem Client-Zertifikat „{alias}“. Tippen, um ein anderes zu wählen.",
|
||||
"server_cert_clear": "Ohne Zertifikat anmelden",
|
||||
"server_cert_failed": "Zertifikat konnte nicht ausgewählt werden",
|
||||
"server_cert_hint": "Verlangt Ihr Server ein Client-Zertifikat? Wählen Sie eines, das auf diesem Gerät installiert ist."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"vacation_filters_off": "Abwesenheits-Einstellungen gespeichert, aber Ihre Filter sind ausgeschaltet. Speichern Sie noch einmal, um sie wieder einzuschalten."
|
||||
},
|
||||
"offline": {
|
||||
"banner": "Sie sind offline",
|
||||
"discarded_changes": "{count, plural, one {# Offline-Änderung wurde} other {# Offline-Änderungen wurden}} verworfen, weil sich der Mailserver geändert hat",
|
||||
"queued_changes": "{count, plural, one {# Änderung wird} other {# Änderungen werden}} synchronisiert, sobald Sie wieder verbunden sind",
|
||||
"sync": {
|
||||
"cached_count": "{count, plural, one {# neue Nachricht zwischengespeichert} other {# neue Nachrichten zwischengespeichert}} • {size}",
|
||||
@@ -607,6 +792,10 @@
|
||||
"up_to_date": "Bereits aktuell"
|
||||
}
|
||||
},
|
||||
"org_templates": {
|
||||
"personal_in_account": "Ihre Vorlagen werden in Ihrem Konto gespeichert, Sie haben sie also auf jedem Gerät, auf dem Sie sich anmelden. Alle, für die Sie Ihren Posteingang freigeben, können sie lesen.",
|
||||
"section_personal": "Meine Vorlagen"
|
||||
},
|
||||
"push_prompt": {
|
||||
"body": "Aktivieren Sie Hintergrundbenachrichtigungen, damit neue Nachrichten Sie auch bei geschlossener App erreichen.",
|
||||
"dismiss": "Nicht jetzt",
|
||||
@@ -614,12 +803,22 @@
|
||||
"title": "Bei neuen E-Mails benachrichtigt werden"
|
||||
},
|
||||
"scheduled": {
|
||||
"already_sent": "Diese Nachricht wurde bereits gesendet.",
|
||||
"cancel_body": "Die Nachricht wird nicht zugestellt. Eine Kopie bleibt in Ihrem Gesendet-Ordner.",
|
||||
"cancel_failed": "Abbrechen fehlgeschlagen",
|
||||
"edit_failed": "Die Nachricht konnte nicht zum Bearbeiten geöffnet werden",
|
||||
"keep_scheduled": "Geplant lassen",
|
||||
"no_longer_scheduled": "Diese Nachricht ist nicht mehr geplant.",
|
||||
"reschedule_failed": "Neu planen fehlgeschlagen",
|
||||
"send_now_failed": "Sofortiges Senden fehlgeschlagen"
|
||||
"send_now_failed": "Sofortiges Senden fehlgeschlagen",
|
||||
"actions": "Aktionen",
|
||||
"cancel_to_drafts_body": "Die Nachricht wird nicht gesendet. Sie kommt zurück in Ihre Entwürfe.",
|
||||
"cancelled_not_moved": "Senden abgebrochen, aber nicht in Entwürfe verschoben",
|
||||
"cancelled_not_moved_body": "Die Nachricht wird nicht gesendet. Sie liegt noch in „Gesendet“.",
|
||||
"login_switched": "Diese geplanten Nachrichten gehören zu einem anderen Konto. Wechseln Sie zurück zu diesem Konto, um sie zu ändern.",
|
||||
"reschedule_lost": "Die Nachricht ist nicht mehr geplant. Prüfen Sie Entwürfe und Gesendet, bevor Sie sie erneut senden.",
|
||||
"reschedule_lost_drafts": "Die Nachricht ist nicht mehr geplant. Sie liegt wieder in den Entwürfen, wo Sie sie erneut senden können.",
|
||||
"send_cancelled": "Senden abgebrochen"
|
||||
},
|
||||
"settings": {
|
||||
"account": {
|
||||
@@ -648,6 +847,15 @@
|
||||
}
|
||||
},
|
||||
"appearance": {
|
||||
"cross_all": {
|
||||
"label_mobile": "Alle Mails"
|
||||
},
|
||||
"cross_starred": {
|
||||
"label_mobile": "Alle markierten"
|
||||
},
|
||||
"cross_unread": {
|
||||
"label_mobile": "Alle ungelesenen"
|
||||
},
|
||||
"language": {
|
||||
"restart_for_direction": "Starten Sie die App neu, um die neue Schreibrichtung anzuwenden.",
|
||||
"system_default": "Systemstandard"
|
||||
@@ -655,7 +863,10 @@
|
||||
"toolbar_labels": {
|
||||
"description_mobile": "Textbeschriftungen neben den Symbolleistensymbolen anzeigen."
|
||||
},
|
||||
"description_mobile": "Passe an, wie die App aussieht"
|
||||
"description_mobile": "Passen Sie an, wie die App aussieht",
|
||||
"accessibility_link": {
|
||||
"description_mobile": "Schriftgröße, reduzierte Bewegung und die Voreinstellungen zur Barrierefreiheit finden Sie unter Barrierefreiheit."
|
||||
}
|
||||
},
|
||||
"badges": {
|
||||
"experimental": "Experimentell",
|
||||
@@ -802,6 +1013,10 @@
|
||||
"ask_mobile": "Fragen",
|
||||
"block_mobile": "Blockieren"
|
||||
},
|
||||
"placeholder_warning": {
|
||||
"label": "Warnung bei Platzhaltern",
|
||||
"description": "Vor dem Senden einer Nachricht nachfragen, die noch Platzhalter wie [TIME] oder [Ihr Name], leere Vorlagenfelder oder unfertigen Text wie TODO oder TBD enthält."
|
||||
},
|
||||
"plain_text_mode": {
|
||||
"description_mobile": "Nur im Klartext verfassen und lesen."
|
||||
},
|
||||
@@ -826,6 +1041,13 @@
|
||||
"sync_add_failed": "Synchronisierung mit dem Adressbuch fehlgeschlagen",
|
||||
"sync_remove_failed": "Entfernen aus dem Adressbuch fehlgeschlagen",
|
||||
"synced_badge": "Über Adressbuch synchronisiert"
|
||||
},
|
||||
"disable_threading": {
|
||||
"description": "E-Mails als einzelne Nachrichten statt nach Konversation gruppiert anzeigen",
|
||||
"label": "Konversationsgruppierung deaktivieren"
|
||||
},
|
||||
"recipient_mentions": {
|
||||
"description_mobile": "Wenn Sie in der Nachricht @ tippen, werden deren Empfänger angeboten und der Vorname des gewählten wird eingefügt."
|
||||
}
|
||||
},
|
||||
"experimental": {
|
||||
@@ -835,8 +1057,11 @@
|
||||
"remove_action": "Aktion entfernen",
|
||||
"remove_condition": "Bedingung entfernen",
|
||||
"sieve_editor": {
|
||||
"line_count": "{count, plural, one {# Zeile} other {# Zeilen}}"
|
||||
}
|
||||
"line_count": "{count, plural, one {# Zeile} other {# Zeilen}}",
|
||||
"confirm_save": "Speichern bestätigen",
|
||||
"save_warning": "Das Speichern überschreibt alle visuellen Regeln. Dies kann nicht rückgängig gemacht werden. Klicken Sie erneut auf Speichern zur Bestätigung."
|
||||
},
|
||||
"validation_header_name": "Geben Sie einen Header-Namen ein, der nur die Buchstaben A–Z, Ziffern, Bindestriche und Unterstriche enthält, zum Beispiel X-Spam-Flag."
|
||||
},
|
||||
"folders": {
|
||||
"account": "Konto",
|
||||
@@ -851,6 +1076,7 @@
|
||||
"not_empty_message": "\"{name}\" enthält {count} E-Mails. Trotzdem löschen?",
|
||||
"not_empty_title": "Ordner nicht leer",
|
||||
"own_account": "Meine Ordner",
|
||||
"parent_folder": "Übergeordneter Ordner",
|
||||
"role": "Ordnerrolle",
|
||||
"role_hint": "Weisen Sie diesem Ordner eine Sonderrolle zu (Archiv, Spam, …). Jede Rolle kann nur einem Ordner zugewiesen werden.",
|
||||
"save_failed": "Speichern fehlgeschlagen",
|
||||
@@ -878,7 +1104,8 @@
|
||||
"no_name": "(kein Name)",
|
||||
"primary": "primär",
|
||||
"save_failed": "Speichern fehlgeschlagen",
|
||||
"text_signature": "Klartext-Signatur"
|
||||
"text_signature": "Klartext-Signatur",
|
||||
"title": "Sendeidentitäten"
|
||||
},
|
||||
"keywords": {
|
||||
"delete_confirm_title": "Tag „{label}“ löschen?",
|
||||
@@ -936,7 +1163,9 @@
|
||||
"email": {
|
||||
"app_icon_badge": "Ungelesen-Zähler auf dem App-Symbol",
|
||||
"app_icon_badge_desc": "Die Anzahl ungelesener Nachrichten im Posteingang als Badge auf dem App-Symbol anzeigen.",
|
||||
"sound_channel_hint": "Ton und Vibration für E-Mail-Benachrichtigungen werden über den Android-Benachrichtigungskanal gesteuert: Tippen Sie lange auf eine Benachrichtigung oder öffnen Sie die App-Einstellungen des Systems."
|
||||
"sound_channel_hint": "Ton und Vibration für E-Mail-Benachrichtigungen werden über den Android-Benachrichtigungskanal gesteuert: Tippen Sie lange auf eine Benachrichtigung oder öffnen Sie die App-Einstellungen des Systems.",
|
||||
"mute_account": "Dieses Konto stummschalten",
|
||||
"mute_account_desc": "Keine Benachrichtigungen über neue E-Mails in diesem Konto. Tippen Sie in der Seitenleiste lange auf einen Ordner, um nur ihn stummzuschalten oder wieder einzuschalten."
|
||||
},
|
||||
"push": {
|
||||
"confirm_disable_message_mobile": "Dieses Gerät erhält für dieses Konto keine Benachrichtigungen über neue E-Mails mehr.",
|
||||
@@ -945,8 +1174,11 @@
|
||||
"err_fcm_empty": "Firebase hat ein leeres Geräte-Token zurückgegeben.",
|
||||
"err_fcm_token": "Firebase konnte kein Geräte-Token ausstellen ({detail}). Push über FCM erfordert Google Play-Dienste auf diesem Gerät.",
|
||||
"err_fcm_token_up": "Firebase konnte kein Geräte-Token ausstellen ({detail}). Push über FCM erfordert Google Play-Dienste auf diesem Gerät. Auf diesem Gerät ist ein UnifiedPush-Distributor installiert - stellen Sie die Zustellmethode stattdessen auf UnifiedPush um.",
|
||||
"err_forbidden": "Der Mailserver erlaubt für dieses Konto keine Hintergrundbenachrichtigungen.",
|
||||
"err_jmap_list": "Die Push-Registrierungen konnten nicht vom Mailserver gelesen werden: {detail}",
|
||||
"err_jmap_refused": "Der Mailserver hat das Push-Abonnement abgelehnt: {detail}",
|
||||
"err_no_account": "Kein Konto geladen - Push kann nicht eingerichtet werden.",
|
||||
"err_over_quota": "Für dieses Konto gibt es auf dem Mailserver zu viele Push-Registrierungen (meist sind 15 erlaubt). Widerrufen Sie Geräte, die Sie nicht mehr nutzen, in den Benachrichtigungseinstellungen und versuchen Sie es erneut.",
|
||||
"err_permission": "Die Benachrichtigungsberechtigung wurde nicht erteilt.",
|
||||
"err_relay_https": "Die Relay-URL muss https:// verwenden.",
|
||||
"err_relay_rejected": "Das Push-Relay hat die Registrierung abgelehnt: {detail}",
|
||||
@@ -966,6 +1198,7 @@
|
||||
"relay_desc_mobile": "Der Server, der Ihre Benachrichtigungen zustellt. Standardmäßig das gehostete Bulwark-Relay; ändern Sie dies nur, wenn Sie selbst hosten. Muss https verwenden.",
|
||||
"relay_invalid": "Geben Sie eine gültige https://-URL ein",
|
||||
"setup_failed": "Einrichtung fehlgeschlagen",
|
||||
"shared_mailboxes_no_push": "Postfächer, die andere mit Ihnen teilen, senden keine Hintergrundbenachrichtigungen.",
|
||||
"status_android_only": "Hintergrundbenachrichtigungen sind nur unter Android verfügbar.",
|
||||
"transport_desc": "Wie Benachrichtigungen dieses Gerät erreichen: Googles Firebase Cloud Messaging oder eine UnifiedPush-Distributor-App (z. B. ntfy) für die Zustellung ohne Google.",
|
||||
"transport_fcm": "Google (FCM)",
|
||||
@@ -973,7 +1206,17 @@
|
||||
"transport_unifiedpush": "UnifiedPush",
|
||||
"up_distributor_desc": "Mehrere UnifiedPush-Distributor-Apps sind installiert - wählen Sie die aus, die Benachrichtigungen zustellen soll.",
|
||||
"up_distributor_label": "Distributor",
|
||||
"up_no_distributor": "Auf diesem Gerät ist keine UnifiedPush-Distributor-App installiert. Installieren Sie eine (z. B. ntfy) und versuchen Sie es erneut."
|
||||
"up_no_distributor": "Auf diesem Gerät ist keine UnifiedPush-Distributor-App installiert. Installieren Sie eine (z. B. ntfy) und versuchen Sie es erneut.",
|
||||
"accounts_desc_mobile": "Hintergrundbenachrichtigungen für jedes angemeldete Konto auf diesem Gerät. Wechseln Sie zu einem Konto, um sie dafür ein- oder auszuschalten.",
|
||||
"err_account_changed": "Das Konto hat gewechselt, während Push eingerichtet wurde. Versuchen Sie es erneut.",
|
||||
"err_apns_empty": "iOS hat ein leeres Push-Token zurückgegeben.",
|
||||
"err_apns_token": "iOS konnte kein Push-Token ausstellen ({detail}).",
|
||||
"err_relay_no_apns": "Das Push-Relay unter {url} stellt noch nicht an iPhones und iPads zu.",
|
||||
"err_url_refused": "Der Mailserver weigert sich, Benachrichtigungen an das Relay unter {url} zu senden. Er akzeptiert nur https-Adressen mit einem öffentlichen Domainnamen, keine private IP-Adresse und keinen lokalen Namen wie localhost, .local oder .lan. Verwenden Sie das gehostete Relay oder geben Sie Ihrem Relay einen öffentlichen Domainnamen."
|
||||
},
|
||||
"calendar": {
|
||||
"invitation_parsing": "E-Mail-Einladungen erkennen",
|
||||
"invitation_parsing_desc": "Kalendereinladungen in E-Mail-Anhängen erkennen und Kalenderaktionen anzeigen"
|
||||
}
|
||||
},
|
||||
"offline": {
|
||||
@@ -1005,7 +1248,7 @@
|
||||
"window_24h": "Letzte 24 Stunden",
|
||||
"window_days": "Letzte {count} Tage",
|
||||
"window_desc": "Wie weit zurück zwischengespeichert wird, gemessen am Empfangsdatum der Nachricht.",
|
||||
"description_short": "Neue Nachrichten auf dem Telefon behalten, damit sie auch ohne Verbindung aufgehen.",
|
||||
"description_short": "Neue Nachrichten auf diesem Gerät behalten, damit sie auch ohne Verbindung aufgehen.",
|
||||
"keep_24h": "24 Stunden",
|
||||
"keep_days": "{count} Tage",
|
||||
"keep_from": "Mails der letzten",
|
||||
@@ -1030,7 +1273,10 @@
|
||||
"not_set": "Nicht festgelegt",
|
||||
"pick": "Zertifikat auswählen",
|
||||
"pick_failed": "Auswahl fehlgeschlagen",
|
||||
"title": "TLS-Client-Zertifikat"
|
||||
"title": "TLS-Client-Zertifikat",
|
||||
"clear_message_ios": "Neue Anfragen legen kein Zertifikat mehr vor. Das Zertifikat wird aus der App entfernt; um es wieder zu verwenden, wählen Sie die Datei erneut aus.",
|
||||
"description_ios": "Importiert eine Zertifikatsdatei, mit der sich die App beim TLS-Handshake am Server authentifiziert. Nützlich, wenn der Reverse-Proxy mTLS erzwingt.",
|
||||
"hint_ios": "Wählen Sie die Zertifikatsdatei (.p12 oder .pfx) in Dateien aus und geben Sie ihr Passwort ein."
|
||||
},
|
||||
"credentials": {
|
||||
"allowed_ips": "Erlaubte IPs (optional)",
|
||||
@@ -1058,12 +1304,15 @@
|
||||
"password": {
|
||||
"change": "Passwort ändern",
|
||||
"changed": "Ihr Kontopasswort wurde aktualisiert.",
|
||||
"changed_sign_in_again": "Ihr Kontopasswort wurde aktualisiert. Melden Sie sich mit dem neuen Passwort erneut an.",
|
||||
"changed_title": "Passwort geändert",
|
||||
"description": "Das Passwort für dieses Konto ändern.",
|
||||
"error": "Passwort konnte nicht geändert werden.",
|
||||
"mismatch": "Die Passwörter stimmen nicht überein.",
|
||||
"toggle_visibility": "Passwort ein- oder ausblenden",
|
||||
"too_short": "Das neue Passwort muss mindestens 8 Zeichen lang sein."
|
||||
"too_short": "Das neue Passwort muss mindestens 8 Zeichen lang sein.",
|
||||
"session_ended": "Durch die Passwortänderung wurde diese Sitzung beendet. Melden Sie sich mit Ihrem neuen Passwort erneut an.",
|
||||
"session_ended_title": "Erneut anmelden"
|
||||
},
|
||||
"public_keys": {
|
||||
"add_error": "Öffentlicher Schlüssel konnte nicht hinzugefügt werden",
|
||||
@@ -1082,6 +1331,7 @@
|
||||
"disable": "Deaktivieren",
|
||||
"disable_error": "Zwei-Faktor-Authentifizierung konnte nicht deaktiviert werden.",
|
||||
"disable_hint": "Geben Sie Ihr Passwort ein, um die Zwei-Faktor-Authentifizierung zu deaktivieren.",
|
||||
"disable_hint_code": "Geben Sie Ihr Passwort und einen Code aus Ihrer Authentifizierungs-App ein, um die Zwei-Faktor-Authentifizierung zu deaktivieren.",
|
||||
"enable_error": "Zwei-Faktor-Authentifizierung konnte nicht aktiviert werden.",
|
||||
"enabled_message": "Bei der nächsten Anmeldung werden Sie nach einem Code gefragt.",
|
||||
"enabled_title": "Zwei-Faktor-Authentifizierung aktiviert",
|
||||
@@ -1094,6 +1344,12 @@
|
||||
"open_app": "Zur Authenticator-App hinzufügen",
|
||||
"password_required": "Geben Sie Ihr aktuelles Passwort ein.",
|
||||
"section_title": "Zwei-Faktor-Authentifizierung"
|
||||
},
|
||||
"restricted": "Ihr Server erlaubt nur Apps, die sein Administrator freigegeben hat, das Passwort, die Zwei-Faktor-Anmeldung, App-Passwörter und API-Schlüssel zu ändern. Bulwark hat sich ohne diese Freigabe angemeldet: E-Mail, Kontakte, Kalender und Dateien funktionieren, diese Einstellungen aber nicht. Ändern Sie sie in der Weboberfläche Ihres Servers oder im Bulwark Webmail.",
|
||||
"screen_privacy": {
|
||||
"block_screenshots": "Bildschirmfotos blockieren",
|
||||
"block_screenshots_description": "Verhindert Bildschirmfotos und Bildschirmaufnahmen der App und verbirgt ihren Inhalt in der Ansicht der zuletzt verwendeten Apps.",
|
||||
"title": "Bildschirmschutz"
|
||||
}
|
||||
},
|
||||
"sidebar_apps": {
|
||||
@@ -1122,7 +1378,16 @@
|
||||
"updates": "Updates",
|
||||
"storage": "Speicher & Sicherung",
|
||||
"about": "Über",
|
||||
"troubleshooting": "Fehlerbehebung"
|
||||
"troubleshooting": "Fehlerbehebung",
|
||||
"content_senders": "Inhalte & Absender",
|
||||
"identities": "Identitäten",
|
||||
"keywords": "Labels",
|
||||
"language": "Sprache, Region & Zeit",
|
||||
"sidebar_apps": "Sidebar-Apps",
|
||||
"templates": "Vorlagen",
|
||||
"themes": "Designs",
|
||||
"downloads": "Downloads",
|
||||
"layout": "Layout"
|
||||
},
|
||||
"templates": {
|
||||
"delete_confirm": "\"{name}\" endgültig löschen?",
|
||||
@@ -1134,15 +1399,28 @@
|
||||
"imported_count": "{count, plural, one {# Vorlage} other {# Vorlagen}} hinzugefügt.",
|
||||
"is_html": "Text ist HTML",
|
||||
"placeholders_hint": "Platzhalter: {{recipient_name}}, {{company}}, {{date}}, {{day_of_week}}, {{sender_name}}",
|
||||
"share_unavailable": "Teilen nicht möglich"
|
||||
"share_unavailable": "Teilen nicht möglich",
|
||||
"import_json_label": "Vorlagen als JSON"
|
||||
},
|
||||
"themes": {
|
||||
"default_description": "Die Standardpaletten für Hell und Dunkel.",
|
||||
"mobile_description": "Wählen Sie ein Farbdesign. Designs folgen der Hell/Dunkel-Einstellung unter Darstellung; eigene Designpakete werden im Webmail verwaltet."
|
||||
},
|
||||
"vacation": {
|
||||
"status": {
|
||||
"ended": "Beendet",
|
||||
"scheduled": "Geplant"
|
||||
},
|
||||
"warnings": {
|
||||
"date_format": "Datumsangaben müssen das Format \"YYYY-MM-DD\" oder \"YYYY-MM-DD HH:MM\" haben"
|
||||
},
|
||||
"date_range": {
|
||||
"description": "Die automatische Antwort optional auf einen bestimmten Zeitraum beschränken",
|
||||
"title": "Zeitraum"
|
||||
},
|
||||
"message": {
|
||||
"description": "Die Nachricht, die als Antwort gesendet wird",
|
||||
"title": "Text der automatischen Antwort"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
@@ -1152,19 +1430,19 @@
|
||||
"licences_desc": "AGPL-3.0 für die App und die Bibliotheken, die sie nutzt",
|
||||
"never_checked": "Noch nicht geprüft",
|
||||
"privacy": "Datenschutz",
|
||||
"privacy_desc": "Was auf diesem Telefon bleibt und was an deinen Server geht",
|
||||
"privacy_desc": "Was auf diesem Gerät bleibt und was an Ihren Server geht",
|
||||
"report_problem": "Problem melden",
|
||||
"report_problem_desc": "Öffnet ein Issue auf GitHub mit eingetragener Version",
|
||||
"server": "Server",
|
||||
"server_value": "{host} · angemeldet als {user}",
|
||||
"source_code": "Quellcode",
|
||||
"this_device": "Dieses Gerät",
|
||||
"this_device_desc": "Womit die App verbunden ist und wo du Hilfe bekommst.",
|
||||
"this_device_desc": "Womit die App verbunden ist und wo Sie Hilfe bekommen.",
|
||||
"up_to_date": "Aktuell",
|
||||
"updates": "Updates"
|
||||
},
|
||||
"backup": {
|
||||
"description": "Übertrage deine Einstellungen auf ein anderes Telefon oder ins Webmail.",
|
||||
"description": "Übertragen Sie Ihre Einstellungen auf ein anderes Gerät oder ins Webmail.",
|
||||
"export_import": "Exportieren oder importieren",
|
||||
"export_import_desc": "Eine JSON-Datei im selben Format wie im Webmail",
|
||||
"reset": "Zurücksetzen",
|
||||
@@ -1172,26 +1450,26 @@
|
||||
"title": "Einstellungen sichern"
|
||||
},
|
||||
"licences": {
|
||||
"app_desc": "Die App ist freie Software unter der {licence}. Du darfst sie nutzen, untersuchen, weitergeben und ändern.",
|
||||
"app_desc": "Die App ist freie Software unter der {licence}. Sie dürfen sie nutzen, untersuchen, weitergeben und ändern.",
|
||||
"libraries": "Bibliotheken",
|
||||
"libraries_desc": "Open-Source-Code, mit dem die App gebaut ist.",
|
||||
"read_licence": "Den vollständigen Lizenztext lesen"
|
||||
},
|
||||
"privacy": {
|
||||
"favicons": "Absendersymbole für geschäftliche Mails kommen vom Symboldienst von DuckDuckGo, der dabei die Domain des Absenders sieht. Ausschalten kannst du sie unter Darstellung → Absender-Favicons.",
|
||||
"favicons": "Absendersymbole für geschäftliche Mails kommen vom Symboldienst von DuckDuckGo, der dabei die Domain des Absenders sieht. Ausschalten können Sie sie unter Darstellung → Absender-Favicons.",
|
||||
"no_tracking": "Die App hat keine Analyse und keine Werbung.",
|
||||
"other_title": "Weitere Verbindungen",
|
||||
"phone_backup": "Nichts davon landet in der Cloud-Sicherung von Android.",
|
||||
"phone_secure": "Deine Anmeldedaten liegen im sicheren Speicher des Systems. Einstellungen, der Offline-Cache und noch nicht synchronisierte Änderungen liegen im Speicher der App.",
|
||||
"phone_title": "Auf diesem Telefon",
|
||||
"push": "Wenn Benachrichtigungen an sind, meldet dein Server dem Push-Relay ({relay}), dass sich etwas geändert hat, und das Relay weckt die App über {service}. Das Relay sieht ein Gerätetoken und eine Änderungsmarke, nie deine Mails. Unter Benachrichtigungen kannst du ein eigenes Relay eintragen.",
|
||||
"phone_secure": "Ihre Anmeldedaten liegen im sicheren Speicher des Systems. Einstellungen, der Offline-Cache und noch nicht synchronisierte Änderungen liegen im Speicher der App.",
|
||||
"phone_title": "Auf diesem Gerät",
|
||||
"push": "Wenn Benachrichtigungen an sind, meldet Ihr Server dem Push-Relay ({relay}), dass sich etwas geändert hat, und das Relay weckt die App über {service}. Das Relay sieht ein Gerätetoken und eine Änderungsmarke, nie Ihre Mails. Unter Benachrichtigungen können Sie ein eigenes Relay eintragen.",
|
||||
"push_title": "Push-Benachrichtigungen",
|
||||
"server": "Mails, Kalender, Kontakte und Dateien werden von deinem Server geladen und dort gespeichert. Außer den Verbindungen unten spricht die App nur mit deinem Server.",
|
||||
"server_title": "Dein Mailserver",
|
||||
"server": "Mails, Kalender, Kontakte und Dateien werden von Ihrem Server geladen und dort gespeichert. Außer den Verbindungen unten spricht die App nur mit Ihrem Server.",
|
||||
"server_title": "Ihr Mailserver",
|
||||
"service_apple": "den Push-Dienst von Apple",
|
||||
"service_google": "den Push-Dienst von Google",
|
||||
"updates": "Die App fragt bei GitHub nach der neuesten Version, um dir Updates anzuzeigen.",
|
||||
"updates_android": "Die App fragt bei GitHub nach der neuesten Version, um dir Updates anzuzeigen. Ausschalten kannst du das unter Updates."
|
||||
"updates": "Die App fragt bei GitHub nach der neuesten Version, um Ihnen Updates anzuzeigen.",
|
||||
"updates_android": "Die App fragt bei GitHub nach der neuesten Version, um Ihnen Updates anzuzeigen. Ausschalten können Sie das unter Updates."
|
||||
},
|
||||
"summary": {
|
||||
"events_off": "Termine aus",
|
||||
@@ -1205,7 +1483,7 @@
|
||||
"layout": "Schnellaktionen, Wischen, Reihenfolge",
|
||||
"reading": "Gelesen-Status, Löschen, Anhänge",
|
||||
"composing": "Signatur, Sendeverzögerung, Entwürfe",
|
||||
"identities": "Adressen, von denen du sendest",
|
||||
"identities": "Adressen, von denen Sie senden",
|
||||
"vacation": "Automatische Antwort bei Abwesenheit",
|
||||
"filters": "Regeln für eingehende Mails",
|
||||
"templates": "Wiederverwendbare Nachrichten",
|
||||
@@ -1217,15 +1495,77 @@
|
||||
"contacts": "Adressbücher, Sortierung",
|
||||
"files": "Ansicht, Sortierung, Vorschaubilder",
|
||||
"sidebar_apps": "Web-Apps im Seitenmenü",
|
||||
"updates": "Neue Versionen installieren"
|
||||
"updates": "Neue Versionen installieren",
|
||||
"accessibility": "Schriftgröße, Bewegung, Bestätigungen"
|
||||
},
|
||||
"troubleshooting": {
|
||||
"cache_desc": "Wenn eine Liste veraltet oder falsch aussieht.",
|
||||
"cache_title": "Zwischengespeicherte Daten",
|
||||
"row_desc": "Daten neu laden, Debug-Protokoll",
|
||||
"title": "Fehlerbehebung"
|
||||
},
|
||||
"composer": {
|
||||
"autosave": {
|
||||
"1m": "Jede Minute",
|
||||
"2m": "Alle 2 Minuten",
|
||||
"30s": "Alle 30 Sekunden",
|
||||
"5m": "Alle 5 Minuten",
|
||||
"description": "Wie oft Entwürfe automatisch gespeichert werden",
|
||||
"label": "Automatisches Speichern"
|
||||
},
|
||||
"description": "Konfigurieren Sie die E-Mail-Erstellungseinstellungen",
|
||||
"title": "Editor"
|
||||
},
|
||||
"tab_groups": {
|
||||
"advanced": "Erweitert",
|
||||
"appearance": "Darstellung",
|
||||
"general": "Allgemein",
|
||||
"privacy": "Datenschutz & Sicherheit"
|
||||
},
|
||||
"accessibility": {
|
||||
"presets": {
|
||||
"dyslexia": {
|
||||
"description_mobile": "Atkinson Hyperlegible als Schrift, auch für Nachrichten als reiner Text."
|
||||
},
|
||||
"applied_mobile": "{name} hat {count, plural, one {# Einstellung} other {# Einstellungen}} geändert",
|
||||
"apply": "Anwenden",
|
||||
"apply_named": "{name} anwenden",
|
||||
"low-vision": {
|
||||
"description_mobile": "Text auf 150 % und Beschriftungen unter den Symbolleistensymbolen."
|
||||
},
|
||||
"motion": {
|
||||
"description_mobile": "Keine Animationen und keine gleitenden Übergänge."
|
||||
},
|
||||
"screen-reader": {
|
||||
"description_mobile": "Keine Gruppierung nach Konversation, jede Nachricht hat also eine eigene Zeile."
|
||||
}
|
||||
},
|
||||
"font": {
|
||||
"description_ios": "Atkinson Hyperlegible und Lexend sind so gestaltet, dass sich die Buchstaben gut unterscheiden; OpenDyslexic hilft manchen Menschen mit Legasthenie. Auf diesem Gerät gilt die Schrift für Nachrichten als reiner Text."
|
||||
},
|
||||
"cvd_palette": {
|
||||
"description_mobile": "Verwendet Farben für Tags und Kalender, die bei Rot-Grün- oder Blau-Gelb-Schwäche unterscheidbar bleiben."
|
||||
},
|
||||
"plain_language": {
|
||||
"description_mobile": "Einfachere Formulierungen in Einstellungen und Fehlermeldungen. Auf Deutsch und Englisch verfügbar."
|
||||
},
|
||||
"reduce_motion": {
|
||||
"description_mobile": "Schaltet gleitende Bereiche und Übergänge aus. Die App tut das auch, wenn in den Einstellungen Ihres Geräts „Bewegung reduzieren“ aktiviert ist."
|
||||
},
|
||||
"text_size": {
|
||||
"description_mobile": "Skaliert den Text der App zusätzlich zur Schriftgröße Ihres Geräts. 100 % entspricht der Größe Ihres Geräts."
|
||||
},
|
||||
"toast_duration": {
|
||||
"automatic": "Automatisch (länger mit Screenreader)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sharing": {
|
||||
"directory_failed": "Die Liste der Benutzer und Gruppen konnte nicht geladen werden.",
|
||||
"directory_not_browsable": "Ihr Server erlaubt Ihnen nicht, sein Benutzerverzeichnis zu durchsuchen.",
|
||||
"directory_restricted": "Ihr Server erlaubt kein Durchsuchen seines Benutzerverzeichnisses, daher werden nur Ihre Gruppen und Personen aufgeführt, die etwas mit Ihnen geteilt haben. Ein Administrator kann Verzeichnisabfragen in den Freigabeeinstellungen von Stalwart aktivieren.",
|
||||
"unknown_user": "Unbekannter Benutzer"
|
||||
},
|
||||
"sidebar": {
|
||||
"account_error": "Aktion erforderlich",
|
||||
"remove_account_hint": "Lange drücken, um dieses Konto zu entfernen",
|
||||
@@ -1237,7 +1577,8 @@
|
||||
"sign_out_confirm_message": "Das Konto wird von diesem Gerät entfernt. Sie können sich jederzeit wieder anmelden.",
|
||||
"sign_out_confirm_title": "Abmelden?",
|
||||
"sign_out_confirm_title_account": "Von {account} abmelden?",
|
||||
"tab_unavailable": "{name} (nicht verfügbar)"
|
||||
"tab_unavailable": "{name} (nicht verfügbar)",
|
||||
"account_sign_in_again": "Erneut anmelden"
|
||||
},
|
||||
"unified_mailbox": {
|
||||
"accounts_failed": "{count, plural, one {# Konto konnte nicht geladen werden} other {# Konten konnten nicht geladen werden}}"
|
||||
@@ -1479,5 +1820,110 @@
|
||||
"unsupported": "Abwesenheitsnotizen sind nicht verfügbar",
|
||||
"until": "Bis {date}"
|
||||
}
|
||||
},
|
||||
"deep_link": {
|
||||
"open_failed": "Konnte nicht geöffnet werden"
|
||||
},
|
||||
"encryption": {
|
||||
"armored_key": "Schlüssel im ASCII-Format",
|
||||
"banner": {
|
||||
"checking": "Verschlüsselung und Signatur werden geprüft…",
|
||||
"decrypted": "Entschlüsselt ({kind})",
|
||||
"failed": "Entschlüsseln fehlgeschlagen: {reason}",
|
||||
"invalid": "Ungültige Signatur ({kind})",
|
||||
"invalid_reason": "Ungültige Signatur ({kind}): {reason}",
|
||||
"locked": "Verschlüsselt ({kind}). Entsperren Sie Ihren Schlüssel, um die Nachricht zu lesen.",
|
||||
"no_key": "Verschlüsselt ({kind}) für einen Schlüssel, der nicht auf diesem Gerät ist.",
|
||||
"self_signed": "Das Zertifikat ist selbstsigniert und nicht von einer bekannten Stelle ausgestellt.",
|
||||
"signed": "Gültige Signatur ({kind})",
|
||||
"signed_by": "Gültige Signatur ({kind}) von {signer}",
|
||||
"signer_mismatch": "Der Unterzeichner ist nicht der unter „Von“ angezeigte Absender.",
|
||||
"unknown_key": "Signiert ({kind}) mit einem Schlüssel, den Sie nicht importiert haben.",
|
||||
"unsupported": "Verschlüsselt mit {algorithm}, das die App nicht entschlüsseln kann.",
|
||||
"key_offered": "Die Nachricht enthält einen öffentlichen Schlüssel für {email}. Fingerabdruck: {fingerprint}",
|
||||
"save_key": "Schlüssel für {email} speichern",
|
||||
"save_key_failed": "Der Schlüssel konnte nicht gespeichert werden"
|
||||
},
|
||||
"check_description": "Führt jeden Algorithmus, den PGP und S/MIME verwenden, auf diesem Gerät aus.",
|
||||
"check_run": "Prüfung starten",
|
||||
"check_title": "Verschlüsselungsunterstützung prüfen",
|
||||
"delete_private_message": "{key} und der zugehörige private Schlüssel werden von diesem Gerät entfernt. Damit verschlüsselte E-Mails können hier nicht mehr gelesen werden, es sei denn, Sie importieren ihn erneut.",
|
||||
"delete_public_message": "{key} wird von diesem Gerät entfernt.",
|
||||
"delete_title": "Schlüssel löschen?",
|
||||
"expired": "Abgelaufen am {date}",
|
||||
"expires": "Läuft am {date} ab",
|
||||
"find_key": "Schlüssel auf keys.openpgp.org suchen",
|
||||
"import": "Importieren",
|
||||
"import_cert": "Zertifikat importieren",
|
||||
"import_p12": "S/MIME-Zertifikat importieren (.p12)",
|
||||
"import_pgp_file": "PGP-Schlüsseldatei importieren",
|
||||
"import_public_file": "Datei mit öffentlichem Schlüssel importieren",
|
||||
"imported": "{key} importiert",
|
||||
"lock": "Sperren",
|
||||
"lock_all": "Alle Schlüssel sperren",
|
||||
"lock_all_description": "Die entsperrten Schlüssel vergessen, bis Sie ihre Passphrase erneut eingeben.",
|
||||
"locked": "Gesperrt",
|
||||
"locked_all": "Schlüssel gesperrt",
|
||||
"lookup_confirm": "Vergleichen Sie diesen Fingerabdruck mit dem, den Ihnen der Inhaber gegeben hat (persönlich, per Telefon), bevor Sie ihn importieren.",
|
||||
"lookup_none": "Kein Schlüssel gefunden.",
|
||||
"lookup_placeholder": "E-Mail-Adresse oder Fingerabdruck",
|
||||
"lookup_search": "Suchen",
|
||||
"lookup_verified": "Der Schlüssel hat den Fingerabdruck, nach dem Sie gesucht haben.",
|
||||
"no_other_keys": "Noch keine.",
|
||||
"no_own_keys": "Noch keine Schlüssel.",
|
||||
"not_a_key": "Dies ist kein Schlüssel und kein Zertifikat, das die App lesen kann.",
|
||||
"others_description": "Öffentliche Schlüssel und Zertifikate, um damit zu verschlüsseln und Signaturen zu prüfen. Schlüssel aus signierten E-Mails und aus Ihren Kontakten werden ebenfalls verwendet.",
|
||||
"others_title": "Schlüssel anderer Personen",
|
||||
"own_description": "E-Mails mit OpenPGP oder S/MIME signieren und entschlüsseln. Private Schlüssel bleiben im sicheren Speicher dieses Geräts und werden einmal pro App-Sitzung mit ihrer Passphrase entsperrt.",
|
||||
"own_title": "Ihre Schlüssel",
|
||||
"p12_password_message": "Geben Sie das Passwort ein, mit dem die .p12-Datei exportiert wurde. Sie brauchen es erneut, um den Schlüssel nach einem Neustart der App zu entsperren.",
|
||||
"p12_password_title": "Zertifikatspasswort",
|
||||
"passphrase": "Passphrase",
|
||||
"passphrase_message": "Geben Sie die Passphrase dieses Schlüssels ein. Sie wird jetzt geprüft und einmal pro App-Sitzung erneut abgefragt.",
|
||||
"passphrase_title": "Schlüssel-Passphrase",
|
||||
"password": "Passwort",
|
||||
"paste_pgp": "PGP-Schlüssel einfügen",
|
||||
"paste_public": "Öffentlichen Schlüssel einfügen",
|
||||
"share_cert": "Zertifikat teilen",
|
||||
"share_key": "Öffentlichen Schlüssel teilen",
|
||||
"tab": "Verschlüsselung",
|
||||
"title": "Verschlüsselung",
|
||||
"unlock": "Entsperren",
|
||||
"unlock_pgp_message": "Geben Sie die Passphrase Ihres PGP-Schlüssels {key} ein.",
|
||||
"unlock_read_reason": "Die Nachricht ist für diesen Schlüssel verschlüsselt.",
|
||||
"unlock_smime_message": "Geben Sie das Passwort Ihres S/MIME-Zertifikats {key} ein.",
|
||||
"unlock_title": "Schlüssel entsperren",
|
||||
"unlocked": "Entsperrt",
|
||||
"unlocking": "Wird entsperrt…",
|
||||
"wrong_passphrase": "Diese Passphrase ist nicht richtig."
|
||||
},
|
||||
"outbox": {
|
||||
"discard": "Verwerfen",
|
||||
"discard_body": "Sie wird nicht gesendet, und ihr Text geht verloren.",
|
||||
"discard_title": "Diese Nachricht verwerfen?",
|
||||
"failed_count": "{count, plural, one {# Nachricht} other {# Nachrichten}} nicht gesendet",
|
||||
"move_failed_body": "Die Nachricht bleibt im Postausgang. Versuchen Sie es erneut, wenn die Verbindung wieder besteht.",
|
||||
"move_failed_title": "Verschieben in Entwürfe fehlgeschlagen",
|
||||
"move_to_drafts": "In Entwürfe verschieben",
|
||||
"open_hint": "Öffnet den Postausgang",
|
||||
"queued_body": "Sie wird gesendet, sobald Sie wieder online sind.",
|
||||
"queued_title": "Im Postausgang gespeichert",
|
||||
"retry": "Erneut versuchen",
|
||||
"send_failed": "Eine Nachricht im Postausgang konnte nicht gesendet werden",
|
||||
"sent": "Nachricht aus dem Postausgang gesendet",
|
||||
"status_failed": "Nicht gesendet",
|
||||
"status_failed_reason": "Nicht gesendet: {reason}",
|
||||
"status_offline": "Wartet auf eine Verbindung",
|
||||
"status_retrying": "Wartet auf das Senden",
|
||||
"status_sending": "Wird gesendet…",
|
||||
"title": "Postausgang",
|
||||
"waiting": "{count, plural, one {# Nachricht} other {# Nachrichten}} im Postausgang",
|
||||
"already_sent": "Diese Nachricht wurde bereits gesendet"
|
||||
},
|
||||
"push": {
|
||||
"ios": {
|
||||
"more_messages": "und {count} weitere",
|
||||
"no_subject": "(kein Betreff)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+587
-14
File diff suppressed because it is too large
Load Diff
+780
-35
File diff suppressed because it is too large
Load Diff
+1844
File diff suppressed because it is too large
Load Diff
+768
-35
File diff suppressed because it is too large
Load Diff
+778
-35
File diff suppressed because it is too large
Load Diff
+772
-35
File diff suppressed because it is too large
Load Diff
+770
-35
File diff suppressed because it is too large
Load Diff
+776
-35
File diff suppressed because it is too large
Load Diff
+774
-35
File diff suppressed because it is too large
Load Diff
+772
-35
File diff suppressed because it is too large
Load Diff
+773
-35
File diff suppressed because it is too large
Load Diff
+769
-35
File diff suppressed because it is too large
Load Diff
+771
-35
File diff suppressed because it is too large
Load Diff
+774
-35
File diff suppressed because it is too large
Load Diff
+774
-35
File diff suppressed because it is too large
Load Diff
+777
-35
File diff suppressed because it is too large
Load Diff
+774
-35
File diff suppressed because it is too large
Load Diff
+777
-35
File diff suppressed because it is too large
Load Diff
+773
-35
File diff suppressed because it is too large
Load Diff
+1844
File diff suppressed because it is too large
Load Diff
+775
-35
File diff suppressed because it is too large
Load Diff
+776
-35
File diff suppressed because it is too large
Load Diff
+773
-35
File diff suppressed because it is too large
Load Diff
+773
-35
File diff suppressed because it is too large
Load Diff
+1750
-208
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user