Compare commits
1222 Commits
fdroid-v0.
...
auth-v4.0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd9bcf607c | ||
|
|
30e26a815f | ||
|
|
d873921422 | ||
|
|
c160afc6de | ||
|
|
f971b968af | ||
|
|
c8468efd20 | ||
|
|
9515cf70f5 | ||
|
|
963650db4b | ||
|
|
a2841a8af2 | ||
|
|
6a6db0813f | ||
|
|
66fad15743 | ||
|
|
eb4d77bd24 | ||
|
|
e2c0aed2e4 | ||
|
|
5a5e046192 | ||
|
|
42a6e3ac25 | ||
|
|
7fdb8a6dae | ||
|
|
ef63d4c7a0 | ||
|
|
60b9d1e43f | ||
|
|
c453c7dc81 | ||
|
|
5c41e8ad3c | ||
|
|
53c706fba7 | ||
|
|
27a34a08f4 | ||
|
|
6bea1fa0f5 | ||
|
|
a97ca411d7 | ||
|
|
636cd1395c | ||
|
|
6e23e5e453 | ||
|
|
77f5d21dad | ||
|
|
5d210ab740 | ||
|
|
8d14997f36 | ||
|
|
b6059273fb | ||
|
|
1dde716d26 | ||
|
|
8629212584 | ||
|
|
08cf14a72b | ||
|
|
fe5da2ee8b | ||
|
|
f255ded0b6 | ||
|
|
59bf51346c | ||
|
|
3288f3250b | ||
|
|
1eb5eaece9 | ||
|
|
08f84c9cf8 | ||
|
|
6d969ab72a | ||
|
|
da8326229c | ||
|
|
05763a5d83 | ||
|
|
d7e2330f20 | ||
|
|
99ba5a31d3 | ||
|
|
2aaa23312b | ||
|
|
cc262aad0c | ||
|
|
72c93a1703 | ||
|
|
2f27ae7b19 | ||
|
|
039256cb05 | ||
|
|
167c4efc40 | ||
|
|
5b73eee14c | ||
|
|
eafc8fc4cb | ||
|
|
370d4af008 | ||
|
|
4bb6aa2b39 | ||
|
|
01f31c352b | ||
|
|
50c60dff1c | ||
|
|
7bdbaec443 | ||
|
|
57d245d9e0 | ||
|
|
8a953cab88 | ||
|
|
2827a166dc | ||
|
|
4e04739d54 | ||
|
|
7a60b1e15e | ||
|
|
e2e374fbf4 | ||
|
|
71adb1e366 | ||
|
|
393878a52e | ||
|
|
7644900bd8 | ||
|
|
e70f9b5ccd | ||
|
|
a37ff3cf57 | ||
|
|
9235e41855 | ||
|
|
71369bf5c9 | ||
|
|
924f5ce19b | ||
|
|
4d4b3f8bef | ||
|
|
27a0d7707e | ||
|
|
57ea097a5d | ||
|
|
2c0f2d43e7 | ||
|
|
528f503444 | ||
|
|
fa9d171287 | ||
|
|
43ba2ee2da | ||
|
|
3292655ff7 | ||
|
|
968067d6aa | ||
|
|
de6a494da7 | ||
|
|
e13f2a379a | ||
|
|
4c0c05a54a | ||
|
|
ac04ceadce | ||
|
|
91127b6ce5 | ||
|
|
f95cc1f135 | ||
|
|
64d7959c95 | ||
|
|
9a444b4881 | ||
|
|
795187177d | ||
|
|
54c5c2ce7e | ||
|
|
1a966fdedd | ||
|
|
6f9cd84b6d | ||
|
|
4506e5b6d9 | ||
|
|
1c74eae132 | ||
|
|
262ff01999 | ||
|
|
1daa0f3e07 | ||
|
|
c4931de42e | ||
|
|
77cdf673a9 | ||
|
|
7ff5f74fa6 | ||
|
|
57a425e14c | ||
|
|
642c85fe59 | ||
|
|
0e046b9c8a | ||
|
|
be222f53bf | ||
|
|
0498c70bad | ||
|
|
08fcc02282 | ||
|
|
aef32027a1 | ||
|
|
f9cbce66c0 | ||
|
|
39d39eb195 | ||
|
|
b052026526 | ||
|
|
8a79ae9b96 | ||
|
|
dbd160c135 | ||
|
|
7634ba0dea | ||
|
|
840ba2803e | ||
|
|
3093894b12 | ||
|
|
41d5960d1f | ||
|
|
fb63b1d832 | ||
|
|
68c93537d3 | ||
|
|
0f73a68681 | ||
|
|
306bc56c21 | ||
|
|
feec9a475b | ||
|
|
8314cda12a | ||
|
|
4cc8be748c | ||
|
|
5a33820877 | ||
|
|
830f1b9c18 | ||
|
|
c81cf5a432 | ||
|
|
f71c3f4171 | ||
|
|
5b99902d68 | ||
|
|
2569337be8 | ||
|
|
87f7d3a484 | ||
|
|
8fa6adb16a | ||
|
|
00c9572b43 | ||
|
|
9424b7a65e | ||
|
|
af680b6da3 | ||
|
|
3826c1f957 | ||
|
|
3b6bee6042 | ||
|
|
2163a5fbea | ||
|
|
8e485bfe39 | ||
|
|
c361fcbff4 | ||
|
|
ba063bf4a7 | ||
|
|
82c0bbb879 | ||
|
|
711f31991d | ||
|
|
9c0d9ac538 | ||
|
|
2521cd6d31 | ||
|
|
b5db5e2b83 | ||
|
|
7d52b60cd9 | ||
|
|
08765ccd39 | ||
|
|
3dcf5fa860 | ||
|
|
cda925fc80 | ||
|
|
7b2206161e | ||
|
|
bbd7be4423 | ||
|
|
c8ab6be9f8 | ||
|
|
5609309660 | ||
|
|
fa19254bbc | ||
|
|
70476b2011 | ||
|
|
7dd9d8aef3 | ||
|
|
13727b9a96 | ||
|
|
6decb15be7 | ||
|
|
a93c43d341 | ||
|
|
40b1cdcabb | ||
|
|
f5ee46189c | ||
|
|
48cab57d53 | ||
|
|
7c867f94bf | ||
|
|
3465253fcc | ||
|
|
61b324ca05 | ||
|
|
cb57351951 | ||
|
|
3f4250dab3 | ||
|
|
1efbbf2b2f | ||
|
|
1ff21b3d8e | ||
|
|
31ffc5bee5 | ||
|
|
6cd43707a8 | ||
|
|
3324019b38 | ||
|
|
cc02236ca9 | ||
|
|
ece4980d94 | ||
|
|
47b6e758d2 | ||
|
|
53c19bc64e | ||
|
|
bd232c151a | ||
|
|
8970074f80 | ||
|
|
27d901bc60 | ||
|
|
e87a6a5106 | ||
|
|
7f132b1827 | ||
|
|
b763cab1ba | ||
|
|
e1a7d61d49 | ||
|
|
0a4d415950 | ||
|
|
c9af246a3a | ||
|
|
718c2544c8 | ||
|
|
2c4cbed467 | ||
|
|
cd917f58ca | ||
|
|
15a7e0b805 | ||
|
|
f702a93031 | ||
|
|
432ef74101 | ||
|
|
dd5dae2833 | ||
|
|
2c0739e1d1 | ||
|
|
3449021272 | ||
|
|
c01a439b81 | ||
|
|
1d5cca6ee1 | ||
|
|
55022ec1ee | ||
|
|
821787f81d | ||
|
|
bc7704916a | ||
|
|
7ed620d817 | ||
|
|
1bfa7b1998 | ||
|
|
484b05bdb2 | ||
|
|
0cff1ef315 | ||
|
|
fe2d69aad3 | ||
|
|
e55c6fef4d | ||
|
|
e53312304a | ||
|
|
f6f81c4eeb | ||
|
|
9574704154 | ||
|
|
bad6fd9fae | ||
|
|
06bd58edce | ||
|
|
459b49a121 | ||
|
|
6d0f44b1da | ||
|
|
5076cf7647 | ||
|
|
c656a1c6a4 | ||
|
|
14a3f426ce | ||
|
|
cfe83a40e5 | ||
|
|
c90feccfa4 | ||
|
|
06a55bc11a | ||
|
|
a0df76c2c3 | ||
|
|
aa8d910a0b | ||
|
|
e10613fa0f | ||
|
|
f45b48a347 | ||
|
|
75362199e5 | ||
|
|
4d56b4b406 | ||
|
|
f07f3b320e | ||
|
|
006eed1cd8 | ||
|
|
3e5a9271d7 | ||
|
|
9dd18f0137 | ||
|
|
002a652cfd | ||
|
|
40f34417d9 | ||
|
|
ffa80c9db2 | ||
|
|
62dadd7e75 | ||
|
|
d2fe71f478 | ||
|
|
f856db2cb7 | ||
|
|
25386d194b | ||
|
|
35f7ce07f1 | ||
|
|
8c722c39ec | ||
|
|
c71c3e7ef8 | ||
|
|
d3ab7f530c | ||
|
|
be2b01f722 | ||
|
|
9ac05bc873 | ||
|
|
be7bb8e4b6 | ||
|
|
be4536ac30 | ||
|
|
47e2d2f915 | ||
|
|
b47c027cbd | ||
|
|
4e6dd14c71 | ||
|
|
34edc9b2e8 | ||
|
|
51f64799aa | ||
|
|
4791b10d91 | ||
|
|
c595c88e82 | ||
|
|
6657695858 | ||
|
|
bc5be62de4 | ||
|
|
78fd9cc6e6 | ||
|
|
29aa608399 | ||
|
|
310e319057 | ||
|
|
bef3d0949c | ||
|
|
e45baf6238 | ||
|
|
454f93fadb | ||
|
|
76a6b7402c | ||
|
|
9d4c5b29b3 | ||
|
|
ae50c83960 | ||
|
|
c6fdd6352e | ||
|
|
bed57c083c | ||
|
|
cda5caec3f | ||
|
|
01d48b0fcc | ||
|
|
4780f545fe | ||
|
|
80ed1e47b0 | ||
|
|
5c7f775f14 | ||
|
|
7ff5d40152 | ||
|
|
b4dbd942ab | ||
|
|
627a53428a | ||
|
|
35581099fc | ||
|
|
e62dfe0a07 | ||
|
|
21ae075674 | ||
|
|
2660cee263 | ||
|
|
2ae8a11138 | ||
|
|
70c62a62a6 | ||
|
|
a43599aad2 | ||
|
|
5bcce4d610 | ||
|
|
d61d9b95ad | ||
|
|
682d97f15a | ||
|
|
46b210c600 | ||
|
|
4f5f90259d | ||
|
|
d8d5e62888 | ||
|
|
2cef85dfd9 | ||
|
|
31ef1e4e29 | ||
|
|
952e9254b4 | ||
|
|
8e7ec58f45 | ||
|
|
7422568093 | ||
|
|
39b09abc50 | ||
|
|
8b3315e85f | ||
|
|
19273927d1 | ||
|
|
374625f5b3 | ||
|
|
2fae026a77 | ||
|
|
01488be836 | ||
|
|
953c915508 | ||
|
|
9a0ea3ac43 | ||
|
|
42d36b35d2 | ||
|
|
630b6d4101 | ||
|
|
72ba8bcd64 | ||
|
|
93f753fdff | ||
|
|
27defa92c2 | ||
|
|
6ba9ed9f8d | ||
|
|
6c5c3131f5 | ||
|
|
bb23c750a5 | ||
|
|
c036c8c7ba | ||
|
|
6464bf172f | ||
|
|
46e6af660e | ||
|
|
b18d987ba2 | ||
|
|
6b12f0a595 | ||
|
|
40b2b725b4 | ||
|
|
00346db9f9 | ||
|
|
78826d7782 | ||
|
|
31f591c28f | ||
|
|
0b0d8bd026 | ||
|
|
76e4d535b4 | ||
|
|
7f00b2619e | ||
|
|
6a592af94a | ||
|
|
bea6e8b473 | ||
|
|
7cfdd6ec55 | ||
|
|
b97cf93c12 | ||
|
|
2865113c4d | ||
|
|
ee1dbd7e84 | ||
|
|
5a21c932df | ||
|
|
c4021a82f5 | ||
|
|
6e209a68e2 | ||
|
|
090e2f235b | ||
|
|
a536d6af63 | ||
|
|
53cf029f00 | ||
|
|
48d6c2d008 | ||
|
|
28095ca935 | ||
|
|
d31d7592cb | ||
|
|
7645ec0e24 | ||
|
|
5c0fe9c411 | ||
|
|
80416d5b90 | ||
|
|
9ce432681e | ||
|
|
e91cddbc25 | ||
|
|
a37dcceb41 | ||
|
|
aa31e4354c | ||
|
|
a92115ffb1 | ||
|
|
7f0d028b55 | ||
|
|
628edf713b | ||
|
|
4f74470abe | ||
|
|
b5ad13ee69 | ||
|
|
4383512540 | ||
|
|
59a68b56bc | ||
|
|
09c4b19449 | ||
|
|
d4cd71b56c | ||
|
|
a01ea511e9 | ||
|
|
ab6f514d23 | ||
|
|
b4d8dea2ef | ||
|
|
2abd0b0588 | ||
|
|
dae0492800 | ||
|
|
94a8ff2c6f | ||
|
|
aae00dcc15 | ||
|
|
18c7d59f90 | ||
|
|
fcf87d237b | ||
|
|
f88022730a | ||
|
|
753ed30d5c | ||
|
|
216be38915 | ||
|
|
164ace9f8c | ||
|
|
a97bb195b6 | ||
|
|
9ecb7c4044 | ||
|
|
1895e90b3e | ||
|
|
f118a9d2f2 | ||
|
|
bb5261f73b | ||
|
|
225dade722 | ||
|
|
c39a3c789d | ||
|
|
28580cf107 | ||
|
|
6799ee3832 | ||
|
|
d86df11f15 | ||
|
|
7b04bd548a | ||
|
|
ffaf4659ce | ||
|
|
8fc38244f3 | ||
|
|
415cf451a8 | ||
|
|
f4709d3442 | ||
|
|
0f6c8a6441 | ||
|
|
f337b1ff36 | ||
|
|
2799652d3a | ||
|
|
6b92acbb11 | ||
|
|
a343fe5427 | ||
|
|
d10e37454d | ||
|
|
1cb80e619b | ||
|
|
d307b4bf07 | ||
|
|
5967f2a66a | ||
|
|
6e6248a3c2 | ||
|
|
aff37dc5df | ||
|
|
b2b4a703ac | ||
|
|
46f9a16db9 | ||
|
|
d232c94547 | ||
|
|
dc6217eb7a | ||
|
|
98123438fa | ||
|
|
4c51c960f2 | ||
|
|
a89d7e472f | ||
|
|
ef1b192ced | ||
|
|
19d82d332c | ||
|
|
0d97355838 | ||
|
|
04b296587a | ||
|
|
3193778c98 | ||
|
|
4a7ec88424 | ||
|
|
201bda3999 | ||
|
|
50ab94a355 | ||
|
|
790f34662a | ||
|
|
d0138310dc | ||
|
|
cb66494924 | ||
|
|
a322a28e44 | ||
|
|
a95a21790b | ||
|
|
e8d0673a9a | ||
|
|
f8f4db409b | ||
|
|
f4e260cfb3 | ||
|
|
e605169ac1 | ||
|
|
811fe93dcc | ||
|
|
6d7a6b86f7 | ||
|
|
5760b6a56b | ||
|
|
6b04ef69ed | ||
|
|
2f0d06cad7 | ||
|
|
08ee4e2861 | ||
|
|
282b440d0f | ||
|
|
c9e29dbcbe | ||
|
|
ca62fb5105 | ||
|
|
58ca0a5cd4 | ||
|
|
3ef7185166 | ||
|
|
b92e9f4c6e | ||
|
|
2f2eb4e265 | ||
|
|
17aa385782 | ||
|
|
0ac6ea9af8 | ||
|
|
fce4c9869e | ||
|
|
1eca25b050 | ||
|
|
47a0ee749e | ||
|
|
1c86a69cd6 | ||
|
|
ce8310c874 | ||
|
|
e607b4e3ed | ||
|
|
498a4dcd4e | ||
|
|
624726734f | ||
|
|
4b4350c107 | ||
|
|
ecb785d75c | ||
|
|
2c1637c55c | ||
|
|
31f210da31 | ||
|
|
777f298ca3 | ||
|
|
c5c86c484e | ||
|
|
e9b3e76b50 | ||
|
|
15e31f1763 | ||
|
|
fffebec025 | ||
|
|
35957b1f40 | ||
|
|
6ee7500011 | ||
|
|
803195afdd | ||
|
|
2c23c3463e | ||
|
|
39c5a3f1bd | ||
|
|
c625593162 | ||
|
|
5864648c24 | ||
|
|
b605e41f9e | ||
|
|
ffc082eec4 | ||
|
|
692e12979b | ||
|
|
9ff6a79ada | ||
|
|
530ae6c0be | ||
|
|
1aab753046 | ||
|
|
3e131464e6 | ||
|
|
1aa940d410 | ||
|
|
33bc19978c | ||
|
|
4670c1d712 | ||
|
|
ff32e6852e | ||
|
|
b39e727e0a | ||
|
|
2a1931157c | ||
|
|
15370fa731 | ||
|
|
b65a7055c1 | ||
|
|
1a336769b9 | ||
|
|
aa6eebca27 | ||
|
|
c41ad8b1a9 | ||
|
|
9ca651f4b7 | ||
|
|
255302e3cd | ||
|
|
a4fa8e0deb | ||
|
|
ffa1d90ed8 | ||
|
|
ef6734195f | ||
|
|
50a0dc754b | ||
|
|
19eb1bdb22 | ||
|
|
f11493842e | ||
|
|
b7a8e33665 | ||
|
|
abce21d819 | ||
|
|
2db43536f7 | ||
|
|
ee685bcc5d | ||
|
|
6a489ad520 | ||
|
|
3c61b49548 | ||
|
|
6a24528ed0 | ||
|
|
ebc40d1b65 | ||
|
|
405c0c343f | ||
|
|
f8b77f71b5 | ||
|
|
ed907c71f8 | ||
|
|
94513e8c8e | ||
|
|
f3e98cff34 | ||
|
|
49e7c4baaf | ||
|
|
29a72ac4a1 | ||
|
|
2722b50cc0 | ||
|
|
36079aa2dc | ||
|
|
891af00454 | ||
|
|
783c0c48ef | ||
|
|
ed1c9df007 | ||
|
|
f64c0dcc86 | ||
|
|
9d1332bff1 | ||
|
|
000fe87ebb | ||
|
|
6344a3c640 | ||
|
|
18a0b18a13 | ||
|
|
d4cdfc8834 | ||
|
|
aa2b81ad7e | ||
|
|
df17b11573 | ||
|
|
8ca3b80e94 | ||
|
|
345cc2f34f | ||
|
|
c4f70c370e | ||
|
|
e8b692b5ad | ||
|
|
7b552a1ee3 | ||
|
|
5d6ac29d71 | ||
|
|
8a031360c5 | ||
|
|
c9fd0183e7 | ||
|
|
6753f1e9f7 | ||
|
|
806098961b | ||
|
|
21e45e8138 | ||
|
|
cdbcc93571 | ||
|
|
1de1273391 | ||
|
|
8ea7481a98 | ||
|
|
12da709445 | ||
|
|
5c601ab2cc | ||
|
|
87bdab027e | ||
|
|
50f4878d0f | ||
|
|
523336d644 | ||
|
|
4b7104bf4e | ||
|
|
6a8ca4c2cf | ||
|
|
2e6c7d29e4 | ||
|
|
b7f86b3e89 | ||
|
|
384b4d2c35 | ||
|
|
e6d7d2298c | ||
|
|
6139ed45cd | ||
|
|
6662f51a5f | ||
|
|
1108fa9f79 | ||
|
|
2b02ea7409 | ||
|
|
cdca58eb3c | ||
|
|
0381dee786 | ||
|
|
1c727131ad | ||
|
|
944070eb23 | ||
|
|
f2b86ff1e1 | ||
|
|
a14160f799 | ||
|
|
dcca546e5a | ||
|
|
bb0bdf113e | ||
|
|
a323c7b31b | ||
|
|
2d46b70d8f | ||
|
|
e695f2eccb | ||
|
|
1942935c3c | ||
|
|
cef85ddd9f | ||
|
|
341ef58970 | ||
|
|
983cfe4482 | ||
|
|
2ae23dfa3d | ||
|
|
b269fddac2 | ||
|
|
ca5be3518b | ||
|
|
b85a90e5dd | ||
|
|
a4c47ffbd4 | ||
|
|
4ee9815971 | ||
|
|
5f873a0f7b | ||
|
|
d02da225f8 | ||
|
|
a8c7dd52ba | ||
|
|
84900159ae | ||
|
|
6ed0ad806e | ||
|
|
c1b6458e2e | ||
|
|
a7cc96d994 | ||
|
|
2a483edbe4 | ||
|
|
428d786f10 | ||
|
|
7d66b4c29f | ||
|
|
fa7c417e69 | ||
|
|
affb9bb783 | ||
|
|
8d737f79b5 | ||
|
|
41b20a6b48 | ||
|
|
fcd1554f7c | ||
|
|
fe37bb5544 | ||
|
|
6493e54db9 | ||
|
|
d23cab124e | ||
|
|
ee0ebe8602 | ||
|
|
00415cb6fe | ||
|
|
6af1604026 | ||
|
|
53b7ea6203 | ||
|
|
2e01a96477 | ||
|
|
fa8a8dd639 | ||
|
|
4a03388cf1 | ||
|
|
a9da87881d | ||
|
|
89815d47ff | ||
|
|
2cc1573b1b | ||
|
|
160565f216 | ||
|
|
6f84371cfa | ||
|
|
7c07c8dadb | ||
|
|
27c191eaf1 | ||
|
|
6872587083 | ||
|
|
bc7f4fd9fc | ||
|
|
7bf37c2800 | ||
|
|
215126f695 | ||
|
|
0aad23d860 | ||
|
|
34762cc303 | ||
|
|
b254bb5b0c | ||
|
|
c80a066518 | ||
|
|
00ffe8f129 | ||
|
|
70ee3e3fbe | ||
|
|
f37f807045 | ||
|
|
6254fe032b | ||
|
|
63138a539a | ||
|
|
37765169a4 | ||
|
|
b7a45b4327 | ||
|
|
1521971a5d | ||
|
|
d62ca44675 | ||
|
|
02c428971c | ||
|
|
beabb4815b | ||
|
|
87f9537bb2 | ||
|
|
69a5795c86 | ||
|
|
212ab374ac | ||
|
|
dc23c7eebd | ||
|
|
31459d9339 | ||
|
|
5cc4a5ed07 | ||
|
|
cda7dda3cb | ||
|
|
6b831378ba | ||
|
|
49b34bdb19 | ||
|
|
bf17902e73 | ||
|
|
3fa85b4ce9 | ||
|
|
8b4d38a50a | ||
|
|
57e2ff4908 | ||
|
|
f890f612f4 | ||
|
|
038a40b320 | ||
|
|
5fa05e3406 | ||
|
|
3577b1f213 | ||
|
|
9df36a50bf | ||
|
|
7e16f4f5be | ||
|
|
30b508fa56 | ||
|
|
d308eb9274 | ||
|
|
b4c6805d42 | ||
|
|
9c1f744f73 | ||
|
|
5ce1a91146 | ||
|
|
41512540e4 | ||
|
|
2c4dd13ab0 | ||
|
|
637974f63b | ||
|
|
70fdfb6901 | ||
|
|
4e7d9ed7d9 | ||
|
|
5da924f7e4 | ||
|
|
2988767f97 | ||
|
|
232d02a990 | ||
|
|
df13dd2bfc | ||
|
|
18a7fce523 | ||
|
|
c03b3fd203 | ||
|
|
42ce7a0d39 | ||
|
|
bcd1fd0cc8 | ||
|
|
e799843c59 | ||
|
|
fff633aebe | ||
|
|
63144d50fc | ||
|
|
0431493736 | ||
|
|
7d647cf4f1 | ||
|
|
e66c4390bb | ||
|
|
df44e6d1a9 | ||
|
|
fd29a982cc | ||
|
|
6720f16ceb | ||
|
|
8685bae282 | ||
|
|
72c1d83714 | ||
|
|
bf65faef33 | ||
|
|
29503a077d | ||
|
|
f2f332062f | ||
|
|
e79e932014 | ||
|
|
f5dba2f36e | ||
|
|
9f92787ac6 | ||
|
|
66c3870518 | ||
|
|
98e0aeee8a | ||
|
|
785cac2c70 | ||
|
|
258c47eba4 | ||
|
|
466ec23248 | ||
|
|
3c7b8fd0e8 | ||
|
|
6a1021c353 | ||
|
|
6e07aacc3f | ||
|
|
454f498a7c | ||
|
|
9169b344fb | ||
|
|
13e6c982a6 | ||
|
|
3d096311f4 | ||
|
|
a97dddf8e5 | ||
|
|
23574a2e3a | ||
|
|
ce5354e19d | ||
|
|
d11aeee29c | ||
|
|
776881d75f | ||
|
|
b9eef77d3a | ||
|
|
93ab5f4a84 | ||
|
|
50e8757c8f | ||
|
|
a5f543151b | ||
|
|
2d0ceebb43 | ||
|
|
17991bb6fc | ||
|
|
64090ad9ee | ||
|
|
9e26397c1b | ||
|
|
1e8b184ed0 | ||
|
|
aed48026c3 | ||
|
|
7911c14e40 | ||
|
|
c315bc9da1 | ||
|
|
6fe4c0147a | ||
|
|
5e7c5e0d6f | ||
|
|
802ba2bc5b | ||
|
|
fb4f5c2095 | ||
|
|
9ce8470af5 | ||
|
|
e0fe62e717 | ||
|
|
8056720e23 | ||
|
|
e6d4e835db | ||
|
|
f4995cba56 | ||
|
|
73a323d927 | ||
|
|
8ed58127e1 | ||
|
|
9c975eae69 | ||
|
|
139e47867d | ||
|
|
6008243c86 | ||
|
|
04afcbe678 | ||
|
|
6c603cb012 | ||
|
|
daa9e01729 | ||
|
|
a6ab51727c | ||
|
|
6d1ab3f1ca | ||
|
|
660b5160f2 | ||
|
|
964075a32c | ||
|
|
d25420c867 | ||
|
|
a9c646ca77 | ||
|
|
10d6e18332 | ||
|
|
0feb5519ba | ||
|
|
30a2752f9d | ||
|
|
608234d4ed | ||
|
|
2a8eb2aaf7 | ||
|
|
304a7df033 | ||
|
|
52fe082bae | ||
|
|
3a45640a17 | ||
|
|
5c1b3886ed | ||
|
|
3a10c853d0 | ||
|
|
4f00a87e29 | ||
|
|
2db5ca81f5 | ||
|
|
3352e2a075 | ||
|
|
266d75a574 | ||
|
|
217d4d950e | ||
|
|
c42ebf3377 | ||
|
|
d3b30052e2 | ||
|
|
490996193a | ||
|
|
02d69b3b1c | ||
|
|
0ea77ee1f4 | ||
|
|
0379d14640 | ||
|
|
6d316d25d3 | ||
|
|
6ba88d5d19 | ||
|
|
34a8dc21c6 | ||
|
|
864d150552 | ||
|
|
fbef356ab5 | ||
|
|
0947b2bbaa | ||
|
|
b74de7f424 | ||
|
|
651899cd23 | ||
|
|
534250320c | ||
|
|
deef4886eb | ||
|
|
58b82b46f3 | ||
|
|
82712e9313 | ||
|
|
accf563519 | ||
|
|
e8acaf7457 | ||
|
|
b5f8964dc4 | ||
|
|
0275d08e27 | ||
|
|
75c3bc1c84 | ||
|
|
08a77a2def | ||
|
|
74dc15c38c | ||
|
|
baeb47f94b | ||
|
|
ef5f58e5a2 | ||
|
|
d77f4af04b | ||
|
|
0c8f05076a | ||
|
|
023a13927c | ||
|
|
c8c3d8f814 | ||
|
|
9400f2e134 | ||
|
|
8cd43e9e4b | ||
|
|
44d66da742 | ||
|
|
bc0d6adfd1 | ||
|
|
cc80453b12 | ||
|
|
816d74a5e6 | ||
|
|
710bb61f21 | ||
|
|
d649cbd9fa | ||
|
|
85fe4b317d | ||
|
|
623f2c1985 | ||
|
|
5bcb8fe2ea | ||
|
|
b8f2b850c3 | ||
|
|
d976986473 | ||
|
|
e6ad4db6d6 | ||
|
|
897dd78ffd | ||
|
|
016761be9a | ||
|
|
2990ba855f | ||
|
|
09f65eeff7 | ||
|
|
bd18dc7a62 | ||
|
|
9ccb597e6e | ||
|
|
38b3e04718 | ||
|
|
af9e865745 | ||
|
|
c47fcba5cc | ||
|
|
02501caa71 | ||
|
|
823eb068f0 | ||
|
|
954afd6409 | ||
|
|
9933e18ba5 | ||
|
|
1ff0ab1adf | ||
|
|
3bb9790229 | ||
|
|
0ad84be3ab | ||
|
|
35916af7bf | ||
|
|
72648286f2 | ||
|
|
42611085f4 | ||
|
|
f7e37c6b2c | ||
|
|
f44f21c5ad | ||
|
|
05200878f2 | ||
|
|
583163968d | ||
|
|
53c553db02 | ||
|
|
d65597c44f | ||
|
|
117c884b3e | ||
|
|
32315b1149 | ||
|
|
a9537e59cf | ||
|
|
2cd2aee11c | ||
|
|
5aa9671037 | ||
|
|
859cfc46d3 | ||
|
|
22927fa285 | ||
|
|
ae52adc0f5 | ||
|
|
dd1321a555 | ||
|
|
060a055d38 | ||
|
|
bb92766d5e | ||
|
|
aedb689e45 | ||
|
|
877c0a7c73 | ||
|
|
74f6e52c74 | ||
|
|
5bc5823ef2 | ||
|
|
45b0dd4887 | ||
|
|
18f622d007 | ||
|
|
ae7134a80f | ||
|
|
d06f7a869e | ||
|
|
be9c686d71 | ||
|
|
fcd4f36036 | ||
|
|
fbd8346edf | ||
|
|
4c5b59b453 | ||
|
|
03b6ed6f1a | ||
|
|
b5eaa757da | ||
|
|
ea46ac0196 | ||
|
|
88a0a2f9fc | ||
|
|
eb2d1f04c4 | ||
|
|
feb0dde706 | ||
|
|
973eac2b34 | ||
|
|
a16830f5ca | ||
|
|
315529eebf | ||
|
|
d3f72a036f | ||
|
|
e308a63f2b | ||
|
|
b0bcb4550e | ||
|
|
8b43f18f6a | ||
|
|
66bc855e4a | ||
|
|
6854d2885a | ||
|
|
21ff032cad | ||
|
|
c1dbc82338 | ||
|
|
4c7d3a37b4 | ||
|
|
658768f853 | ||
|
|
29a88e673f | ||
|
|
f2f6160aff | ||
|
|
27091f568f | ||
|
|
30f1b4e006 | ||
|
|
168d7a1652 | ||
|
|
bd0c35f64f | ||
|
|
f4c7e60b5a | ||
|
|
734de8129a | ||
|
|
1fd9ea7a27 | ||
|
|
c2ea68c39c | ||
|
|
e0db188195 | ||
|
|
1317791fed | ||
|
|
044e1b687f | ||
|
|
75b06a1cab | ||
|
|
57cf26c01c | ||
|
|
0ae32a3882 | ||
|
|
2f9bbbc84c | ||
|
|
5b2be09a6a | ||
|
|
bf001794b4 | ||
|
|
d5027c4c65 | ||
|
|
db13764b04 | ||
|
|
37022f7d7d | ||
|
|
ace2e5bb27 | ||
|
|
c809d572f7 | ||
|
|
a4a2623074 | ||
|
|
87e31965e5 | ||
|
|
012a796894 | ||
|
|
cc3caf2e5d | ||
|
|
1d239d409f | ||
|
|
71d77a62fd | ||
|
|
79f75ee861 | ||
|
|
f3ea825429 | ||
|
|
0d991a1420 | ||
|
|
985ebc2f77 | ||
|
|
17c17dd901 | ||
|
|
0004650b2f | ||
|
|
f3a3c68488 | ||
|
|
83ffce2a24 | ||
|
|
9fa1e77947 | ||
|
|
ac10329597 | ||
|
|
7b890e21d5 | ||
|
|
509f10f765 | ||
|
|
dd9cd53176 | ||
|
|
5e18c87bee | ||
|
|
2e0a771068 | ||
|
|
558b9e0b70 | ||
|
|
79cd1dd0d1 | ||
|
|
0d079fc593 | ||
|
|
30901462bc | ||
|
|
403801aac8 | ||
|
|
1dcfcb6250 | ||
|
|
b279ed4489 | ||
|
|
3cc1f3e9d7 | ||
|
|
23d9ed65ec | ||
|
|
a496debb5f | ||
|
|
d199260a63 | ||
|
|
353646e4bf | ||
|
|
b46c2c13cc | ||
|
|
a8caeb38ba | ||
|
|
6592ab6188 | ||
|
|
6dc552f015 | ||
|
|
8365ca000c | ||
|
|
dbb596b3c2 | ||
|
|
a947cc69de | ||
|
|
a3c0e46f1d | ||
|
|
7794be5993 | ||
|
|
a6e5f01ab9 | ||
|
|
c6ba5e4f70 | ||
|
|
093c95263f | ||
|
|
a4d068468d | ||
|
|
066d4c201d | ||
|
|
ae5ebf4bf9 | ||
|
|
c9a96df720 | ||
|
|
537f376ebd | ||
|
|
58f1cec916 | ||
|
|
4c3007ccc8 | ||
|
|
73cae89fc5 | ||
|
|
c3cb706f51 | ||
|
|
a703847130 | ||
|
|
4299ebcf1b | ||
|
|
a15d5e7d1e | ||
|
|
849d2c3d3e | ||
|
|
3e136baee2 | ||
|
|
f11c455b6b | ||
|
|
b0f7ce3f61 | ||
|
|
9c18bbd46e | ||
|
|
9c81d59833 | ||
|
|
51ee4a128a | ||
|
|
e5411cb1da | ||
|
|
289d403e0c | ||
|
|
0fe35ac161 | ||
|
|
ff1fa3d391 | ||
|
|
87f74b5ade | ||
|
|
4a21d0bfac | ||
|
|
b2e9b242ad | ||
|
|
acdd38743c | ||
|
|
c00c10301d | ||
|
|
d05438e034 | ||
|
|
d3cf65f1c0 | ||
|
|
960104b042 | ||
|
|
d78acd4205 | ||
|
|
dd4dca4473 | ||
|
|
05c9f83399 | ||
|
|
821edd00f7 | ||
|
|
068cad2207 | ||
|
|
9e068bdc90 | ||
|
|
e70bf6a379 | ||
|
|
2a0c7d1e56 | ||
|
|
32a4204609 | ||
|
|
74b3b00ea5 | ||
|
|
01fba0b722 | ||
|
|
fa290752c9 | ||
|
|
4ed769271d | ||
|
|
7f1e7615cf | ||
|
|
2d4dc568fb | ||
|
|
02eefcd00f | ||
|
|
e742342dd8 | ||
|
|
b812070ae1 | ||
|
|
70dada90b2 | ||
|
|
b3d2e29e28 | ||
|
|
bb0f31f814 | ||
|
|
c0c477a3b7 | ||
|
|
a55c735d13 | ||
|
|
86bbfbe254 | ||
|
|
ba270000b2 | ||
|
|
5fceb9898f | ||
|
|
1cf28e0dd5 | ||
|
|
ffe290d56d | ||
|
|
7e8344cd4e | ||
|
|
3fc66ce202 | ||
|
|
167b9cfa42 | ||
|
|
00070d06b6 | ||
|
|
9520898032 | ||
|
|
71ee7ac019 | ||
|
|
e77d7a42f5 | ||
|
|
99291c2576 | ||
|
|
e9bd4a4dc5 | ||
|
|
1b95ce330a | ||
|
|
26641a4584 | ||
|
|
27140c04c9 | ||
|
|
0bf3c64ceb | ||
|
|
13565a0904 | ||
|
|
642b7ae5ba | ||
|
|
6776c49750 | ||
|
|
1189610b42 | ||
|
|
839cddfe7f | ||
|
|
86e2db388e | ||
|
|
608b078065 | ||
|
|
74d2c07ea0 | ||
|
|
a84cc07aa2 | ||
|
|
d2f2028f55 | ||
|
|
53e2aeff3f | ||
|
|
80aebe3869 | ||
|
|
7f0e2bcb9e | ||
|
|
cc2b0a610f | ||
|
|
d7a2883b5c | ||
|
|
6a76583e1b | ||
|
|
c161d6272a | ||
|
|
62cdfc9680 | ||
|
|
ff5826ec21 | ||
|
|
69ec80831c | ||
|
|
b80bb138e2 | ||
|
|
0fb114967a | ||
|
|
e9d55adfff | ||
|
|
ba1ef42355 | ||
|
|
c23bb13187 | ||
|
|
330ff5b9d0 | ||
|
|
132c399e32 | ||
|
|
c5c2a1bbf4 | ||
|
|
3ce5b43126 | ||
|
|
423c9c7b9c | ||
|
|
45b05fc823 | ||
|
|
808fc51966 | ||
|
|
bb4f415ae1 | ||
|
|
f6f7fb3b8f | ||
|
|
d22d97ed65 | ||
|
|
3a2881b350 | ||
|
|
0d59377284 | ||
|
|
b3016dffca | ||
|
|
32601826c1 | ||
|
|
f5aebd7044 | ||
|
|
457d0977b6 | ||
|
|
befa3f82ed | ||
|
|
56ca068a2c | ||
|
|
d09b447574 | ||
|
|
e5939e3ca5 | ||
|
|
2bd45883ce | ||
|
|
d3b47776c3 | ||
|
|
c1d9763945 | ||
|
|
83126c3ec4 | ||
|
|
c4f08715ed | ||
|
|
5ad7e2f128 | ||
|
|
0299024804 | ||
|
|
aaa4727bc0 | ||
|
|
f69760808a | ||
|
|
9598ad7a73 | ||
|
|
bf00d844dd | ||
|
|
87110408ef | ||
|
|
546e40c1ff | ||
|
|
8306b5d534 | ||
|
|
34897247dd | ||
|
|
9037973ced | ||
|
|
672865c062 | ||
|
|
96f10bebd4 | ||
|
|
8aed23ca93 | ||
|
|
19f18fe032 | ||
|
|
9bf4edecdb | ||
|
|
af6feaf735 | ||
|
|
289ddd673b | ||
|
|
0aeff4a8ae | ||
|
|
f15daa9a0f | ||
|
|
c7da14f52e | ||
|
|
23c27aa781 | ||
|
|
e5671a1fcc | ||
|
|
3b5c2b830e | ||
|
|
7fc7d79554 | ||
|
|
417ac32f54 | ||
|
|
713b01fc7d | ||
|
|
8ed80d3feb | ||
|
|
b59b07e93d | ||
|
|
1f2b09e704 | ||
|
|
6b3d39fbd7 | ||
|
|
98ed6c3fcb | ||
|
|
faa09134d5 | ||
|
|
b271e19dc8 | ||
|
|
762fa7150e | ||
|
|
db6f8dbff8 | ||
|
|
af25c83aa2 | ||
|
|
cca59a33b9 | ||
|
|
4246d2a91b | ||
|
|
b52476855f | ||
|
|
d5e7dbc762 | ||
|
|
af8756218e | ||
|
|
ce271d2288 | ||
|
|
60dd2dd800 | ||
|
|
52880e94a9 | ||
|
|
974cec75be | ||
|
|
3baa699d79 | ||
|
|
3423a7aa5d | ||
|
|
60f7282774 | ||
|
|
980a1f4c5a | ||
|
|
3cb0c1b325 | ||
|
|
752ae51f46 | ||
|
|
c4c9f71b01 | ||
|
|
7a85fa5c61 | ||
|
|
e9a6b55ba4 | ||
|
|
8e7a3a9347 | ||
|
|
d6344093b6 | ||
|
|
34211dafef | ||
|
|
e140b2f663 | ||
|
|
d2deea9502 | ||
|
|
31bfb53dfb | ||
|
|
b6ced23649 | ||
|
|
6af03fdfca | ||
|
|
88684c9725 | ||
|
|
9ab641c19a | ||
|
|
c8740e7331 | ||
|
|
6aa9bd7e8c | ||
|
|
d6bf9689ff | ||
|
|
1876fb7431 | ||
|
|
50a5394165 | ||
|
|
b54a572da6 | ||
|
|
1f934eaefa | ||
|
|
87d75fd718 | ||
|
|
d22e39b3f3 | ||
|
|
add5b856dc | ||
|
|
2f19a21b5f | ||
|
|
ee9db04c08 | ||
|
|
68c51c4549 | ||
|
|
762bf413e8 | ||
|
|
6159f5e4ee | ||
|
|
b70444acac | ||
|
|
dd53cf4e58 | ||
|
|
23eaab0757 | ||
|
|
c662023819 | ||
|
|
3d1c106759 | ||
|
|
90e2dca36b | ||
|
|
79b2933be7 | ||
|
|
eb16c925d2 | ||
|
|
1bef528fde | ||
|
|
bcf579e7d7 | ||
|
|
bf7cbe141d | ||
|
|
fce4295e2a | ||
|
|
fc95069421 | ||
|
|
77ac215b76 | ||
|
|
85397732c8 | ||
|
|
49a81c10db | ||
|
|
b742ffcafd | ||
|
|
55ece20d70 | ||
|
|
261a7f278b | ||
|
|
e6605d7ac9 | ||
|
|
20247493c8 | ||
|
|
cfea740511 | ||
|
|
9e48010ee6 | ||
|
|
ff4b388877 | ||
|
|
5195e2ac74 | ||
|
|
f958b16343 | ||
|
|
18e3adde11 | ||
|
|
9476d26972 | ||
|
|
8cfe36be68 | ||
|
|
6e8514e08c | ||
|
|
c67a6b0c9e | ||
|
|
13199bb3f7 | ||
|
|
5fd0b46756 | ||
|
|
c90315679f | ||
|
|
a9be915f87 | ||
|
|
57e7eb9e05 | ||
|
|
785e96036a | ||
|
|
98979a2271 | ||
|
|
eb91b6ea6d | ||
|
|
9af44e15b4 | ||
|
|
4edaebe054 | ||
|
|
fe92c1b30f | ||
|
|
e9d6f46997 | ||
|
|
9b743d7afe | ||
|
|
e1feb8bcd5 | ||
|
|
c94db7e9b8 | ||
|
|
dfc35d00da | ||
|
|
8b2322ef6c | ||
|
|
9bd29228b0 | ||
|
|
3437b41114 | ||
|
|
76a1423e72 | ||
|
|
b99145e863 | ||
|
|
e2ec5469a7 | ||
|
|
9920578ca2 | ||
|
|
be1ca57515 | ||
|
|
7c2ce513df | ||
|
|
1b6dbbc288 | ||
|
|
138515da02 | ||
|
|
5820d03bb5 | ||
|
|
4999e9d944 | ||
|
|
3ef528cdf6 | ||
|
|
1571b70716 | ||
|
|
16181dd1b9 | ||
|
|
f7d9a3cd17 | ||
|
|
1e6d91b50f | ||
|
|
73bdba5076 | ||
|
|
c67540b728 | ||
|
|
3df7ec4692 | ||
|
|
bd94e27af6 | ||
|
|
9a37542158 | ||
|
|
4fe7ec6257 | ||
|
|
bdacd1058e | ||
|
|
7fb31eee0a | ||
|
|
f1adcd4573 | ||
|
|
130b2757a9 | ||
|
|
c71b7de363 | ||
|
|
ea8c28a205 | ||
|
|
f448c66c17 | ||
|
|
3c850ef195 | ||
|
|
9afb59ea87 | ||
|
|
b3c6c0fc9b | ||
|
|
ae758a114a | ||
|
|
01e1af1fbd | ||
|
|
3759019cbc | ||
|
|
9cff56c1a4 | ||
|
|
511fb8de6a | ||
|
|
fd36ede27b | ||
|
|
a904263ed5 | ||
|
|
02aae58e71 | ||
|
|
7495b6c2c4 | ||
|
|
aa1e2188fb | ||
|
|
8c3c401efb | ||
|
|
5cd37a02c7 | ||
|
|
2bb1670428 | ||
|
|
485e8444fe | ||
|
|
5395ca5caf | ||
|
|
c416819f0a | ||
|
|
816b26475a | ||
|
|
46d7d4e587 | ||
|
|
263f94418d | ||
|
|
f990863bb2 | ||
|
|
d91cd53375 | ||
|
|
6a9fdd6c77 | ||
|
|
f1185fcade | ||
|
|
1fde4b9f16 | ||
|
|
db1eb19ef0 | ||
|
|
9b0d11963c | ||
|
|
0b5e057e4e | ||
|
|
d139d23c21 | ||
|
|
6185556b9b | ||
|
|
bbaabeef38 | ||
|
|
093b736a3b | ||
|
|
70cc6a35d7 | ||
|
|
5792c2bae6 |
56
.github/workflows/auth-internal-release.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: "Internal release (auth mobile)"
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allow manually running the action
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: auth
|
||||
|
||||
steps:
|
||||
- name: Checkout code and submodules
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup JDK 17
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 17
|
||||
|
||||
- name: Install Flutter ${{ env.FLUTTER_VERSION }}
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Setup keys
|
||||
uses: timheuer/base64-to-file@v1
|
||||
with:
|
||||
fileName: "keystore/ente_auth_key.jks"
|
||||
encodedString: ${{ secrets.SIGNING_KEY }}
|
||||
|
||||
- name: Build PlayStore AAB
|
||||
run: |
|
||||
flutter build appbundle --release --flavor playstore --dart-define=app.flavor=playstore
|
||||
env:
|
||||
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_auth_key.jks"
|
||||
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
|
||||
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
|
||||
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
||||
|
||||
- name: Upload AAB to PlayStore
|
||||
uses: r0adkll/upload-google-play@v1
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
|
||||
packageName: io.ente.auth
|
||||
releaseFiles: auth/build/app/outputs/bundle/playstoreRelease/app-playstore-release.aab
|
||||
track: internal
|
||||
2
.github/workflows/auth-lint.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
- ".github/workflows/auth-lint.yml"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.1"
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
||||
15
.github/workflows/auth-release.yml
vendored
@@ -29,7 +29,7 @@ on:
|
||||
- "auth-v*"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.1"
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
|
||||
jobs:
|
||||
build-ubuntu:
|
||||
@@ -45,6 +45,11 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup JDK 17
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 17
|
||||
|
||||
- name: Install Flutter ${{ env.FLUTTER_VERSION }}
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
@@ -91,7 +96,8 @@ jobs:
|
||||
- name: Build desktop app
|
||||
run: |
|
||||
flutter config --enable-linux-desktop
|
||||
dart pub global activate flutter_distributor
|
||||
# dart pub global activate flutter_distributor
|
||||
dart pub global activate --source git https://github.com/prateekmedia/flutter_distributor --git-ref develop --git-path packages/flutter_distributor
|
||||
flutter_distributor package --platform=linux --targets=deb --skip-clean
|
||||
mv dist/**/*-*-linux.deb artifacts/ente-${{ github.ref_name }}-x86_64.deb
|
||||
env:
|
||||
@@ -158,7 +164,7 @@ jobs:
|
||||
run: |
|
||||
flutter config --enable-linux-desktop
|
||||
# dart pub global activate flutter_distributor
|
||||
dart pub global activate --source git https://github.com/prateekmedia/flutter_distributor --git-ref pacman --git-path packages/flutter_distributor
|
||||
dart pub global activate --source git https://github.com/prateekmedia/flutter_distributor --git-ref develop --git-path packages/flutter_distributor
|
||||
# Run below command if it is a beta or nightly
|
||||
if [[ ${{ github.ref }} =~ beta|nightly ]]; then
|
||||
flutter_distributor package --platform=linux --targets=pacman --skip-clean
|
||||
@@ -206,7 +212,8 @@ jobs:
|
||||
- name: Build Windows installer
|
||||
run: |
|
||||
flutter config --enable-windows-desktop
|
||||
dart pub global activate flutter_distributor
|
||||
# dart pub global activate flutter_distributor
|
||||
dart pub global activate --source git https://github.com/prateekmedia/flutter_distributor --git-ref develop --git-path packages/flutter_distributor
|
||||
make innoinstall
|
||||
flutter_distributor package --platform=windows --targets=exe --skip-clean
|
||||
mv dist/**/ente_auth-*-windows-setup.exe artifacts/ente-${{ github.ref_name }}-installer.exe
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch: # Allow manually running the action
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.1"
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
2
.github/workflows/mobile-lint.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
env:
|
||||
|
||||
FLUTTER_VERSION: "3.24.1"
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
||||
2
.github/workflows/mobile-release.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
- "photos-v*"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.1"
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
5
.github/workflows/web-lint.yml
vendored
@@ -7,6 +7,11 @@ on:
|
||||
- "web/**"
|
||||
- ".github/workflows/web-lint.yml"
|
||||
|
||||
# Cancel in-progress lint runs when a new commit is pushed.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -70,7 +70,7 @@ want to give back, please check out Ente Photos or spread the word.
|
||||
[<img height="42" src=".github/assets/app-store-badge.svg">](https://apps.apple.com/app/id6444121398)
|
||||
[<img height="42" src=".github/assets/play-store-badge.png">](https://play.google.com/store/apps/details?id=io.ente.auth)
|
||||
[<img height="42" src=".github/assets/f-droid-badge.png">](https://f-droid.org/packages/io.ente.auth/)
|
||||
[<img height="42" src=".github/assets/desktop-badge.png">](https://github.com/ente-io/ente/releases?q=tag%3Aauth-v3)
|
||||
[<img height="42" src=".github/assets/desktop-badge.png">](https://github.com/ente-io/ente/releases?q=tag%3Aauth-v4)
|
||||
[<img height="42" src=".github/assets/web-badge.svg">](https://auth.ente.io)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ multi-device sync.
|
||||
### Android
|
||||
|
||||
This repository's [GitHub
|
||||
releases](https://github.com/ente-io/ente/releases?q=tag%3Aauth-v3)
|
||||
releases](https://github.com/ente-io/ente/releases?q=tag%3Aauth-v4)
|
||||
contains APKs, built straight from source. These builds keep themselves updated,
|
||||
without relying on third party stores.
|
||||
|
||||
@@ -33,7 +33,7 @@ You can alternatively install the build from PlayStore or F-Droid.
|
||||
|
||||
### Desktop
|
||||
|
||||
You can [**download**](https://github.com/ente-io/ente/releases?q=tag%3Aauth-v3)
|
||||
You can [**download**](https://github.com/ente-io/ente/releases?q=tag%3Aauth-v4)
|
||||
a native desktop app from this repository's GitHub releases. The desktop app
|
||||
works on Windows, Linux and macOS.
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
{
|
||||
"title": "1xBet"
|
||||
},
|
||||
{
|
||||
"title": "23andme"
|
||||
},
|
||||
{
|
||||
"title": "3Commas"
|
||||
},
|
||||
@@ -119,6 +122,12 @@
|
||||
{
|
||||
"title": "Bybit"
|
||||
},
|
||||
{
|
||||
"title": "Canva"
|
||||
},
|
||||
{
|
||||
"title": "Capacities"
|
||||
},
|
||||
{
|
||||
"title": "CERN"
|
||||
},
|
||||
@@ -136,6 +145,9 @@
|
||||
{
|
||||
"title": "Cloudflare"
|
||||
},
|
||||
{
|
||||
"title": "Coinbase"
|
||||
},
|
||||
{
|
||||
"title": "CoinDCX"
|
||||
},
|
||||
@@ -185,6 +197,9 @@
|
||||
"title": "dus.net",
|
||||
"slug": "dusnet"
|
||||
},
|
||||
{
|
||||
"title": "eBay"
|
||||
},
|
||||
{
|
||||
"title": "ecitizen kenya",
|
||||
"slug": "ecitizen_kenya"
|
||||
@@ -220,6 +235,9 @@
|
||||
"title": "Firefox",
|
||||
"slug": "mozilla"
|
||||
},
|
||||
{
|
||||
"title": "ForUsAll"
|
||||
},
|
||||
{
|
||||
"title": "G2A"
|
||||
},
|
||||
@@ -242,6 +260,19 @@
|
||||
"Госуслуги"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "GOV.UK",
|
||||
"slug": "gov_uk",
|
||||
"altNames": [
|
||||
"Government Gateway"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Guideline"
|
||||
},
|
||||
{
|
||||
"title": "Gusto"
|
||||
},
|
||||
{
|
||||
"title": "Habbo"
|
||||
},
|
||||
@@ -264,6 +295,10 @@
|
||||
{
|
||||
"title": "IceDrive"
|
||||
},
|
||||
{
|
||||
"title": "ID.me",
|
||||
"slug": "IDme"
|
||||
},
|
||||
{
|
||||
"title": "Infomaniak"
|
||||
},
|
||||
@@ -333,6 +368,10 @@
|
||||
"Local Wordpress"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Login.gov",
|
||||
"slug": "login_gov"
|
||||
},
|
||||
{
|
||||
"title": "Marketplace.tf",
|
||||
"slug": "marketplacedottf"
|
||||
@@ -576,6 +615,13 @@
|
||||
"title": "Synology DSM",
|
||||
"slug": "synology_dsm"
|
||||
},
|
||||
{
|
||||
"title": "T-Mobile",
|
||||
"altNames": [
|
||||
"T Mobile",
|
||||
"T-Mobile ID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "TCPShield"
|
||||
},
|
||||
@@ -597,6 +643,9 @@
|
||||
"title": "Termius",
|
||||
"hex": "858585"
|
||||
},
|
||||
{
|
||||
"title": "Titan"
|
||||
},
|
||||
{
|
||||
"title": "TorGuard"
|
||||
},
|
||||
@@ -688,4 +737,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
1
auth/assets/custom-icons/icons/23andme.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 90 90" xmlns="http://www.w3.org/2000/svg"><g transform="translate(26.145 5.696)" fill="none" fill-rule="evenodd"><path d="M7.824 10.031a6 6 0 0 1 7.492 3.984L32.42 69.958a6 6 0 1 1-11.476 3.509L3.84 17.523a6 6 0 0 1 3.984-7.492Z" fill="#D91A62"/><rect fill="#7BC144" transform="rotate(26 20.845 34.313)" x="14.845" y="-.937" width="12" height="70.5" rx="6"/><path fill="#49A848" d="m18.689 25.047 5.144 16.826-7.713 15.815-5.144-16.827 7.713-15.814z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 480 B |
6
auth/assets/custom-icons/icons/IDme.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="126" height="45" viewBox="0 0 126 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.76597 0.337891H4.59152C1.87715 0.337891 0.5 1.14359 0.5 2.73083V42.0032C0.5 43.5914 1.87715 44.3961 4.59152 44.3961H6.76597C9.4814 44.3961 10.8575 43.5904 10.8575 42.0032V2.73083C10.8585 1.14359 9.4814 0.337891 6.76597 0.337891Z" fill="white"/>
|
||||
<path d="M45.1179 39.5856C45.1179 34.9006 48.2073 30.9351 52.4743 29.5706C52.9617 27.3867 53.2075 24.978 53.2117 22.3445C53.2117 8.14862 45.9908 0.330078 32.878 0.330078H18.8996C16.9468 0.330078 16.0371 1.23137 16.0371 3.16736V41.5216C16.0371 43.4576 16.9468 44.3589 18.8985 44.3589H32.877C37.8761 44.3589 42.0159 43.2191 45.2282 41.0268C45.1578 40.5499 45.1211 40.0678 45.1169 39.5856M32.8759 35.5298H26.4398V9.15916H32.8759C40.9287 9.15916 42.6199 16.3296 42.6199 22.3445C42.6199 28.3594 40.9287 35.5298 32.8759 35.5298Z" fill="white"/>
|
||||
<path d="M55.7368 34.7764C58.4165 34.7764 60.5889 36.9298 60.5889 39.5864C60.5889 42.243 58.4165 44.3964 55.7368 44.3964C53.0571 44.3964 50.8848 42.243 50.8848 39.5864C50.8848 36.9298 53.0571 34.7764 55.7368 34.7764Z" fill="#2EA76D"/>
|
||||
<path d="M124.735 32.8363C124.593 32.7754 124.441 32.7417 124.286 32.7365C123.623 32.7144 123.171 32.9728 122.772 33.6126C122.536 33.996 122.303 34.3857 122.072 34.7754C121.192 36.2618 120.283 37.7965 118.962 38.8974C117.168 40.3912 114.57 41.0897 112.342 40.6842C111.017 40.4437 110.071 39.493 109.51 38.7388C108.482 37.3522 107.981 35.4855 108.061 33.3342C111.341 33.06 121.224 31.6114 122.01 24.1847C122.163 22.7446 121.749 21.4399 120.814 20.4104C119.629 19.1058 117.736 18.3862 115.487 18.3862C108.895 18.3862 102.389 25.2215 101.564 33.0138C101.338 35.1515 101.607 37.1253 102.364 38.8848C101.884 39.3449 101.335 39.7283 100.74 40.0224C100.187 40.283 99.7092 40.3365 99.3584 40.1769C98.9245 39.9773 98.7481 39.4741 98.6787 39.0886C98.4193 37.6621 98.7544 36.0223 99.1514 34.3279C99.3846 33.3342 99.6777 32.2995 99.9351 31.3856C100.732 28.5672 101.557 25.6533 101.282 22.7088C101.032 20.0134 99.1231 18.3389 96.3016 18.3389C92.3455 18.3389 89.7404 21.1374 88.0544 23.5366C88.0187 21.7487 87.5891 20.4504 86.7466 19.5827C85.9451 18.757 84.7676 18.3379 83.2486 18.3379C79.3682 18.3379 76.7956 21.026 75.1139 23.379C75.1359 23.1626 75.159 22.942 75.1832 22.7214C75.2893 21.713 75.3366 20.2676 74.5246 19.3747C74.0414 18.8432 73.3144 18.5732 72.3638 18.5732C71.7104 18.5532 71.057 18.6037 70.4152 18.7255C70.4057 18.7266 69.5339 18.8957 69.2019 19.1982C68.621 19.7266 68.8059 20.4556 68.912 20.8716C68.9256 20.9241 68.9382 20.9724 68.9445 21.0134C69.0107 21.4746 69.0307 21.941 69.0055 22.4063C68.8836 25.2362 68.3542 28.0651 67.851 30.4591C67.5789 31.7491 67.2733 33.0611 66.9707 34.3563C66.2985 37.2303 65.6051 40.2 65.249 43.1875C65.1703 43.7684 65.5757 44.3031 66.1566 44.3818C66.2092 44.3892 66.2627 44.3923 66.3163 44.3913L66.5253 44.3934C68.6578 44.4218 70.7534 44.3346 71.2461 43.1286C71.7913 41.7988 72.0928 40.1811 72.3596 38.7503L72.4772 38.1159C73.1096 34.8258 73.6621 32.4686 74.7756 29.4381C75.3492 27.876 76.3975 26.4695 77.2452 25.4169C78.2715 24.1427 79.3787 22.8958 80.6277 22.8643C81.1445 22.838 81.4817 22.9904 81.7359 23.3255C82.9492 24.9306 81.4387 30.1103 80.7937 32.3226C80.654 32.8006 80.5363 33.205 80.4617 33.5012L80.0174 35.2103C79.363 37.702 78.6865 40.2777 78.3398 42.8839C78.3052 43.1507 78.2726 43.4186 78.2453 43.6896L78.2159 44.053L78.489 44.261C79.0394 44.6844 82.9702 44.0919 83.0017 44.0825C84.4514 43.5971 84.7665 42.2452 84.8705 41.8009C85.1405 40.6517 85.3789 39.4773 85.609 38.3428L85.6258 38.2587C86.0586 36.1211 86.5061 33.9098 87.1752 31.7764C88.482 27.625 90.2236 24.7929 92.3539 23.358C93.2647 22.7425 94.2007 22.6742 94.624 23.1941C95.3604 24.0912 94.8939 26.5199 94.6944 27.5567C94.4191 28.9958 94.0662 30.4591 93.7258 31.8751L93.708 31.9476C93.4895 32.8499 93.2731 33.7512 93.0714 34.6557C92.4485 37.4583 91.9369 40.9332 93.5284 42.9385C94.3383 43.9606 95.5914 44.4785 97.2533 44.4785C99.0159 44.4785 100.674 43.8955 102.471 42.6444C102.966 42.2967 103.466 41.8923 104.05 41.4111C106.073 43.6906 108.37 44.6676 111.687 44.6676C119.185 44.6676 122.71 39.7283 124.71 36.0129C124.976 35.537 125.211 35.0443 125.412 34.538C125.66 33.8542 125.362 33.1062 124.734 32.8373M115.539 23.4557C115.549 23.5713 115.559 23.6868 115.56 23.8034C115.56 23.9778 115.551 24.1522 115.533 24.3265C115.477 24.9043 115.363 25.4757 115.194 26.0314C114.124 29.4717 111.112 30.0809 108.506 30.1681C108.849 28.6996 109.358 27.2741 110.021 25.919C111.288 23.358 112.881 21.7676 114.175 21.7666C114.295 21.7666 114.414 21.7844 114.528 21.817C114.588 21.838 114.646 21.8632 114.701 21.8937L114.756 21.9178C114.778 21.9262 114.799 21.9368 114.819 21.9494C114.876 21.9872 114.928 22.0313 114.977 22.0786L115.021 22.1195C115.039 22.1342 115.054 22.15 115.07 22.1668C115.105 22.2078 115.137 22.2519 115.166 22.2981L115.204 22.3559C115.233 22.3958 115.259 22.4378 115.282 22.4809C115.296 22.5082 115.307 22.5387 115.342 22.6185C115.379 22.7036 115.41 22.7908 115.438 22.879L115.462 22.9862C115.491 23.0986 115.513 23.212 115.528 23.3265L115.539 23.4557Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 MiB |
1
auth/assets/custom-icons/icons/canva.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 508 508" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2"><g transform="matrix(.26718 0 0 .26718 0 0)"><circle cx="950" cy="950" r="950" fill="#7d2ae7"/><circle cx="950" cy="950" r="950" fill="url(#prefix___Radial1)"/><circle cx="950" cy="950" r="950" fill="url(#prefix___Radial2)"/><circle cx="950" cy="950" r="950" fill="url(#prefix___Radial3)"/><circle cx="950" cy="950" r="950" fill="url(#prefix___Radial4)"/></g><path d="M446.744 276.845c-.665 0-1.271.43-1.584 1.33-4.011 11.446-9.43 18.254-13.891 18.254-2.563 0-3.6-2.856-3.6-7.336 0-11.21 6.71-34.982 10.095-45.82.392-1.312.646-2.485.646-3.483 0-3.15-1.722-4.696-5.987-4.696-4.598 0-9.547 1.8-14.36 10.233-1.663-7.435-6.691-10.683-13.715-10.683-8.12 0-15.965 5.224-22.421 13.696-6.456 8.471-14.048 11.25-19.76 9.88 4.108-10.057 5.634-17.57 5.634-23.145 0-8.746-4.324-14.028-11.308-14.028-10.624 0-16.747 10.134-16.747 20.797 0 8.237 3.736 16.708 11.954 20.817-6.887 15.573-16.943 29.66-20.758 29.66-4.93 0-6.379-24.123-6.105-41.38.176-9.9.998-10.408.998-13.401 0-1.722-1.115-2.896-5.595-2.896-10.448 0-13.676 8.844-14.165 18.998a50.052 50.052 0 01-1.8 11.406c-4.363 15.573-13.363 27.39-19.232 27.39-2.72 0-3.463-2.72-3.463-6.28 0-11.21 6.28-25.219 6.28-37.173 0-8.784-3.854-14.34-11.112-14.34-8.55 0-19.858 10.173-30.56 29.229 3.521-14.595 4.97-28.721-5.459-28.721a14.115 14.115 0 00-6.476 1.683 3.689 3.689 0 00-2.113 3.56c.998 15.535-12.521 55.329-25.336 55.329-2.328 0-3.463-2.524-3.463-6.593 0-11.23 6.691-34.943 10.056-45.801.43-1.409.666-2.622.666-3.678 0-2.974-1.84-4.5-6.007-4.5-4.578 0-9.547 1.741-14.34 10.174-1.683-7.435-6.711-10.683-13.735-10.683-11.523 0-24.397 12.19-30.051 28.076-7.572 21.208-22.832 41.692-43.375 41.692-18.645 0-28.486-15.515-28.486-40.03 0-35.392 25.982-64.308 45.253-64.308 9.215 0 13.617 5.869 13.617 14.869 0 10.897-6.085 15.964-6.085 20.112 0 1.272 1.057 2.524 3.15 2.524 8.374 0 18.234-9.841 18.234-23.262 0-13.422-10.897-23.243-30.168-23.243-31.851 0-63.898 32.047-63.898 73.113 0 32.673 16.121 52.374 44 52.374 19.017 0 35.628-14.79 44.588-32.047 1.018 14.302 7.513 21.776 17.413 21.776 8.804 0 15.925-5.243 21.364-14.458 2.094 9.645 7.65 14.36 14.87 14.36 8.275 0 15.201-5.243 21.794-14.986-.097 7.65 1.644 14.85 8.276 14.85 3.13 0 6.867-.725 7.533-3.464 6.984-28.877 24.24-52.453 29.523-52.453 1.565 0 1.995 1.507 1.995 3.287 0 7.846-5.537 23.928-5.537 34.2 0 11.092 4.716 18.43 14.459 18.43 10.8 0 21.775-13.227 29.092-32.556 2.29 18.058 7.24 32.633 14.987 32.633 9.508 0 26.392-20.014 36.625-41.203 4.01.509 10.036.372 15.827-3.717-2.465 6.241-3.912 13.07-3.912 19.897 0 19.663 9.39 25.18 17.47 25.18 8.785 0 15.907-5.243 21.365-14.458 1.8 8.315 6.398 14.34 14.85 14.34 13.225 0 24.71-13.519 24.71-24.612 0-2.934-1.252-4.715-2.72-4.715zm-274.51 18.547c-5.342 0-7.435-5.38-7.435-13.401 0-13.93 9.528-37.193 19.604-37.193 4.402 0 6.065 5.185 6.065 11.524 0 14.145-9.059 39.07-18.235 39.07zm182.948-41.574c-3.189-3.796-4.343-8.961-4.343-13.559 0-5.673 2.074-10.467 4.558-10.467 2.485 0 3.248 2.446 3.248 5.85 0 5.693-2.035 14.008-3.463 18.176zm41.418 41.574c-5.34 0-7.434-6.182-7.434-13.401 0-13.441 9.528-37.193 19.682-37.193 4.402 0 5.967 5.146 5.967 11.524 0 14.145-8.902 39.07-18.215 39.07z" fill="#fff" fill-rule="nonzero"/><defs><radialGradient id="prefix___Radial1" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(1469.491) rotate(-49.416 1.37 .302)"><stop offset="0" stop-color="#6420ff"/><stop offset="1" stop-color="#6420ff" stop-opacity="0"/></radialGradient><radialGradient id="prefix___Radial2" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="rotate(54.703 42.717 594.194) scale(1657.122)"><stop offset="0" stop-color="#00c4cc"/><stop offset="1" stop-color="#00c4cc" stop-opacity="0"/></radialGradient><radialGradient id="prefix___Radial3" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1023 -1030 473.711 470.491 367 1684)"><stop offset="0" stop-color="#6420ff"/><stop offset="1" stop-color="#6420ff" stop-opacity="0"/></radialGradient><radialGradient id="prefix___Radial4" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(595.999 1372 -2298.41 998.431 777 256)"><stop offset="0" stop-color="#00c4cc" stop-opacity=".73"/><stop offset="0" stop-color="#00c4cc"/><stop offset="1" stop-color="#00c4cc" stop-opacity="0"/></radialGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
6
auth/assets/custom-icons/icons/capacities.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="100%" height="100%" viewBox="0 0 78 78" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g style="mix-blend-mode:difference">
|
||||
<circle cx="38.8209" cy="39" r="13.45" transform="rotate(-15 38.8209 39)" fill="white"></circle>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M54.7914 66.0811C54.6942 66.3374 54.5105 66.5518 54.271 66.6852C52.0346 67.9311 49.6099 68.9243 47.0245 69.617C30.1152 74.1478 12.7346 64.1131 8.20379 47.2038C3.67296 30.2946 13.7077 12.9139 30.6169 8.38311C33.2023 7.69037 35.7987 7.33813 38.3584 7.2989C38.6325 7.29469 38.8988 7.38849 39.1111 7.56192C41.8794 9.82347 44.4239 12.3787 46.6916 15.197C47.4731 16.1681 46.2983 17.5912 45.101 17.2444C41.2693 16.1343 37.0954 16.023 32.9609 17.1308C20.8828 20.3671 13.7152 32.7819 16.9515 44.8599C20.1878 56.938 32.6025 64.1056 44.6806 60.8693C48.8151 59.7615 52.3742 57.5781 55.1375 54.7009C56.001 53.8018 57.7299 54.4469 57.5387 55.6787C56.9839 59.2532 56.058 62.7384 54.7914 66.0811ZM65.4625 56.1737C64.7914 57.2153 63.3044 56.5354 63.4669 55.307C64.4343 47.9938 64.0074 40.3692 61.9779 32.7952C59.9484 25.2211 56.5058 18.4044 52.0114 12.5547C51.2565 11.5722 52.2043 10.2398 53.3063 10.8064C60.9824 14.7532 67.0278 21.8023 69.4377 30.7963C71.8476 39.7902 70.1368 48.9176 65.4625 56.1737Z" fill="white"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
14
auth/assets/custom-icons/icons/coinbase.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="katman_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 800 600" style="enable-background:new 0 0 800 600;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#0052FF;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M399.8,69.1L399.8,69.1c127.3,0,230.5,103.2,230.5,230.5l0,0c0,127.3-103.2,230.5-230.5,230.5l0,0
|
||||
c-127.3,0-230.5-103.2-230.5-230.5l0,0C169.3,172.3,272.5,69.1,399.8,69.1z"/>
|
||||
<path class="st1" d="M399.9,380.6c-44.8,0-81-36.3-81-81s36.3-81,81-81c40.1,0,73.4,29.2,79.8,67.5h81.6
|
||||
c-6.9-83.2-76.5-148.6-161.5-148.6c-89.5,0-162.1,72.6-162.1,162.1s72.6,162.1,162.1,162.1c85,0,154.6-65.4,161.5-148.6h-81.7
|
||||
C473.2,351.4,440,380.6,399.9,380.6z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 904 B |
27
auth/assets/custom-icons/icons/ebay.svg
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
|
||||
<svg
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="300"
|
||||
height="120.32412">
|
||||
|
||||
<path id="e"
|
||||
d="m 38.866448,26.308378 c -21.145729,0 -38.76645103,8.97108 -38.76645103,36.036419 0,21.441707 11.84866003,34.944406 39.31245703,34.944406 32.326175,0 34.3984,-21.294248 34.3984,-21.294248 l -15.663557,0 c 0,0 -3.358398,11.466134 -19.690354,11.466134 -13.301932,0 -22.869203,-8.985845 -22.869203,-21.580814 l 59.861133,0 0,-7.903529 c 0,-12.460384 -7.91007,-31.668368 -36.582425,-31.668368 z m -0.546007,10.101117 c 12.662062,0 21.294248,7.757047 21.294248,19.383225 l -43.680508,0 c 0,-12.34261 11.267202,-19.383225 22.38626,-19.383225 z"
|
||||
style="fill:#e53238;fill-opacity:1;stroke:none" />
|
||||
|
||||
<path id="b"
|
||||
d="m 75.437762,0.10007279 0,83.59702321 c 0,4.745232 -0.338677,11.408082 -0.338677,11.408082 l 14.939733,0 c 0,0 0.536238,-4.785353 0.536238,-9.1587 0,0 7.381193,11.547477 27.451204,11.547477 21.13453,0 35.49041,-14.673014 35.49041,-35.695165 0,-19.556604 -13.18634,-35.28566 -35.45629,-35.28566 -20.854235,0 -27.33444,11.261381 -27.33444,11.261381 l 0,-37.67443821 z M 114.20421,36.853125 c 14.35199,0 23.47828,10.651661 23.47828,24.945665 0,15.327725 -10.54056,25.35517 -23.3759,25.35517 -15.317854,0 -23.58065,-11.960116 -23.58065,-25.218668 0,-12.354387 7.414449,-25.082167 23.47827,-25.082167 z"
|
||||
style="fill:#0064d2;fill-opacity:1;stroke:none" />
|
||||
|
||||
<path id="a"
|
||||
d="m 190.6451,26.308378 c -31.81215,0 -33.85239,17.418776 -33.85239,20.202235 l 15.83418,0 c 0,0 0.83034,-10.169369 16.9262,-10.169369 10.45935,0 18.56422,4.787411 18.56422,13.991413 l 0,3.276038 -18.56422,0 c -24.64532,0 -37.67444,7.20973 -37.67444,21.840254 0,14.398537 12.03849,22.232696 28.30702,22.232696 22.17148,0 29.31371,-12.251017 29.31371,-12.251017 0,4.872784 0.37568,9.67455 0.37568,9.67455 l 14.07643,0 c 0,0 -0.54601,-5.951939 -0.54601,-9.759864 l 0,-32.913945 c 0,-21.581223 -17.40751,-26.122991 -32.76038,-26.122991 z m 17.47221,37.128431 0,4.368051 c 0,5.697129 -3.51553,19.860981 -24.21197,19.860981 -11.3333,0 -16.1925,-5.656156 -16.1925,-12.216892 0,-11.935273 16.36378,-12.01214 40.40447,-12.01214 z"
|
||||
style="fill:#f5af02;fill-opacity:1;stroke:none" />
|
||||
|
||||
<path id="y"
|
||||
d="m 214.87901,29.041161 17.81346,0 25.56479,51.217345 25.5063,-51.217345 16.13644,0 -46.45929,91.183029 -16.9262,0 13.40641,-25.418513 z"
|
||||
style="fill:#86b817;fill-opacity:1;stroke:none" />
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
3
auth/assets/custom-icons/icons/forusall.svg
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
5
auth/assets/custom-icons/icons/gov_uk.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 30">
|
||||
<g style="mix-blend-mode:difference">
|
||||
<path d="M22.6 10.4c-1 .4-2-.1-2.4-1s.1-2 1-2.4 2 .1 2.4 1-.1 2-1 2.4m-5.9 6.7c-.9.4-2-.1-2.4-1s.1-2 1-2.4 2 .1 2.4 1-.1 2-1 2.4m10.8-3.7c-1 .4-2-.1-2.4-1s.1-2 1-2.4 2 .1 2.4 1 0 2-1 2.4m3.3 4.8c-1 .4-2-.1-2.4-1s.1-2 1-2.4 2 .1 2.4 1-.1 2-1 2.4M17 4.7l2.3 1.2V2.5l-2.3.7-.2-.2.9-3h-3.4l.9 3-.2.2c-.1.1-2.3-.7-2.3-.7v3.4L15 4.7c.1.1.1.2.2.2l-1.3 4c-.1.2-.1.4-.1.6 0 1.1.8 2 1.9 2.2h.7c1-.2 1.9-1.1 1.9-2.1 0-.2 0-.4-.1-.6l-1.3-4c-.1-.2 0-.2.1-.3m-7.6 5.7c.9.4 2-.1 2.4-1s-.1-2-1-2.4-2 .1-2.4 1 0 2 1 2.4m-5 3c.9.4 2-.1 2.4-1s-.1-2-1-2.4-2 .1-2.4 1 .1 2 1 2.4m-3.2 4.8c.9.4 2-.1 2.4-1s-.1-2-1-2.4-2 .1-2.4 1 0 2 1 2.4m14.8 11c4.4 0 8.6.3 12.3.8 1.1-4.5 2.4-7 3.7-8.8l-2.5-.9c.2 1.3.3 1.9 0 2.7-.4-.4-.8-1.1-1.1-2.3l-1.2 4c.7-.5 1.3-.8 2-.9-1.1 2.5-2.6 3.1-3.5 3-1.1-.2-1.7-1.2-1.5-2.1.3-1.2 1.5-1.5 2.1-.1 1.1-2.3-.8-3-2-2.3 1.9-1.9 2.1-3.5.6-5.6-2.1 1.6-2.1 3.2-1.2 5.5-1.2-1.4-3.2-.6-2.5 1.6.9-1.4 2.1-.5 1.9.8-.2 1.1-1.7 2.1-3.5 1.9-2.7-.2-2.9-2.1-2.9-3.6.7-.1 1.9.5 2.9 1.9l.4-4.3c-1.1 1.1-2.1 1.4-3.2 1.4.4-1.2 2.1-3 2.1-3h-5.4s1.7 1.9 2.1 3c-1.1 0-2.1-.2-3.2-1.4l.4 4.3c1-1.4 2.2-2 2.9-1.9-.1 1.5-.2 3.4-2.9 3.6-1.9.2-3.4-.8-3.5-1.9-.2-1.3 1-2.2 1.9-.8.7-2.3-1.2-3-2.5-1.6.9-2.2.9-3.9-1.2-5.5-1.5 2-1.3 3.7.6 5.6-1.2-.7-3.1 0-2 2.3.6-1.4 1.8-1.1 2.1.1.2.9-.3 1.9-1.5 2.1-.9.2-2.4-.5-3.5-3 .6 0 1.2.3 2 .9l-1.2-4c-.3 1.1-.7 1.9-1.1 2.3-.3-.8-.2-1.4 0-2.7l-2.9.9C1.3 23 2.6 25.5 3.7 30c3.7-.5 7.9-.8 12.3-.8" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
auth/assets/custom-icons/icons/guideline.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 400 400"><path fill="#150A45" d="M0 0h400v400H0z"/><path fill="#fff" d="M234.117 91.106C225.896 77.188 211.92 69 192.19 69 155.196 69 133 98.475 133 139.413c0 40.937 22.196 70.412 59.19 70.412 6.577 0 13.976-.819 19.73-3.275 6.577-2.456 12.332-6.55 16.442-11.463v19.65c0 13.1-7.399 29.475-29.595 29.475-17.264 0-25.485-9.825-28.773-19.65L139.577 239.3c8.221 18.012 26.306 34.388 61.656 34.388 51.792 0 65.767-35.207 65.767-65.5V70.638h-32.061l-.822 20.468Zm-33.706 86.788c-18.908 0-29.595-14.738-29.595-38.481 0-23.744 10.687-38.482 29.595-38.482s29.595 14.738 29.595 38.482c0 22.925-10.687 38.481-29.595 38.481ZM267 284.332V331H133.822v-27.837h106.049v-18.831H267Z"/></svg>
|
||||
|
After Width: | Height: | Size: 739 B |
23
auth/assets/custom-icons/icons/gusto.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 333.8 127" style="enable-background:new 0 0 333.8 127;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#F45D48;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M296.4,18.6c-20.6,0-37.4,16.8-37.4,37.6c0,20.7,16.8,37.6,37.4,37.6c20.6,0,37.4-16.9,37.4-37.6
|
||||
C333.8,35.4,317,18.6,296.4,18.6L296.4,18.6z M296.4,75.9c-10.9,0-19.7-8.9-19.7-19.8c0-10.9,8.8-19.8,19.7-19.8
|
||||
c10.8,0,19.7,8.9,19.7,19.8C316.1,67,307.2,75.9,296.4,75.9L296.4,75.9z M186.2,48.5l-6.5-3.3c-2.8-1.4-4.6-2.5-5.3-3.3
|
||||
c-0.7-0.7-1.1-1.7-1.1-2.6c0-1.3,0.6-2.3,1.7-3.2c1.1-0.9,2.7-1.3,4.7-1.3c3.7,0,7.7,2.2,12.2,6.7l11-11.1
|
||||
c-2.7-3.7-6.3-6.7-10.4-8.8c-4-2-8.5-3.1-13-3c-6.7,0-12.3,2-16.6,6c-4.3,4-6.5,8.8-6.5,14.5c0,8.7,5.7,16,17.2,21.8l6,3
|
||||
c5.2,2.6,7.8,5.3,7.8,8.1c0,1.5-0.7,2.8-2.1,4c-1.4,1.1-3.3,1.7-5.7,1.7c-2.2,0-4.7-0.8-7.5-2.3c-2.8-1.5-5.3-3.5-7.3-5.9l-11,12
|
||||
c6.2,8.1,14.5,12.2,24.8,12.2c7.8,0,14-2.1,18.6-6.4c4.6-4.2,7-9.5,7-15.9c0-4.8-1.3-8.9-3.8-12.5C197.8,55.4,193.1,52,186.2,48.5
|
||||
L186.2,48.5z M75.2,85.5V20.1H57.4v4.4c-6-3.9-13-5.9-20.1-5.9C16.8,18.6,0,35.4,0,56.1c0,20.7,16.8,37.6,37.4,37.6
|
||||
c7,0,13.9-2,19.9-5.7c0,0.6,0,1.1,0,1.4c0,10.9-8.8,19.8-19.7,19.8c-3.7,0-7.3-1.1-10.4-3l-8.8,15.4c5.8,3.5,12.4,5.4,19.2,5.4
|
||||
c20.6,0,37.6-16.9,37.6-37.6C75.2,88.6,75.3,86.3,75.2,85.5L75.2,85.5z M37.4,75.9c-10.8,0-19.7-8.9-19.7-19.8
|
||||
c0-10.9,8.8-19.8,19.7-19.8c10.9,0,19.7,8.9,19.7,19.8C57.1,67,48.2,75.9,37.4,75.9L37.4,75.9z M145.9,20h-17.8v35.7
|
||||
c0,5.1,0,13.8-3.7,17.6c-1.8,1.8-3.7,3.4-7.8,3.4c-4.1,0-6.1-1.6-7.9-3.4c-3.7-3.7-3.7-12.5-3.7-17.6V20H87.2v35.6
|
||||
c-0.1,7.4-0.1,21,8.8,30c5.2,5.3,11.5,8,20.5,8c9,0,15.3-2.7,20.5-8c8.9-9,8.9-22.7,8.8-30L145.9,20z M247,72.7
|
||||
c-1.4,1.1-4.9,3.5-8.8,3c-3.2-0.4-5.8-2.8-6.2-10.7V35.3h21V20h-21V0h-17.8v3.9h0v58.7c0,9.3,2.3,31,24,31
|
||||
c10.9-0.2,17.6-6.1,20.4-8.5l0.4-0.3l-10.7-13.2C248.2,71.8,247.5,72.4,247,72.7L247,72.7z M247,72.7">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
1
auth/assets/custom-icons/icons/login_gov.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90"><path fill="#FFF" d="M0 0h90v90H0z"/><path fill="#FFF" d="M33.258 25.45h26.027v33.51H33.258z"/><path d="m39.22 53.522 2.194-12.578a6.684 6.684 0 0 1-2.21-7.444 6.645 6.645 0 0 1 6.311-4.495v-13.96H28.567c-3.627 0-6.567 2.952-6.567 6.593v32.324c-.005.61.13 1.213.394 1.762C23.816 58.61 29.43 67.876 45.5 75V54.038a36.635 36.635 0 0 1-6.28-.516Z" fill="#E21D3E"/><path d="M62.417 15H45.5v13.96a6.644 6.644 0 0 1 6.302 4.458 6.684 6.684 0 0 1-2.14 7.435l2.193 12.638c-2.098.367-4.225.55-6.355.547V74.97c16.055-7.094 21.684-16.39 23.106-19.276.264-.55.399-1.153.394-1.762v-32.31a6.606 6.606 0 0 0-1.918-4.686A6.555 6.555 0 0 0 62.417 15Z" fill="#B51E23"/></svg>
|
||||
|
After Width: | Height: | Size: 717 B |
@@ -1,5 +1,12 @@
|
||||
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M446.258 230.173C434.83 295.328 381.676 344.578 311.926 344.578H267.645C258.614 344.578 249.514 352.94 248.072 362.188L228.68 485.284C227.555 492.294 224.272 494.623 217.172 494.623H145.945C138.743 494.623 137.039 492.214 138.118 485.114L146.298 399.986L60.8024 395.566C53.6116 395.566 51.0102 391.647 52.0325 384.467L110.366 14.5526C111.491 7.54283 115.853 4.53217 122.93 4.53217H270.871C342.087 4.53217 387.14 52.4756 392.65 114.643C435.023 143.273 454.834 181.366 446.269 230.162L446.258 230.173Z" fill="#001C64"/>
|
||||
<path d="M167.972 262.157L146.309 400L132.609 486.48C132.354 488.148 132.462 489.85 132.924 491.472C133.386 493.094 134.192 494.598 135.287 495.881C136.382 497.163 137.741 498.195 139.27 498.906C140.8 499.617 142.464 499.99 144.151 500H219.376C222.81 499.996 226.13 498.767 228.739 496.535C231.348 494.303 233.075 491.213 233.61 487.821L253.411 362.168C253.949 358.778 255.677 355.691 258.286 353.461C260.895 351.231 264.213 350.004 267.645 350.001H311.926C345.668 349.957 378.285 337.859 403.896 315.889C429.507 293.919 446.428 263.521 451.608 230.176C459.276 181.244 434.659 136.698 392.525 114.692C392.43 119.905 391.974 125.106 391.162 130.256C385.962 163.584 369.035 193.96 343.428 215.913C317.822 237.867 285.219 249.955 251.491 250.001H182.195C178.766 250.002 175.449 251.227 172.842 253.456C170.235 255.684 168.508 258.77 167.972 262.157Z" fill="#0070E0"/>
|
||||
<path d="M146.297 399.998H58.7005C57.0105 399.99 55.3422 399.617 53.8091 398.906C52.276 398.195 50.9144 397.161 49.8168 395.876C48.7193 394.591 47.9117 393.084 47.4491 391.458C46.9864 389.833 46.8797 388.126 47.1361 386.456L106.163 12.179C106.693 8.78702 108.418 5.6961 111.025 3.46315C113.633 1.23021 116.952 0.00205828 120.385 0H270.791C342.007 0 393.786 51.8289 392.57 114.621C373.615 104.841 352.565 99.8231 331.238 99.9995H205.846C202.412 100 199.091 101.226 196.48 103.456C193.869 105.687 192.139 108.775 191.601 112.167L167.972 262.155L146.297 399.998Z" fill="#003087"/>
|
||||
<g clip-path="url(#clip0_203_147)">
|
||||
<path d="M395.978 115C395.978 176.933 338.822 250 252.345 250H169.045L164.956 275.8L145.522 400H42.0002L104.278 0H272C328.478 0 372.911 31.4778 389.278 75.2222C393.998 87.9408 396.271 101.437 395.978 115Z" fill="#002991"/>
|
||||
<path d="M455.645 230C450.119 263.571 432.827 294.081 406.862 316.067C380.898 338.054 347.956 350.082 313.933 350H256.089L232.011 500H129.045L145.522 400L164.967 275.8L169.045 250H252.345C338.711 250 395.978 176.933 395.978 115C438.478 136.933 463.256 181.256 455.645 230Z" fill="#60CDFF"/>
|
||||
<path d="M395.978 115C378.156 105.678 356.545 100 333.022 100H192.578L169.045 250H252.345C338.711 250 395.978 176.933 395.978 115Z" fill="#008CFF"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_203_147">
|
||||
<rect width="500" height="500" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 914 B |
1
auth/assets/custom-icons/icons/titan.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
@@ -82,9 +82,9 @@ PODS:
|
||||
- qr_code_scanner (0.2.0):
|
||||
- Flutter
|
||||
- MTBBarcodeScanner
|
||||
- SDWebImage (5.19.2):
|
||||
- SDWebImage/Core (= 5.19.2)
|
||||
- SDWebImage/Core (5.19.2)
|
||||
- SDWebImage (5.19.7):
|
||||
- SDWebImage/Core (= 5.19.7)
|
||||
- SDWebImage/Core (5.19.7)
|
||||
- Sentry/HybridSDK (8.33.0)
|
||||
- sentry_flutter (8.7.0):
|
||||
- Flutter
|
||||
@@ -100,16 +100,16 @@ PODS:
|
||||
- sqflite (0.0.3):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- "sqlite3 (3.46.0+1)":
|
||||
- "sqlite3/common (= 3.46.0+1)"
|
||||
- "sqlite3/common (3.46.0+1)"
|
||||
- "sqlite3/dbstatvtab (3.46.0+1)":
|
||||
- "sqlite3 (3.46.1+1)":
|
||||
- "sqlite3/common (= 3.46.1+1)"
|
||||
- "sqlite3/common (3.46.1+1)"
|
||||
- "sqlite3/dbstatvtab (3.46.1+1)":
|
||||
- sqlite3/common
|
||||
- "sqlite3/fts5 (3.46.0+1)":
|
||||
- "sqlite3/fts5 (3.46.1+1)":
|
||||
- sqlite3/common
|
||||
- "sqlite3/perf-threadsafe (3.46.0+1)":
|
||||
- "sqlite3/perf-threadsafe (3.46.1+1)":
|
||||
- sqlite3/common
|
||||
- "sqlite3/rtree (3.46.0+1)":
|
||||
- "sqlite3/rtree (3.46.1+1)":
|
||||
- sqlite3/common
|
||||
- sqlite3_flutter_libs (0.0.1):
|
||||
- Flutter
|
||||
@@ -245,14 +245,14 @@ SPEC CHECKSUMS:
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
|
||||
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
|
||||
SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a
|
||||
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
|
||||
Sentry: 8560050221424aef0bebc8e31eedf00af80f90a6
|
||||
sentry_flutter: e26b861f744e5037a3faf9bf56603ec65d658a61
|
||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
sodium_libs: 1faae17af662384acbd13e41867a0008cd2e2318
|
||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||
sqlite3: 292c3e1bfe89f64e51ea7fc7dab9182a017c8630
|
||||
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
||||
sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
||||
|
||||
@@ -13,11 +13,11 @@ import 'package:ente_auth/models/key_attributes.dart';
|
||||
import 'package:ente_auth/models/key_gen_result.dart';
|
||||
import 'package:ente_auth/models/private_key_attributes.dart';
|
||||
import 'package:ente_auth/store/authenticator_db.dart';
|
||||
import 'package:ente_auth/utils/directory_utils.dart';
|
||||
import 'package:ente_auth/utils/lock_screen_settings.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
@@ -56,7 +56,6 @@ class Configuration {
|
||||
static final _logger = Logger("Configuration");
|
||||
|
||||
String? _cachedToken;
|
||||
late String _documentsDirectory;
|
||||
late SharedPreferences _preferences;
|
||||
String? _key;
|
||||
String? _secretKey;
|
||||
@@ -75,8 +74,7 @@ class Configuration {
|
||||
_preferences = await SharedPreferences.getInstance();
|
||||
sqfliteFfiInit();
|
||||
_secureStorage = const FlutterSecureStorage();
|
||||
_documentsDirectory = (await getApplicationDocumentsDirectory()).path;
|
||||
_tempDirectory = "$_documentsDirectory/temp/";
|
||||
_tempDirectory = (await DirectoryUtils.getDirectoryForInit()).path;
|
||||
final tempDirectory = io.Directory(_tempDirectory);
|
||||
try {
|
||||
final currentTime = DateTime.now().microsecondsSinceEpoch;
|
||||
|
||||
@@ -66,24 +66,25 @@ final lightThemeData = ThemeData(
|
||||
color: Colors.black,
|
||||
width: 2,
|
||||
),
|
||||
fillColor: MaterialStateProperty.resolveWith((states) {
|
||||
return states.contains(MaterialState.selected)
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
return states.contains(WidgetState.selected)
|
||||
? const Color.fromRGBO(0, 0, 0, 1)
|
||||
: const Color.fromRGBO(255, 255, 255, 1);
|
||||
}),
|
||||
checkColor: MaterialStateProperty.resolveWith((states) {
|
||||
return states.contains(MaterialState.selected)
|
||||
checkColor: WidgetStateProperty.resolveWith((states) {
|
||||
return states.contains(WidgetState.selected)
|
||||
? const Color.fromRGBO(255, 255, 255, 1)
|
||||
: const Color.fromRGBO(0, 0, 0, 1);
|
||||
}),
|
||||
),
|
||||
|
||||
radioTheme: RadioThemeData(
|
||||
fillColor:
|
||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return null;
|
||||
}
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const Color.fromRGBO(102, 187, 106, 1);
|
||||
}
|
||||
return null;
|
||||
@@ -91,21 +92,21 @@ final lightThemeData = ThemeData(
|
||||
),
|
||||
switchTheme: SwitchThemeData(
|
||||
thumbColor:
|
||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return null;
|
||||
}
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const Color.fromRGBO(102, 187, 106, 1);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
trackColor:
|
||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return null;
|
||||
}
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const Color.fromRGBO(102, 187, 106, 1);
|
||||
}
|
||||
return null;
|
||||
@@ -114,7 +115,7 @@ final lightThemeData = ThemeData(
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: Colors.black,
|
||||
secondary: Color.fromARGB(255, 163, 163, 163),
|
||||
).copyWith(background: const Color.fromRGBO(255, 255, 255, 1)),
|
||||
).copyWith(surface: const Color.fromRGBO(255, 255, 255, 1)),
|
||||
);
|
||||
|
||||
final darkThemeData = ThemeData(
|
||||
@@ -176,15 +177,15 @@ final darkThemeData = ThemeData(
|
||||
color: Colors.grey,
|
||||
width: 2,
|
||||
),
|
||||
fillColor: MaterialStateProperty.resolveWith((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const Color.fromRGBO(158, 158, 158, 1);
|
||||
} else {
|
||||
return const Color.fromRGBO(0, 0, 0, 1);
|
||||
}
|
||||
}),
|
||||
checkColor: MaterialStateProperty.resolveWith((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
checkColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const Color.fromRGBO(0, 0, 0, 1);
|
||||
} else {
|
||||
return const Color.fromRGBO(158, 158, 158, 1);
|
||||
@@ -193,11 +194,11 @@ final darkThemeData = ThemeData(
|
||||
),
|
||||
radioTheme: RadioThemeData(
|
||||
fillColor:
|
||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return null;
|
||||
}
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const Color.fromRGBO(102, 187, 106, 1);
|
||||
}
|
||||
return null;
|
||||
@@ -205,28 +206,28 @@ final darkThemeData = ThemeData(
|
||||
),
|
||||
switchTheme: SwitchThemeData(
|
||||
thumbColor:
|
||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return null;
|
||||
}
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const Color.fromRGBO(102, 187, 106, 1);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
trackColor:
|
||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return null;
|
||||
}
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const Color.fromRGBO(102, 187, 106, 1);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
),
|
||||
colorScheme: const ColorScheme.dark(primary: Colors.white)
|
||||
.copyWith(background: const Color.fromRGBO(0, 0, 0, 1)),
|
||||
.copyWith(surface: const Color.fromRGBO(0, 0, 0, 1)),
|
||||
);
|
||||
|
||||
TextTheme _buildTextTheme(Color textColor) {
|
||||
@@ -458,17 +459,17 @@ OutlinedButtonThemeData buildOutlinedButtonThemeData({
|
||||
fontSize: 18,
|
||||
),
|
||||
).copyWith(
|
||||
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
backgroundColor: WidgetStateProperty.resolveWith<Color>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return bgDisabled;
|
||||
}
|
||||
return bgEnabled;
|
||||
},
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
foregroundColor: WidgetStateProperty.resolveWith<Color>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return fgDisabled;
|
||||
}
|
||||
return fgEnabled;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"account": "حسابي",
|
||||
"account": "الحساب",
|
||||
"unlock": "فتح القفل",
|
||||
"recoveryKey": "مفتاح الاسترداد",
|
||||
"counterAppBarTitle": "العداد",
|
||||
@@ -19,6 +19,20 @@
|
||||
"pleaseVerifyDetails": "من فضلك تأكد من بياناتك وحاول مرة أخرى",
|
||||
"codeIssuerHint": "المصدِّر",
|
||||
"codeSecretKeyHint": "الرمز السري",
|
||||
"secret": "سري",
|
||||
"all": "الكل",
|
||||
"notes": "ملاحظات",
|
||||
"notesLengthLimit": "يمكن أن تصل الملاحظات إلى {count} حرفا كحد أقصى",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "الحساب (you@domain.com)",
|
||||
"codeTagHint": "وسم",
|
||||
"accountKeyType": "نوع المفتاح",
|
||||
@@ -79,7 +93,7 @@
|
||||
"data": "البيانات",
|
||||
"importCodes": "استورد شيفرات",
|
||||
"importTypePlainText": "نص بسيط",
|
||||
"importTypeEnteEncrypted": "تصدير Ente Ecrypted",
|
||||
"importTypeEnteEncrypted": "تصدير مشفر ente",
|
||||
"passwordForDecryptingExport": "كلمة المرور لفك تشفير التصدير",
|
||||
"passwordEmptyError": "لا يمكن أن تكون كلمة المرور فارغة",
|
||||
"importFromApp": "استورد الشيفرات من {appName}",
|
||||
@@ -137,7 +151,6 @@
|
||||
"leaveFamily": "مغادرة خطة العائلة",
|
||||
"leaveFamilyMessage": "هل أنت متأكد من الخروج من خطة العائلة؟",
|
||||
"inFamilyPlanMessage": "أنت مندرج ضمن خطة عائلية!",
|
||||
"swipeHint": "اسحب لليسار لتحرير أو إزالة الرموز",
|
||||
"scan": "مسح",
|
||||
"scanACode": "فحص رمز Qr",
|
||||
"verify": "التحقق",
|
||||
@@ -182,6 +195,7 @@
|
||||
"security": "الأمان",
|
||||
"lockscreen": "شاشة القفل",
|
||||
"authToChangeLockscreenSetting": "الرجاء المصادقة لتغيير إعدادات شاشة القفل",
|
||||
"deviceLockEnablePreSteps": "لتمكين قفل التطبيق، فضلا أعد شيفرة مرور للجهاز أو قفل الشاشة في إعدادات نظامك.",
|
||||
"viewActiveSessions": "عرض الجلسات النشطة",
|
||||
"authToViewYourActiveSessions": "الرجاء المصادقة لعرض جلساتك النشطة",
|
||||
"searchHint": "بحث...",
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
"pleaseVerifyDetails": "Моля, проверете данните и опитайте отново",
|
||||
"codeIssuerHint": "Издател",
|
||||
"codeSecretKeyHint": "Код",
|
||||
"secret": "Код",
|
||||
"all": "Всички",
|
||||
"notes": "Бележки",
|
||||
"notesLengthLimit": "Бележките могат да съдържат най-много {count} знака",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Профил (you@domain.com)",
|
||||
"codeTagHint": "Етикет",
|
||||
"accountKeyType": "Тип на кода",
|
||||
@@ -34,6 +48,9 @@
|
||||
"nextTotpTitle": "следващ",
|
||||
"deleteCodeTitle": "Изтриване на кода?",
|
||||
"deleteCodeMessage": "Сигурни ли сте, че искате да изтриете този код? Това действие е необратимо.",
|
||||
"trashCode": "Преместване в кошчето на кода?",
|
||||
"trashCodeMessage": "Сигурни ли сте, че искате да изместите кода в кошчето за {account}?",
|
||||
"trash": "Кошче",
|
||||
"viewLogsAction": "Преглед на историята на действията",
|
||||
"sendLogsDescription": "Това ще изпрати файлове с история на действията, за да ни помогне да отстраним проблема Ви. Въпреки че вземаме предпазни мерки, за да гарантираме, че чувствителната информация не се регистрира, препоръчваме Ви да прегледате тези файлове с история на действията, преди да ги споделите.",
|
||||
"preparingLogsTitle": "Подготвяне на файловете с историята...",
|
||||
@@ -100,6 +117,7 @@
|
||||
"emailVerificationToggle": "Потвърждение чрез имейл",
|
||||
"emailVerificationEnableWarning": "За да избегнете загуба на достъп до акаунта си, не забравяйте да съхраните копие от Вашия имейл 2FA извън Ente Auth, преди да активирате потвърждението чрез имейл.",
|
||||
"authToChangeEmailVerificationSetting": "Моля, удостоверете се, за да промените потвърждението чрез имейл",
|
||||
"authenticateGeneric": "Моля, удостоверете се",
|
||||
"authToViewYourRecoveryKey": "Моля, удостоверете се, за да видите Вашия ключ за възстановяване",
|
||||
"authToChangeYourEmail": "Моля, удостоверете се, за да промените своя имейл",
|
||||
"authToChangeYourPassword": "Моля, удостоверете се, за да промените паролата си",
|
||||
@@ -137,7 +155,8 @@
|
||||
"leaveFamily": "Напуснете семейството",
|
||||
"leaveFamilyMessage": "Сигурни ли сте, че искате да напуснете семейния план?",
|
||||
"inFamilyPlanMessage": "Вие сте на семеен план!",
|
||||
"swipeHint": "Плъзнете наляво, за да редактирате или премахнете кодове",
|
||||
"hintForMobile": "Натиснете продължително код, за да го редактирате или премахнете.",
|
||||
"hintForDesktop": "Натиснете десен бутон върху код, за да го редактирате или премахнете.",
|
||||
"scan": "Сканиране",
|
||||
"scanACode": "Скениране на код",
|
||||
"verify": "Потвърждаване",
|
||||
@@ -193,6 +212,10 @@
|
||||
"scanAQrCode": "Скениране на QR код",
|
||||
"enterDetailsManually": "Въведете подробности ръчно",
|
||||
"edit": "Редактиране",
|
||||
"share": "Споделяне",
|
||||
"shareCodes": "Споделяне на кодове",
|
||||
"shareCodesDuration": "Изберете продължителността, за която искате да споделите кодовете.",
|
||||
"restore": "Възстановяване",
|
||||
"copiedToClipboard": "Копирано в буферната памет",
|
||||
"copiedNextToClipboard": "Следващият код е копиран в буферната памет",
|
||||
"error": "Грешка",
|
||||
@@ -346,6 +369,7 @@
|
||||
"sigInBackupReminder": "Моля, експортирайте Вашите кодове, за да сте сигурни, че имате резервно копие, от което можете да ги възстановите.",
|
||||
"offlineModeWarning": "Избрахте да продължите без резервни копия. Моля, направете ръчни резервни копия, за да сте сигурни, че Вашите кодове са в безопасност.",
|
||||
"showLargeIcons": "Показване на големи икони",
|
||||
"compactMode": "Компактен изглед",
|
||||
"shouldHideCode": "Скриване на кодове",
|
||||
"doubleTapToViewHiddenCode": "Можете да докоснете два пъти върху запис, за да видите кода",
|
||||
"focusOnSearchBar": "Фокусиране на търсенето при стартиране на приложението",
|
||||
@@ -466,5 +490,7 @@
|
||||
"pinLock": "Заключване с ПИН код",
|
||||
"enterPin": "Въведете ПИН код",
|
||||
"setNewPin": "Задаване на нов ПИН код",
|
||||
"importFailureDescNew": "Неуспешно обработване на избрания файл."
|
||||
"importFailureDescNew": "Неуспешно обработване на избрания файл.",
|
||||
"appLockNotEnabled": "Заключването на приложението не е активирано",
|
||||
"appLockNotEnabledDescription": "Моля, активирайте заключването на приложението от Сигурност > Заключване на приложението"
|
||||
}
|
||||
@@ -16,12 +16,26 @@
|
||||
"secretCanNotBeEmpty": "Geheimnis darf nicht leer sein",
|
||||
"bothIssuerAndAccountCanNotBeEmpty": "Sowohl Aussteller als auch Konto können nicht leer sein",
|
||||
"incorrectDetails": "Falsche Angaben",
|
||||
"pleaseVerifyDetails": "Überprüfe die Angaben und versuche es erneut",
|
||||
"pleaseVerifyDetails": "Bitte überprüfen Sie die Details und versuchen Sie es erneut",
|
||||
"codeIssuerHint": "Aussteller",
|
||||
"codeSecretKeyHint": "Geheimer Schlüssel",
|
||||
"secret": "Geheimnis",
|
||||
"all": "Alle",
|
||||
"notes": "Notizen",
|
||||
"notesLengthLimit": "Notizen können maximal {count} Zeichen lang sein",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Konto (you@domain.com)",
|
||||
"codeTagHint": "Tag",
|
||||
"accountKeyType": "Art des Keys",
|
||||
"accountKeyType": "Art des Schlüssels",
|
||||
"sessionExpired": "Sitzung abgelaufen",
|
||||
"@sessionExpired": {
|
||||
"description": "Title of the dialog when the users current session is invalid/expired"
|
||||
@@ -34,6 +48,9 @@
|
||||
"nextTotpTitle": "Nächster Code",
|
||||
"deleteCodeTitle": "Code löschen?",
|
||||
"deleteCodeMessage": "Sind Sie sicher, dass Sie diesen Code löschen wollen? Diese Aktion ist unumkehrbar.",
|
||||
"trashCode": "Code löschen?",
|
||||
"trashCodeMessage": "Sind Sie sicher, dass Sie den Code für {account} löschen wollen?",
|
||||
"trash": "Papierkorb",
|
||||
"viewLogsAction": "Protokolle anzeigen",
|
||||
"sendLogsDescription": "Dadurch werden Protokolle übermittelt, die uns bei der Behebung Ihres Problems helfen. Obwohl wir Vorkehrungen treffen, um sicherzustellen, dass keine sensiblen Informationen protokolliert werden, empfehlen wir Ihnen, diese Protokolle anzusehen, bevor Sie sie weitergeben.",
|
||||
"preparingLogsTitle": "Vorbereiten der Protokolle...",
|
||||
@@ -48,9 +65,9 @@
|
||||
},
|
||||
"copyEmailAction": "E-Mail kopieren",
|
||||
"exportLogsAction": "Protokolle exportieren",
|
||||
"reportABug": "Fehlermelden",
|
||||
"reportABug": "Einen Fehler melden",
|
||||
"crashAndErrorReporting": "Absturz- und Fehlerberichte",
|
||||
"reportBug": "Fehlermelden",
|
||||
"reportBug": "Fehler melden",
|
||||
"emailUsMessage": "Bitte senden Sie uns eine E-Mail an {email}",
|
||||
"@emailUsMessage": {
|
||||
"placeholders": {
|
||||
@@ -60,7 +77,7 @@
|
||||
}
|
||||
},
|
||||
"contactSupport": "Support kontaktieren",
|
||||
"rateUsOnStore": "Bewerte uns auf {storeName}",
|
||||
"rateUsOnStore": "Bewerten Sie uns auf {storeName}",
|
||||
"blog": "Blog",
|
||||
"merchandise": "Merchandise",
|
||||
"verifyPassword": "Passwort überprüfen",
|
||||
@@ -73,7 +90,7 @@
|
||||
"welcomeBack": "Willkommen zurück!",
|
||||
"madeWithLoveAtPrefix": "gemacht mit ❤️ bei ",
|
||||
"supportDevs": "Bei <bold-green>ente</bold-green> registrieren, um das Projekt zu unterstützen",
|
||||
"supportDiscount": "Benutze den Rabattcode \"AUTH\" für 10% Rabatt im ersten Jahr",
|
||||
"supportDiscount": "Benutzen Sie den Rabattcode \"AUTH\" für 10 % Rabatt im ersten Jahr",
|
||||
"changeEmail": "E-Mail ändern",
|
||||
"changePassword": "Passwort ändern",
|
||||
"data": "Datei",
|
||||
@@ -100,6 +117,7 @@
|
||||
"emailVerificationToggle": "E-Mail-Verifizierung",
|
||||
"emailVerificationEnableWarning": "Stellen Sie sicher, eine Kopie Ihrer Zwei-Faktor-Authentifizierung an anderer Stelle zu speichern, um zu vermeiden, dass Sie sich versehentlich aus Ihrem Account aussperren.",
|
||||
"authToChangeEmailVerificationSetting": "Bitte Authentifizieren um die E-Mail Bestätigung zu ändern",
|
||||
"authenticateGeneric": "Bitte authentifizieren",
|
||||
"authToViewYourRecoveryKey": "Bitte authentifizieren um ihren Wiederherstellungscode anzuzeigen",
|
||||
"authToChangeYourEmail": "Bitte authentifizieren um ihre Emailadresse zu ändern",
|
||||
"authToChangeYourPassword": "Bitte authentifizieren um ihr Passwort zu ändern",
|
||||
@@ -137,7 +155,6 @@
|
||||
"leaveFamily": "Familie verlassen",
|
||||
"leaveFamilyMessage": "Sind Sie sicher, dass Sie den Familien-Plan verlassen wollen?",
|
||||
"inFamilyPlanMessage": "Sie haben einen Familien-Plan!",
|
||||
"swipeHint": "Wischen Sie nach links, um Codes zu bearbeiten oder zu entfernen",
|
||||
"scan": "Scannen",
|
||||
"scanACode": "Scan einen Code",
|
||||
"verify": "Überprüfen Sie",
|
||||
@@ -164,7 +181,7 @@
|
||||
"invalidEmailTitle": "Ungültige E-Mail Adresse",
|
||||
"invalidEmailMessage": "Bitte geben Sie eine gültige E-Mail Adresse ein.",
|
||||
"deleteAccount": "Konto löschen",
|
||||
"deleteAccountQuery": "Es wird uns leid tun, Sie gehen zu sehen. Haben Sie ein Problem?",
|
||||
"deleteAccountQuery": "Es tut uns leid, dass Sie gehen. Haben Sie ein Problem?",
|
||||
"yesSendFeedbackAction": "Ja, Feedback senden",
|
||||
"noDeleteAccountAction": "Nein, Konto löschen",
|
||||
"initiateAccountDeleteTitle": "Bitte authentifizieren Sie sich, um die Kontolöschung einzuleiten",
|
||||
@@ -193,6 +210,10 @@
|
||||
"scanAQrCode": "QR-Code scannen",
|
||||
"enterDetailsManually": "Details manuell hinzufügen",
|
||||
"edit": "Editieren",
|
||||
"share": "Teilen",
|
||||
"shareCodes": "Codes teilen",
|
||||
"shareCodesDuration": "Wählen Sie die Dauer aus, für die Sie die Codes teilen möchten.",
|
||||
"restore": "Wiederherstellen",
|
||||
"copiedToClipboard": "In die Zwischenablage kopieren",
|
||||
"copiedNextToClipboard": "Nächster Code wurde in die Zwischenablage kopiert",
|
||||
"error": "Fehler",
|
||||
@@ -346,6 +367,7 @@
|
||||
"sigInBackupReminder": "Bitte exportieren Sie Ihre Codes, um sicherzustellen, dass Sie ein Backup haben, aus dem Sie wiederherstellen können.",
|
||||
"offlineModeWarning": "Sie haben sich dafür entschieden, ohne Sicherungen fortzufahren. Bitte führen Sie manuelle Sicherungen durch, um sicherzustellen, dass Ihre Codes sicher sind.",
|
||||
"showLargeIcons": "Große Symbole anzeigen",
|
||||
"compactMode": "Kompaktmodus",
|
||||
"shouldHideCode": "Codes ausblenden",
|
||||
"doubleTapToViewHiddenCode": "Sie können auf einen Eintrag doppelt tippen, um den Code anzuzeigen",
|
||||
"focusOnSearchBar": "Suche bei App-Start automatisch öffnen",
|
||||
@@ -466,5 +488,7 @@
|
||||
"pinLock": "PIN-Sperre",
|
||||
"enterPin": "PIN eingeben",
|
||||
"setNewPin": "Neue PIN festlegen",
|
||||
"importFailureDescNew": "Die ausgewählte Datei konnte nicht verarbeitet werden."
|
||||
"importFailureDescNew": "Die ausgewählte Datei konnte nicht verarbeitet werden.",
|
||||
"appLockNotEnabled": "App-Sperre nicht aktiviert",
|
||||
"appLockNotEnabledDescription": "Bitte aktivieren Sie die App-Sperre über Security > App-Sperre"
|
||||
}
|
||||
@@ -19,6 +19,20 @@
|
||||
"pleaseVerifyDetails": "Παρακαλούμε επιβεβαιώστε τα στοιχεία σας και προσπαθήστε ξανά",
|
||||
"codeIssuerHint": "Εκδότης",
|
||||
"codeSecretKeyHint": "Μυστικό Κλειδί",
|
||||
"secret": "Μυστικό",
|
||||
"all": "Όλα",
|
||||
"notes": "Σημειώσεις",
|
||||
"notesLengthLimit": "Οι σημειώσεις μπορούν να είναι το πολύ {count} χαρακτήρες",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Λογαριασμός (you@domain.com)",
|
||||
"codeTagHint": "Ετικέτα",
|
||||
"accountKeyType": "Τύπος κλειδιού",
|
||||
@@ -34,6 +48,9 @@
|
||||
"nextTotpTitle": "επόμενο",
|
||||
"deleteCodeTitle": "Διαγραφή κωδικού;",
|
||||
"deleteCodeMessage": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτόν τον κωδικό; Αυτή η ενέργεια είναι μη αναστρέψιμη.",
|
||||
"trashCode": "Διαγραφή κώδικα;",
|
||||
"trashCodeMessage": "Είστε σίγουροι ότι θέλετε να διαγράψετε τον κώδικα για το {account} σας;",
|
||||
"trash": "Διαγραφή",
|
||||
"viewLogsAction": "Προβολή αρχείων καταγραφής",
|
||||
"sendLogsDescription": "Αυτό θα στείλει σε μας όλα τα αρχεία καταγραφής για να μας βοηθήσει να αποσφαλματώσουμε το πρόβλημά σας. Ενώ λαμβάνουμε προφυλάξεις για να διασφαλίσουμε ότι οι ευαίσθητες πληροφορίες δεν καταγράφονται, σας ενθαρρύνουμε να δείτε αυτά τα αρχεία καταγραφής πριν τα μοιραστείτε.",
|
||||
"preparingLogsTitle": "Προετοιμασία αρχείων καταγραφής...",
|
||||
@@ -71,7 +88,7 @@
|
||||
"useRecoveryKey": "Χρήση κλειδιού ανάκτησης",
|
||||
"incorrectPasswordTitle": "Λάθος κωδικός πρόσβασης",
|
||||
"welcomeBack": "Καλωσορίσατε και πάλι!",
|
||||
"madeWithLoveAtPrefix": "γίνεται με ❤️ στο ",
|
||||
"madeWithLoveAtPrefix": "φτιαγμένη με ❤️ στο ",
|
||||
"supportDevs": "Εγγραφείτε στο <bold-green>ente</bold-green> για να μας υποστηρίξετε",
|
||||
"supportDiscount": "Χρησιμοποιήστε τον κωδικό κουπονιού \"AUTH\" για να πάρετε 10% από το πρώτο έτος",
|
||||
"changeEmail": "Αλλαγή διεύθυνσης ηλ. ταχυδρομείου",
|
||||
@@ -100,6 +117,7 @@
|
||||
"emailVerificationToggle": "Επαλήθευση διεύθυνσης ηλ. ταχυδρομείου",
|
||||
"emailVerificationEnableWarning": "Για να αποφύγετε να κλειδωθείτε έξω από τον λογαριασμό σας, φροντίστε να αποθηκεύσετε ένα αντίγραφο του 2FA του ηλ. ταχυδρομείου σας έξω από το Ente Auth πριν ενεργοποιήσετε την επαλήθευση μέσω ηλ. ταχυδρομείου.",
|
||||
"authToChangeEmailVerificationSetting": "Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να αλλάξετε την επαλήθευση ηλ. ταχυδρομείου",
|
||||
"authenticateGeneric": "Παρακαλώ πιστοποιήστε την ταυτότητά σας",
|
||||
"authToViewYourRecoveryKey": "Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να δείτε το κλειδί ανάκτησης",
|
||||
"authToChangeYourEmail": "Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να αλλάξετε τη διεύθυνση ηλ. ταχυδρομείου σας",
|
||||
"authToChangeYourPassword": "Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να αλλάξετε τον κωδικό πρόσβασής σας",
|
||||
@@ -137,7 +155,8 @@
|
||||
"leaveFamily": "Αποχώρηση από οικογένεια",
|
||||
"leaveFamilyMessage": "Είστε σίγουροι ότι θέλετε να φύγετε από το οικογενειακό πρόγραμμα;",
|
||||
"inFamilyPlanMessage": "Είστε σε οικογενειακό πρόγραμμα!",
|
||||
"swipeHint": "Σύρετε αριστερά για επεξεργασία ή αφαίρεση κωδικών",
|
||||
"hintForMobile": "Πατήστε παρατεταμένα σε έναν κωδικό για να τον τροποποιήσετε ή να τον διαγράψετε.",
|
||||
"hintForDesktop": "Κάντε δεξί κλικ σε έναν κωδικό για να τον τροποποιήσετε ή να τον διαγράψετε.",
|
||||
"scan": "Σάρωση",
|
||||
"scanACode": "Σάρωση κωδικού",
|
||||
"verify": "Επαλήθευση",
|
||||
@@ -193,6 +212,10 @@
|
||||
"scanAQrCode": "Σαρώστε έναν κωδικό QR",
|
||||
"enterDetailsManually": "Χειροκίνητη εισαγωγή στοιχείων",
|
||||
"edit": "Επεξεργασία",
|
||||
"share": "Κοινοποίηση",
|
||||
"shareCodes": "Κοινοποίηση κωδικών",
|
||||
"shareCodesDuration": "Επιλέξτε τη διάρκεια για την οποία θέλετε να μοιραστείτε τους κωδικούς.",
|
||||
"restore": "Επαναφορά",
|
||||
"copiedToClipboard": "Αντιγράφηκε στο πρόχειρο",
|
||||
"copiedNextToClipboard": "Αντιγράφηκε ο επόμενος κωδικός στο πρόχειρο",
|
||||
"error": "Σφάλμα",
|
||||
@@ -346,6 +369,7 @@
|
||||
"sigInBackupReminder": "Παρακαλώ εξάγετε τους κωδικούς σας για να βεβαιωθείτε ότι έχετε ένα αντίγραφο ασφαλείας από το οποίο μπορείτε να επαναφέρετε.",
|
||||
"offlineModeWarning": "Επιλέξατε να προχωρήσετε χωρίς αντίγραφα ασφαλείας. Παρακαλώ κάντε χειροκίνητα αντίγραφα ασφαλείας για να βεβαιωθείτε ότι οι κωδικοί σας είναι ασφαλείς.",
|
||||
"showLargeIcons": "Εμφάνιση μεγάλων εικονιδίων",
|
||||
"compactMode": "Συμπαγής λειτουργία",
|
||||
"shouldHideCode": "Απόκρυψη κωδικών",
|
||||
"doubleTapToViewHiddenCode": "Μπορείτε να πατήσετε δύο φορές σε μια καταχώρηση για να δείτε τον κωδικό",
|
||||
"focusOnSearchBar": "Εστίαση στην αναζήτηση κατά την εκκίνηση εφαρμογής",
|
||||
@@ -466,5 +490,7 @@
|
||||
"pinLock": "Κλείδωμα καρφιτσωμάτων",
|
||||
"enterPin": "Εισαγωγή PIN",
|
||||
"setNewPin": "Ορίστε νέο PIN",
|
||||
"importFailureDescNew": "Αδυναμία ανάλυσης του επιλεγμένου αρχείου."
|
||||
"importFailureDescNew": "Αδυναμία ανάλυσης του επιλεγμένου αρχείου.",
|
||||
"appLockNotEnabled": "Το κλείδωμα εφαρμογής δεν είναι ενεργοποιημένο",
|
||||
"appLockNotEnabledDescription": "Παρακαλώ ενεργοποιήστε το κλείδωμα εφαρμογής μέσω της επιλογής Ασφάλεια > Κλείδωμα εφαρμογής"
|
||||
}
|
||||
@@ -19,6 +19,20 @@
|
||||
"pleaseVerifyDetails": "Please verify the details and try again",
|
||||
"codeIssuerHint": "Issuer",
|
||||
"codeSecretKeyHint": "Secret Key",
|
||||
"secret": "Secret",
|
||||
"all": "All",
|
||||
"notes": "Notes",
|
||||
"notesLengthLimit": "Notes can be at most {count} characters long",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Account (you@domain.com)",
|
||||
"codeTagHint": "Tag",
|
||||
"accountKeyType": "Type of key",
|
||||
@@ -34,6 +48,9 @@
|
||||
"nextTotpTitle": "next",
|
||||
"deleteCodeTitle": "Delete code?",
|
||||
"deleteCodeMessage": "Are you sure you want to delete this code? This action is irreversible.",
|
||||
"trashCode": "Trash code?",
|
||||
"trashCodeMessage": "Are you sure you want to trash code for {account}?",
|
||||
"trash": "Trash",
|
||||
"viewLogsAction": "View logs",
|
||||
"sendLogsDescription": "This will send across logs to help us debug your issue. While we take precautions to ensure that sensitive information is not logged, we encourage you to view these logs before sharing them.",
|
||||
"preparingLogsTitle": "Preparing logs...",
|
||||
@@ -100,6 +117,7 @@
|
||||
"emailVerificationToggle": "Email verification",
|
||||
"emailVerificationEnableWarning": "To avoid getting locked out of your account, be sure to store a copy of your email 2FA outside of Ente Auth before enabling email verification.",
|
||||
"authToChangeEmailVerificationSetting": "Please authenticate to change email verification",
|
||||
"authenticateGeneric": "Please authenticate",
|
||||
"authToViewYourRecoveryKey": "Please authenticate to view your recovery key",
|
||||
"authToChangeYourEmail": "Please authenticate to change your email",
|
||||
"authToChangeYourPassword": "Please authenticate to change your password",
|
||||
@@ -123,21 +141,12 @@
|
||||
"oops": "Oops",
|
||||
"suggestFeatures": "Suggest features",
|
||||
"faq": "FAQ",
|
||||
"faq_q_1": "How secure is Auth?",
|
||||
"faq_a_1": "All codes you backup via Auth is stored end-to-end encrypted. This means only you can access your codes. Our apps are open source and our cryptography has been externally audited.",
|
||||
"faq_q_2": "Can I access my codes on desktop?",
|
||||
"faq_a_2": "You can access your codes on the web @ auth.ente.io.",
|
||||
"faq_q_3": "How can I delete codes?",
|
||||
"faq_a_3": "You can delete a code by swiping left on that item.",
|
||||
"faq_q_4": "How can I support this project?",
|
||||
"faq_a_4": "You can support the development of this project by subscribing to our Photos app @ ente.io.",
|
||||
"faq_q_5": "How can I enable FaceID lock in Auth",
|
||||
"faq_a_5": "You can enable FaceID lock under Settings → Security → Lockscreen.",
|
||||
"somethingWentWrongMessage": "Something went wrong, please try again",
|
||||
"leaveFamily": "Leave family",
|
||||
"leaveFamilyMessage": "Are you sure that you want to leave the family plan?",
|
||||
"inFamilyPlanMessage": "You are on a family plan!",
|
||||
"swipeHint": "Swipe left to edit or remove codes",
|
||||
"hintForMobile": "Long press on a code to edit or remove.",
|
||||
"hintForDesktop": "Right click on a code to edit or remove.",
|
||||
"scan": "Scan",
|
||||
"scanACode": "Scan a code",
|
||||
"verify": "Verify",
|
||||
@@ -193,6 +202,10 @@
|
||||
"scanAQrCode": "Scan a QR code",
|
||||
"enterDetailsManually": "Enter details manually",
|
||||
"edit": "Edit",
|
||||
"share": "Share",
|
||||
"shareCodes": "Share codes",
|
||||
"shareCodesDuration": "Select the duration for which you want to share codes.",
|
||||
"restore": "Restore",
|
||||
"copiedToClipboard": "Copied to clipboard",
|
||||
"copiedNextToClipboard": "Copied next code to clipboard",
|
||||
"error": "Error",
|
||||
@@ -346,6 +359,7 @@
|
||||
"sigInBackupReminder": "Please export your codes to ensure that you have a backup you can restore from.",
|
||||
"offlineModeWarning": "You have chosen to proceed without backups. Please take manual backups to make sure your codes are safe.",
|
||||
"showLargeIcons": "Show large icons",
|
||||
"compactMode": "Compact mode",
|
||||
"shouldHideCode": "Hide codes",
|
||||
"doubleTapToViewHiddenCode": "You can double tap on an entry to view code",
|
||||
"focusOnSearchBar": "Focus search on app start",
|
||||
@@ -466,5 +480,8 @@
|
||||
"pinLock": "Pin lock",
|
||||
"enterPin": "Enter PIN",
|
||||
"setNewPin": "Set new PIN",
|
||||
"importFailureDescNew": "Could not parse the selected file."
|
||||
"importFailureDescNew": "Could not parse the selected file.",
|
||||
"appLockNotEnabled": "App lock not enabled",
|
||||
"appLockNotEnabledDescription": "Please enable app lock from Security > App Lock",
|
||||
"authToViewPasskey": "Please authenticate to view passkey"
|
||||
}
|
||||
@@ -137,7 +137,6 @@
|
||||
"leaveFamily": "Dejar plan familiar",
|
||||
"leaveFamilyMessage": "¿Está seguro de que desea abandonar el plan familiar?",
|
||||
"inFamilyPlanMessage": "¡Estás en un plan familiar!",
|
||||
"swipeHint": "Deslice a la izquierda para editar o eliminar códigos",
|
||||
"scan": "Escanear",
|
||||
"scanACode": "Escanear un código",
|
||||
"verify": "Verificar",
|
||||
|
||||
@@ -92,7 +92,6 @@
|
||||
"leaveFamily": "خروج از طرح خانواده",
|
||||
"leaveFamilyMessage": "آیا مطمئنید که میخواهید از طرح خانواده خارج شوید؟",
|
||||
"inFamilyPlanMessage": "طرح خانواده برای شما فعال است!",
|
||||
"swipeHint": "برای ویرایش یا حذف کدها به چپ بکشید",
|
||||
"scan": "اسکن",
|
||||
"scanACode": "یک کد را اسکن کنید",
|
||||
"verify": "تایید",
|
||||
|
||||
@@ -106,7 +106,6 @@
|
||||
"leaveFamily": "Poistu perheestä",
|
||||
"leaveFamilyMessage": "Oletko varma että haluat jättää tämän perhemallin?",
|
||||
"inFamilyPlanMessage": "Olet perhemallissa!",
|
||||
"swipeHint": "Pyyhkäise vasemmalle muokataksesi tai poistaaksesi koodeja",
|
||||
"scan": "Lue",
|
||||
"scanACode": "Lue koodi",
|
||||
"verify": "Vahvista",
|
||||
|
||||
@@ -19,6 +19,19 @@
|
||||
"pleaseVerifyDetails": "Veuillez vérifier vos informations et réessayez",
|
||||
"codeIssuerHint": "Émetteur",
|
||||
"codeSecretKeyHint": "Clé secrète",
|
||||
"secret": "Confidentiel",
|
||||
"notes": "Notes",
|
||||
"notesLengthLimit": "Les notes peuvent contenir au maximum {count} caractères",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Compte (nom@exemple.com)",
|
||||
"codeTagHint": "Tag",
|
||||
"accountKeyType": "Type de clé",
|
||||
@@ -34,6 +47,9 @@
|
||||
"nextTotpTitle": "suivant",
|
||||
"deleteCodeTitle": "Supprimer le code ?",
|
||||
"deleteCodeMessage": "Êtes-vous sûr de vouloir supprimer ce code ? Cette action est irréversible.",
|
||||
"trashCode": "Supprimer le code?",
|
||||
"trashCodeMessage": "Êtes-vous sûr de vouloir supprimer le code pour {account}?",
|
||||
"trash": "Corbeille",
|
||||
"viewLogsAction": "Afficher les journaux",
|
||||
"sendLogsDescription": "Cela enverra des logs pour nous aider à déboguer votre problème. Bien que nous prenions des précautions pour nous assurer que les informations sensibles ne sont pas enregistrées, nous vous encourageons à consulter ces journaux avant de les partager.",
|
||||
"preparingLogsTitle": "Préparation des journaux...",
|
||||
@@ -100,6 +116,7 @@
|
||||
"emailVerificationToggle": "Vérification de l'adresse e-mail",
|
||||
"emailVerificationEnableWarning": "Si vous stockez le 2FA dans votre e-mail avec nous, l'activation de la vérification d'e-mail pourrait entraîner un blocage. Si vous êtes exclu d'un service, il se peut que vous ne puissiez pas vous connecter à l'autre.",
|
||||
"authToChangeEmailVerificationSetting": "Veuillez vous authentifier pour modifier votre adresse e-mail",
|
||||
"authenticateGeneric": "Veuillez vous authentifier",
|
||||
"authToViewYourRecoveryKey": "Veuillez vous authentifier pour afficher votre clé de récupération",
|
||||
"authToChangeYourEmail": "Veuillez vous authentifier pour modifier votre adresse e-mail",
|
||||
"authToChangeYourPassword": "Veuillez vous authentifier pour modifier votre mot de passe",
|
||||
@@ -137,7 +154,8 @@
|
||||
"leaveFamily": "Quitter le plan familial",
|
||||
"leaveFamilyMessage": "Êtes-vous sûr de vouloir quitter le plan familial ?",
|
||||
"inFamilyPlanMessage": "Vous êtes sur un plan familial !",
|
||||
"swipeHint": "Glisser vers la gauche pour modifier ou supprimer des codes",
|
||||
"hintForMobile": "Appuyez longuement sur un code pour modifier ou supprimer.",
|
||||
"hintForDesktop": "Clic droit sur un code à modifier ou à supprimer.",
|
||||
"scan": "Analyser",
|
||||
"scanACode": "Scanner un code",
|
||||
"verify": "Vérifier",
|
||||
@@ -193,6 +211,10 @@
|
||||
"scanAQrCode": "Scanner un QR Code",
|
||||
"enterDetailsManually": "Saisir les détails manuellement",
|
||||
"edit": "Éditer",
|
||||
"share": "Partager",
|
||||
"shareCodes": "Partager les codes",
|
||||
"shareCodesDuration": "Sélectionnez la durée pour laquelle vous souhaitez partager les codes.",
|
||||
"restore": "Restaurer",
|
||||
"copiedToClipboard": "Copié dans le presse-papiers",
|
||||
"copiedNextToClipboard": "Code suivant copié dans le presse-papiers",
|
||||
"error": "Erreur",
|
||||
@@ -346,6 +368,7 @@
|
||||
"sigInBackupReminder": "Veuillez exporter vos codes pour vous assurer que vous avez une sauvegarde à partir de laquelle vous pouvez restaurer.",
|
||||
"offlineModeWarning": "Vous avez choisi de procéder sans sauvegarde. Veuillez prendre des sauvegardes manuelles pour vous assurer que vos codes sont sûrs.",
|
||||
"showLargeIcons": "Afficher les grandes icônes",
|
||||
"compactMode": "Mode compact",
|
||||
"shouldHideCode": "Cacher les codes",
|
||||
"doubleTapToViewHiddenCode": "Vous pouvez appuyer deux fois sur une entrée pour afficher le code",
|
||||
"focusOnSearchBar": "Cibler le champ de recherche au démarrage de l'application",
|
||||
|
||||
@@ -126,7 +126,6 @@
|
||||
"leaveFamily": "עזוב משפחה",
|
||||
"leaveFamilyMessage": "האם אתה בטוח שאתה רוצה לעזוב את התוכנית המשפחתית?",
|
||||
"inFamilyPlanMessage": "אתה על תוכנית משפחתית!",
|
||||
"swipeHint": "החלק שמאלה כדי לערוך או להסיר קודים",
|
||||
"scan": "סרוק",
|
||||
"scanACode": "סרוק קוד",
|
||||
"verify": "אמת",
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
"account": "Akun",
|
||||
"unlock": "Buka",
|
||||
"recoveryKey": "Kunci pemulihan",
|
||||
"counterAppBarTitle": "Penghitung",
|
||||
"@counterAppBarTitle": {
|
||||
"description": "Text shown in the AppBar of the Counter Page"
|
||||
},
|
||||
"onBoardingBody": "Cadangkan kode 2FA kamu dengan aman",
|
||||
"onBoardingGetStarted": "Mulai",
|
||||
"setupFirstAccount": "Siapkan akun pertama kamu",
|
||||
@@ -9,12 +13,29 @@
|
||||
"qrCode": "Kode QR",
|
||||
"importEnterSetupKey": "Masukkan kunci penyiapan",
|
||||
"importAccountPageTitle": "Masukkan detail akun",
|
||||
"secretCanNotBeEmpty": "Rahasia tidak boleh kosong",
|
||||
"bothIssuerAndAccountCanNotBeEmpty": "Nama penerbit dan akun tidak boleh kosong",
|
||||
"incorrectDetails": "Rincian salah",
|
||||
"pleaseVerifyDetails": "Periksa kembali data kamu dan coba lagi",
|
||||
"codeIssuerHint": "Penerbit",
|
||||
"codeSecretKeyHint": "Kunci Rahasia",
|
||||
"secret": "Rahasia",
|
||||
"all": "Semua",
|
||||
"notes": "Catatan",
|
||||
"notesLengthLimit": "Catatan diperbolehkan sebanyak maksimal {count} karakter",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Akun (kamu@domain.com)",
|
||||
"codeTagHint": "Tag",
|
||||
"accountKeyType": "Tipe kunci",
|
||||
"sessionExpired": "Sesi berakhir",
|
||||
"@sessionExpired": {
|
||||
"description": "Title of the dialog when the users current session is invalid/expired"
|
||||
@@ -26,16 +47,38 @@
|
||||
"nextTotpTitle": "berikutnya",
|
||||
"deleteCodeTitle": "Hapus kode?",
|
||||
"deleteCodeMessage": "Apakah kamu yakin ingin menghapus kode ini? Tindakan ini tidak dapat dikembalikan ke semula.",
|
||||
"trashCode": "Hapus kode?",
|
||||
"trashCodeMessage": "Apakah anda yakin ingin menghapus kode untuk {account}?",
|
||||
"trash": "Hapus",
|
||||
"viewLogsAction": "Lihat log",
|
||||
"sendLogsDescription": "Langkah ini akan mengirimkan log untuk membantu kami menganalisa masalah kamu. Meskipun kami melakukan tindakan pencegahan untuk memastikan bahwa informasi sensitif tidak dicatat, kami menganjurkan kamu untuk melihat log ini sebelum membagikannya.",
|
||||
"preparingLogsTitle": "Menyiapkan log...",
|
||||
"emailLogsTitle": "Log email",
|
||||
"emailLogsMessage": "Harap kirim log ke {email}",
|
||||
"@emailLogsMessage": {
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyEmailAction": "Salin email",
|
||||
"exportLogsAction": "Ekspor log",
|
||||
"reportABug": "Laporkan bug",
|
||||
"crashAndErrorReporting": "Pelaporan Error dan Crash",
|
||||
"reportBug": "Laporkan bug",
|
||||
"emailUsMessage": "Harap email kami di {email}",
|
||||
"@emailUsMessage": {
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"contactSupport": "Hubungi dukungan",
|
||||
"rateUsOnStore": "Nilai kami di {storeName}",
|
||||
"blog": "Blog",
|
||||
"merchandise": "Merchandise",
|
||||
"verifyPassword": "Verifikasi sandi",
|
||||
"pleaseWait": "Harap tunggu...",
|
||||
"generatingEncryptionKeysTitle": "Membuat kunci enkripsi...",
|
||||
@@ -52,11 +95,16 @@
|
||||
"data": "Data",
|
||||
"importCodes": "Impor kode",
|
||||
"importTypePlainText": "Teks biasa",
|
||||
"importTypeEnteEncrypted": "Eksport enkripsi ente",
|
||||
"passwordForDecryptingExport": "Kata sandi untuk mendekripsi ekspor",
|
||||
"passwordEmptyError": "Kata sandi tidak boleh kosong",
|
||||
"importFromApp": "Impor kode dari {appName}",
|
||||
"importSelectJsonFile": "Pilih File JSON",
|
||||
"importSelectAppExport": "Pilih file ekspor dari {appName}",
|
||||
"importEnteEncGuide": "Pilih file enkripsi JSON yang telah diekspor dari Ente",
|
||||
"importRaivoGuide": "Gunakan opsi \"Export OTPs to Zip archive\" pada pengaturan Raivo.\n\nEkstrak file zip dan impor file JSON tersebut.",
|
||||
"importBitwardenGuide": "Gunakan opsi \"Export vault\" didalam fitur Bitwarden Tools dan impor file JSON yang tidak terenkripsi.",
|
||||
"importAegisGuide": "Gunakan opsi \"Export vault\" didalam fitur Bitwarden Tools dan impor file JSON yang tidak terenkripsi.",
|
||||
"selectFile": "Pilih file",
|
||||
"emailVerificationToggle": "Verifikasi email",
|
||||
"emailVerificationEnableWarning": "Untuk menghindari akun kamu terkunci, pastikan untuk menyimpan salinan 2FA email kamu di luar Ente Auth sebelum mengaktifkan verifikasi email.",
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
"pleaseVerifyDetails": "Verifica i dettagli e riprova",
|
||||
"codeIssuerHint": "Emittente",
|
||||
"codeSecretKeyHint": "Codice segreto",
|
||||
"secret": "Segreto",
|
||||
"all": "Tutto",
|
||||
"notes": "Note",
|
||||
"notesLengthLimit": "Le note possono essere al massimo {count} caratteri",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Account (username@dominio.it)",
|
||||
"codeTagHint": "Tag",
|
||||
"accountKeyType": "Tipo di chiave",
|
||||
@@ -34,6 +48,9 @@
|
||||
"nextTotpTitle": "successivo",
|
||||
"deleteCodeTitle": "Eliminare il codice?",
|
||||
"deleteCodeMessage": "Sei sicuro di voler rimuovere questo codice? L'azione è irreversibile.",
|
||||
"trashCode": "Codice del cestino?",
|
||||
"trashCodeMessage": "Sei sicuro di voler cestinare il codice per {account}?",
|
||||
"trash": "Cestino",
|
||||
"viewLogsAction": "Visualizza i log",
|
||||
"sendLogsDescription": "Invierai i tuoi log per aiutarci a risolvere il tuo problema. Prendiamo precauzioni per garantire che le informazioni sensibili non siano registrate, tuttavia ti invitiamo a leggerli prima di condividerli con noi.",
|
||||
"preparingLogsTitle": "Preparando i log...",
|
||||
@@ -100,6 +117,7 @@
|
||||
"emailVerificationToggle": "Verifica email",
|
||||
"emailVerificationEnableWarning": "Se memorizzate il 2FA per accedere alla vostra email con noi, l'attivazione della verifica dell'email potrebbe provocare dei problemi. Se siete rimasti bloccati fuori da un servizio, potreste non essere in grado di accedere anche all'altro.",
|
||||
"authToChangeEmailVerificationSetting": "Autenticati per cambiare la verifica email",
|
||||
"authenticateGeneric": "Si prega di autenticarsi",
|
||||
"authToViewYourRecoveryKey": "Autenticati per visualizzare la tua chiave di recupero",
|
||||
"authToChangeYourEmail": "Autenticati per cambiare la tua email",
|
||||
"authToChangeYourPassword": "Autenticati per cambiare la tua password",
|
||||
@@ -137,7 +155,8 @@
|
||||
"leaveFamily": "Abbandona il piano famiglia",
|
||||
"leaveFamilyMessage": "Sei sicuro di voler uscire dal piano famiglia?",
|
||||
"inFamilyPlanMessage": "Sei un utente con piano famiglia!",
|
||||
"swipeHint": "Scorri a sinistra per modificare o rimuovere i codici",
|
||||
"hintForMobile": "Premi a lungo su un codice per modificare o rimuovere.",
|
||||
"hintForDesktop": "Fare clic con il tasto destro su un codice per modificare o rimuovere.",
|
||||
"scan": "Scansiona",
|
||||
"scanACode": "Scansiona un codice",
|
||||
"verify": "Verifica",
|
||||
@@ -193,6 +212,10 @@
|
||||
"scanAQrCode": "Scansiona un codice QR",
|
||||
"enterDetailsManually": "Inserisci i dettagli manualmente",
|
||||
"edit": "Modifica",
|
||||
"share": "Condividi",
|
||||
"shareCodes": "Condividi codice",
|
||||
"shareCodesDuration": "Seleziona la durata per la quale vuoi condividere i codici.",
|
||||
"restore": "Ripristina",
|
||||
"copiedToClipboard": "Copiato negli appunti",
|
||||
"copiedNextToClipboard": "Copiato il codice successivo negli appunti",
|
||||
"error": "Errore",
|
||||
@@ -346,6 +369,7 @@
|
||||
"sigInBackupReminder": "Si prega di esportare i codici per assicurarsi di avere un backup da cui è possibile ripristinare.",
|
||||
"offlineModeWarning": "Hai scelto di procedere senza backup. Si prega di eseguire backup manuali per assicurarsi che i codici siano al sicuro.",
|
||||
"showLargeIcons": "Mostra icone grandi",
|
||||
"compactMode": "Modalità compatta",
|
||||
"shouldHideCode": "Nascondi i codici",
|
||||
"doubleTapToViewHiddenCode": "Puoi toccare due volte una voce per visualizzare il codice",
|
||||
"focusOnSearchBar": "Apri ricerca all'avvio dell'app",
|
||||
@@ -466,5 +490,7 @@
|
||||
"pinLock": "Blocco con PIN",
|
||||
"enterPin": "Inserisci PIN",
|
||||
"setNewPin": "Imposta un nuovo PIN",
|
||||
"importFailureDescNew": "Impossibile elaborare il file selezionato."
|
||||
"importFailureDescNew": "Impossibile elaborare il file selezionato.",
|
||||
"appLockNotEnabled": "Blocco app non abilitato",
|
||||
"appLockNotEnabledDescription": "Si prega di abilitare il blocco dell'app da Sicurezza > Blocco App"
|
||||
}
|
||||
@@ -137,7 +137,6 @@
|
||||
"leaveFamily": "ファミリープランから退会",
|
||||
"leaveFamilyMessage": "本当にファミリープランを退会しますか?",
|
||||
"inFamilyPlanMessage": "ファミリープランに入会しています!",
|
||||
"swipeHint": "左にスワイプしてコードを編集、削除します",
|
||||
"scan": "読み取り",
|
||||
"scanACode": "コードを読み取り",
|
||||
"verify": "認証",
|
||||
|
||||
@@ -137,7 +137,6 @@
|
||||
"leaveFamily": "Familie verlaten",
|
||||
"leaveFamilyMessage": "Weet je zeker dat je het familie-plan wil verlaten?",
|
||||
"inFamilyPlanMessage": "Je hebt een familie-plan!",
|
||||
"swipeHint": "Veeg naar links om codes te bewerken of te verwijderen",
|
||||
"scan": "Scannen",
|
||||
"scanACode": "Scan een code",
|
||||
"verify": "Verifiëren",
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
"pleaseVerifyDetails": "Prosimy zweryfikować szczegóły i spróbować ponownie",
|
||||
"codeIssuerHint": "Wydawca",
|
||||
"codeSecretKeyHint": "Tajny Klucz",
|
||||
"secret": "Sekret",
|
||||
"all": "Wszystko",
|
||||
"notes": "Notatki",
|
||||
"notesLengthLimit": "Notatki mogą mieć maksymalnie {count} znaków",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Konto (ty@domena.com)",
|
||||
"codeTagHint": "Oznacz",
|
||||
"accountKeyType": "Rodzaj klucza",
|
||||
@@ -34,6 +48,9 @@
|
||||
"nextTotpTitle": "dalej",
|
||||
"deleteCodeTitle": "Usunąć kod?",
|
||||
"deleteCodeMessage": "Czy na pewno chcesz usunąć ten kod? Ta akcja jest nieodwracalna.",
|
||||
"trashCode": "Przenieść kod do kosza?",
|
||||
"trashCodeMessage": "Czy na pewno chcesz przenieść do kosza kod dla {account}?",
|
||||
"trash": "Kosz",
|
||||
"viewLogsAction": "Wyświetl logi",
|
||||
"sendLogsDescription": "Spowoduje to przesłanie logów, które pomogą nam rozwiązać Twój problem. Chociaż podejmujemy środki ostrożności, aby zapewnić, że wrażliwe informacje nie są rejestrowane, zachęcamy do obejrzenia tych dzienników przed ich udostępnieniem.",
|
||||
"preparingLogsTitle": "Przygotowywanie logów...",
|
||||
@@ -100,6 +117,7 @@
|
||||
"emailVerificationToggle": "Weryfikacja e-mail",
|
||||
"emailVerificationEnableWarning": "Aby uniknąć zablokowania się z konta, upewnij się, że przed włączeniem weryfikacji e-mail przechowujesz kopię 2FA dla swojego adresu e-mail poza Ente Auth.",
|
||||
"authToChangeEmailVerificationSetting": "Prosimy uwierzytelnić się, aby zmienić weryfikację e-mail",
|
||||
"authenticateGeneric": "Prosimy się uwierzytelnić",
|
||||
"authToViewYourRecoveryKey": "Prosimy uwierzytelnić się, aby wyświetlić swój klucz odzyskiwania",
|
||||
"authToChangeYourEmail": "Prosimy uwierzytelnić się, aby zmienić swój adres e-mail",
|
||||
"authToChangeYourPassword": "Prosimy uwierzytelnić się, aby zmienić hasło",
|
||||
@@ -137,7 +155,8 @@
|
||||
"leaveFamily": "Opuść rodzinę",
|
||||
"leaveFamilyMessage": "Czy jesteś pewien/pewna, że chcesz opuścić plan rodzinny?",
|
||||
"inFamilyPlanMessage": "Jesteś w planie rodzinnym!",
|
||||
"swipeHint": "Przesuń palcem w lewo, aby edytować lub usunąć kody",
|
||||
"hintForMobile": "Przytrzymaj kod, aby edytować lub usunąć.",
|
||||
"hintForDesktop": "Kliknij prawym przyciskiem myszy na kod, aby edytować lub usunąć.",
|
||||
"scan": "Skanuj",
|
||||
"scanACode": "Skanuj kod",
|
||||
"verify": "Zweryfikuj",
|
||||
@@ -193,6 +212,10 @@
|
||||
"scanAQrCode": "Zeskanuj kod QR",
|
||||
"enterDetailsManually": "Wprowadź dane ręcznie",
|
||||
"edit": "Edytuj",
|
||||
"share": "Udostępnij",
|
||||
"shareCodes": "Udostępnij kody",
|
||||
"shareCodesDuration": "Wybierz okres, przez który chcesz udostępniać kody.",
|
||||
"restore": "Przywróć",
|
||||
"copiedToClipboard": "Skopiowano do schowka",
|
||||
"copiedNextToClipboard": "Skopiowano następny kod do schowka",
|
||||
"error": "Błąd",
|
||||
@@ -346,6 +369,7 @@
|
||||
"sigInBackupReminder": "Prosimy wyeksportować swoje kody, aby upewnić się, że masz kopię zapasową, z której możesz przywrócić swoje kody.",
|
||||
"offlineModeWarning": "Zdecydowano się kontynuować bez kopii zapasowych. Prosimy wykonywać ręczne kopie zapasowe, aby upewnić się, że Twoje kody są bezpieczne.",
|
||||
"showLargeIcons": "Pokaż duże ikony",
|
||||
"compactMode": "Tryb kompaktowy",
|
||||
"shouldHideCode": "Ukryj kody",
|
||||
"doubleTapToViewHiddenCode": "Możesz kliknąć dwukrotnie na wpis, aby wyświetlić kod",
|
||||
"focusOnSearchBar": "Uaktywnij wyszukiwanie przy uruchamianiu aplikacji",
|
||||
@@ -466,5 +490,7 @@
|
||||
"pinLock": "Blokada PIN",
|
||||
"enterPin": "Wprowadź kod PIN",
|
||||
"setNewPin": "Ustaw nowy kod PIN",
|
||||
"importFailureDescNew": "Nie udało się przetworzyć wybranego pliku."
|
||||
"importFailureDescNew": "Nie udało się przetworzyć wybranego pliku.",
|
||||
"appLockNotEnabled": "Blokada aplikacji nie jest włączona",
|
||||
"appLockNotEnabledDescription": "Prosimy włączyć blokadę aplikacji z Zabezpieczenia > Blokada Aplikacji"
|
||||
}
|
||||
@@ -19,6 +19,20 @@
|
||||
"pleaseVerifyDetails": "Por favor, verifique os detalhes e tente novamente",
|
||||
"codeIssuerHint": "Emissor",
|
||||
"codeSecretKeyHint": "Chave secreta",
|
||||
"secret": "Secreto",
|
||||
"all": "Todos",
|
||||
"notes": "Notas",
|
||||
"notesLengthLimit": "Notas tem um limite de até {count} caracteres",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Conta (você@domínio.com)",
|
||||
"codeTagHint": "Etiqueta",
|
||||
"accountKeyType": "Tipo de chave",
|
||||
@@ -34,6 +48,9 @@
|
||||
"nextTotpTitle": "avançar",
|
||||
"deleteCodeTitle": "Apagar código?",
|
||||
"deleteCodeMessage": "Deseja mesmo excluir este código? Esta ação é irreversível.",
|
||||
"trashCode": "Excluir código?",
|
||||
"trashCodeMessage": "Você tem certeza de que quer excluir o código para {account}?",
|
||||
"trash": "Excluir",
|
||||
"viewLogsAction": "Ver logs",
|
||||
"sendLogsDescription": "Isto compartilhará seus dados para nos ajudarmos a resolver seu problema. Enquanto tomamos precauções para ter certeza que as informações sensíveis não estejam registradas, nós encorajamos você a visualizar esses dados antes de compartilhá-los.",
|
||||
"preparingLogsTitle": "Preparando logs...",
|
||||
@@ -100,6 +117,7 @@
|
||||
"emailVerificationToggle": "Verificação por e-mail",
|
||||
"emailVerificationEnableWarning": "Para evitar ficar bloqueado do acesso a sua conta, certifique-se de armazenar uma cópia do código 2FA do seu e-mail fora do Ente Auth antes de habilitar a verificação por e-mail.",
|
||||
"authToChangeEmailVerificationSetting": "Por favor, autentique-se para alterar a verificação por e-mail",
|
||||
"authenticateGeneric": "Por favor, autentique",
|
||||
"authToViewYourRecoveryKey": "Por favor, autentique-se para visualizar sua chave de recuperação",
|
||||
"authToChangeYourEmail": "Por favor, autentique-se para alterar o seu e-mail",
|
||||
"authToChangeYourPassword": "Por favor, autentique-se para alterar sua senha",
|
||||
@@ -137,7 +155,8 @@
|
||||
"leaveFamily": "Sair da família",
|
||||
"leaveFamilyMessage": "Deseja mesmo sair do plano familiar?",
|
||||
"inFamilyPlanMessage": "Você está em um plano familiar!",
|
||||
"swipeHint": "Deslize para a esquerda para editar ou remover os códigos",
|
||||
"hintForMobile": "Pressione em um código para editar ou excluir.",
|
||||
"hintForDesktop": "Clique esquerdo em um código para editar ou excluir.",
|
||||
"scan": "Escanear",
|
||||
"scanACode": "Escanear código",
|
||||
"verify": "Verificar",
|
||||
@@ -193,6 +212,10 @@
|
||||
"scanAQrCode": "Escanear QR code",
|
||||
"enterDetailsManually": "Inserir dados manualmente",
|
||||
"edit": "Editar",
|
||||
"share": "Compartilhar",
|
||||
"shareCodes": "Compartilhar códigos",
|
||||
"shareCodesDuration": "Selecione a duração em que você queira compartilhar os códigos.",
|
||||
"restore": "Restaurar",
|
||||
"copiedToClipboard": "Copiado para a área de transferência",
|
||||
"copiedNextToClipboard": "Próximo código copiado para a área de transferência",
|
||||
"error": "Erro",
|
||||
@@ -346,6 +369,7 @@
|
||||
"sigInBackupReminder": "Por favor, exporte seus códigos para garantir que você tenha um backup do qual você possa restaurar.",
|
||||
"offlineModeWarning": "Você escolheu prosseguir sem backups. Por favor, faça backups manuais para ter certeza de que seus códigos estão seguros.",
|
||||
"showLargeIcons": "Mostrar ícones grandes",
|
||||
"compactMode": "Modo compacto",
|
||||
"shouldHideCode": "Ocultar códigos",
|
||||
"doubleTapToViewHiddenCode": "Você pode tocar duas vezes em uma entrada para ver o código",
|
||||
"focusOnSearchBar": "Foco na busca ao iniciar o app",
|
||||
@@ -466,5 +490,7 @@
|
||||
"pinLock": "Bloqueio PIN",
|
||||
"enterPin": "Insira o PIN",
|
||||
"setNewPin": "Definir novo PIN",
|
||||
"importFailureDescNew": "Não foi possível analisar o arquivo selecionado."
|
||||
"importFailureDescNew": "Não foi possível analisar o arquivo selecionado.",
|
||||
"appLockNotEnabled": "Bloqueio de aplicativo não ativado",
|
||||
"appLockNotEnabledDescription": "Ative o bloqueio de aplicativo em Segurança > Bloqueio de aplicativo"
|
||||
}
|
||||
@@ -137,7 +137,6 @@
|
||||
"leaveFamily": "Покинуть семью",
|
||||
"leaveFamilyMessage": "Вы уверены, что хотите отказаться от семейного плана?",
|
||||
"inFamilyPlanMessage": "Вы на семейном плане!",
|
||||
"swipeHint": "Проведите пальцем влево, чтобы отредактировать или удалить коды",
|
||||
"scan": "Сканировать",
|
||||
"scanACode": "Сканировать QR-код",
|
||||
"verify": "Подтвердить",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"account": "Konto",
|
||||
"account": "Účet",
|
||||
"unlock": "Odomknúť",
|
||||
"recoveryKey": "Kľúč pre obnovenie",
|
||||
"counterAppBarTitle": "Počítadlo",
|
||||
@@ -8,18 +8,18 @@
|
||||
},
|
||||
"onBoardingBody": "Zabezpečte svoje kódy 2FA",
|
||||
"onBoardingGetStarted": "Poďme na to",
|
||||
"setupFirstAccount": "Vytvorte svoj prvý účet",
|
||||
"setupFirstAccount": "Nastav si svoj prvý účet",
|
||||
"importScanQrCode": "Naskenovať QR kód",
|
||||
"qrCode": "QR kód",
|
||||
"importEnterSetupKey": "Vložte kľúč nastavenia",
|
||||
"importAccountPageTitle": "Vložte detaily o konte",
|
||||
"importAccountPageTitle": "Zadaj údaje o účte",
|
||||
"secretCanNotBeEmpty": "Tajný kľúč nemôže ostať prázdny",
|
||||
"bothIssuerAndAccountCanNotBeEmpty": "Buď vydavateľ alebo účet nemôže ostať prázdny",
|
||||
"bothIssuerAndAccountCanNotBeEmpty": "Vydavateľ aj účet nemôžu byť prázdne",
|
||||
"incorrectDetails": "Chybné údaje",
|
||||
"pleaseVerifyDetails": "Prosím, skontrolujte svoje údaje a skúste to znova",
|
||||
"codeIssuerHint": "Vydavateľ",
|
||||
"codeSecretKeyHint": "Tajný kľúč",
|
||||
"codeAccountHint": "Konto (ucet@domena.com)",
|
||||
"codeAccountHint": "Účet (ty@domena.sk)",
|
||||
"codeTagHint": "Tag",
|
||||
"accountKeyType": "Typ kľúča",
|
||||
"sessionExpired": "Relácia vypršala",
|
||||
@@ -137,7 +137,6 @@
|
||||
"leaveFamily": "Opustiť rodinku",
|
||||
"leaveFamilyMessage": "Ste si istý, že chcete opustiť rodinku?",
|
||||
"inFamilyPlanMessage": "Ste prihlásený k rodinke!",
|
||||
"swipeHint": "Potiahnite doľava pre upravenie alebo vymazanie kódov",
|
||||
"scan": "Skenovať",
|
||||
"scanACode": "Skenovať kód",
|
||||
"verify": "Overiť",
|
||||
@@ -147,7 +146,7 @@
|
||||
"twoFactorAuthTitle": "Dvojfaktorové overovanie",
|
||||
"passkeyAuthTitle": "Overenie pomocou passkey",
|
||||
"verifyPasskey": "Overiť passkey",
|
||||
"recoverAccount": "Obnoviť konto",
|
||||
"recoverAccount": "Obnoviť účet",
|
||||
"enterRecoveryKeyHint": "Vložte váš kód pre obnovenie",
|
||||
"recover": "Obnoviť",
|
||||
"contactSupportViaEmailMessage": "Pošlite e-mail na adresu {email} z vašej registrovanej e-mailovej adresy",
|
||||
@@ -163,13 +162,13 @@
|
||||
"enterEmailHint": "Zadajte vašu emailovú adresu",
|
||||
"invalidEmailTitle": "Neplatná emailová adresa",
|
||||
"invalidEmailMessage": "Zadajte platnú e-mailovú adresu.",
|
||||
"deleteAccount": "Odstrániť konto",
|
||||
"deleteAccountQuery": "Bude nám tu bez vás smutno. Vyskytol sa nejaký problém?",
|
||||
"deleteAccount": "Odstrániť účet",
|
||||
"deleteAccountQuery": "Bude nám ľúto ak odídeš. Máš nejaký problém?",
|
||||
"yesSendFeedbackAction": "Áno, odoslať spätnú väzbu",
|
||||
"noDeleteAccountAction": "Nie, odstrániť účet",
|
||||
"initiateAccountDeleteTitle": "Pre odstránenie účtu sa musíte overiť",
|
||||
"initiateAccountDeleteTitle": "Je potrebné overenie pre spustenie odstránenia účtu",
|
||||
"sendEmail": "Odoslať email",
|
||||
"createNewAccount": "Vytvoriť nové konto",
|
||||
"createNewAccount": "Vytvoriť nový účet",
|
||||
"weakStrength": "Slabé",
|
||||
"strongStrength": "Silné",
|
||||
"moderateStrength": "Mierne",
|
||||
@@ -353,8 +352,8 @@
|
||||
"editCodeAuthMessage": "Overte sa pre zmenu kódu",
|
||||
"deleteCodeAuthMessage": "Overte sa pre vymazanie kódu",
|
||||
"showQRAuthMessage": "Overte sa pre zobrazenie QR kódu",
|
||||
"confirmAccountDeleteTitle": "Potvrdiť odstránenie účtu",
|
||||
"confirmAccountDeleteMessage": "Tento účet je prepojený s inými aplikáciami Ente, ak nejaké používate.\n\nVšetky nahrané údaje v aplikáciách od Ente budú naplánované na výmaz a váš účet bude natrvalo odstránený.",
|
||||
"confirmAccountDeleteTitle": "Potvrď odstránenie účtu",
|
||||
"confirmAccountDeleteMessage": "Tento účet je prepojený s inými aplikáciami Ente, ak nejaké používaš.\n\nTvoje nahrané údaje vo všetkých Ente aplikáciách budú naplánované na odstránenie a tvoj účet bude natrvalo odstránený.",
|
||||
"androidBiometricHint": "Overiť identitu",
|
||||
"@androidBiometricHint": {
|
||||
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
|
||||
@@ -410,7 +409,7 @@
|
||||
"noInternetConnection": "Žiadne internetové pripojenie",
|
||||
"pleaseCheckYourInternetConnectionAndTryAgain": "Skontrolujte svoje internetové pripojenie a skúste to znova.",
|
||||
"signOutFromOtherDevices": "Odhlásiť sa z iných zariadení",
|
||||
"signOutOtherBody": "Ak si myslíte, že niekto môže vedieť vaše heslo, môžete vynútiť odhlásenie všetkých ostatných zariadení vo vašom účte.",
|
||||
"signOutOtherBody": "Ak si myslíš, že by niekto mohol poznať tvoje heslo, môžeš vynútiť odhlásenie všetkých ostatných zariadení používajúcich tvoj účet.",
|
||||
"signOutOtherDevices": "Odhlásiť iné zariadenie",
|
||||
"doNotSignOut": "Neodhlasovať",
|
||||
"hearUsWhereTitle": "Ako ste sa dozvedeli o Ente? (voliteľné)",
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
"pleaseVerifyDetails": "Kontrollera dina detaljer och försök igen",
|
||||
"codeIssuerHint": "Utfärdare",
|
||||
"codeSecretKeyHint": "Secret Key",
|
||||
"all": "Alla",
|
||||
"codeAccountHint": "Konto (du@domän.com)",
|
||||
"codeTagHint": "Tagg",
|
||||
"sessionExpired": "Sessionen har gått ut",
|
||||
"@sessionExpired": {
|
||||
"description": "Title of the dialog when the users current session is invalid/expired"
|
||||
@@ -26,6 +28,7 @@
|
||||
"nextTotpTitle": "nästa",
|
||||
"deleteCodeTitle": "Radera kod?",
|
||||
"deleteCodeMessage": "Vill du ta bort den här koden? Det går inte att ångra den här åtgärden.",
|
||||
"trash": "Papperskorg",
|
||||
"viewLogsAction": "Visa loggar",
|
||||
"emailLogsTitle": "E-posta loggar",
|
||||
"emailLogsMessage": "Skicka loggarna till {email}",
|
||||
@@ -62,8 +65,14 @@
|
||||
"welcomeBack": "Välkommen tillbaka!",
|
||||
"changePassword": "Ändra lösenord",
|
||||
"importCodes": "Importera koder",
|
||||
"importFromApp": "Importera koder från {appName}",
|
||||
"importSelectJsonFile": "Välj JSON-fil",
|
||||
"exportCodes": "Exportera koder",
|
||||
"importLabel": "Importera",
|
||||
"selectFile": "Välj fil",
|
||||
"authToViewYourRecoveryKey": "Autentisera för att visa din återställningsnyckel",
|
||||
"authToChangeYourEmail": "Autentisera för att ändra din e-postadress",
|
||||
"authToChangeYourPassword": "Autentisera för att ändra ditt lösenord",
|
||||
"ok": "OK",
|
||||
"cancel": "Avbryt",
|
||||
"yes": "Ja",
|
||||
@@ -79,6 +88,7 @@
|
||||
"faq": "FAQ",
|
||||
"scan": "Skanna",
|
||||
"twoFactorAuthTitle": "Tvåfaktorsautentisering",
|
||||
"verifyPasskey": "Verifiera nyckel",
|
||||
"enterRecoveryKeyHint": "Ange din återställningsnyckel",
|
||||
"invalidQRCode": "Ogiltig QR-kod",
|
||||
"noRecoveryKeyTitle": "Ingen återställningsnyckel?",
|
||||
@@ -96,6 +106,10 @@
|
||||
"close": "Stäng",
|
||||
"selectLanguage": "Välj språk",
|
||||
"language": "Språk",
|
||||
"security": "Säkerhet",
|
||||
"lockscreen": "Låsskärm",
|
||||
"viewActiveSessions": "Visa aktiva sessioner",
|
||||
"authToViewYourActiveSessions": "Autentisera för att visa dina aktiva sessioner",
|
||||
"searchHint": "Sök...",
|
||||
"search": "Sök",
|
||||
"sorryUnableToGenCode": "Tyvärr, det gick inte att generera en kod för {issuerName}",
|
||||
@@ -104,6 +118,9 @@
|
||||
"scanAQrCode": "Skanna en QR-kod",
|
||||
"enterDetailsManually": "Ange uppgifter manuellt",
|
||||
"edit": "Redigera",
|
||||
"share": "Dela",
|
||||
"shareCodes": "Dela koder",
|
||||
"restore": "Återställ",
|
||||
"copiedToClipboard": "Kopierat till urklipp",
|
||||
"copiedNextToClipboard": "Kopierade nästa kod till urklipp",
|
||||
"error": "Fel",
|
||||
@@ -130,6 +147,7 @@
|
||||
"privacyPolicyTitle": "Integritetspolicy",
|
||||
"termsOfServicesTitle": "Villkor",
|
||||
"encryption": "Kryptering",
|
||||
"setPasswordTitle": "Ställ in lösenord",
|
||||
"changePasswordTitle": "Ändra lösenord",
|
||||
"resetPasswordTitle": "Återställ lösenord",
|
||||
"encryptionKeys": "Krypteringsnycklar",
|
||||
@@ -169,6 +187,9 @@
|
||||
"signInToBackup": "Logga in för att säkerhetskopiera dina koder",
|
||||
"singIn": "Logga in",
|
||||
"shouldHideCode": "Dölj koder",
|
||||
"editCodeAuthMessage": "Autentisera för att redigera kod",
|
||||
"deleteCodeAuthMessage": "Autentisera för att radera kod",
|
||||
"showQRAuthMessage": "Autentisera för att visa QR-kod",
|
||||
"androidCancelButton": "Avbryt",
|
||||
"@androidCancelButton": {
|
||||
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters."
|
||||
@@ -180,7 +201,11 @@
|
||||
"noInternetConnection": "Ingen internetanslutning",
|
||||
"pleaseCheckYourInternetConnectionAndTryAgain": "Kontrollera din internetanslutning och försök igen.",
|
||||
"signOutOtherDevices": "Logga ut andra enheter",
|
||||
"passkey": "Nyckel",
|
||||
"loginSessionExpiredDetails": "Din session har upphört. Logga in igen.",
|
||||
"tags": "Taggar",
|
||||
"createNewTag": "Skapa ny tagg",
|
||||
"tag": "Tagg",
|
||||
"create": "Skapa",
|
||||
"editTag": "Redigera tagg",
|
||||
"deleteTagTitle": "Radera tagg?",
|
||||
@@ -190,9 +215,9 @@
|
||||
"next": "Nästa",
|
||||
"tooManyIncorrectAttempts": "För många felaktiga försök",
|
||||
"tapToUnlock": "Tryck för att låsa upp",
|
||||
"setNewPassword": "Ange nytt lösenord",
|
||||
"setNewPassword": "Ställ in nytt lösenord",
|
||||
"deviceLock": "Enhetslås",
|
||||
"hideContent": "Dölj innehåll",
|
||||
"enterPin": "Ange PIN-kod",
|
||||
"setNewPin": "Ange ny PIN-kod"
|
||||
"setNewPin": "Ställ in ny PIN-kod"
|
||||
}
|
||||
@@ -132,7 +132,6 @@
|
||||
"leaveFamily": "Leave family",
|
||||
"leaveFamilyMessage": "Are you sure that you want to leave the family plan?",
|
||||
"inFamilyPlanMessage": "You are on a family plan!",
|
||||
"swipeHint": "Swipe left to edit or remove codes",
|
||||
"scan": "Scan",
|
||||
"scanACode": "Scan a code",
|
||||
"verify": "Verify",
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
"pleaseVerifyDetails": "Lütfen bilgileri doğrulayın ve tekrar deneyin",
|
||||
"codeIssuerHint": "Yayınlayan",
|
||||
"codeSecretKeyHint": "Gizli Anahtar",
|
||||
"secret": "Gizli",
|
||||
"all": "Tümü",
|
||||
"notes": "Notlar",
|
||||
"notesLengthLimit": "Notlar en fazla {count} karakter uzunluğunda olabilir",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Hesap (ornek@domain.com)",
|
||||
"codeTagHint": "Etiket",
|
||||
"accountKeyType": "Anahtar türü",
|
||||
@@ -34,6 +48,9 @@
|
||||
"nextTotpTitle": "sonraki",
|
||||
"deleteCodeTitle": "Kodu silmek istiyor musunuz?",
|
||||
"deleteCodeMessage": "Bu kodu silmek istediğinize emin misiniz? Bu geri alınamaz bir işlemdir.",
|
||||
"trashCode": "Kod çöpe atılsın mı?",
|
||||
"trashCodeMessage": "{account} için kodu çöpe atmak istediğinize emin misiniz?",
|
||||
"trash": "Çöp",
|
||||
"viewLogsAction": "Günlüğü görüntüle",
|
||||
"sendLogsDescription": "Günlüğünüz hatanızı çözmemize yardımcı olacaktır. Hassas bilginin kaydedilmediğine dikkat etsek de bu günlükleri paylaşmadan önce kontrol etmenizi isteriz.",
|
||||
"preparingLogsTitle": "Günlük hazırlanıyor...",
|
||||
@@ -100,6 +117,7 @@
|
||||
"emailVerificationToggle": "E-posta doğrulama",
|
||||
"emailVerificationEnableWarning": "E-postanız için 2FA'yı bizde saklıyorsanız, e-posta doğrulamasını açmak bir kilitlenmeye neden olabilir. Bir hizmetin dışında kalırsanız, diğerine giriş yapamayabilirsiniz.",
|
||||
"authToChangeEmailVerificationSetting": "E-posta doğrulamasını değiştirmek için lütfen kimlik doğrulaması yapın",
|
||||
"authenticateGeneric": "Lütfen doğrulama yapın",
|
||||
"authToViewYourRecoveryKey": "Kurtarma anahtarınızı görmek için lütfen kimliğinizi doğrulayın",
|
||||
"authToChangeYourEmail": "Epostanızı değiştirmek için lütfen kimliğinizi doğrulayın",
|
||||
"authToChangeYourPassword": "Şifrenizi değiştirmek için lütfen kimliğinizi doğrulayın",
|
||||
@@ -137,7 +155,8 @@
|
||||
"leaveFamily": "Aile planından ayrıl",
|
||||
"leaveFamilyMessage": "Aile planından ayrılmak istediğinize emin misiniz?",
|
||||
"inFamilyPlanMessage": "Aile planı kullanıyorsunuz!",
|
||||
"swipeHint": "Kodları düzenlemek ya da kaldırmak için sola kaydırın",
|
||||
"hintForMobile": "Bir koda uzun basarak düzenleyin veya silin.",
|
||||
"hintForDesktop": "Bir koda sağ tıklayıp düzenleyin veya silin.",
|
||||
"scan": "Tara",
|
||||
"scanACode": "Bir kodu tarayın",
|
||||
"verify": "Doğrula",
|
||||
@@ -193,6 +212,10 @@
|
||||
"scanAQrCode": "Bir QR kod tarayın",
|
||||
"enterDetailsManually": "Bilgileri elle girin",
|
||||
"edit": "Düzenle",
|
||||
"share": "Paylaş",
|
||||
"shareCodes": "Kodları paylaş",
|
||||
"shareCodesDuration": "Kodları paylaşma süresini seçin.",
|
||||
"restore": "Geri yükle",
|
||||
"copiedToClipboard": "Panoya kopyalandı",
|
||||
"copiedNextToClipboard": "Sonraki kod panoya kopyalandı",
|
||||
"error": "Hata",
|
||||
@@ -346,6 +369,7 @@
|
||||
"sigInBackupReminder": "Geri yükleyebileceğiniz bir yedeğiniz olduğundan emin olmak için lütfen kodlarınızı dışa aktarın.",
|
||||
"offlineModeWarning": "Yedekleme yapmadan devam etmeyi seçtiniz. Kodlarınızın güvende olduğundan emin olmak için lütfen manuel yedekleme yapın.",
|
||||
"showLargeIcons": "Büyük simgeler göster",
|
||||
"compactMode": "Sıkıştırılmış mod",
|
||||
"shouldHideCode": "Kodları gizle",
|
||||
"doubleTapToViewHiddenCode": "Kodu görüntülemek için bir girdiye çift dokunabilirsiniz",
|
||||
"focusOnSearchBar": "Uygulama başladığında arama bölümüne odaklan",
|
||||
@@ -466,5 +490,7 @@
|
||||
"pinLock": "Pin kilidi",
|
||||
"enterPin": "PIN Girin",
|
||||
"setNewPin": "Yeni PIN belirleyin",
|
||||
"importFailureDescNew": "Seçilen dosya ayrıştırılamadı."
|
||||
"importFailureDescNew": "Seçilen dosya ayrıştırılamadı.",
|
||||
"appLockNotEnabled": "Uygulama kilidi etkin değil",
|
||||
"appLockNotEnabledDescription": "Uygulama kilidini Güvenlik -> Uygulama Kilidi üzerinden etkinleştirin"
|
||||
}
|
||||
@@ -137,7 +137,6 @@
|
||||
"leaveFamily": "Залишити сімейний план",
|
||||
"leaveFamilyMessage": "Ви впевнені, що хочете залишити сімейний план?",
|
||||
"inFamilyPlanMessage": "Ви знаходитесь на сімейному плані!",
|
||||
"swipeHint": "Проведіть пальцем вліво, щоб редагувати або видаляти коди",
|
||||
"scan": "Сканувати",
|
||||
"scanACode": "Сканувати код",
|
||||
"verify": "Перевірити",
|
||||
|
||||
@@ -136,7 +136,6 @@
|
||||
"leaveFamily": "Rời khỏi gia đình",
|
||||
"leaveFamilyMessage": "Bạn có chắc chắn muốn thoát khỏi gói dành cho gia đình không?",
|
||||
"inFamilyPlanMessage": "Bạn đang sử dụng gói dành cho gia đình!",
|
||||
"swipeHint": "Vuốt sang trái để chỉnh sửa hoặc xóa mã",
|
||||
"scan": "Quét",
|
||||
"scanACode": "Quét mã",
|
||||
"verify": "Xác minh",
|
||||
@@ -200,6 +199,8 @@
|
||||
"saveKey": "Lưu khóa",
|
||||
"save": "Lưu",
|
||||
"send": "Gửi",
|
||||
"saveOrSendDescription": "Bạn có muốn lưu vào bộ nhớ (Mặc định lưu vào thư mục Tải về) hoặc chuyển qua ứng dụng khác?",
|
||||
"saveOnlyDescription": "Bạn có muốn lưu vào bộ nhớ không (Mặc định lưu vào thư mục Tải về)?",
|
||||
"back": "Quay lại",
|
||||
"createAccount": "Tạo tài khoản",
|
||||
"passwordStrength": "Độ mạnh mật khẩu: {passwordStrengthValue}",
|
||||
@@ -266,6 +267,7 @@
|
||||
"privacy": "Riêng tư",
|
||||
"terms": "Điều khoản",
|
||||
"checkForUpdates": "Kiểm tra cập nhật",
|
||||
"checkStatus": "Kiểm tra trạng thái",
|
||||
"downloadUpdate": "Tải xuống",
|
||||
"criticalUpdateAvailable": "Đã có bản cập nhật quan trọng",
|
||||
"updateAvailable": "Đã có bản cập nhật",
|
||||
@@ -411,13 +413,20 @@
|
||||
"hearUsWhereTitle": "Bạn biết đến Ente bằng cách nào? (không bắt buộc)",
|
||||
"hearUsExplanation": "Chúng tôi không theo dõi lượt cài đặt ứng dụng. Sẽ rất hữu ích nếu bạn cho chúng tôi biết nơi bạn tìm thấy chúng tôi!",
|
||||
"recoveryKeySaved": "Đã lưu khoá dự phòng vào thư mục Tải về!",
|
||||
"waitingForBrowserRequest": "Đang chờ yêu cầu từ trình duyệt...",
|
||||
"waitingForVerification": "Đang chờ xác thực",
|
||||
"passKeyPendingVerification": "Đang chờ xác thực",
|
||||
"loginSessionExpired": "Phiên làm việc hết hạn",
|
||||
"loginSessionExpiredDetails": "Phiên làm việc hết hạn. Vui lòng đăng nhập lại.",
|
||||
"developerSettingsWarning": "Bạn có chắc chắn muốn thay đổi Tuỳ chọn cho nhà phát triển không?",
|
||||
"developerSettings": "Cài đặt cho nhà phát triển",
|
||||
"invalidEndpoint": "Điểm cuối không hợp lệ",
|
||||
"endpointUpdatedMessage": "Cập nhật điểm cuối thành công",
|
||||
"customEndpoint": "Đã kết nối đến",
|
||||
"pinText": "Ghim",
|
||||
"unpinText": "Bỏ ghim",
|
||||
"pinnedCodeMessage": "{code} đã được ghim",
|
||||
"unpinnedCodeMessage": "{code} đã được bỏ ghim",
|
||||
"tags": "Thẻ",
|
||||
"createNewTag": "Tạo thẻ mới",
|
||||
"tag": "Thẻ",
|
||||
@@ -428,7 +437,10 @@
|
||||
"updateNotAvailable": "Cập nhật không khả dụng",
|
||||
"viewRawCodes": "Xem mã nguồn",
|
||||
"rawCodes": "Mã nguồn",
|
||||
"rawCodeData": "Dữ liệu thô",
|
||||
"appLock": "Khóa ứng dụng",
|
||||
"noSystemLockFound": "Không thấy khoá hệ thống",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Để bật khoá ứng dụng, vui lòng thiết lập mật khẩu thiết bị hoặc khóa màn hình trong cài đặt hệ thống của bạn.",
|
||||
"autoLock": "Tự động khóa",
|
||||
"immediately": "Tức thì",
|
||||
"reEnterPassword": "Nhập lại mật khẩu",
|
||||
@@ -441,7 +453,10 @@
|
||||
"hideContent": "Ẩn nội dung",
|
||||
"hideContentDescriptionAndroid": "Ẩn nội dung khi chuyển ứng dụng và chặn chụp màn hình",
|
||||
"hideContentDescriptioniOS": "Ẩn nội dung khi chuyển ứng dụng",
|
||||
"autoLockFeatureDescription": "Thời gian ứng dụng tự khoá sau khi ở trạng thái nền",
|
||||
"appLockDescription": "Chọn giữa màn hình khoá mặc định của thiết bị và màn hình khoá tự chọn dùng mã PIN hoặc mật khẩu.",
|
||||
"pinLock": "Mã PIN",
|
||||
"enterPin": "Nhập mã PIN",
|
||||
"setNewPin": "Đổi mã PIN"
|
||||
"setNewPin": "Đổi mã PIN",
|
||||
"importFailureDescNew": "Không thể phân tích file đã chọn."
|
||||
}
|
||||
@@ -19,6 +19,20 @@
|
||||
"pleaseVerifyDetails": "请验证详细信息并重试",
|
||||
"codeIssuerHint": "发行人",
|
||||
"codeSecretKeyHint": "私钥",
|
||||
"secret": "秘密",
|
||||
"all": "全部",
|
||||
"notes": "备注",
|
||||
"notesLengthLimit": "备注最多可以包含 {count} 个字符",
|
||||
"@notesLengthLimit": {
|
||||
"description": "Text to indicate the maximum number of characters allowed for notes",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"description": "The maximum number of characters allowed for notes",
|
||||
"type": "int",
|
||||
"example": "100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "账户 (you@domain.com)",
|
||||
"codeTagHint": "标签",
|
||||
"accountKeyType": "密钥类型",
|
||||
@@ -34,6 +48,9 @@
|
||||
"nextTotpTitle": "下一个",
|
||||
"deleteCodeTitle": "要删除代码吗?",
|
||||
"deleteCodeMessage": "您确定要删除此代码吗?此操作不可逆。",
|
||||
"trashCode": "要删除代码吗?",
|
||||
"trashCodeMessage": "您确定要删除 {account} 的代码吗?",
|
||||
"trash": "删除",
|
||||
"viewLogsAction": "查看日志",
|
||||
"sendLogsDescription": "这将发送日志以帮助我们调试您的问题。虽然我们采取预防措施确保不记录敏感信息,但我们建议您在共享这些日志之前先查看它们。",
|
||||
"preparingLogsTitle": "正在准备日志...",
|
||||
@@ -100,6 +117,7 @@
|
||||
"emailVerificationToggle": "电子邮件验证",
|
||||
"emailVerificationEnableWarning": "为避免被锁在您的账户之外,请在启用电子邮件验证之前确保在 Ente Auth 之外存储电子邮件双重验证的副本。",
|
||||
"authToChangeEmailVerificationSetting": "请进行身份验证以更改电子邮件验证",
|
||||
"authenticateGeneric": "请验证",
|
||||
"authToViewYourRecoveryKey": "请验证以查看您的恢复密钥",
|
||||
"authToChangeYourEmail": "请验证以更改您的电子邮件",
|
||||
"authToChangeYourPassword": "请验证以更改密码",
|
||||
@@ -137,7 +155,8 @@
|
||||
"leaveFamily": "离开家庭",
|
||||
"leaveFamilyMessage": "您确定要离开家庭计划吗?",
|
||||
"inFamilyPlanMessage": "你在一个家庭计划中!",
|
||||
"swipeHint": "向左滑动以编辑或删除代码",
|
||||
"hintForMobile": "长按代码即可编辑或删除。",
|
||||
"hintForDesktop": "右键单击代码即可编辑或删除。",
|
||||
"scan": "扫描",
|
||||
"scanACode": "扫描代码",
|
||||
"verify": "验证",
|
||||
@@ -193,6 +212,10 @@
|
||||
"scanAQrCode": "扫描二维码",
|
||||
"enterDetailsManually": "手动输入详细信息",
|
||||
"edit": "编辑",
|
||||
"share": "分享",
|
||||
"shareCodes": "分享代码",
|
||||
"shareCodesDuration": "选择您要分享代码的期限。",
|
||||
"restore": "恢复",
|
||||
"copiedToClipboard": "已复制到剪贴板",
|
||||
"copiedNextToClipboard": "已将下一个代码复制到剪贴板",
|
||||
"error": "错误",
|
||||
@@ -346,6 +369,7 @@
|
||||
"sigInBackupReminder": "请导出您的代码以确保您有可以恢复的备份。",
|
||||
"offlineModeWarning": "您已选择在不进行备份的情况下继续操作。请手动备份以确保您的代码安全。",
|
||||
"showLargeIcons": "显示大图标",
|
||||
"compactMode": "紧凑模式",
|
||||
"shouldHideCode": "隐藏代码",
|
||||
"doubleTapToViewHiddenCode": "您可以双击条目来查看代码",
|
||||
"focusOnSearchBar": "应用启动后聚焦搜索",
|
||||
@@ -466,5 +490,7 @@
|
||||
"pinLock": "Pin 锁定",
|
||||
"enterPin": "输入 PIN 码",
|
||||
"setNewPin": "设置新 PIN 码",
|
||||
"importFailureDescNew": "无法解析选定的文件。"
|
||||
"importFailureDescNew": "无法解析选定的文件。",
|
||||
"appLockNotEnabled": "应用锁未启用",
|
||||
"appLockNotEnabledDescription": "请从“安全”>“应用锁”启用应用锁"
|
||||
}
|
||||
@@ -5,7 +5,10 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
// Add more language to the list only when at least 90% of the strings are
|
||||
// translated in the corresponding language.
|
||||
const List<Locale> appSupportedLocales = <Locale>[
|
||||
Locale('ar'),
|
||||
Locale('bg'),
|
||||
Locale('de'),
|
||||
Locale('el'),
|
||||
Locale('en'),
|
||||
Locale('es', 'ES'),
|
||||
Locale('fa'),
|
||||
@@ -16,8 +19,10 @@ const List<Locale> appSupportedLocales = <Locale>[
|
||||
Locale('pl'),
|
||||
Locale('pt', 'BR'),
|
||||
Locale('ru'),
|
||||
Locale('sk'),
|
||||
Locale('tr'),
|
||||
Locale('uk'),
|
||||
Locale('vi'),
|
||||
Locale("zh", "CN"),
|
||||
];
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/ui/tools/app_lock.dart';
|
||||
import 'package:ente_auth/ui/tools/lock_screen.dart';
|
||||
import 'package:ente_auth/ui/utils/icon_utils.dart';
|
||||
import 'package:ente_auth/utils/directory_utils.dart';
|
||||
import 'package:ente_auth/utils/lock_screen_settings.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_auth/utils/window_protocol_handler.dart';
|
||||
@@ -69,6 +70,10 @@ void main() async {
|
||||
HttpOverrides.global = WindowsHttpOverrides();
|
||||
}
|
||||
|
||||
if (Platform.isLinux) {
|
||||
await DirectoryUtils.migrateNamingChanges();
|
||||
}
|
||||
|
||||
if (PlatformUtil.isDesktop()) {
|
||||
await windowManager.ensureInitialized();
|
||||
await WindowListenerService.instance.init();
|
||||
|
||||
@@ -27,9 +27,15 @@ class Code {
|
||||
|
||||
bool get isPinned => display.pinned;
|
||||
|
||||
bool get isTrashed => display.trashed;
|
||||
String get note => display.note;
|
||||
|
||||
final Object? err;
|
||||
bool get hasError => err != null;
|
||||
|
||||
String get issuerAccount =>
|
||||
account.isNotEmpty ? '$issuer ($account)' : issuer;
|
||||
|
||||
Code(
|
||||
this.account,
|
||||
this.issuer,
|
||||
@@ -81,6 +87,7 @@ class Code {
|
||||
final Type updatedType = type ?? this.type;
|
||||
final int updatedCounter = counter ?? this.counter;
|
||||
final CodeDisplay updatedDisplay = display ?? this.display;
|
||||
final String encodedIssuer = Uri.encodeQueryComponent(updateIssuer);
|
||||
|
||||
return Code(
|
||||
updateAccount,
|
||||
@@ -92,7 +99,7 @@ class Code {
|
||||
updatedType,
|
||||
updatedCounter,
|
||||
"otpauth://${updatedType.name}/$updateIssuer:$updateAccount?algorithm=${updatedAlgo.name}"
|
||||
"&digits=$updatedDigits&issuer=$updateIssuer"
|
||||
"&digits=$updatedDigits&issuer=$encodedIssuer"
|
||||
"&period=$updatePeriod&secret=$updatedSecret${updatedType == Type.hotp ? "&counter=$updatedCounter" : ""}",
|
||||
generatedID: generatedID,
|
||||
display: updatedDisplay,
|
||||
@@ -107,6 +114,7 @@ class Code {
|
||||
CodeDisplay? display,
|
||||
int digits,
|
||||
) {
|
||||
final String encodedIssuer = Uri.encodeQueryComponent(issuer);
|
||||
return Code(
|
||||
account,
|
||||
issuer,
|
||||
@@ -116,7 +124,7 @@ class Code {
|
||||
Algorithm.sha1,
|
||||
type,
|
||||
0,
|
||||
"otpauth://${type.name}/$issuer:$account?algorithm=SHA1&digits=$digits&issuer=$issuer&period=30&secret=$secret",
|
||||
"otpauth://${type.name}/$issuer:$account?algorithm=SHA1&digits=$digits&issuer=$encodedIssuer&period=30&secret=$secret",
|
||||
display: display ?? CodeDisplay(),
|
||||
);
|
||||
}
|
||||
@@ -124,7 +132,7 @@ class Code {
|
||||
static Code fromOTPAuthUrl(String rawData, {CodeDisplay? display}) {
|
||||
Uri uri = Uri.parse(rawData);
|
||||
final issuer = _getIssuer(uri);
|
||||
final account = _getAccount(uri);
|
||||
final account = _getAccount(uri, issuer);
|
||||
|
||||
try {
|
||||
final code = Code(
|
||||
@@ -155,7 +163,7 @@ class Code {
|
||||
}
|
||||
}
|
||||
|
||||
static String _getAccount(Uri uri) {
|
||||
static String _getAccount(Uri uri, String issuer) {
|
||||
try {
|
||||
String path = Uri.decodeComponent(uri.path);
|
||||
if (path.startsWith("/")) {
|
||||
@@ -166,8 +174,14 @@ class Code {
|
||||
if (uri.queryParameters.containsKey("issuer") && !path.contains(":")) {
|
||||
return path;
|
||||
}
|
||||
return path.split(':')[1];
|
||||
} catch (e) {
|
||||
// handle case where issuer name contains colon
|
||||
if (path.startsWith('$issuer:')) {
|
||||
return path.substring(issuer.length + 1);
|
||||
}
|
||||
return path
|
||||
.substring(path.indexOf(':') + 1); // return data after first colon
|
||||
} catch (e, s) {
|
||||
Logger('_getAccount').severe('Error while parsing account', e, s);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ class CodeDisplay {
|
||||
final bool trashed;
|
||||
final int lastUsedAt;
|
||||
final int tapCount;
|
||||
String note;
|
||||
final List<String> tags;
|
||||
|
||||
CodeDisplay({
|
||||
@@ -17,6 +18,7 @@ class CodeDisplay {
|
||||
this.lastUsedAt = 0,
|
||||
this.tapCount = 0,
|
||||
this.tags = const [],
|
||||
this.note = '',
|
||||
});
|
||||
|
||||
// copyWith
|
||||
@@ -26,12 +28,14 @@ class CodeDisplay {
|
||||
int? lastUsedAt,
|
||||
int? tapCount,
|
||||
List<String>? tags,
|
||||
String? note,
|
||||
}) {
|
||||
final bool updatedPinned = pinned ?? this.pinned;
|
||||
final bool updatedTrashed = trashed ?? this.trashed;
|
||||
final int updatedLastUsedAt = lastUsedAt ?? this.lastUsedAt;
|
||||
final int updatedTapCount = tapCount ?? this.tapCount;
|
||||
final List<String> updatedTags = tags ?? this.tags;
|
||||
final String updatedNote = note ?? this.note;
|
||||
|
||||
return CodeDisplay(
|
||||
pinned: updatedPinned,
|
||||
@@ -39,6 +43,7 @@ class CodeDisplay {
|
||||
lastUsedAt: updatedLastUsedAt,
|
||||
tapCount: updatedTapCount,
|
||||
tags: updatedTags,
|
||||
note: updatedNote,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,6 +57,7 @@ class CodeDisplay {
|
||||
lastUsedAt: json['lastUsedAt'] ?? 0,
|
||||
tapCount: json['tapCount'] ?? 0,
|
||||
tags: List<String>.from(json['tags'] ?? []),
|
||||
note: json['note'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -92,6 +98,7 @@ class CodeDisplay {
|
||||
'lastUsedAt': lastUsedAt,
|
||||
'tapCount': tapCount,
|
||||
'tags': tags,
|
||||
'note': note,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -104,6 +111,7 @@ class CodeDisplay {
|
||||
other.trashed == trashed &&
|
||||
other.lastUsedAt == lastUsedAt &&
|
||||
other.tapCount == tapCount &&
|
||||
other.note == note &&
|
||||
listEquals(other.tags, tags);
|
||||
}
|
||||
|
||||
@@ -113,6 +121,7 @@ class CodeDisplay {
|
||||
trashed.hashCode ^
|
||||
lastUsedAt.hashCode ^
|
||||
tapCount.hashCode ^
|
||||
note.hashCode ^
|
||||
tags.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
25
auth/lib/onboarding/view/common/field_label.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FieldLabel extends StatelessWidget {
|
||||
final String label;
|
||||
|
||||
const FieldLabel(
|
||||
this.label, {
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 12.0),
|
||||
child: SizedBox(
|
||||
width: 80,
|
||||
child: Text(
|
||||
label,
|
||||
style: getEnteTextTheme(context).miniBoldMuted,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ class TagChip extends StatelessWidget {
|
||||
final VoidCallback? onTap;
|
||||
final TagChipState state;
|
||||
final TagChipAction action;
|
||||
final IconData? iconData;
|
||||
|
||||
const TagChip({
|
||||
super.key,
|
||||
@@ -17,11 +18,16 @@ class TagChip extends StatelessWidget {
|
||||
this.state = TagChipState.unselected,
|
||||
this.action = TagChipAction.none,
|
||||
this.onTap,
|
||||
this.iconData,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = getEnteColorScheme(context);
|
||||
final color = state == TagChipState.selected ||
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: colorScheme.tagTextUnselectedColor;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
@@ -51,15 +57,24 @@ class TagChip extends StatelessWidget {
|
||||
data: MediaQuery.of(context).copyWith(
|
||||
textScaler: const TextScaler.linear(1),
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: state == TagChipState.selected ||
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: colorScheme.tagTextUnselectedColor,
|
||||
fontSize: 14,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
if (iconData != null) ...[
|
||||
Icon(
|
||||
iconData,
|
||||
size: 16,
|
||||
color: color,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (state == TagChipState.selected &&
|
||||
|
||||
@@ -8,13 +8,17 @@ import 'package:ente_auth/models/code_display.dart';
|
||||
import 'package:ente_auth/onboarding/model/tag_enums.dart';
|
||||
import 'package:ente_auth/onboarding/view/common/add_chip.dart';
|
||||
import 'package:ente_auth/onboarding/view/common/add_tag.dart';
|
||||
import 'package:ente_auth/onboarding/view/common/field_label.dart';
|
||||
import 'package:ente_auth/onboarding/view/common/tag_chip.dart';
|
||||
import 'package:ente_auth/store/code_display_store.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_result.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:ente_auth/utils/totp_util.dart';
|
||||
import "package:flutter/material.dart";
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class SetupEnterSecretKeyPage extends StatefulWidget {
|
||||
final Code? code;
|
||||
@@ -27,11 +31,15 @@ class SetupEnterSecretKeyPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
final Logger _logger = Logger('_SetupEnterSecretKeyPageState');
|
||||
final int _notesLimit = 500;
|
||||
final int _otherTextLimit = 200;
|
||||
late TextEditingController _issuerController;
|
||||
late TextEditingController _accountController;
|
||||
late TextEditingController _secretController;
|
||||
late TextEditingController _notesController;
|
||||
late bool _secretKeyObscured;
|
||||
late List<String> tags = [...?widget.code?.display.tags];
|
||||
late List<String> selectedTags = [...?widget.code?.display.tags];
|
||||
List<String> allTags = [];
|
||||
StreamSubscription<CodesUpdatedEvent>? _streamSubscription;
|
||||
|
||||
@@ -47,17 +55,52 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
_secretController = TextEditingController(
|
||||
text: widget.code?.secret,
|
||||
);
|
||||
_notesController = TextEditingController(
|
||||
text: widget.code?.display.note,
|
||||
);
|
||||
_secretKeyObscured = widget.code != null;
|
||||
_loadTags();
|
||||
_streamSubscription = Bus.instance.on<CodesUpdatedEvent>().listen((event) {
|
||||
_loadTags();
|
||||
});
|
||||
_notesController.addListener(() {
|
||||
if (_notesController.text.length > _notesLimit) {
|
||||
_notesController.text = _notesController.text.substring(0, _notesLimit);
|
||||
_notesController.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: _notesController.text.length),
|
||||
);
|
||||
showToast(context, context.l10n.notesLengthLimit(_notesLimit));
|
||||
}
|
||||
});
|
||||
|
||||
if (widget.code == null ||
|
||||
(widget.code!.issuer.length < _otherTextLimit &&
|
||||
widget.code!.account.length < _otherTextLimit &&
|
||||
widget.code!.secret.length < _otherTextLimit)) {
|
||||
_limitTextLength(_issuerController, _otherTextLimit);
|
||||
_limitTextLength(_accountController, _otherTextLimit);
|
||||
_limitTextLength(_secretController, _otherTextLimit);
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _limitTextLength(TextEditingController controller, int limit) {
|
||||
controller.addListener(() {
|
||||
if (controller.text.length > limit) {
|
||||
controller.text = controller.text.substring(0, limit);
|
||||
controller.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: controller.text.length),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_streamSubscription?.cancel();
|
||||
_issuerController.dispose();
|
||||
_accountController.dispose();
|
||||
_notesController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -72,163 +115,197 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.importAccountPageTitle),
|
||||
),
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 40),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextFormField(
|
||||
// The validator receives the text that the user has entered.
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return "Please enter some text";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.codeIssuerHint,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.auto,
|
||||
labelText: l10n.codeIssuerHint,
|
||||
),
|
||||
controller: _issuerController,
|
||||
autofocus: true,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
TextFormField(
|
||||
// The validator receives the text that the user has entered.
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return "Please enter some text";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.codeSecretKeyHint,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.auto,
|
||||
labelText: l10n.codeSecretKeyHint,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_secretKeyObscured = !_secretKeyObscured;
|
||||
});
|
||||
},
|
||||
icon: _secretKeyObscured
|
||||
? const Icon(Icons.visibility_off_rounded)
|
||||
: const Icon(Icons.visibility_rounded),
|
||||
),
|
||||
),
|
||||
obscureText: _secretKeyObscured,
|
||||
controller: _secretController,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
TextFormField(
|
||||
// The validator receives the text that the user has entered.
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return "Please enter some text";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.codeAccountHint,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.auto,
|
||||
labelText: l10n.codeAccountHint,
|
||||
),
|
||||
controller: _accountController,
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
l10n.tags,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
alignment: WrapAlignment.start,
|
||||
children: [
|
||||
...allTags.map(
|
||||
(e) => TagChip(
|
||||
label: e,
|
||||
action: TagChipAction.check,
|
||||
state: tags.contains(e)
|
||||
? TagChipState.selected
|
||||
: TagChipState.unselected,
|
||||
onTap: () {
|
||||
if (tags.contains(e)) {
|
||||
tags.remove(e);
|
||||
} else {
|
||||
tags.add(e);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
AddChip(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AddTagDialog(
|
||||
onTap: (tag) {
|
||||
if (allTags.contains(tag) &&
|
||||
tags.contains(tag)) {
|
||||
return;
|
||||
}
|
||||
allTags.add(tag);
|
||||
tags.add(tag);
|
||||
setState(() {});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
barrierColor: Colors.black.withOpacity(0.85),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
SizedBox(
|
||||
width: 400,
|
||||
child: OutlinedButton(
|
||||
onPressed: () async {
|
||||
if ((_accountController.text.trim().isEmpty &&
|
||||
_issuerController.text.trim().isEmpty) ||
|
||||
_secretController.text.trim().isEmpty) {
|
||||
String message;
|
||||
if (_secretController.text.trim().isEmpty) {
|
||||
message = context.l10n.secretCanNotBeEmpty;
|
||||
} else {
|
||||
message =
|
||||
context.l10n.bothIssuerAndAccountCanNotBeEmpty;
|
||||
appBar: AppBar(title: Text(l10n.importAccountPageTitle)),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
FieldLabel(l10n.codeIssuerHint),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
// The validator receives the text that the user has entered.
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return "Please enter some text";
|
||||
}
|
||||
_showIncorrectDetailsDialog(context, message: message);
|
||||
return;
|
||||
}
|
||||
await _saveCode();
|
||||
},
|
||||
child: Text(l10n.saveAction),
|
||||
return null;
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 12.0),
|
||||
),
|
||||
style: getEnteTextTheme(context).small,
|
||||
controller: _issuerController,
|
||||
autofocus: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
FieldLabel(l10n.secret),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
// The validator receives the text that the user has entered.
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return "Please enter some text";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: getEnteTextTheme(context).small,
|
||||
decoration: InputDecoration(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 12.0),
|
||||
suffixIcon: GestureDetector(
|
||||
// padding: EdgeInsets.zero,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_secretKeyObscured = !_secretKeyObscured;
|
||||
});
|
||||
},
|
||||
child: _secretKeyObscured
|
||||
? const Icon(
|
||||
Icons.visibility_off_rounded,
|
||||
size: 18,
|
||||
)
|
||||
: const Icon(
|
||||
Icons.visibility_rounded,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
obscureText: _secretKeyObscured,
|
||||
controller: _secretController,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
FieldLabel(l10n.account),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
// The validator receives the text that the user has entered.
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return "Please enter some text";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 12.0),
|
||||
),
|
||||
style: getEnteTextTheme(context).small,
|
||||
controller: _accountController,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
FieldLabel(l10n.notes),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
// The validator receives the text that the user has entered.
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return "Please enter some text";
|
||||
}
|
||||
if (value.length > _notesLimit) {
|
||||
return "Notes can't be more than 1000 characters";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
maxLength: _notesLimit,
|
||||
minLines: 1,
|
||||
maxLines: 5,
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 12.0),
|
||||
),
|
||||
style: getEnteTextTheme(context).small,
|
||||
controller: _notesController,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
alignment: WrapAlignment.start,
|
||||
children: [
|
||||
...allTags.map(
|
||||
(e) => TagChip(
|
||||
label: e,
|
||||
action: TagChipAction.check,
|
||||
state: selectedTags.contains(e)
|
||||
? TagChipState.selected
|
||||
: TagChipState.unselected,
|
||||
onTap: () {
|
||||
if (selectedTags.contains(e)) {
|
||||
selectedTags.remove(e);
|
||||
} else {
|
||||
selectedTags.add(e);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
AddChip(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AddTagDialog(
|
||||
onTap: (tag) {
|
||||
final exist = allTags.contains(tag);
|
||||
if (exist && selectedTags.contains(tag)) {
|
||||
return Navigator.pop(context);
|
||||
}
|
||||
if (!exist) allTags.add(tag);
|
||||
selectedTags.add(tag);
|
||||
setState(() {});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
barrierColor: Colors.black.withOpacity(0.85),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
SizedBox(
|
||||
width: 400,
|
||||
child: OutlinedButton(
|
||||
onPressed: () async {
|
||||
if ((_accountController.text.trim().isEmpty &&
|
||||
_issuerController.text.trim().isEmpty) ||
|
||||
_secretController.text.trim().isEmpty) {
|
||||
String message;
|
||||
if (_secretController.text.trim().isEmpty) {
|
||||
message = context.l10n.secretCanNotBeEmpty;
|
||||
} else {
|
||||
message =
|
||||
context.l10n.bothIssuerAndAccountCanNotBeEmpty;
|
||||
}
|
||||
_showIncorrectDetailsDialog(context, message: message);
|
||||
return;
|
||||
}
|
||||
await _saveCode();
|
||||
},
|
||||
child: Text(l10n.saveAction),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -240,7 +317,13 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
final account = _accountController.text.trim();
|
||||
final issuer = _issuerController.text.trim();
|
||||
final secret = _secretController.text.trim().replaceAll(' ', '');
|
||||
final isStreamCode = issuer.toLowerCase() == "steam" || issuer.toLowerCase().contains('steampowered.com');
|
||||
final notes = _notesController.text.trim();
|
||||
final isStreamCode = issuer.toLowerCase() == "steam" ||
|
||||
issuer.toLowerCase().contains('steampowered.com');
|
||||
final CodeDisplay display =
|
||||
widget.code?.display.copyWith(tags: selectedTags) ??
|
||||
CodeDisplay(tags: selectedTags);
|
||||
display.note = notes;
|
||||
if (widget.code != null && widget.code!.secret != secret) {
|
||||
ButtonResult? result = await showChoiceActionSheet(
|
||||
context,
|
||||
@@ -255,8 +338,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final CodeDisplay display =
|
||||
widget.code?.display.copyWith(tags: tags) ?? CodeDisplay(tags: tags);
|
||||
|
||||
final Code newCode = widget.code == null
|
||||
? Code.fromAccountAndSecret(
|
||||
isStreamCode ? Type.steam : Type.totp,
|
||||
@@ -275,7 +357,8 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
// Verify the validity of the code
|
||||
getOTP(newCode);
|
||||
Navigator.of(context).pop(newCode);
|
||||
} catch (e) {
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error saving code", e, s);
|
||||
_showIncorrectDetailsDialog(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,11 +31,11 @@ class ViewQrPage extends StatelessWidget {
|
||||
data: code!.rawData,
|
||||
eyeStyle: QrEyeStyle(
|
||||
eyeShape: QrEyeShape.square,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
dataModuleStyle: QrDataModuleStyle(
|
||||
dataModuleShape: QrDataModuleShape.square,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
version: QrVersions.auto,
|
||||
size: qrSize,
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:ente_auth/utils/auth_util.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/lock_screen_settings.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_local_authentication/flutter_local_authentication.dart';
|
||||
@@ -19,11 +20,19 @@ class LocalAuthenticationService {
|
||||
static final LocalAuthenticationService instance =
|
||||
LocalAuthenticationService._privateConstructor();
|
||||
final logger = Logger((LocalAuthenticationService).toString());
|
||||
int lastAuthTime = 0;
|
||||
|
||||
Future<bool> requestLocalAuthentication(
|
||||
BuildContext context,
|
||||
String infoMessage,
|
||||
) async {
|
||||
if (kDebugMode) {
|
||||
// if last auth time is less than 60 seconds, don't ask for auth again
|
||||
if (lastAuthTime != 0 &&
|
||||
DateTime.now().millisecondsSinceEpoch - lastAuthTime < 60000) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (await isLocalAuthSupportedOnDevice() ||
|
||||
LockScreenSettings.instance.getIsAppLockSet()) {
|
||||
AppLock.of(context)!.setEnabled(false);
|
||||
@@ -39,6 +48,7 @@ class LocalAuthenticationService {
|
||||
showToast(context, infoMessage);
|
||||
return false;
|
||||
} else {
|
||||
lastAuthTime = DateTime.now().millisecondsSinceEpoch;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,12 @@ class PreferenceService {
|
||||
|
||||
late final SharedPreferences _prefs;
|
||||
|
||||
static const kHasShownCoachMarkKey = "has_shown_coach_mark";
|
||||
static const kHasShownCoachMarkKey = "has_shown_coach_mark_v2";
|
||||
static const kShouldShowLargeIconsKey = "should_show_large_icons";
|
||||
static const kShouldHideCodesKey = "should_hide_codes";
|
||||
static const kShouldAutoFocusOnSearchBar = "should_auto_focus_on_search_bar";
|
||||
static const kShouldMinimizeOnCopy = "should_minimize_on_copy";
|
||||
static const kCompactMode = "vi.compactMode";
|
||||
|
||||
Future<void> init() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
@@ -48,6 +49,14 @@ class PreferenceService {
|
||||
return _prefs.getBool(kShouldHideCodesKey) ?? false;
|
||||
}
|
||||
|
||||
bool isCompactMode() {
|
||||
return _prefs.getBool(kCompactMode) ?? false;
|
||||
}
|
||||
|
||||
Future<void> setCompactMode(bool value) async {
|
||||
await _prefs.setBool(kCompactMode, value);
|
||||
}
|
||||
|
||||
Future<void> setHideCodes(bool value) async {
|
||||
await _prefs.setBool(kShouldHideCodesKey, value);
|
||||
Bus.instance.fire(IconsChangedEvent());
|
||||
|
||||
@@ -585,9 +585,8 @@ class UserService {
|
||||
final clientS = client.calculateSecret(serverB);
|
||||
final clientM = client.calculateClientEvidenceMessage();
|
||||
|
||||
late Response _;
|
||||
if (setKeysRequest == null) {
|
||||
_ = await _enteDio.post(
|
||||
await _enteDio.post(
|
||||
"/users/srp/complete",
|
||||
data: {
|
||||
'setupID': setupSRPResponse.setupID,
|
||||
@@ -595,7 +594,7 @@ class UserService {
|
||||
},
|
||||
);
|
||||
} else {
|
||||
_ = await _enteDio.post(
|
||||
await _enteDio.post(
|
||||
"/users/srp/update",
|
||||
data: {
|
||||
'setupID': setupSRPResponse.setupID,
|
||||
|
||||
@@ -30,6 +30,7 @@ class CodeDisplayStore {
|
||||
final tags = <String>{};
|
||||
for (final code in codes) {
|
||||
if (code.hasError) continue;
|
||||
if (code.isTrashed) continue;
|
||||
tags.addAll(code.display.tags);
|
||||
}
|
||||
return tags.toList()..sort();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
@@ -30,6 +31,7 @@ class _CodeTimerProgressState extends State<CodeTimerProgress>
|
||||
late final Ticker _ticker;
|
||||
late final ValueNotifier<double> _progress;
|
||||
late final int _microSecondsInPeriod;
|
||||
late bool _isCompactMode=false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -38,6 +40,7 @@ class _CodeTimerProgressState extends State<CodeTimerProgress>
|
||||
_progress = ValueNotifier<double>(0.0);
|
||||
_ticker = createTicker(_updateTimeRemaining);
|
||||
_ticker.start();
|
||||
_isCompactMode = PreferenceService.instance.isCompactMode();
|
||||
_updateTimeRemaining(Duration.zero);
|
||||
}
|
||||
|
||||
@@ -57,7 +60,7 @@ class _CodeTimerProgressState extends State<CodeTimerProgress>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 3,
|
||||
height: _isCompactMode ?1:3,
|
||||
child: ValueListenableBuilder<double>(
|
||||
valueListenable: _progress,
|
||||
builder: (context, progress, _) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:clipboard/clipboard.dart';
|
||||
@@ -15,24 +14,29 @@ import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/code_timer_progress.dart';
|
||||
import 'package:ente_auth/ui/components/bottom_action_bar_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/ui/share/code_share.dart';
|
||||
import 'package:ente_auth/ui/utils/icon_utils.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:ente_auth/utils/totp_util.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_context_menu/flutter_context_menu.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:move_to_background/move_to_background.dart';
|
||||
|
||||
class CodeWidget extends StatefulWidget {
|
||||
final Code code;
|
||||
final bool isCompactMode;
|
||||
|
||||
const CodeWidget(
|
||||
this.code, {
|
||||
super.key,
|
||||
required this.isCompactMode,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -50,30 +54,33 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
late bool _hideCode;
|
||||
bool isMaskingEnabled = false;
|
||||
int _codeTimeStep = -1;
|
||||
int lastRefreshTime = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
isMaskingEnabled = PreferenceService.instance.shouldHideCodes();
|
||||
|
||||
_hideCode = isMaskingEnabled;
|
||||
_everySecondTimer =
|
||||
Timer.periodic(const Duration(milliseconds: 500), (Timer t) {
|
||||
int newStep = 0;
|
||||
int epochSeconds = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
if (widget.code.type != Type.hotp) {
|
||||
newStep = (((DateTime.now().millisecondsSinceEpoch ~/ 1000).round()) ~/
|
||||
widget.code.period)
|
||||
.floor();
|
||||
newStep = ((epochSeconds.round()) ~/ widget.code.period).floor();
|
||||
} else {
|
||||
newStep = widget.code.counter;
|
||||
}
|
||||
if (_codeTimeStep != newStep) {
|
||||
_codeTimeStep = newStep;
|
||||
if (_codeTimeStep != newStep ||
|
||||
epochSeconds - lastRefreshTime > widget.code.period) {
|
||||
String newCode = _getCurrentOTP();
|
||||
if (newCode != _currentCode.value) {
|
||||
if (newCode != _currentCode.value && mounted) {
|
||||
_currentCode.value = newCode;
|
||||
if (widget.code.type.isTOTPCompatible) {
|
||||
_nextCode.value = _getNextTotp();
|
||||
}
|
||||
_codeTimeStep = newStep;
|
||||
lastRefreshTime = epochSeconds;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -115,6 +122,18 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
painter: PinBgPainter(
|
||||
color: colorScheme.pinnedBgColor,
|
||||
),
|
||||
size: widget.isCompactMode
|
||||
? const Size(24, 24)
|
||||
: const Size(39, 39),
|
||||
),
|
||||
),
|
||||
if (widget.code.isTrashed && kDebugMode)
|
||||
Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: CustomPaint(
|
||||
painter: PinBgPainter(
|
||||
color: colorScheme.warning700,
|
||||
),
|
||||
size: const Size(39, 39),
|
||||
),
|
||||
),
|
||||
@@ -126,7 +145,9 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
CodeTimerProgressCache.getCachedWidget(
|
||||
widget.code.period,
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
widget.isCompactMode
|
||||
? const SizedBox(height: 4)
|
||||
: const SizedBox(height: 28),
|
||||
Row(
|
||||
children: [
|
||||
_shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(),
|
||||
@@ -134,22 +155,32 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
child: Column(
|
||||
children: [
|
||||
_getTopRow(),
|
||||
const SizedBox(height: 4),
|
||||
widget.isCompactMode
|
||||
? const SizedBox.shrink()
|
||||
: const SizedBox(height: 4),
|
||||
_getBottomRow(l10n),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
widget.isCompactMode
|
||||
? const SizedBox(height: 4)
|
||||
: const SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
if (widget.code.isPinned) ...[
|
||||
Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 6, top: 6),
|
||||
child: SvgPicture.asset("assets/svg/pin-card.svg"),
|
||||
padding: widget.isCompactMode
|
||||
? const EdgeInsets.only(right: 4, top: 4)
|
||||
: const EdgeInsets.only(right: 6, top: 6),
|
||||
child: SvgPicture.asset(
|
||||
"assets/svg/pin-card.svg",
|
||||
width: widget.isCompactMode ? 8 : null,
|
||||
height: widget.isCompactMode ? 8 : null,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -186,7 +217,22 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
}
|
||||
: null,
|
||||
onLongPress: () {
|
||||
_copyCurrentOTPToClipboard();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return BottomActionBarWidget(
|
||||
code: widget.code,
|
||||
onEdit: () => _onEditPressed(true),
|
||||
onShare: () => _onSharePressed(true),
|
||||
onPin: () => _onPinPressed(true),
|
||||
onTrashed: () => _onTrashPressed(true),
|
||||
onDelete: () => _onDeletePressed(true),
|
||||
onRestore: () => _onRestoreClicked(true),
|
||||
onShowQR: () => _onShowQrPressed(true),
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: getCardContents(l10n),
|
||||
),
|
||||
@@ -196,36 +242,65 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
}
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8, top: 8),
|
||||
margin: widget.isCompactMode
|
||||
? const EdgeInsets.only(left: 16, right: 16, bottom: 6, top: 6)
|
||||
: const EdgeInsets.only(left: 16, right: 16, bottom: 8, top: 8),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (PlatformUtil.isDesktop()) {
|
||||
return ContextMenuRegion(
|
||||
contextMenu: ContextMenu(
|
||||
entries: <ContextMenuEntry>[
|
||||
MenuItem(
|
||||
label: 'QR',
|
||||
icon: Icons.qr_code_2_outlined,
|
||||
onSelected: () => _onShowQrPressed(null),
|
||||
),
|
||||
MenuItem(
|
||||
label: widget.code.isPinned ? l10n.unpinText : l10n.pinText,
|
||||
icon: widget.code.isPinned
|
||||
? Icons.push_pin
|
||||
: Icons.push_pin_outlined,
|
||||
onSelected: () => _onPinPressed(null),
|
||||
),
|
||||
MenuItem(
|
||||
label: l10n.edit,
|
||||
icon: Icons.edit,
|
||||
onSelected: () => _onEditPressed(null),
|
||||
),
|
||||
if (!widget.code.isTrashed &&
|
||||
widget.code.type.isTOTPCompatible)
|
||||
MenuItem(
|
||||
label: context.l10n.share,
|
||||
icon: Icons.adaptive.share_outlined,
|
||||
onSelected: () => _onSharePressed(null),
|
||||
),
|
||||
if (!widget.code.isTrashed)
|
||||
MenuItem(
|
||||
label: 'QR',
|
||||
icon: Icons.qr_code_2_outlined,
|
||||
onSelected: () => _onShowQrPressed(null),
|
||||
),
|
||||
if (widget.code.note.isNotEmpty)
|
||||
MenuItem(
|
||||
label: context.l10n.notes,
|
||||
icon: Icons.notes_outlined,
|
||||
onSelected: () => _onShowNotesPressed(null),
|
||||
),
|
||||
if (!widget.code.isTrashed)
|
||||
MenuItem(
|
||||
label:
|
||||
widget.code.isPinned ? l10n.unpinText : l10n.pinText,
|
||||
icon: widget.code.isPinned
|
||||
? Icons.push_pin
|
||||
: Icons.push_pin_outlined,
|
||||
onSelected: () => _onPinPressed(null),
|
||||
),
|
||||
if (!widget.code.isTrashed)
|
||||
MenuItem(
|
||||
label: l10n.edit,
|
||||
icon: Icons.edit,
|
||||
onSelected: () => _onEditPressed(null),
|
||||
)
|
||||
else
|
||||
MenuItem(
|
||||
label: l10n.restore,
|
||||
icon: Icons.restore_outlined,
|
||||
onSelected: () => _onRestoreClicked(null),
|
||||
),
|
||||
const MenuDivider(),
|
||||
MenuItem(
|
||||
label: l10n.delete,
|
||||
label: widget.code.isTrashed ? l10n.delete : l10n.trash,
|
||||
value: "Delete",
|
||||
icon: Icons.delete,
|
||||
onSelected: () => _onDeletePressed(null),
|
||||
icon: widget.code.isTrashed
|
||||
? Icons.delete_forever
|
||||
: Icons.delete,
|
||||
onSelected: () => widget.code.isTrashed
|
||||
? _onDeletePressed(null)
|
||||
: _onTrashPressed(null),
|
||||
),
|
||||
],
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
@@ -234,95 +309,7 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
return Slidable(
|
||||
key: ValueKey(widget.code.hashCode),
|
||||
endActionPane: ActionPane(
|
||||
extentRatio: 0.90,
|
||||
motion: const ScrollMotion(),
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 14,
|
||||
),
|
||||
SlidableAction(
|
||||
onPressed: _onShowQrPressed,
|
||||
backgroundColor: Colors.grey.withOpacity(0.1),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.inverseBackgroundColor,
|
||||
icon: Icons.qr_code_2_outlined,
|
||||
label: "QR",
|
||||
padding: const EdgeInsets.only(left: 4, right: 0),
|
||||
spacing: 8,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 14,
|
||||
),
|
||||
CustomSlidableAction(
|
||||
onPressed: _onPinPressed,
|
||||
backgroundColor: Colors.grey.withOpacity(0.1),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.inverseBackgroundColor,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (widget.code.isPinned)
|
||||
SvgPicture.asset(
|
||||
"assets/svg/pin-active.svg",
|
||||
colorFilter: ui.ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
)
|
||||
else
|
||||
SvgPicture.asset(
|
||||
"assets/svg/pin-inactive.svg",
|
||||
colorFilter: ui.ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
widget.code.isPinned ? l10n.unpinText : l10n.pinText,
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 4, right: 0),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 14,
|
||||
),
|
||||
SlidableAction(
|
||||
onPressed: _onEditPressed,
|
||||
backgroundColor: Colors.grey.withOpacity(0.1),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.inverseBackgroundColor,
|
||||
icon: Icons.edit_outlined,
|
||||
label: l10n.edit,
|
||||
padding: const EdgeInsets.only(left: 4, right: 0),
|
||||
spacing: 8,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 14,
|
||||
),
|
||||
SlidableAction(
|
||||
onPressed: _onDeletePressed,
|
||||
backgroundColor: Colors.grey.withOpacity(0.1),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
foregroundColor: colorScheme.deleteCodeTextColor,
|
||||
icon: Icons.delete,
|
||||
label: l10n.delete,
|
||||
padding: const EdgeInsets.only(left: 0, right: 0),
|
||||
spacing: 8,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) => clippedCard(l10n),
|
||||
),
|
||||
);
|
||||
return clippedCard(l10n);
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -343,7 +330,7 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
type: MaterialType.transparency,
|
||||
child: AutoSizeText(
|
||||
_getFormattedCode(value),
|
||||
style: const TextStyle(fontSize: 24),
|
||||
style: TextStyle(fontSize: widget.isCompactMode ? 14 : 24),
|
||||
maxLines: 1,
|
||||
),
|
||||
);
|
||||
@@ -370,8 +357,8 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
type: MaterialType.transparency,
|
||||
child: Text(
|
||||
_getFormattedCode(value),
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
style: TextStyle(
|
||||
fontSize: widget.isCompactMode ? 12 : 18,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
@@ -404,6 +391,7 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
}
|
||||
|
||||
Widget _getTopRow() {
|
||||
bool isCompactMode = widget.isCompactMode;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||
child: Row(
|
||||
@@ -415,13 +403,15 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
children: [
|
||||
Text(
|
||||
safeDecode(widget.code.issuer).trim(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
style: isCompactMode
|
||||
? Theme.of(context).textTheme.bodyMedium
|
||||
: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
if (!isCompactMode) const SizedBox(height: 2),
|
||||
Text(
|
||||
safeDecode(widget.code.account).trim(),
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontSize: 12,
|
||||
fontSize: isCompactMode ? 12 : 12,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
@@ -452,12 +442,14 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
Widget _getIcon() {
|
||||
return Padding(
|
||||
padding: _shouldShowLargeIcon
|
||||
? const EdgeInsets.only(left: 16)
|
||||
? EdgeInsets.only(left: widget.isCompactMode ? 12 : 16)
|
||||
: const EdgeInsets.all(0),
|
||||
child: IconUtils.instance.getIcon(
|
||||
context,
|
||||
safeDecode(widget.code.issuer).trim(),
|
||||
width: _shouldShowLargeIcon ? 42 : 24,
|
||||
width: widget.isCompactMode
|
||||
? (_shouldShowLargeIcon ? 32 : 24)
|
||||
: (_shouldShowLargeIcon ? 42 : 24),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -502,7 +494,24 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onEditPressed(_) async {
|
||||
Future<void> _onShowNotesPressed([bool? pop]) async {
|
||||
if (mounted && pop == true) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
await showChoiceDialog(
|
||||
context,
|
||||
title: context.l10n.notes,
|
||||
body: widget.code.note,
|
||||
firstButtonLabel: context.l10n.close,
|
||||
firstButtonType: ButtonType.secondary,
|
||||
secondButtonLabel: null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onEditPressed([bool? pop]) async {
|
||||
if (mounted && pop == true) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
bool isAuthSuccessful = await LocalAuthenticationService.instance
|
||||
.requestLocalAuthentication(context, context.l10n.editCodeAuthMessage);
|
||||
await PlatformUtil.refocusWindows();
|
||||
@@ -523,7 +532,10 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onShowQrPressed(_) async {
|
||||
Future<void> _onShowQrPressed([bool? pop]) async {
|
||||
if (mounted && pop == true) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
bool isAuthSuccessful = await LocalAuthenticationService.instance
|
||||
.requestLocalAuthentication(context, context.l10n.showQRAuthMessage);
|
||||
await PlatformUtil.refocusWindows();
|
||||
@@ -540,7 +552,23 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onPinPressed(_) async {
|
||||
Future<void> _onSharePressed([bool? pop]) async {
|
||||
if (mounted && pop == true) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
bool isAuthSuccessful = await LocalAuthenticationService.instance
|
||||
.requestLocalAuthentication(context, context.l10n.authenticateGeneric);
|
||||
await PlatformUtil.refocusWindows();
|
||||
if (!isAuthSuccessful) {
|
||||
return;
|
||||
}
|
||||
showShareDialog(context, widget.code);
|
||||
}
|
||||
|
||||
Future<void> _onPinPressed([bool? pop]) async {
|
||||
if (mounted && pop == true) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
bool currentlyPinned = widget.code.isPinned;
|
||||
final display = widget.code.display;
|
||||
final Code code = widget.code.copyWith(
|
||||
@@ -558,7 +586,14 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
void _onDeletePressed(_) async {
|
||||
void _onDeletePressed([bool? pop]) async {
|
||||
if (mounted && pop == true) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
if (!widget.code.isTrashed) {
|
||||
showToast(context, 'Code can only be deleted from trash');
|
||||
return;
|
||||
}
|
||||
bool isAuthSuccessful =
|
||||
await LocalAuthenticationService.instance.requestLocalAuthentication(
|
||||
context,
|
||||
@@ -581,6 +616,72 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
void _onTrashPressed([bool? pop]) async {
|
||||
if (mounted && pop == true) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
if (widget.code.isTrashed) {
|
||||
showToast(context, 'Code is already trashed');
|
||||
return;
|
||||
}
|
||||
bool isAuthSuccessful =
|
||||
await LocalAuthenticationService.instance.requestLocalAuthentication(
|
||||
context,
|
||||
context.l10n.deleteCodeAuthMessage,
|
||||
);
|
||||
if (!isAuthSuccessful) {
|
||||
return;
|
||||
}
|
||||
FocusScope.of(context).requestFocus();
|
||||
final l10n = context.l10n;
|
||||
final String issuerAccount = widget.code.account.isNotEmpty
|
||||
? '${widget.code.issuer} (${widget.code.account})'
|
||||
: widget.code.issuer;
|
||||
await showChoiceActionSheet(
|
||||
context,
|
||||
title: l10n.trashCode,
|
||||
body: l10n.trashCodeMessage(issuerAccount),
|
||||
firstButtonLabel: l10n.trash,
|
||||
isCritical: true,
|
||||
firstButtonOnTap: () async {
|
||||
try {
|
||||
final display = widget.code.display;
|
||||
final Code code = widget.code.copyWith(
|
||||
display: display.copyWith(trashed: true),
|
||||
);
|
||||
await CodeStore.instance.addCode(code);
|
||||
} catch (e) {
|
||||
logger.severe('Failed to trash code: ${e.toString()}');
|
||||
showGenericErrorDialog(context: context, error: e).ignore();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _onRestoreClicked([bool? pop]) async {
|
||||
if (mounted && pop == true) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
if (!widget.code.isTrashed) {
|
||||
showToast(context, 'Code is already restored');
|
||||
return;
|
||||
}
|
||||
FocusScope.of(context).requestFocus();
|
||||
|
||||
try {
|
||||
final display = widget.code.display;
|
||||
final Code code = widget.code.copyWith(
|
||||
display: display.copyWith(trashed: false),
|
||||
);
|
||||
await CodeStore.instance.addCode(code);
|
||||
} catch (e) {
|
||||
logger.severe('Failed to restore code: ${e.toString()}');
|
||||
if (mounted) {
|
||||
showGenericErrorDialog(context: context, error: e).ignore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _getCurrentOTP() {
|
||||
try {
|
||||
return getOTP(widget.code);
|
||||
@@ -603,10 +704,18 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
// replace all digits with •
|
||||
code = code.replaceAll(RegExp(r'\S'), '•');
|
||||
}
|
||||
if (code.length == 6) {
|
||||
return "${code.substring(0, 3)} ${code.substring(3, 6)}";
|
||||
switch (code.length) {
|
||||
case 6:
|
||||
return "${code.substring(0, 3)} ${code.substring(3, 6)}";
|
||||
case 7:
|
||||
return "${code.substring(0, 3)} ${code.substring(3, 4)} ${code.substring(4, 7)}";
|
||||
case 8:
|
||||
return "${code.substring(0, 3)} ${code.substring(3, 5)} ${code.substring(5, 8)}";
|
||||
case 9:
|
||||
return "${code.substring(0, 3)} ${code.substring(3, 6)} ${code.substring(6, 9)}";
|
||||
default:
|
||||
return code;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BottomShadowWidget extends StatelessWidget {
|
||||
@@ -15,7 +13,7 @@ class BottomShadowWidget extends StatelessWidget {
|
||||
color: Colors.transparent,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: shadowColor ?? Theme.of(context).colorScheme.background,
|
||||
color: shadowColor ?? Theme.of(context).colorScheme.surface,
|
||||
spreadRadius: 42,
|
||||
blurRadius: 42,
|
||||
offset: Offset(0, offsetDy), // changes position of shadow
|
||||
|
||||
@@ -24,7 +24,7 @@ class DynamicFAB extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
spreadRadius: 200,
|
||||
blurRadius: 100,
|
||||
offset: const Offset(0, 230),
|
||||
|
||||
76
auth/lib/ui/components/actions_bar_widget.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ActionBarWidget extends StatefulWidget {
|
||||
final VoidCallback? onCancel;
|
||||
final Code code;
|
||||
|
||||
const ActionBarWidget({
|
||||
required this.onCancel,
|
||||
required this.code,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ActionBarWidget> createState() => _ActionBarWidgetState();
|
||||
}
|
||||
|
||||
class _ActionBarWidgetState extends State<ActionBarWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textTheme = getEnteTextTheme(context);
|
||||
return SizedBox(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 8, 20, 8),
|
||||
child: Column(
|
||||
// left align the text
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.code.note.isNotEmpty)
|
||||
SelectableText(
|
||||
widget.code.note,
|
||||
style: textTheme.miniMuted,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: Column(
|
||||
children: [
|
||||
SelectableText(
|
||||
widget.code.issuerAccount,
|
||||
style: textTheme.miniMuted,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
widget.onCancel?.call();
|
||||
},
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
context.l10n.cancel,
|
||||
style: textTheme.mini,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
86
auth/lib/ui/components/bottom_action_bar_widget.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/components/actions_bar_widget.dart';
|
||||
import 'package:ente_auth/ui/components/code_selection_actions_widget.dart';
|
||||
import 'package:ente_auth/ui/components/components_constants.dart';
|
||||
import "package:ente_auth/ui/components/divider_widget.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BottomActionBarWidget extends StatelessWidget {
|
||||
final Code code;
|
||||
final VoidCallback? onCancel;
|
||||
final Color? backgroundColor;
|
||||
final VoidCallback? onShare;
|
||||
final VoidCallback? onPin;
|
||||
final VoidCallback? onShowQR;
|
||||
final VoidCallback? onEdit;
|
||||
final VoidCallback? onRestore;
|
||||
final VoidCallback? onDelete;
|
||||
final VoidCallback? onTrashed;
|
||||
|
||||
const BottomActionBarWidget({
|
||||
required this.code,
|
||||
this.onCancel,
|
||||
this.backgroundColor,
|
||||
super.key,
|
||||
this.onShare,
|
||||
this.onPin,
|
||||
this.onShowQR,
|
||||
this.onEdit,
|
||||
this.onRestore,
|
||||
this.onDelete,
|
||||
this.onTrashed,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bottomPadding = MediaQuery.of(context).padding.bottom;
|
||||
final widthOfScreen = MediaQuery.of(context).size.width;
|
||||
final colorScheme = getEnteColorScheme(context);
|
||||
final double leftRightPadding = min(
|
||||
widthOfScreen > restrictedMaxWidth
|
||||
? (widthOfScreen - restrictedMaxWidth) / 2
|
||||
: 0,
|
||||
20,
|
||||
);
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor ?? colorScheme.backgroundElevated2,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(8),
|
||||
topRight: Radius.circular(8),
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.only(
|
||||
top: 4,
|
||||
bottom: bottomPadding,
|
||||
right: leftRightPadding,
|
||||
left: leftRightPadding,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
CodeSelectionActionsWidget(
|
||||
code: code,
|
||||
onShare: onShare,
|
||||
onPin: onPin,
|
||||
onShowQR: onShowQR,
|
||||
onEdit: onEdit,
|
||||
onRestore: onRestore,
|
||||
onDelete: onDelete,
|
||||
onTrashed: onTrashed,
|
||||
),
|
||||
const DividerWidget(dividerType: DividerType.bottomBar),
|
||||
ActionBarWidget(
|
||||
code: code,
|
||||
onCancel: onCancel,
|
||||
),
|
||||
// const SizedBox(height: 2)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
138
auth/lib/ui/components/code_selection_actions_widget.dart
Normal file
@@ -0,0 +1,138 @@
|
||||
import "package:ente_auth/l10n/l10n.dart";
|
||||
import "package:ente_auth/models/code.dart";
|
||||
import "package:ente_auth/ui/components/selection_action_button.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CodeSelectionActionsWidget extends StatefulWidget {
|
||||
final Code code;
|
||||
final VoidCallback? onShare;
|
||||
final VoidCallback? onPin;
|
||||
final VoidCallback? onShowQR;
|
||||
final VoidCallback? onEdit;
|
||||
final VoidCallback? onRestore;
|
||||
final VoidCallback? onDelete;
|
||||
final VoidCallback? onTrashed;
|
||||
|
||||
const CodeSelectionActionsWidget({
|
||||
super.key,
|
||||
required this.code,
|
||||
this.onShare,
|
||||
this.onPin,
|
||||
this.onShowQR,
|
||||
this.onEdit,
|
||||
this.onRestore,
|
||||
this.onDelete,
|
||||
this.onTrashed,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CodeSelectionActionsWidget> createState() =>
|
||||
_CodeSelectionActionsWidgetState();
|
||||
}
|
||||
|
||||
class _CodeSelectionActionsWidgetState
|
||||
extends State<CodeSelectionActionsWidget> {
|
||||
late final scrollController = ScrollController();
|
||||
|
||||
// static final _logger = Logger("CodeSelectionActionsWidget");
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<SelectionActionButton> items = [];
|
||||
|
||||
if (!widget.code.isTrashed) {
|
||||
items.add(
|
||||
SelectionActionButton(
|
||||
labelText: context.l10n.share,
|
||||
icon: Icons.adaptive.share_outlined,
|
||||
onTap: widget.onShare,
|
||||
),
|
||||
);
|
||||
|
||||
items.add(
|
||||
SelectionActionButton(
|
||||
labelText: widget.code.isPinned
|
||||
? context.l10n.unpinText
|
||||
: context.l10n.pinText,
|
||||
icon: widget.code.isPinned ? Icons.push_pin : Icons.push_pin_outlined,
|
||||
onTap: widget.onPin,
|
||||
),
|
||||
);
|
||||
|
||||
items.add(
|
||||
SelectionActionButton(
|
||||
labelText: 'QR',
|
||||
icon: Icons.qr_code_2_outlined,
|
||||
onTap: widget.onShowQR,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (widget.code.isTrashed) {
|
||||
items.add(
|
||||
SelectionActionButton(
|
||||
labelText: context.l10n.restore,
|
||||
icon: Icons.restore_outlined,
|
||||
onTap: widget.onRestore,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
items.add(
|
||||
SelectionActionButton(
|
||||
labelText: context.l10n.edit,
|
||||
icon: Icons.edit,
|
||||
onTap: widget.onEdit,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
items.add(
|
||||
SelectionActionButton(
|
||||
labelText:
|
||||
widget.code.isTrashed ? context.l10n.delete : context.l10n.trash,
|
||||
icon: widget.code.isTrashed ? Icons.delete_forever : Icons.delete,
|
||||
onTap: widget.code.isTrashed ? widget.onDelete : widget.onTrashed,
|
||||
),
|
||||
);
|
||||
|
||||
if (items.isNotEmpty) {
|
||||
// h4ck: https://github.com/flutter/flutter/issues/57920#issuecomment-893970066
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).removePadding(removeBottom: true),
|
||||
child: SafeArea(
|
||||
child: Scrollbar(
|
||||
radius: const Radius.circular(1),
|
||||
thickness: 2,
|
||||
controller: scrollController,
|
||||
thumbVisibility: true,
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
physics: const BouncingScrollPhysics(
|
||||
decelerationRate: ScrollDecelerationRate.fast,
|
||||
),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(bottom: 24),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(width: 4),
|
||||
...items,
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
}
|
||||
}
|
||||
@@ -77,17 +77,19 @@ class DialogWidget extends StatelessWidget {
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ContentContainer(
|
||||
title: title,
|
||||
body: body,
|
||||
icon: icon,
|
||||
),
|
||||
const SizedBox(height: 36),
|
||||
Actions(buttons),
|
||||
],
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ContentContainer(
|
||||
title: title,
|
||||
body: body,
|
||||
icon: icon,
|
||||
),
|
||||
const SizedBox(height: 36),
|
||||
Actions(buttons),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
196
auth/lib/ui/components/selection_action_button.dart
Normal file
@@ -0,0 +1,196 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import "package:ente_auth/theme/ente_theme.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_svg/svg.dart";
|
||||
|
||||
/// Pass icon or asset path of svg
|
||||
class SelectionActionButton extends StatelessWidget {
|
||||
final String labelText;
|
||||
final IconData? icon;
|
||||
final String? svgAssetPath;
|
||||
final VoidCallback? onTap;
|
||||
final bool shouldShow;
|
||||
|
||||
const SelectionActionButton({
|
||||
required this.labelText,
|
||||
required this.onTap,
|
||||
this.icon,
|
||||
this.svgAssetPath,
|
||||
this.shouldShow = true,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(icon != null || svgAssetPath != null);
|
||||
return AnimatedSize(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
curve: Curves.easeInOutCirc,
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: shouldShow
|
||||
? _Body(
|
||||
labelText: labelText,
|
||||
icon: icon,
|
||||
onTap: onTap,
|
||||
svgAssetPath: svgAssetPath,
|
||||
)
|
||||
: const SizedBox(
|
||||
height: 60,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Body extends StatefulWidget {
|
||||
final String labelText;
|
||||
final IconData? icon;
|
||||
final String? svgAssetPath;
|
||||
final VoidCallback? onTap;
|
||||
const _Body({
|
||||
required this.labelText,
|
||||
required this.onTap,
|
||||
this.icon,
|
||||
this.svgAssetPath,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_Body> createState() => __BodyState();
|
||||
}
|
||||
|
||||
class __BodyState extends State<_Body> {
|
||||
static const minWidth = 64.0;
|
||||
late double widthOfButton;
|
||||
Color? backgroundColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
widthOfButton = getWidthOfButton();
|
||||
final colorScheme = getEnteColorScheme(context);
|
||||
return GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
onTapDown: (details) {
|
||||
setState(() {
|
||||
backgroundColor = colorScheme.fillFaintPressed;
|
||||
});
|
||||
},
|
||||
onTapUp: (details) {
|
||||
setState(() {
|
||||
backgroundColor = null;
|
||||
});
|
||||
},
|
||||
onTapCancel: () {
|
||||
setState(() {
|
||||
backgroundColor = null;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: backgroundColor,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
||||
child: SizedBox(
|
||||
width: widthOfButton,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.icon == Icons.navigation_rounded)
|
||||
Transform.rotate(
|
||||
angle: math.pi / 2,
|
||||
child: Icon(
|
||||
widget.icon,
|
||||
size: 24,
|
||||
color: getEnteColorScheme(context).primary300,
|
||||
shadows: const [
|
||||
BoxShadow(
|
||||
color: Color.fromARGB(12, 0, 179, 60),
|
||||
offset: Offset(0, 2.51),
|
||||
blurRadius: 5.02,
|
||||
spreadRadius: 0,
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color.fromARGB(24, 0, 179, 60),
|
||||
offset: Offset(0, 1.25),
|
||||
blurRadius: 3.76,
|
||||
spreadRadius: 0,
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color.fromARGB(24, 0, 179, 60),
|
||||
offset: Offset(0, 0.63),
|
||||
blurRadius: 1.88,
|
||||
spreadRadius: 0,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
else
|
||||
widget.svgAssetPath != null
|
||||
? SvgPicture.asset(
|
||||
widget.svgAssetPath!,
|
||||
colorFilter: ColorFilter.mode(
|
||||
getEnteColorScheme(context).textMuted,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
width: 24,
|
||||
height: 24,
|
||||
)
|
||||
: Icon(
|
||||
widget.icon,
|
||||
size: 24,
|
||||
color: getEnteColorScheme(context).textMuted,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
widget.labelText,
|
||||
textAlign: TextAlign.center,
|
||||
//textTheme in [getWidthOfLongestWord] should be same as this
|
||||
style: getEnteTextTheme(context).miniMuted,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
getWidthOfButton() {
|
||||
final widthOfWidestWord = getWidthOfWidestWord(
|
||||
widget.labelText,
|
||||
);
|
||||
if (widthOfWidestWord > minWidth) return widthOfWidestWord;
|
||||
return minWidth;
|
||||
}
|
||||
|
||||
double getWidthOfWidestWord(String labelText) {
|
||||
final words = labelText.split(RegExp(r'\s+'));
|
||||
if (words.isEmpty) return 0.0;
|
||||
|
||||
double maxWidth = 0.0;
|
||||
for (String word in words) {
|
||||
final width =
|
||||
computeWidthOfWord(word, getEnteTextTheme(context).miniMuted);
|
||||
if (width > maxWidth) {
|
||||
maxWidth = width;
|
||||
}
|
||||
}
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
//Todo: this doesn't give the correct width of the word, make it right
|
||||
double computeWidthOfWord(String text, TextStyle style) {
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(text: text, style: style),
|
||||
maxLines: 1,
|
||||
textDirection: TextDirection.ltr,
|
||||
textScaler: MediaQuery.textScalerOf(context),
|
||||
)..layout();
|
||||
//buffer of 8 added as width is shorter than actual text width
|
||||
return textPainter.size.width + 8;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/events/codes_updated_event.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CoachMarkWidget extends StatelessWidget {
|
||||
@@ -22,7 +23,7 @@ class CoachMarkWidget extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
color: Theme.of(context).colorScheme.background.withOpacity(0.1),
|
||||
color: Theme.of(context).colorScheme.surface.withOpacity(0.1),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
|
||||
child: Row(
|
||||
@@ -33,14 +34,16 @@ class CoachMarkWidget extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.swipe_left,
|
||||
Icons.info_outline,
|
||||
size: 42,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Text(
|
||||
l10n.swipeHint,
|
||||
PlatformUtil.isDesktop()
|
||||
? l10n.hintForDesktop
|
||||
: l10n.hintForMobile,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/settings/data/import_page.dart';
|
||||
import 'package:ente_auth/ui/settings/faq.dart';
|
||||
import 'package:ente_auth/utils/navigation_util.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class HomeEmptyStateWidget extends StatelessWidget {
|
||||
final VoidCallback? onScanTap;
|
||||
@@ -73,15 +73,16 @@ class HomeEmptyStateWidget extends StatelessWidget {
|
||||
const SizedBox(height: 18),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showModalBottomSheet<void>(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.background,
|
||||
barrierColor: Colors.black87,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return const FAQQuestionsWidget();
|
||||
},
|
||||
);
|
||||
try {
|
||||
PlatformUtil.openWebView(
|
||||
context,
|
||||
context.l10n.faq,
|
||||
"https://help.ente.io/auth/faq",
|
||||
);
|
||||
} catch (e) {
|
||||
Logger("HomeEmptyStateWidget")
|
||||
.severe("Failed to open FAQ", e);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
l10n.faq,
|
||||
|
||||
@@ -22,11 +22,15 @@ import 'package:ente_auth/ui/account/logout_dialog.dart';
|
||||
import 'package:ente_auth/ui/code_error_widget.dart';
|
||||
import 'package:ente_auth/ui/code_widget.dart';
|
||||
import 'package:ente_auth/ui/common/loading_widget.dart';
|
||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/dialog_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/ui/home/coach_mark_widget.dart';
|
||||
import 'package:ente_auth/ui/home/home_empty_state.dart';
|
||||
import 'package:ente_auth/ui/home/speed_dial_label_widget.dart';
|
||||
import 'package:ente_auth/ui/scanner_page.dart';
|
||||
import 'package:ente_auth/ui/settings_page.dart';
|
||||
import 'package:ente_auth/ui/tools/app_lock.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_auth/utils/totp_util.dart';
|
||||
@@ -70,6 +74,10 @@ class _HomePageState extends State<HomePage> {
|
||||
StreamSubscription<TriggerLogoutEvent>? _triggerLogoutEvent;
|
||||
StreamSubscription<IconsChangedEvent>? _iconsChangedEvent;
|
||||
String selectedTag = "";
|
||||
bool _isTrashOpen = false;
|
||||
bool hasTrashedCodes = false;
|
||||
bool hasNonTrashedCodes = false;
|
||||
bool isCompactMode = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -99,10 +107,27 @@ class _HomePageState extends State<HomePage> {
|
||||
void _loadCodes() {
|
||||
CodeStore.instance.getAllCodes().then((codes) {
|
||||
_allCodes = codes;
|
||||
hasTrashedCodes = false;
|
||||
hasNonTrashedCodes = false;
|
||||
for (final c in _allCodes ?? []) {
|
||||
if (c.isTrashed) {
|
||||
hasTrashedCodes = true;
|
||||
} else {
|
||||
hasNonTrashedCodes = true;
|
||||
}
|
||||
if (hasNonTrashedCodes && hasTrashedCodes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasTrashedCodes) {
|
||||
_isTrashOpen = false;
|
||||
}
|
||||
if (!hasNonTrashedCodes && hasTrashedCodes) {
|
||||
_isTrashOpen = true;
|
||||
}
|
||||
|
||||
CodeDisplayStore.instance.getAllTags(allCodes: _allCodes).then((value) {
|
||||
tags = value;
|
||||
|
||||
if (mounted) {
|
||||
if (!tags.contains(selectedTag)) {
|
||||
selectedTag = "";
|
||||
@@ -129,7 +154,8 @@ class _HomePageState extends State<HomePage> {
|
||||
for (final Code codeState in _allCodes!) {
|
||||
if (codeState.hasError ||
|
||||
selectedTag != "" &&
|
||||
!codeState.display.tags.contains(selectedTag)) {
|
||||
!codeState.display.tags.contains(selectedTag) ||
|
||||
(codeState.isTrashed != _isTrashOpen)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -141,11 +167,19 @@ class _HomePageState extends State<HomePage> {
|
||||
}
|
||||
_filteredCodes = issuerMatch;
|
||||
_filteredCodes.addAll(accountMatch);
|
||||
} else if (_isTrashOpen) {
|
||||
_filteredCodes = _allCodes
|
||||
?.where(
|
||||
(element) => !element.hasError && element.isTrashed,
|
||||
)
|
||||
.toList() ??
|
||||
[];
|
||||
} else {
|
||||
_filteredCodes = _allCodes
|
||||
?.where(
|
||||
(element) =>
|
||||
!element.hasError &&
|
||||
!element.isTrashed &&
|
||||
(selectedTag == "" ||
|
||||
element.display.tags.contains(selectedTag)),
|
||||
)
|
||||
@@ -199,12 +233,35 @@ class _HomePageState extends State<HomePage> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> navigateToLockScreen() async {
|
||||
final bool shouldShowLockScreen =
|
||||
await Configuration.instance.shouldShowLockScreen();
|
||||
if (shouldShowLockScreen) {
|
||||
await AppLock.of(context)!.showLockScreen();
|
||||
} else {
|
||||
await showDialogWidget(
|
||||
context: context,
|
||||
title: context.l10n.appLockNotEnabled,
|
||||
body: context.l10n.appLockNotEnabledDescription,
|
||||
isDismissible: true,
|
||||
buttons: const [
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.secondary,
|
||||
labelText: "OK",
|
||||
isInAlert: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
isCompactMode = PreferenceService.instance.isCompactMode();
|
||||
|
||||
return PopScope(
|
||||
onPopInvoked: (_) async {
|
||||
onPopInvokedWithResult: (_, result) async {
|
||||
if (_isSettingsOpen) {
|
||||
scaffoldKey.currentState!.closeDrawer();
|
||||
return;
|
||||
@@ -251,8 +308,20 @@ class _HomePageState extends State<HomePage> {
|
||||
),
|
||||
focusNode: searchBoxFocusNode,
|
||||
),
|
||||
centerTitle: true,
|
||||
centerTitle: PlatformUtil.isDesktop() ? false : true,
|
||||
actions: <Widget>[
|
||||
PlatformUtil.isDesktop()
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.lock),
|
||||
tooltip: l10n.appLock,
|
||||
onPressed: () async {
|
||||
await navigateToLockScreen();
|
||||
},
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
const SizedBox(
|
||||
width: 4,
|
||||
),
|
||||
IconButton(
|
||||
icon: _showSearchBox
|
||||
? const Icon(Icons.clear)
|
||||
@@ -302,6 +371,8 @@ class _HomePageState extends State<HomePage> {
|
||||
final anyCodeHasError =
|
||||
_allCodes?.firstWhereOrNull((element) => element.hasError) != null;
|
||||
final indexOffset = anyCodeHasError ? 1 : 0;
|
||||
final itemCount = (hasNonTrashedCodes ? tags.length + 1 : 0) +
|
||||
(hasTrashedCodes ? 1 : 0);
|
||||
|
||||
final list = Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -315,21 +386,37 @@ class _HomePageState extends State<HomePage> {
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
|
||||
separatorBuilder: (context, index) =>
|
||||
const SizedBox(width: 8),
|
||||
itemCount: tags.length + 1,
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
if (index == 0 && hasNonTrashedCodes) {
|
||||
return TagChip(
|
||||
label: "All",
|
||||
state: selectedTag == ""
|
||||
label: l10n.all,
|
||||
state: selectedTag == "" && _isTrashOpen == false
|
||||
? TagChipState.selected
|
||||
: TagChipState.unselected,
|
||||
onTap: () {
|
||||
selectedTag = "";
|
||||
_isTrashOpen = false;
|
||||
setState(() {});
|
||||
_applyFilteringAndRefresh();
|
||||
},
|
||||
);
|
||||
}
|
||||
if (index == itemCount - 1 && hasTrashedCodes) {
|
||||
return TagChip(
|
||||
label: l10n.trash,
|
||||
state: _isTrashOpen
|
||||
? TagChipState.selected
|
||||
: TagChipState.unselected,
|
||||
onTap: () {
|
||||
selectedTag = "";
|
||||
_isTrashOpen = !_isTrashOpen;
|
||||
setState(() {});
|
||||
_applyFilteringAndRefresh();
|
||||
},
|
||||
iconData: Icons.delete,
|
||||
);
|
||||
}
|
||||
return TagChip(
|
||||
label: tags[index - 1],
|
||||
action: TagChipAction.menu,
|
||||
@@ -337,6 +424,7 @@ class _HomePageState extends State<HomePage> {
|
||||
? TagChipState.selected
|
||||
: TagChipState.unselected,
|
||||
onTap: () {
|
||||
_isTrashOpen = false;
|
||||
if (selectedTag == tags[index - 1]) {
|
||||
selectedTag = "";
|
||||
setState(() {});
|
||||
@@ -369,9 +457,13 @@ class _HomePageState extends State<HomePage> {
|
||||
}
|
||||
final newIndex = index - indexOffset;
|
||||
|
||||
final code = _filteredCodes[newIndex];
|
||||
|
||||
return ClipRect(
|
||||
child: CodeWidget(
|
||||
_filteredCodes[newIndex],
|
||||
key: ValueKey('${code.hashCode}_$newIndex'),
|
||||
code,
|
||||
isCompactMode: isCompactMode,
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -401,7 +493,9 @@ class _HomePageState extends State<HomePage> {
|
||||
itemBuilder: ((context, index) {
|
||||
final codeState = _filteredCodes[index];
|
||||
return CodeWidget(
|
||||
key: ValueKey('${codeState.hashCode}_$index'),
|
||||
codeState,
|
||||
isCompactMode: isCompactMode,
|
||||
);
|
||||
}),
|
||||
itemCount: _filteredCodes.length,
|
||||
@@ -499,7 +593,7 @@ class _HomePageState extends State<HomePage> {
|
||||
foregroundColor: Theme.of(context).colorScheme.fabForegroundColor,
|
||||
backgroundColor: Theme.of(context).colorScheme.fabBackgroundColor,
|
||||
overlayOpacity: 0.5,
|
||||
overlayColor: Theme.of(context).colorScheme.background,
|
||||
overlayColor: Theme.of(context).colorScheme.surface,
|
||||
elevation: 8.0,
|
||||
animationCurve: Curves.elasticInOut,
|
||||
children: [
|
||||
|
||||
@@ -62,8 +62,8 @@ class _AppUpdateDialogState extends State<AppUpdateDialog> {
|
||||
height: 64,
|
||||
child: OutlinedButton(
|
||||
style: Theme.of(context).outlinedButtonTheme.style!.copyWith(
|
||||
textStyle: MaterialStateProperty.resolveWith<TextStyle?>(
|
||||
(Set<MaterialState> states) {
|
||||
textStyle: WidgetStateProperty.resolveWith<TextStyle?>(
|
||||
(Set<WidgetState> states) {
|
||||
return Theme.of(context).textTheme.titleMedium;
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/ui/common/loading_widget.dart';
|
||||
import 'package:expansion_tile_card/expansion_tile_card.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FAQQuestionsWidget extends StatelessWidget {
|
||||
const FAQQuestionsWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<List<FaqItem>>(
|
||||
future: Future.value(_getFAQs(context)),
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final faqs = <Widget>[];
|
||||
faqs.add(
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Text(
|
||||
context.l10n.faq,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
for (final faq in snapshot.data) {
|
||||
faqs.add(FaqWidget(faq: faq));
|
||||
}
|
||||
faqs.add(
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
),
|
||||
);
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: faqs,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const EnteLoadingWidget();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
List<FaqItem> _getFAQs(BuildContext context) {
|
||||
final l01n = context.l10n;
|
||||
List<FaqItem> faqs = [];
|
||||
faqs.add(FaqItem(q: l01n.faq_q_1, a: l01n.faq_a_1));
|
||||
faqs.add(FaqItem(q: l01n.faq_q_2, a: l01n.faq_a_2));
|
||||
faqs.add(FaqItem(q: l01n.faq_q_3, a: l01n.faq_a_3));
|
||||
faqs.add(FaqItem(q: l01n.faq_q_4, a: l01n.faq_a_4));
|
||||
faqs.add(FaqItem(q: l01n.faq_q_5, a: l01n.faq_a_5));
|
||||
return faqs;
|
||||
}
|
||||
}
|
||||
|
||||
class FaqWidget extends StatelessWidget {
|
||||
const FaqWidget({
|
||||
super.key,
|
||||
required this.faq,
|
||||
});
|
||||
|
||||
final FaqItem? faq;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: ExpansionTileCard(
|
||||
elevation: 0,
|
||||
title: Text(faq!.q),
|
||||
expandedTextColor: Theme.of(context).colorScheme.alternativeColor,
|
||||
baseColor: Theme.of(context).cardColor,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 12,
|
||||
),
|
||||
child: Text(
|
||||
faq!.a,
|
||||
style: const TextStyle(
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FaqItem {
|
||||
final String q;
|
||||
final String a;
|
||||
|
||||
FaqItem({
|
||||
required this.q,
|
||||
required this.a,
|
||||
});
|
||||
|
||||
factory FaqItem.fromMap(Map<String, dynamic> map) {
|
||||
return FaqItem(
|
||||
q: map['q'] ?? 'q',
|
||||
a: map['a'] ?? 'a',
|
||||
);
|
||||
}
|
||||
|
||||
factory FaqItem.fromJson(String source) =>
|
||||
FaqItem.fromMap(json.decode(source));
|
||||
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ente_auth/app/view/app.dart';
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/core/logging/super_logging.dart';
|
||||
import 'package:ente_auth/events/icons_changed_event.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/locale.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
@@ -78,6 +80,22 @@ class _AdvancedSectionWidgetState extends State<AdvancedSectionWidget> {
|
||||
),
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
title: l10n.compactMode,
|
||||
),
|
||||
trailingWidget: ToggleSwitchWidget(
|
||||
value: () => PreferenceService.instance.isCompactMode(),
|
||||
onChanged: () async {
|
||||
await PreferenceService.instance.setCompactMode(
|
||||
!PreferenceService.instance.isCompactMode(),
|
||||
);
|
||||
Bus.instance.fire(IconsChangedEvent());
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
title: l10n.shouldHideCode,
|
||||
@@ -88,7 +106,7 @@ class _AdvancedSectionWidgetState extends State<AdvancedSectionWidget> {
|
||||
await PreferenceService.instance.setHideCodes(
|
||||
!PreferenceService.instance.shouldHideCodes(),
|
||||
);
|
||||
if(PreferenceService.instance.shouldHideCodes()) {
|
||||
if (PreferenceService.instance.shouldHideCodes()) {
|
||||
showToast(context, context.l10n.doubleTapToViewHiddenCode);
|
||||
}
|
||||
setState(() {});
|
||||
|
||||
@@ -124,8 +124,14 @@ class _ItemsWidgetState extends State<ItemsWidget> {
|
||||
|
||||
String _getLanguageName(Locale locale) {
|
||||
switch (locale.languageCode) {
|
||||
case 'ar':
|
||||
return 'العربية';
|
||||
case 'en':
|
||||
return 'English';
|
||||
case 'bg':
|
||||
return 'Български';
|
||||
case 'el':
|
||||
return 'Ελληνικά';
|
||||
case 'es':
|
||||
switch (locale.countryCode) {
|
||||
case 'ES':
|
||||
@@ -152,10 +158,14 @@ class _ItemsWidgetState extends State<ItemsWidget> {
|
||||
}
|
||||
case 'ru':
|
||||
return 'Русский';
|
||||
case 'sk':
|
||||
return 'Slovenčina';
|
||||
case 'tr':
|
||||
return 'Türkçe';
|
||||
case 'uk':
|
||||
return 'Українська';
|
||||
case 'vi':
|
||||
return 'Tiếng Việt';
|
||||
case 'fi':
|
||||
return 'Suomi';
|
||||
case 'zh':
|
||||
|
||||
@@ -140,12 +140,13 @@ class CustomPinKeypad extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _Button extends StatelessWidget {
|
||||
class _Button extends StatefulWidget {
|
||||
final String number;
|
||||
final String text;
|
||||
final VoidCallback? onTap;
|
||||
final bool muteButton;
|
||||
final Widget? icon;
|
||||
|
||||
const _Button({
|
||||
required this.number,
|
||||
required this.text,
|
||||
@@ -154,47 +155,78 @@ class _Button extends StatelessWidget {
|
||||
this.icon,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_Button> createState() => _ButtonState();
|
||||
}
|
||||
|
||||
class _ButtonState extends State<_Button> {
|
||||
bool isPressed = false;
|
||||
|
||||
void _onTapDown(TapDownDetails details) {
|
||||
setState(() {
|
||||
isPressed = true;
|
||||
});
|
||||
}
|
||||
|
||||
void _onTapUp(TapUpDetails details) async {
|
||||
setState(() {
|
||||
isPressed = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = getEnteColorScheme(context);
|
||||
final textTheme = getEnteTextTheme(context);
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
color: muteButton
|
||||
? colorScheme.fillFaintPressed
|
||||
: icon == null
|
||||
? colorScheme.backgroundElevated2
|
||||
: null,
|
||||
),
|
||||
child: Center(
|
||||
child: muteButton
|
||||
? const SizedBox.shrink()
|
||||
: icon != null
|
||||
? Container(
|
||||
child: icon,
|
||||
)
|
||||
: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
number,
|
||||
style: textTheme.h3,
|
||||
),
|
||||
Text(
|
||||
text,
|
||||
style: textTheme.tinyBold,
|
||||
),
|
||||
],
|
||||
onTap: widget.onTap,
|
||||
onTapDown: _onTapDown,
|
||||
onTapUp: _onTapUp,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 100),
|
||||
curve: Curves.easeOut,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
color: isPressed
|
||||
? colorScheme.backgroundElevated
|
||||
: widget.muteButton
|
||||
? colorScheme.fillFaintPressed
|
||||
: widget.icon == null
|
||||
? colorScheme.backgroundElevated2
|
||||
: null,
|
||||
),
|
||||
child: Center(
|
||||
child: widget.muteButton
|
||||
? const SizedBox.shrink()
|
||||
: widget.icon != null
|
||||
? Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4,
|
||||
vertical: 10,
|
||||
),
|
||||
child: widget.icon,
|
||||
)
|
||||
: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
widget.number,
|
||||
style: textTheme.h3,
|
||||
),
|
||||
Text(
|
||||
widget.text,
|
||||
style: textTheme.tinyBold,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -5,9 +5,12 @@ import "package:ente_auth/core/configuration.dart";
|
||||
import "package:ente_auth/l10n/l10n.dart";
|
||||
import "package:ente_auth/services/local_authentication_service.dart";
|
||||
import "package:ente_auth/theme/ente_theme.dart";
|
||||
import "package:ente_auth/ui/components/buttons/button_widget.dart";
|
||||
import "package:ente_auth/ui/components/captioned_text_widget.dart";
|
||||
import "package:ente_auth/ui/components/dialog_widget.dart";
|
||||
import "package:ente_auth/ui/components/divider_widget.dart";
|
||||
import "package:ente_auth/ui/components/menu_item_widget.dart";
|
||||
import "package:ente_auth/ui/components/models/button_type.dart";
|
||||
import "package:ente_auth/ui/components/title_bar_title_widget.dart";
|
||||
import "package:ente_auth/ui/components/title_bar_widget.dart";
|
||||
import "package:ente_auth/ui/components/toggle_switch_widget.dart";
|
||||
@@ -15,7 +18,6 @@ import "package:ente_auth/ui/settings/lock_screen/lock_screen_auto_lock.dart";
|
||||
import "package:ente_auth/ui/settings/lock_screen/lock_screen_password.dart";
|
||||
import "package:ente_auth/ui/settings/lock_screen/lock_screen_pin.dart";
|
||||
import "package:ente_auth/ui/tools/app_lock.dart";
|
||||
import "package:ente_auth/utils/dialog_util.dart";
|
||||
import "package:ente_auth/utils/lock_screen_settings.dart";
|
||||
import "package:ente_auth/utils/navigation_util.dart";
|
||||
import "package:ente_auth/utils/platform_util.dart";
|
||||
@@ -67,10 +69,18 @@ class _LockScreenOptionsState extends State<LockScreenOptions> {
|
||||
await _lockscreenSetting.removePinAndPassword();
|
||||
await _configuration.setSystemLockScreen(!isSystemLockEnabled);
|
||||
} else {
|
||||
await showErrorDialog(
|
||||
context,
|
||||
context.l10n.noSystemLockFound,
|
||||
context.l10n.deviceLockEnablePreSteps,
|
||||
await showDialogWidget(
|
||||
context: context,
|
||||
title: context.l10n.noSystemLockFound,
|
||||
body: context.l10n.deviceLockEnablePreSteps,
|
||||
isDismissible: true,
|
||||
buttons: const [
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.secondary,
|
||||
labelText: "OK",
|
||||
isInAlert: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
await _initializeSettings();
|
||||
|
||||
@@ -101,7 +101,9 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async => await onPasskeyClick(context),
|
||||
onTap: () async {
|
||||
await onPasskeyClick(context);
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
@@ -178,6 +180,15 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
||||
|
||||
Future<void> onPasskeyClick(BuildContext buildContext) async {
|
||||
try {
|
||||
final hasAuthenticated =
|
||||
await LocalAuthenticationService.instance.requestLocalAuthentication(
|
||||
context,
|
||||
context.l10n.authenticateGeneric,
|
||||
);
|
||||
await PlatformUtil.refocusWindows();
|
||||
if (!hasAuthenticated) {
|
||||
return;
|
||||
}
|
||||
final isPassKeyResetEnabled =
|
||||
await PasskeyService.instance.isPasskeyRecoveryEnabled();
|
||||
if (!isPassKeyResetEnabled) {
|
||||
|
||||
@@ -5,8 +5,8 @@ import 'package:ente_auth/ui/components/captioned_text_widget.dart';
|
||||
import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart';
|
||||
import 'package:ente_auth/ui/components/menu_item_widget.dart';
|
||||
import 'package:ente_auth/ui/settings/common_settings.dart';
|
||||
import 'package:ente_auth/ui/settings/faq.dart';
|
||||
import 'package:ente_auth/utils/email_util.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
@@ -42,15 +42,15 @@ class _SupportSectionWidgetState extends State<SupportSectionWidget> {
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
// ignore: unawaited_futures
|
||||
showModalBottomSheet<void>(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
barrierColor: Colors.black87,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return const FAQQuestionsWidget();
|
||||
},
|
||||
);
|
||||
try {
|
||||
PlatformUtil.openWebView(
|
||||
context,
|
||||
context.l10n.faq,
|
||||
"https://help.ente.io/auth/faq",
|
||||
);
|
||||
} catch (e) {
|
||||
Logger("SupportSection").severe("Failed to open FAQ", e);
|
||||
}
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
|
||||
@@ -142,7 +142,10 @@ class SettingsPage extends StatelessWidget {
|
||||
sectionSpacing,
|
||||
]);
|
||||
|
||||
if (Platform.isAndroid || kDebugMode) {
|
||||
if (Platform.isAndroid ||
|
||||
Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
kDebugMode) {
|
||||
contents.addAll([
|
||||
const ThemeSwitchWidget(),
|
||||
sectionSpacing,
|
||||
|
||||
154
auth/lib/ui/share/code_share.dart
Normal file
@@ -0,0 +1,154 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/share_utils.dart';
|
||||
import 'package:ente_auth/utils/totp_util.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class ShareCodeDialog extends StatefulWidget {
|
||||
final Code code;
|
||||
const ShareCodeDialog({super.key, required this.code});
|
||||
|
||||
@override
|
||||
State<ShareCodeDialog> createState() => _ShareCodeDialogState();
|
||||
}
|
||||
|
||||
class _ShareCodeDialogState extends State<ShareCodeDialog> {
|
||||
final Logger logger = Logger('_ShareCodeDialogState');
|
||||
final List<int> _durationInMins = [2, 5, 10];
|
||||
late int selectedValue;
|
||||
|
||||
String getItemLabel(int min) {
|
||||
if (min == 60) return '1 hour';
|
||||
if (min > 60) {
|
||||
var hour = '${min ~/ 60}';
|
||||
if (min % 60 == 0) return '$hour hour';
|
||||
var minx = '${min % 60}';
|
||||
return '$hour hr $minx min';
|
||||
}
|
||||
return '$min min';
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
selectedValue = _durationInMins[1];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(context.l10n.shareCodes),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(context.l10n.shareCodesDuration),
|
||||
const SizedBox(height: 10),
|
||||
DropdownButtonHideUnderline(
|
||||
child: DropdownButton2(
|
||||
hint: const Text('Select an option'),
|
||||
items: _durationInMins
|
||||
.map(
|
||||
(item) => DropdownMenuItem<int>(
|
||||
value: item,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(getItemLabel(item)),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
value: selectedValue,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
selectedValue = value ?? 2;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.primary,
|
||||
buttonSize: ButtonSize.large,
|
||||
labelText: context.l10n.share,
|
||||
onTap: () async {
|
||||
try {
|
||||
await shareCode(selectedValue);
|
||||
Navigator.of(context).pop();
|
||||
} catch (e, s) {
|
||||
logger.severe('Failed to generate shared codes', e, s);
|
||||
showGenericErrorDialog(context: context, error: e).ignore();
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.secondary,
|
||||
buttonSize: ButtonSize.large,
|
||||
labelText: context.l10n.cancel,
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> shareCode(int durationInMin) async {
|
||||
final int count = ((durationInMin * 60.0) / widget.code.period).ceil();
|
||||
final result = generateFutureTotpCodes(widget.code, count);
|
||||
Map<String, dynamic> data = {
|
||||
'startTime': result.$1,
|
||||
'step': widget.code.period,
|
||||
'codes': result.$2.join(","),
|
||||
};
|
||||
final Uint8List key = _generate256BitKey();
|
||||
Uint8List input = utf8.encode(jsonEncode(data));
|
||||
final encResult = await CryptoUtil.encryptData(input, key);
|
||||
String url =
|
||||
'https://auth.ente.io/share?data=${_uint8ListToUrlSafeBase64(encResult.encryptedData!)}&header=${_uint8ListToUrlSafeBase64(encResult.header!)}#${_uint8ListToUrlSafeBase64(key)}';
|
||||
try {
|
||||
await shareText(url, context: context);
|
||||
} catch (e) {
|
||||
logger.warning('Failed to share code: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
String _uint8ListToUrlSafeBase64(Uint8List data) {
|
||||
String base64Str = base64UrlEncode(data);
|
||||
return base64Str.replaceAll('=', '');
|
||||
}
|
||||
|
||||
Uint8List _generate256BitKey() {
|
||||
final random = Random.secure();
|
||||
final bytes = Uint8List(32); // 32 bytes = 32 * 8 bits = 256 bits
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
bytes[i] = random
|
||||
.nextInt(256); // Generates a random number between 0 and 255 (1 byte)
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
void showShareDialog(BuildContext context, Code code) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return ShareCodeDialog(
|
||||
code: code,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
@@ -193,6 +194,8 @@ class _LockScreenState extends State<LockScreen> with WidgetsBindingObserver {
|
||||
isCritical: true,
|
||||
firstButtonOnTap: () async {
|
||||
await UserService.instance.logout(context);
|
||||
// To start the app afresh, resetting all state.
|
||||
Process.killPid(pid, ProcessSignal.sigkill);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:confetti/confetti.dart';
|
||||
@@ -14,6 +15,7 @@ import 'package:ente_auth/ui/components/dialog_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_result.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/utils/email_util.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -109,14 +111,14 @@ String parseErrorForUI(
|
||||
final DioException dioError = error;
|
||||
if (dioError.type == DioExceptionType.badResponse) {
|
||||
if (dioError.response?.data["code"] != null) {
|
||||
errorInfo = "Reason: " + dioError.response!.data["code"];
|
||||
errorInfo = "Reason: ${dioError.response!.data["code"]}";
|
||||
} else {
|
||||
errorInfo = "Reason: " + dioError.response!.data.toString();
|
||||
errorInfo = "Reason: ${dioError.response!.data}";
|
||||
}
|
||||
} else if (dioError.type == DioExceptionType.unknown) {
|
||||
errorInfo = "Reason: " + dioError.error.toString();
|
||||
errorInfo = "Reason: $dioError.error";
|
||||
} else {
|
||||
errorInfo = "Reason: " + dioError.type.toString();
|
||||
errorInfo = "Reason: $dioError.type";
|
||||
}
|
||||
} else {
|
||||
if (kDebugMode) {
|
||||
@@ -140,11 +142,19 @@ Future<ButtonResult?> showGenericErrorDialog({
|
||||
bool isDismissible = true,
|
||||
required Object? error,
|
||||
}) async {
|
||||
final errorBody = parseErrorForUI(
|
||||
String errorBody = parseErrorForUI(
|
||||
context,
|
||||
context.l10n.itLooksLikeSomethingWentWrongPleaseRetryAfterSome,
|
||||
error: error,
|
||||
);
|
||||
bool isWindowCertError = false;
|
||||
if (Platform.isWindows &&
|
||||
error != null &&
|
||||
error.toString().contains("CERTIFICATE_VERIFY_FAILED")) {
|
||||
isWindowCertError = true;
|
||||
errorBody =
|
||||
"Certificate verification failed. Please update your system certificates, & restart the app. If the issue persists, please contact support.";
|
||||
}
|
||||
|
||||
return showDialogWidget(
|
||||
context: context,
|
||||
@@ -159,6 +169,20 @@ Future<ButtonResult?> showGenericErrorDialog({
|
||||
buttonAction: ButtonAction.first,
|
||||
isInAlert: true,
|
||||
),
|
||||
if (isWindowCertError)
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.neutral,
|
||||
labelText: 'Update Certificates',
|
||||
buttonAction: ButtonAction.third,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
PlatformUtil.openWebView(
|
||||
context,
|
||||
context.l10n.faq,
|
||||
"https://help.ente.io/auth/troubleshooting/windows-login",
|
||||
);
|
||||
},
|
||||
),
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.secondary,
|
||||
labelText: context.l10n.contactSupport,
|
||||
@@ -215,7 +239,7 @@ Future<ButtonResult?> showChoiceDialog(
|
||||
required String title,
|
||||
String? body,
|
||||
required String firstButtonLabel,
|
||||
String secondButtonLabel = "Cancel",
|
||||
String? secondButtonLabel = "Cancel",
|
||||
ButtonType firstButtonType = ButtonType.neutral,
|
||||
ButtonType secondButtonType = ButtonType.secondary,
|
||||
ButtonAction firstButtonAction = ButtonAction.first,
|
||||
@@ -234,13 +258,14 @@ Future<ButtonResult?> showChoiceDialog(
|
||||
onTap: firstButtonOnTap,
|
||||
buttonAction: firstButtonAction,
|
||||
),
|
||||
ButtonWidget(
|
||||
buttonType: secondButtonType,
|
||||
labelText: secondButtonLabel,
|
||||
isInAlert: true,
|
||||
onTap: secondButtonOnTap,
|
||||
buttonAction: secondButtonAction,
|
||||
),
|
||||
if (secondButtonLabel != null)
|
||||
ButtonWidget(
|
||||
buttonType: secondButtonType,
|
||||
labelText: secondButtonLabel,
|
||||
isInAlert: true,
|
||||
onTap: secondButtonOnTap,
|
||||
buttonAction: secondButtonAction,
|
||||
),
|
||||
];
|
||||
return showDialogWidget(
|
||||
context: context,
|
||||
|
||||
@@ -1,12 +1,95 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:xdg_directories/xdg_directories.dart';
|
||||
|
||||
class DirectoryUtils {
|
||||
static Future<String> getDatabasePath(String databaseName) async => p.joinAll(
|
||||
[
|
||||
(await getApplicationDocumentsDirectory()).path,
|
||||
"ente",
|
||||
".$databaseName",
|
||||
],
|
||||
static final logger = Logger('DirectoryUtils');
|
||||
|
||||
static Future<String> getDatabasePath(String databaseName) async {
|
||||
String? directoryPath;
|
||||
|
||||
if (Platform.isLinux) {
|
||||
try {
|
||||
directoryPath = dataHome.path;
|
||||
} catch (e) {
|
||||
logger.warning("Failed to get dataHome: $e");
|
||||
}
|
||||
}
|
||||
|
||||
directoryPath ??= (await getApplicationDocumentsDirectory()).path;
|
||||
|
||||
return p.joinAll(
|
||||
[
|
||||
directoryPath,
|
||||
"enteauth",
|
||||
".$databaseName",
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static Future<Directory> getDirectoryForInit() async {
|
||||
Directory? directory;
|
||||
if (Platform.isLinux) {
|
||||
try {
|
||||
return cacheHome;
|
||||
} catch (e) {
|
||||
logger.warning("Failed to get cacheHome: $e");
|
||||
}
|
||||
}
|
||||
|
||||
directory ??= await getApplicationDocumentsDirectory();
|
||||
|
||||
return Directory(p.join(directory.path, "enteauthinit"));
|
||||
}
|
||||
|
||||
static Future<Directory> getTempsDir() async {
|
||||
return await getTemporaryDirectory();
|
||||
}
|
||||
|
||||
static migrateNamingChanges() async {
|
||||
final databaseFile = File(
|
||||
p.join(
|
||||
(await getApplicationDocumentsDirectory()).path,
|
||||
"ente",
|
||||
".ente.authenticator.db",
|
||||
),
|
||||
);
|
||||
final offlineDatabaseFile = File(
|
||||
p.join(
|
||||
(await getApplicationDocumentsDirectory()).path,
|
||||
"ente",
|
||||
".ente.offline_authenticator.db",
|
||||
),
|
||||
);
|
||||
|
||||
final oldDataDir = Directory(
|
||||
p.join(dataHome.path, "ente_auth"),
|
||||
);
|
||||
final newDir = Directory(
|
||||
p.join(dataHome.path, "enteauth"),
|
||||
);
|
||||
await newDir.create(recursive: true);
|
||||
|
||||
if (await databaseFile.exists()) {
|
||||
await databaseFile.rename(
|
||||
p.join(newDir.path, ".ente.authenticator.db"),
|
||||
);
|
||||
}
|
||||
|
||||
if (await offlineDatabaseFile.exists()) {
|
||||
await offlineDatabaseFile.rename(
|
||||
p.join(newDir.path, ".ente.offline_authenticator.db"),
|
||||
);
|
||||
}
|
||||
|
||||
if (await oldDataDir.exists()) {
|
||||
await oldDataDir.list().forEach((file) async {
|
||||
await file.rename(p.join(newDir.path, p.basename(file.path)));
|
||||
});
|
||||
await oldDataDir.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:ente_auth/ui/components/dialog_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/ui/tools/debug/log_file_viewer.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/directory_utils.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_auth/utils/share_utils.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
@@ -146,7 +147,7 @@ Future<String> getZippedLogsFile(BuildContext context) async {
|
||||
await dialog.show();
|
||||
final logsPath = (await getApplicationSupportDirectory()).path;
|
||||
final logsDirectory = Directory("$logsPath/logs");
|
||||
final tempPath = (await getTemporaryDirectory()).path;
|
||||
final tempPath = (await DirectoryUtils.getTempsDir()).path;
|
||||
final zipFilePath =
|
||||
"$tempPath/logs-${Configuration.instance.getUserID() ?? 0}.zip";
|
||||
final encoder = ZipFileEncoder();
|
||||
|
||||
@@ -5,6 +5,8 @@ import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/dialog_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
Future<void> shareDialog(
|
||||
BuildContext context,
|
||||
@@ -49,3 +51,52 @@ Future<void> shareDialog(
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Rect _sharePosOrigin(BuildContext? context, GlobalKey? key) {
|
||||
late final Rect rect;
|
||||
if (context != null) {
|
||||
rect = shareButtonRect(context, key);
|
||||
} else {
|
||||
rect = const Offset(20.0, 20.0) & const Size(10, 10);
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
/// Returns the rect of button if context and key are not null
|
||||
/// If key is null, returned rect will be at the center of the screen
|
||||
Rect shareButtonRect(BuildContext context, GlobalKey? shareButtonKey) {
|
||||
Size size = MediaQuery.sizeOf(context);
|
||||
final RenderObject? renderObject =
|
||||
shareButtonKey?.currentContext?.findRenderObject();
|
||||
RenderBox? renderBox;
|
||||
if (renderObject != null && renderObject is RenderBox) {
|
||||
renderBox = renderObject;
|
||||
}
|
||||
if (renderBox == null) {
|
||||
return Rect.fromLTWH(0, 0, size.width, size.height / 2);
|
||||
}
|
||||
size = renderBox.size;
|
||||
final Offset position = renderBox.localToGlobal(Offset.zero);
|
||||
return Rect.fromCenter(
|
||||
center: position + Offset(size.width / 2, size.height / 2),
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
);
|
||||
}
|
||||
|
||||
Future<ShareResult> shareText(
|
||||
String text, {
|
||||
BuildContext? context,
|
||||
GlobalKey? key,
|
||||
}) async {
|
||||
try {
|
||||
final sharePosOrigin = _sharePosOrigin(context, key);
|
||||
return Share.share(
|
||||
text,
|
||||
sharePositionOrigin: sharePosOrigin,
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logger("ShareUtil").severe("failed to share text", e, s);
|
||||
return ShareResult.unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,38 @@ String getNextTotp(Code code) {
|
||||
);
|
||||
}
|
||||
|
||||
// generateFutureTotpCodes generates future TOTP codes based on the current time.
|
||||
// It returns the start time and a list of future codes.
|
||||
(int, List<String>) generateFutureTotpCodes(Code code, int count) {
|
||||
final int startTime =
|
||||
((DateTime.now().millisecondsSinceEpoch ~/ 1000) ~/ code.period) *
|
||||
code.period *
|
||||
1000;
|
||||
final String secret = getSanitizedSecret(code.secret);
|
||||
final List<String> codes = [];
|
||||
if (code.type == Type.steam || code.issuer.toLowerCase() == 'steam') {
|
||||
final SteamTOTP steamTotp = SteamTOTP(secret: code.secret);
|
||||
for (int i = 0; i < count; i++) {
|
||||
int generatedTime = startTime + code.period * 1000 * i;
|
||||
codes.add(steamTotp.generate(generatedTime ~/ 1000));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
int generatedTime = startTime + code.period * 1000 * i;
|
||||
final genCode = otp.OTP.generateTOTPCodeString(
|
||||
secret,
|
||||
generatedTime,
|
||||
length: code.digits,
|
||||
interval: code.period,
|
||||
algorithm: _getAlgorithm(code),
|
||||
isGoogle: true,
|
||||
);
|
||||
codes.add(genCode);
|
||||
}
|
||||
}
|
||||
return (startTime, codes);
|
||||
}
|
||||
|
||||
otp.Algorithm _getAlgorithm(Code code) {
|
||||
switch (code.algorithm) {
|
||||
case Algorithm.sha256:
|
||||
|
||||
@@ -36,7 +36,7 @@ class WindowsProtocolHandler {
|
||||
hKey,
|
||||
txtKey,
|
||||
txtValue,
|
||||
REG_SZ,
|
||||
REG_VALUE_TYPE.REG_SZ,
|
||||
txtData,
|
||||
txtData.length * 2 + 2,
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ project(runner LANGUAGES CXX)
|
||||
|
||||
# The name of the executable created for the application. Change this to change
|
||||
# the on-disk name of your application.
|
||||
set(BINARY_NAME "ente_auth")
|
||||
set(BINARY_NAME "enteauth")
|
||||
# The unique GTK application identifier for this application. See:
|
||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||
set(APPLICATION_ID "io.ente.auth")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
display_name: Auth
|
||||
display_name: Ente Auth
|
||||
package_name: enteauth
|
||||
license: GPLv3
|
||||
|
||||
metainfo: linux/packaging/ente_auth.appdata.xml
|
||||
@@ -26,9 +27,9 @@ startup_notify: false
|
||||
# include:
|
||||
# - libcurl.so.4
|
||||
include:
|
||||
- libffi.so.8
|
||||
- libtiff.so.5
|
||||
- libjpeg.so.8
|
||||
- libffi.so.8
|
||||
- libtiff.so.5
|
||||
- libjpeg.so.8
|
||||
|
||||
supported_mime_type:
|
||||
- x-scheme-handler/enteauth
|
||||
- x-scheme-handler/enteauth
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
display_name: Auth
|
||||
package_name: auth
|
||||
display_name: Ente Auth
|
||||
package_name: enteauth
|
||||
include_build_number: false
|
||||
|
||||
maintainer:
|
||||
name: Ente.io Developers
|
||||
email: auth@ente.io
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
display_name: Auth
|
||||
package_name: auth
|
||||
display_name: Ente Auth
|
||||
package_name: enteauth
|
||||
maintainer:
|
||||
name: Ente.io Developers
|
||||
email: auth@ente.io
|
||||
|
||||
@@ -6,6 +6,7 @@ packager: Ente.io Developers
|
||||
packagerEmail: auth@ente.io
|
||||
license: GPLv3
|
||||
url: https://github.com/ente-io/ente
|
||||
package_name: enteauth
|
||||
|
||||
display_name: Auth
|
||||
|
||||
|
||||