mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-31 11:08:47 +02:00
Compare commits
684 Commits
fix-androi
...
v3.2.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fe6cd503c | ||
|
|
7dd581d175 | ||
|
|
f7d1d92613 | ||
|
|
55e1f6922e | ||
|
|
f408cdff02 | ||
|
|
be7a140877 | ||
|
|
00691fa43b | ||
|
|
8e6db4d81c | ||
|
|
489f0d376d | ||
|
|
6e6be63e7b | ||
|
|
228951da49 | ||
|
|
e806812593 | ||
|
|
3b92c401f3 | ||
|
|
c1a684f14a | ||
|
|
21cbc75b93 | ||
|
|
6c2d95abe1 | ||
|
|
00460f84ee | ||
|
|
eedbded335 | ||
|
|
843178b8cf | ||
|
|
d5ca8d70b7 | ||
|
|
7baaeda722 | ||
|
|
1388e77828 | ||
|
|
caca30240d | ||
|
|
fc476828e8 | ||
|
|
a76c35356d | ||
|
|
d6584664f3 | ||
|
|
25fec5afeb | ||
|
|
512e412b19 | ||
|
|
be615b9b75 | ||
|
|
0d6c693dd1 | ||
|
|
ea7473f757 | ||
|
|
bebb70bf23 | ||
|
|
f683f63abd | ||
|
|
6f9ff0e427 | ||
|
|
3dbb4cd1d5 | ||
|
|
b33046a2c7 | ||
|
|
53ecdd0c0f | ||
|
|
971a490292 | ||
|
|
c9c6ee88d8 | ||
|
|
3d31c7d7bb | ||
|
|
ed079d2efb | ||
|
|
6de57c79f3 | ||
|
|
fc1cb19014 | ||
|
|
097963dbba | ||
|
|
6e33932a2f | ||
|
|
bf0d43d3a2 | ||
|
|
0effa01ef7 | ||
|
|
edacdbcbae | ||
|
|
0e9953ed87 | ||
|
|
cba713990b | ||
|
|
2bce6d3b01 | ||
|
|
4ae4376897 | ||
|
|
2b9bcb2fe2 | ||
|
|
590c6c28c2 | ||
|
|
29229d1372 | ||
|
|
81be2fad9d | ||
|
|
2c45cea746 | ||
|
|
4c9fe1e58a | ||
|
|
b21fb195d1 | ||
|
|
3850ad414b | ||
|
|
22ae09f926 | ||
|
|
7fe3feef22 | ||
|
|
9b70853c57 | ||
|
|
175cf92e74 | ||
|
|
8c6476545b | ||
|
|
a0bf06db90 | ||
|
|
3889aa291b | ||
|
|
c1aa727a6d | ||
|
|
23de5d2229 | ||
|
|
4aab23b117 | ||
|
|
10f6501951 | ||
|
|
2e8173f6ca | ||
|
|
33b6e3a19d | ||
|
|
0fb115df0b | ||
|
|
f1b875b1dd | ||
|
|
c3604da0c1 | ||
|
|
c61706d4f1 | ||
|
|
fa39f2e75d | ||
|
|
8a283baeaf | ||
|
|
4ddc21d783 | ||
|
|
5b828a71a9 | ||
|
|
c3b0d0feb9 | ||
|
|
a4468271f4 | ||
|
|
0127de1290 | ||
|
|
3e270a3906 | ||
|
|
76a7e9318e | ||
|
|
527b6ca1a3 | ||
|
|
d3f8b06fcb | ||
|
|
7d34993d84 | ||
|
|
be5a1605b7 | ||
|
|
3e86167842 | ||
|
|
93713d58d4 | ||
|
|
7da18de68c | ||
|
|
fccf2a7b3e | ||
|
|
b200180684 | ||
|
|
0a5ea5d448 | ||
|
|
739906dbe4 | ||
|
|
4326552ccc | ||
|
|
1ee1ceef73 | ||
|
|
4d83666fed | ||
|
|
86b8a2ae8c | ||
|
|
c8c21d210b | ||
|
|
96343a3b1f | ||
|
|
74ebc4c82e | ||
|
|
3acd3a87a9 | ||
|
|
94888ad94d | ||
|
|
030daf12f2 | ||
|
|
d599a0836f | ||
|
|
7f901e7e75 | ||
|
|
0a5e235e50 | ||
|
|
aec3530d8a | ||
|
|
732e79dd60 | ||
|
|
ca5c596158 | ||
|
|
9e8e9c54d4 | ||
|
|
2c2a79104b | ||
|
|
27769f8682 | ||
|
|
f150637e3a | ||
|
|
f04661e32a | ||
|
|
db65e80dbc | ||
|
|
4e65bbb7bf | ||
|
|
d5ec302bf6 | ||
|
|
51692a67e7 | ||
|
|
ee97d71f09 | ||
|
|
6d089c7332 | ||
|
|
ab1b45bd50 | ||
|
|
fff8b61f71 | ||
|
|
2c4f262fbc | ||
|
|
e5a7e08efb | ||
|
|
b4ef01c688 | ||
|
|
e6d6194b66 | ||
|
|
b6ee0989e5 | ||
|
|
e4230a0709 | ||
|
|
0b17588e5d | ||
|
|
8498f35b95 | ||
|
|
8eecc995fc | ||
|
|
1bbc01346b | ||
|
|
2f3a895ddc | ||
|
|
5caa18543a | ||
|
|
33c493b317 | ||
|
|
ca497f3815 | ||
|
|
d796795c40 | ||
|
|
5c9d98d5d6 | ||
|
|
3b47d97314 | ||
|
|
4360d4b155 | ||
|
|
24d8bfe9bd | ||
|
|
832506b836 | ||
|
|
795f38b4b8 | ||
|
|
d42813b262 | ||
|
|
d522c65d49 | ||
|
|
4b7cecccbe | ||
|
|
151abb94e1 | ||
|
|
b67a307453 | ||
|
|
bf83c87911 | ||
|
|
8e67d3dc23 | ||
|
|
4b24f2c4b6 | ||
|
|
6cd14a5a78 | ||
|
|
6edae50960 | ||
|
|
2a6a377d06 | ||
|
|
c569dbd778 | ||
|
|
36022df189 | ||
|
|
6f585c5e17 | ||
|
|
ffe7d944a6 | ||
|
|
bf0d6857d3 | ||
|
|
69f117bab7 | ||
|
|
cac945501d | ||
|
|
85a0762ccf | ||
|
|
0ce51bf481 | ||
|
|
6adc5065fb | ||
|
|
3b90a6d90e | ||
|
|
aa5e6e7199 | ||
|
|
ca53ec38d1 | ||
|
|
80a3cc2fc1 | ||
|
|
32c4e987dc | ||
|
|
ce0ef0a1cf | ||
|
|
95fc9bd3d0 | ||
|
|
fb21fef82f | ||
|
|
7760f5e898 | ||
|
|
fcc9915b04 | ||
|
|
8347dd5db7 | ||
|
|
c2d3ba087a | ||
|
|
e5883f179c | ||
|
|
f7f1d3b3f0 | ||
|
|
df3674f570 | ||
|
|
a62beab172 | ||
|
|
528810155f | ||
|
|
5c80b5300e | ||
|
|
36c33160f4 | ||
|
|
5cca28daa1 | ||
|
|
5298ccd638 | ||
|
|
a5db1797e3 | ||
|
|
b1d034cf3f | ||
|
|
4620ed0c1c | ||
|
|
5c87b6f3b0 | ||
|
|
29ad783b3f | ||
|
|
3eae13e237 | ||
|
|
ee222b6302 | ||
|
|
ae795a837d | ||
|
|
0396264842 | ||
|
|
e9fcd16fb5 | ||
|
|
944fcf0567 | ||
|
|
251cc08a7d | ||
|
|
d2719123eb | ||
|
|
88c0e2a2ce | ||
|
|
b4366058dd | ||
|
|
b103cc09f2 | ||
|
|
d35c0be116 | ||
|
|
c9cf15e519 | ||
|
|
408ae26b76 | ||
|
|
3159d8fcc5 | ||
|
|
b976788f07 | ||
|
|
a0311c1516 | ||
|
|
e17351f702 | ||
|
|
ffafae9970 | ||
|
|
fa3d993ffd | ||
|
|
2b7b8d526e | ||
|
|
270394dc36 | ||
|
|
552e8f50b7 | ||
|
|
b6d0d124fd | ||
|
|
7e3abc5819 | ||
|
|
6b3065617d | ||
|
|
9752ccf7cb | ||
|
|
ad1921a938 | ||
|
|
ed2201d525 | ||
|
|
09b9719a3f | ||
|
|
673ea6da86 | ||
|
|
77d81ecc12 | ||
|
|
00c38b6bcc | ||
|
|
24d9568c5d | ||
|
|
3b89b0c436 | ||
|
|
940a3de130 | ||
|
|
81b291ae37 | ||
|
|
f5ad31f0a3 | ||
|
|
59d4b92839 | ||
|
|
530c864d10 | ||
|
|
fae6bf8f43 | ||
|
|
c395bc829f | ||
|
|
19ee33ec3a | ||
|
|
64ab6a3436 | ||
|
|
b758cd28e5 | ||
|
|
db8e21f83b | ||
|
|
c23f31f284 | ||
|
|
84d5420b51 | ||
|
|
02bdc61543 | ||
|
|
71811ebf0d | ||
|
|
b07d7e4f3f | ||
|
|
b141907066 | ||
|
|
a0b46d49d6 | ||
|
|
85386c945f | ||
|
|
9de1373bdd | ||
|
|
6f4c35672d | ||
|
|
ed32552155 | ||
|
|
88858308fb | ||
|
|
78f875ab0f | ||
|
|
9104c64170 | ||
|
|
aabb33a435 | ||
|
|
ad43591f2c | ||
|
|
8bc779d882 | ||
|
|
193bddbe9e | ||
|
|
e03d394555 | ||
|
|
72e4116773 | ||
|
|
9b682163db | ||
|
|
076312da7a | ||
|
|
856a9b16e9 | ||
|
|
9de4e3eba6 | ||
|
|
d90d263b04 | ||
|
|
0170e51843 | ||
|
|
b7f5a5dc66 | ||
|
|
20866ab2f5 | ||
|
|
4efc13a602 | ||
|
|
96303f5d0f | ||
|
|
020c9b254e | ||
|
|
24c0f2f409 | ||
|
|
1b608f01ac | ||
|
|
fd98ab0a64 | ||
|
|
3d1dc879e0 | ||
|
|
16253b8bf7 | ||
|
|
3dba3079f7 | ||
|
|
1fe4ae199f | ||
|
|
55842f1265 | ||
|
|
6644940115 | ||
|
|
dfd0dc72b8 | ||
|
|
d7c4b9c776 | ||
|
|
728553115c | ||
|
|
6ee0d28a8b | ||
|
|
ead23c36c6 | ||
|
|
e62891b9c0 | ||
|
|
9483c63cdc | ||
|
|
3eab773fd1 | ||
|
|
30dd0e8657 | ||
|
|
5d05df7f83 | ||
|
|
ebf80aacc5 | ||
|
|
55c6be4622 | ||
|
|
8f1545916f | ||
|
|
7f05043e03 | ||
|
|
e663655924 | ||
|
|
2fab9dc6f1 | ||
|
|
aba79c11d3 | ||
|
|
4e897dde47 | ||
|
|
3375439e35 | ||
|
|
89dce0b655 | ||
|
|
ff53ea322d | ||
|
|
d6f6bc2c5b | ||
|
|
905080e743 | ||
|
|
d33f3c4769 | ||
|
|
b6a1c9b73b | ||
|
|
20823f103c | ||
|
|
25a5d3277b | ||
|
|
72a80c8f57 | ||
|
|
a082c7d33a | ||
|
|
99cac4b041 | ||
|
|
f804d7848f | ||
|
|
446841e0f5 | ||
|
|
1d347fe26c | ||
|
|
abfa5d719a | ||
|
|
32bc6ed328 | ||
|
|
ea2bc359cb | ||
|
|
c540cb0fe4 | ||
|
|
1642495754 | ||
|
|
b034a59422 | ||
|
|
2486c055e4 | ||
|
|
79b43b1058 | ||
|
|
b30fa436bd | ||
|
|
9e79a311cf | ||
|
|
2f695d0b34 | ||
|
|
952a484994 | ||
|
|
74b5e42492 | ||
|
|
7f8dc88c5a | ||
|
|
c9a9b8dffb | ||
|
|
b315a834c2 | ||
|
|
8f84c85c23 | ||
|
|
707ff935fe | ||
|
|
39af23b349 | ||
|
|
318b95beec | ||
|
|
174bb9a935 | ||
|
|
f0fd20051e | ||
|
|
e3d72bd271 | ||
|
|
4dcd0cd356 | ||
|
|
e3793e67aa | ||
|
|
3f344c7249 | ||
|
|
8a1af7d01f | ||
|
|
117fc44f00 | ||
|
|
703456b808 | ||
|
|
b90cc90ea8 | ||
|
|
45730e2a07 | ||
|
|
c0272b9755 | ||
|
|
a27a8804c4 | ||
|
|
073d23aed3 | ||
|
|
7d9a795a1c | ||
|
|
8359fb8919 | ||
|
|
7c0ffff93f | ||
|
|
46d2ea508c | ||
|
|
fb48faa3af | ||
|
|
048bb36165 | ||
|
|
0f01a03587 | ||
|
|
fe17a6e9da | ||
|
|
a17414dd89 | ||
|
|
5d3f2182f0 | ||
|
|
03cd320665 | ||
|
|
2415ede4fa | ||
|
|
844efd5958 | ||
|
|
4ba88b6f03 | ||
|
|
d17d0c190b | ||
|
|
beec9fab22 | ||
|
|
710b3c412f | ||
|
|
6bf31bf0ea | ||
|
|
2f8bef5f3e | ||
|
|
2dcf886322 | ||
|
|
0a362e9ce7 | ||
|
|
e186615163 | ||
|
|
7abf19071d | ||
|
|
30838749e1 | ||
|
|
06df49d5d7 | ||
|
|
2c5e1f611b | ||
|
|
0cc33f7a81 | ||
|
|
8a431fb79b | ||
|
|
12cfc0dafd | ||
|
|
93c8a1827c | ||
|
|
ca60eda7f2 | ||
|
|
c22c51791e | ||
|
|
d5a719c9f6 | ||
|
|
904c452cfe | ||
|
|
2078ef9081 | ||
|
|
fc9ee5bee1 | ||
|
|
d27595cfd9 | ||
|
|
b618386e6b | ||
|
|
2e79b2c7a2 | ||
|
|
4e4990bd98 | ||
|
|
4c8a44353a | ||
|
|
7dd8b13bc8 | ||
|
|
e1aa3b59be | ||
|
|
d79888793a | ||
|
|
b56e381022 | ||
|
|
15b32b312d | ||
|
|
6a0a00f126 | ||
|
|
9876c2ef6d | ||
|
|
fba29e3c80 | ||
|
|
b8275f5a3f | ||
|
|
b7e8365117 | ||
|
|
8df7b5c56b | ||
|
|
04519d1db7 | ||
|
|
7b808bea0b | ||
|
|
2878f1d9cf | ||
|
|
651f6011b8 | ||
|
|
f17cbb5aa3 | ||
|
|
a2a4a165a5 | ||
|
|
9c7ff38f99 | ||
|
|
76f27adbea | ||
|
|
85a5f38590 | ||
|
|
dcdd876a4c | ||
|
|
d055908809 | ||
|
|
89806148b2 | ||
|
|
433be12b2b | ||
|
|
9ff235e767 | ||
|
|
9b6f7ff225 | ||
|
|
99f97ac565 | ||
|
|
7e598098fe | ||
|
|
02afbf6c90 | ||
|
|
b464fafc18 | ||
|
|
e06320108b | ||
|
|
a377b13e2b | ||
|
|
4728212531 | ||
|
|
71fc2a3cac | ||
|
|
303282624e | ||
|
|
2ca02937df | ||
|
|
00b62df2e8 | ||
|
|
6d377ded9a | ||
|
|
087c404225 | ||
|
|
4932763468 | ||
|
|
abcc19d5da | ||
|
|
f1ce1e9b60 | ||
|
|
5a6f99a41b | ||
|
|
1862802052 | ||
|
|
1adddbdb4c | ||
|
|
15f4f2accb | ||
|
|
725f3c3522 | ||
|
|
efa73a1c32 | ||
|
|
00250c6669 | ||
|
|
d11afda1a8 | ||
|
|
1350784c33 | ||
|
|
1852a216a2 | ||
|
|
422c2492c4 | ||
|
|
c9c72741b3 | ||
|
|
99dbcaff7d | ||
|
|
8d83f5474d | ||
|
|
a912a1a22e | ||
|
|
ea7afbe93e | ||
|
|
7b2a8c3e20 | ||
|
|
911adfc151 | ||
|
|
23f710856e | ||
|
|
7f6274821c | ||
|
|
4700843517 | ||
|
|
8823054227 | ||
|
|
7c63a3e004 | ||
|
|
2a59154268 | ||
|
|
ed15d53484 | ||
|
|
fc8a4bb55d | ||
|
|
dd1c1bb11d | ||
|
|
3ffbad5727 | ||
|
|
207ae39d13 | ||
|
|
ae141ac9c5 | ||
|
|
40f058b14b | ||
|
|
865c9f3e32 | ||
|
|
26df9bfdd8 | ||
|
|
1e52e01fb8 | ||
|
|
5fece5faaa | ||
|
|
d7fdaf6c46 | ||
|
|
8e1faf9f60 | ||
|
|
9dce39b32d | ||
|
|
80a0b75887 | ||
|
|
c9704e1031 | ||
|
|
31e32cd154 | ||
|
|
61c296fc37 | ||
|
|
c8d38235b2 | ||
|
|
b717a64607 | ||
|
|
0f2b1d3e14 | ||
|
|
4abefe63db | ||
|
|
fc31b2a8d7 | ||
|
|
2708bac4fb | ||
|
|
a47967dd53 | ||
|
|
53ba5562d3 | ||
|
|
347df32943 | ||
|
|
ac8c63526b | ||
|
|
a48bd360ca | ||
|
|
f476969e9e | ||
|
|
c45539dea6 | ||
|
|
d8f5815b84 | ||
|
|
2eaf07118a | ||
|
|
b636b09a96 | ||
|
|
c16bbcaad5 | ||
|
|
49ffcbea03 | ||
|
|
f772cf0f14 | ||
|
|
3f1406a05b | ||
|
|
21760b2b74 | ||
|
|
d8011f0239 | ||
|
|
b9a8b054e2 | ||
|
|
2ab321869e | ||
|
|
fcb45a24d6 | ||
|
|
7cdb13def6 | ||
|
|
2ab58f9203 | ||
|
|
df74448e17 | ||
|
|
ada98fca93 | ||
|
|
1d3650659f | ||
|
|
9b43af475b | ||
|
|
d9c592c7fb | ||
|
|
f10ac9fbb5 | ||
|
|
b7334c09d4 | ||
|
|
0cd3bd5739 | ||
|
|
33d69480eb | ||
|
|
5cae4eb0d9 | ||
|
|
840c7fda5e | ||
|
|
4ce24ac8fd | ||
|
|
ae9e80e12e | ||
|
|
43356e5b6a | ||
|
|
7b13f35424 | ||
|
|
0517c6ce41 | ||
|
|
54d06328c1 | ||
|
|
c74677acd4 | ||
|
|
7689e1a7c5 | ||
|
|
5ffae2a08b | ||
|
|
5fa80abdc8 | ||
|
|
a30598fedf | ||
|
|
48ffecfb5f | ||
|
|
89fe712104 | ||
|
|
5dd298ee86 | ||
|
|
275b285a8b | ||
|
|
96310a46e9 | ||
|
|
42f9bf1593 | ||
|
|
25aab6913a | ||
|
|
3bd7da68be | ||
|
|
a218738eff | ||
|
|
8bf283b3bc | ||
|
|
cb09430ff4 | ||
|
|
66d75492bb | ||
|
|
d772d38dc7 | ||
|
|
46a74f9512 | ||
|
|
7df1037e3f | ||
|
|
5c3136d07d | ||
|
|
6a9ddb770e | ||
|
|
f4c54fd27f | ||
|
|
05b12df7d7 | ||
|
|
dcf053eede | ||
|
|
5430966b02 | ||
|
|
7271140627 | ||
|
|
eb5ae0773b | ||
|
|
9661dd0c5e | ||
|
|
8e70860918 | ||
|
|
3540ecf4ab | ||
|
|
f1dba9ffc2 | ||
|
|
3f1761a540 | ||
|
|
a25d21038a | ||
|
|
d55a6a6fc0 | ||
|
|
e5cf902780 | ||
|
|
a99996fcf6 | ||
|
|
0cc9c31bc3 | ||
|
|
62da8484ad | ||
|
|
821b8eebaa | ||
|
|
269941c297 | ||
|
|
e37ff88f1f | ||
|
|
1a1b4bc43c | ||
|
|
2c59df9fb7 | ||
|
|
4fc30cc224 | ||
|
|
9bd7d392af | ||
|
|
3bcd288bc7 | ||
|
|
3b86e51c49 | ||
|
|
c286b1d595 | ||
|
|
2f6b1e6bcc | ||
|
|
abb918e179 | ||
|
|
dd0dc09977 | ||
|
|
211ab47165 | ||
|
|
bc44e28e1e | ||
|
|
ade60e4272 | ||
|
|
3f5bfcb499 | ||
|
|
782a979397 | ||
|
|
e7f1e2d533 | ||
|
|
12e5f2acc1 | ||
|
|
dccc79ace3 | ||
|
|
a9a5ba09ff | ||
|
|
a6c8bbbf0a | ||
|
|
e19e9eb96f | ||
|
|
9208140b97 | ||
|
|
8241bb8ae5 | ||
|
|
329c2e220f | ||
|
|
140e343289 | ||
|
|
0d99367a93 | ||
|
|
b802b3e165 | ||
|
|
46583e12d9 | ||
|
|
c9a3f31e8f | ||
|
|
53f68aaf81 | ||
|
|
f31df9a6cc | ||
|
|
c812581bb9 | ||
|
|
1e6e940f17 | ||
|
|
9ab670d933 | ||
|
|
c46e57418b | ||
|
|
83557401f5 | ||
|
|
2277644e89 | ||
|
|
5eb09a1d3e | ||
|
|
d957be7a7b | ||
|
|
44ddb49d0e | ||
|
|
24fcd5cd0c | ||
|
|
2a34225d49 | ||
|
|
e81c825fc7 | ||
|
|
c80286b587 | ||
|
|
e85f8b60b0 | ||
|
|
5daa99d969 | ||
|
|
073bb576b5 | ||
|
|
eaeac130df | ||
|
|
b21519c24e | ||
|
|
4f9fe9b1e0 | ||
|
|
f657380ec7 | ||
|
|
05b2e559d8 | ||
|
|
ab21f02c2d | ||
|
|
12b51ed72a | ||
|
|
b672fd540c | ||
|
|
95a20b3740 | ||
|
|
6f5a7a4923 | ||
|
|
1b4d98ac0e | ||
|
|
ef2ed2bba0 | ||
|
|
abf4612977 | ||
|
|
9c6637908d | ||
|
|
e1006ed501 | ||
|
|
7d034f7f16 | ||
|
|
76eec5f8bb | ||
|
|
698866f53d | ||
|
|
21e1e73bee | ||
|
|
52e89c7842 | ||
|
|
8b357559fa | ||
|
|
2be35fadff | ||
|
|
5fe366f4f3 | ||
|
|
a13cbadd44 | ||
|
|
aa993a2fce | ||
|
|
975af5701b | ||
|
|
3add56cfe4 | ||
|
|
522ea1520d | ||
|
|
c680121eb0 | ||
|
|
a9de0696c9 | ||
|
|
4a4c52e4c8 | ||
|
|
92f5e04788 | ||
|
|
0cad098551 | ||
|
|
46105b2175 | ||
|
|
8ffbda9626 | ||
|
|
1fc55a69c9 | ||
|
|
c417d4df91 | ||
|
|
43ccf72773 | ||
|
|
a177d36571 | ||
|
|
4428ccd3a0 | ||
|
|
218ffbd758 | ||
|
|
5ace95c9d1 | ||
|
|
77a2f86119 | ||
|
|
71db85458d | ||
|
|
0be59c71de | ||
|
|
2357e3de0b | ||
|
|
53b89a59ed | ||
|
|
202c9135c2 | ||
|
|
69e6263d34 | ||
|
|
ebe830bbcc | ||
|
|
44bb9752d2 | ||
|
|
2f6af50fe2 | ||
|
|
1d80ca7d3c | ||
|
|
d33baebac8 | ||
|
|
25aaef361a | ||
|
|
f3af39a093 | ||
|
|
149c2013a2 | ||
|
|
8fb0b6f94a | ||
|
|
f09e5cadcf | ||
|
|
724c69655b | ||
|
|
800f3ca5d3 | ||
|
|
193007370a | ||
|
|
c8aff3764b | ||
|
|
d72b502977 | ||
|
|
263c970320 | ||
|
|
aeebc9878d | ||
|
|
4f37c92ecd | ||
|
|
3180a88ef0 | ||
|
|
6672db6ac2 | ||
|
|
b6da62469e | ||
|
|
e67e6374c3 | ||
|
|
ff1fc5b391 | ||
|
|
2f8bf191aa | ||
|
|
c4af2b2159 | ||
|
|
690b157387 | ||
|
|
5a276a43a4 | ||
|
|
a364ae2e9d | ||
|
|
1e2055c81b | ||
|
|
31d110252c |
@@ -14,6 +14,8 @@ runs:
|
||||
apps/mobile/package-lock.json
|
||||
apps/desktop/package-lock.json
|
||||
apps/web/package-lock.json
|
||||
apps/monograph/package-lock.json
|
||||
apps/theme-builder/package-lock.json
|
||||
extensions/web-clipper/package-lock.json
|
||||
packages/core/package-lock.json
|
||||
packages/crypto/package-lock.json
|
||||
@@ -24,3 +26,4 @@ runs:
|
||||
packages/logger/package-lock.json
|
||||
packages/streamable-fs/package-lock.json
|
||||
packages/theme/package-lock.json
|
||||
packages/intl/package-lock.json
|
||||
|
||||
13
.github/workflows/android.publish.internal.yml
vendored
13
.github/workflows/android.publish.internal.yml
vendored
@@ -4,7 +4,7 @@ on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CMAKE_C_COMPILER_LAUNCHER: ccache
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Use specific Java version for the builds
|
||||
uses: joschi/setup-jdk@v2
|
||||
with:
|
||||
java-version: "11"
|
||||
java-version: "17"
|
||||
architecture: "x64"
|
||||
|
||||
- name: Install node modules
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
ccache -p
|
||||
|
||||
- name: Cache gradle
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@@ -69,8 +69,6 @@ jobs:
|
||||
run: |
|
||||
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-reanimated/android/build.gradle
|
||||
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-reanimated//android/build.gradle
|
||||
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
|
||||
- name: CCache Stats Before Build
|
||||
run: ccache -sv
|
||||
@@ -129,7 +127,7 @@ jobs:
|
||||
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
|
||||
packageName: com.streetwriters.notesnook
|
||||
releaseFiles: ${{steps.sign_app.outputs.signedReleaseFile}}
|
||||
track: alpha
|
||||
track: beta
|
||||
status: completed
|
||||
whatsNewDirectory: apps/mobile/native/android/releasenotes/
|
||||
|
||||
@@ -138,7 +136,7 @@ jobs:
|
||||
with:
|
||||
draft: true
|
||||
tag_name: ${{ steps.package-version.outputs.current-version}}-beta-android
|
||||
name: Notesnook Android v${{ steps.package-version.outputs.current-version}} Beta
|
||||
name: Notesnook Android v${{ steps.package-version.outputs.current-version}}
|
||||
repository: streetwriters/notesnook
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: |
|
||||
@@ -146,6 +144,7 @@ jobs:
|
||||
apps/mobile/native/android/app/build/outputs/apk/release/notesnook-armeabi-v7a.apk
|
||||
apps/mobile/native/android/app/build/outputs/apk/release/notesnook-x86.apk
|
||||
apps/mobile/native/android/app/build/outputs/apk/release/notesnook-x86_64.apk
|
||||
${{steps.sign_app.outputs.signedReleaseFile}}
|
||||
|
||||
- name: Upload sourcemaps
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
6
.github/workflows/android.publish.yml
vendored
6
.github/workflows/android.publish.yml
vendored
@@ -4,7 +4,7 @@ on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CMAKE_C_COMPILER_LAUNCHER: ccache
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
ccache -p
|
||||
|
||||
- name: Cache gradle
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@@ -69,8 +69,6 @@ jobs:
|
||||
run: |
|
||||
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-reanimated/android/build.gradle
|
||||
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-reanimated//android/build.gradle
|
||||
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
|
||||
- name: CCache Stats Before Build
|
||||
run: ccache -sv
|
||||
|
||||
2
.github/workflows/core.tests.yml
vendored
2
.github/workflows/core.tests.yml
vendored
@@ -18,7 +18,7 @@ on:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
120
.github/workflows/desktop.publish.yml
vendored
120
.github/workflows/desktop.publish.yml
vendored
@@ -33,11 +33,19 @@ on:
|
||||
required: true
|
||||
default: true
|
||||
description: "Build for macOS?"
|
||||
release-track:
|
||||
type: choice
|
||||
required: true
|
||||
default: stable
|
||||
description: "Select the release track"
|
||||
options:
|
||||
- stable
|
||||
- beta
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -49,14 +57,30 @@ jobs:
|
||||
run: |
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
npm run bootstrap -- --scope=web
|
||||
npm run bootstrap -- --scope=desktop
|
||||
|
||||
- name: Install sqlite-better-trigram for all arch
|
||||
run: |
|
||||
npm i --cpu arm64 sqlite-better-trigram
|
||||
npm i --cpu x64 sqlite-better-trigram
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate desktop build
|
||||
- name: Generate desktop build (stable)
|
||||
if: ${{ inputs.release-track == 'stable' }}
|
||||
run: npx nx build:desktop @notesnook/web
|
||||
|
||||
- name: Generate desktop build (beta)
|
||||
if: ${{ inputs.release-track == 'beta' }}
|
||||
run: BETA=true npx nx build:desktop @notesnook/web
|
||||
|
||||
- name: Build desktop bundle
|
||||
working-directory: ./apps/desktop
|
||||
run: npm run bundle
|
||||
|
||||
- name: Archive build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -71,7 +95,8 @@ jobs:
|
||||
|
||||
- name: Package desktop build
|
||||
run: |
|
||||
zip -r ./notesnook_build_v${{ steps.app_metadata.outputs.app_version }}.zip ./apps/web/build/
|
||||
cp -r ./apps/web/build ./apps/desktop/
|
||||
zip -r ./notesnook_build_v${{ steps.app_metadata.outputs.app_version }}.zip ./apps/desktop/build/
|
||||
echo "Build folder archived to ./notesnook_build_v${{ steps.app_metadata.outputs.app_version }}.zip"
|
||||
|
||||
- name: Upload desktop build
|
||||
@@ -82,12 +107,14 @@ jobs:
|
||||
name: Notesnook Desktop v${{ steps.app_metadata.outputs.app_version }}
|
||||
tag_name: v${{ steps.app_metadata.outputs.app_version }}
|
||||
files: ./notesnook_build_v${{ steps.app_metadata.outputs.app_version }}.zip
|
||||
prerelease: ${{ endsWith(steps.app_metadata.outputs.app_version, '-beta') }}
|
||||
prerelease: ${{ inputs.release-track == 'beta' }}
|
||||
target_commitish: ${{ github.ref }}
|
||||
|
||||
- name: Generate flatpak sources
|
||||
if: inputs.publish-github && inputs.build-linux
|
||||
run: |
|
||||
sudo apt-get install -y flatpak flatpak-builder pipx
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --quiet flatpak flatpak-builder pipx
|
||||
git clone https://github.com/flatpak/flatpak-builder-tools.git /tmp/flatpak-builder-tools
|
||||
cd /tmp/flatpak-builder-tools/node
|
||||
pipx install .
|
||||
@@ -173,7 +200,7 @@ jobs:
|
||||
CSC_KEY_PASSWORD: ${{ secrets.mac_certs_password }}
|
||||
run: |
|
||||
npx nx run release --project @notesnook/desktop -- --variant=mas
|
||||
yarn electron-builder --mac mas --universal -p never
|
||||
yarn electron-builder --config=electron-builder.config.js --mac mas --universal -p never
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build zip and dmg
|
||||
@@ -187,9 +214,9 @@ jobs:
|
||||
run: |
|
||||
npx nx run release --project @notesnook/desktop
|
||||
if [ ${{ inputs.publish-github }} == true ]; then
|
||||
yarn electron-builder --mac zip dmg --arm64 --x64 -p always
|
||||
yarn electron-builder --config=electron-builder.config.js --mac zip dmg --arm64 --x64 -p always
|
||||
else
|
||||
yarn electron-builder --mac zip dmg --arm64 --x64 -p never
|
||||
yarn electron-builder --config=electron-builder.config.js --mac zip dmg --arm64 --x64 -p never
|
||||
fi
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
@@ -206,7 +233,7 @@ jobs:
|
||||
xcrun altool --upload-package $package -t osx --apiKey $API_KEY_ID --apiIssuer $API_KEY_ISSUER_ID --apple-id ${{ steps.app_metadata.outputs.apple_app_id }} --bundle-id ${{ steps.app_metadata.outputs.app_bundle_id }} --bundle-short-version-string ${{ steps.app_metadata.outputs.app_version }} --bundle-version ${{ steps.app_metadata.outputs.bundle_version }}
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
build-linux:
|
||||
build-linux-x64:
|
||||
name: Build for Linux
|
||||
needs: build
|
||||
if: inputs.build-linux
|
||||
@@ -240,7 +267,6 @@ jobs:
|
||||
|
||||
- name: Install sqlite-better-trigram for all arch
|
||||
run: |
|
||||
npm i --cpu arm64 sqlite-better-trigram
|
||||
npm i --cpu x64 sqlite-better-trigram
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
@@ -251,7 +277,7 @@ jobs:
|
||||
- name: Build snap
|
||||
if: inputs.publish-snap
|
||||
run: |
|
||||
yarn electron-builder --linux snap:x64 -p never
|
||||
yarn electron-builder --config=electron-builder.config.js --linux snap:x64 -p never
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build AppImage
|
||||
@@ -259,9 +285,9 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
if [ ${{ inputs.publish-github }} == true ]; then
|
||||
yarn electron-builder --linux AppImage:x64 AppImage:arm64 -p always
|
||||
yarn electron-builder --config=electron-builder.config.js --linux AppImage:x64 -p always
|
||||
else
|
||||
yarn electron-builder --linux AppImage:x64 AppImage:arm64 -p never
|
||||
yarn electron-builder --config=electron-builder.config.js --linux AppImage:x64 -p never
|
||||
fi
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
@@ -271,6 +297,70 @@ jobs:
|
||||
snapcraft upload --release=stable ./output/notesnook_linux_amd64.snap
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
build-linux-arm64:
|
||||
name: Build for Linux
|
||||
needs: build
|
||||
if: inputs.build-linux
|
||||
runs-on: ubuntu-22.04-arm
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node-with-cache
|
||||
|
||||
- name: Setup Snapcraft Auth
|
||||
if: inputs.publish-snap
|
||||
run: echo "SNAPCRAFT_STORE_CREDENTIALS=${{ secrets.snapcraft_token }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Install Snapcraft
|
||||
uses: samuelmeuli/action-snapcraft@v1
|
||||
if: inputs.publish-snap
|
||||
|
||||
- name: Download build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: build
|
||||
path: ./apps/web/build
|
||||
|
||||
- name: Install packages
|
||||
run: |
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
npm run bootstrap -- --scope=desktop
|
||||
|
||||
- name: Install sqlite-better-trigram for all arch
|
||||
run: |
|
||||
npm i --cpu arm64 sqlite-better-trigram
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build Electron wrapper
|
||||
run: npx nx run release --project @notesnook/desktop
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build snap
|
||||
if: inputs.publish-snap
|
||||
run: |
|
||||
yarn electron-builder --config=electron-builder.config.js --linux snap:arm64 -p never
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build AppImage
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
if [ ${{ inputs.publish-github }} == true ]; then
|
||||
yarn electron-builder --config=electron-builder.config.js --linux AppImage:arm64 -p always
|
||||
else
|
||||
yarn electron-builder --config=electron-builder.config.js --linux AppImage:arm64 -p never
|
||||
fi
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Publish on Snapcraft
|
||||
if: inputs.publish-snap
|
||||
run: |
|
||||
snapcraft upload --release=stable ./output/notesnook_linux_arm64.snap
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
build-windows:
|
||||
name: Build for Windows
|
||||
needs: build
|
||||
@@ -321,8 +411,8 @@ jobs:
|
||||
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
run: |
|
||||
if ($${{ inputs.publish-github }} -eq $true) {
|
||||
yarn electron-builder --win --publish always
|
||||
yarn electron-builder --config=electron-builder.config.js --win --publish always
|
||||
} else {
|
||||
yarn electron-builder --win --publish never
|
||||
yarn electron-builder --config=electron-builder.config.js --win --publish never
|
||||
}
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
24
.github/workflows/desktop.tests.yml
vendored
24
.github/workflows/desktop.tests.yml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -69,12 +69,11 @@ jobs:
|
||||
|
||||
- name: Build app
|
||||
run: |
|
||||
yarn electron-builder --mac --dir --x64
|
||||
yarn electron-builder --config=electron-builder.config.js --mac --dir --x64
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Run tests x64
|
||||
run: |
|
||||
EXECUTABLE_PATH=output/mac/Notesnook.app/Contents/MacOS/Notesnook npm run test
|
||||
run: npm run test
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Upload test results
|
||||
@@ -114,12 +113,11 @@ jobs:
|
||||
|
||||
- name: Build app
|
||||
run: |
|
||||
yarn electron-builder --mac --dir --arm64
|
||||
yarn electron-builder --config=electron-builder.config.js --mac --dir --arm64
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Run tests arm64
|
||||
run: |
|
||||
EXECUTABLE_PATH=output/mac-arm64/Notesnook.app/Contents/MacOS/Notesnook npm run test
|
||||
run: npm run test
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Upload test results
|
||||
@@ -133,7 +131,7 @@ jobs:
|
||||
test-linux:
|
||||
name: Test for Linux
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
@@ -165,12 +163,11 @@ jobs:
|
||||
|
||||
- name: Build app
|
||||
run: |
|
||||
yarn electron-builder --linux --dir --arm64 --x64
|
||||
yarn electron-builder --config=electron-builder.config.js --linux --dir --arm64 --x64
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
EXECUTABLE_PATH=output/linux-unpacked/notesnook xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" -- npm run test
|
||||
run: xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" -- npm run test
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Upload test results
|
||||
@@ -216,12 +213,11 @@ jobs:
|
||||
|
||||
- name: Build app
|
||||
run: |
|
||||
npx cross-env NOTESNOOK_STAGING=true yarn electron-builder --win --dir --arm64 --x64
|
||||
npx cross-env NOTESNOOK_STAGING=true yarn electron-builder --config=electron-builder.config.js --win --dir --arm64 --x64
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
npx cross-env EXECUTABLE_PATH=output/win-unpacked/Notesnook.exe npm run test
|
||||
run: npm run test
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Upload test results
|
||||
|
||||
2
.github/workflows/editor.tests.yml
vendored
2
.github/workflows/editor.tests.yml
vendored
@@ -19,7 +19,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/help.publish.yml
vendored
2
.github/workflows/help.publish.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
11
.github/workflows/ios.publish.yml
vendored
11
.github/workflows/ios.publish.yml
vendored
@@ -4,16 +4,21 @@ on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node-with-cache
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: "16.1"
|
||||
|
||||
- name: Install node modules
|
||||
run: |
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
@@ -47,7 +52,7 @@ jobs:
|
||||
ccache -p
|
||||
|
||||
- name: Cache Pods
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
id: pods-cache
|
||||
with:
|
||||
path: apps/mobile/native/ios/Pods
|
||||
|
||||
2
.github/workflows/monograph.publish.yml
vendored
2
.github/workflows/monograph.publish.yml
vendored
@@ -14,7 +14,7 @@ on: workflow_dispatch
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
2
.github/workflows/theme-builder.publish.yml
vendored
2
.github/workflows/theme-builder.publish.yml
vendored
@@ -5,7 +5,7 @@ on: workflow_dispatch
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/vericrypt.publish.yml
vendored
2
.github/workflows/vericrypt.publish.yml
vendored
@@ -5,7 +5,7 @@ on: workflow_dispatch
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/web.benchmarks.yml
vendored
2
.github/workflows/web.benchmarks.yml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
jobs:
|
||||
bench:
|
||||
name: Bench
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
32
.github/workflows/web.publish.yml
vendored
32
.github/workflows/web.publish.yml
vendored
@@ -1,11 +1,21 @@
|
||||
name: Publish @notesnook/web
|
||||
|
||||
on: workflow_dispatch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-track:
|
||||
type: choice
|
||||
required: true
|
||||
default: stable
|
||||
description: "Select the release track"
|
||||
options:
|
||||
- stable
|
||||
- beta
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -24,8 +34,20 @@ jobs:
|
||||
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate build
|
||||
- name: Generate build (stable)
|
||||
if: ${{ inputs.release-track == 'stable' }}
|
||||
run: npm run build:web
|
||||
|
||||
- name: Publish to Cloudflare Pages
|
||||
run: npx --yes wrangler pages deploy --project-name=notesnook-app ./apps/web/build/
|
||||
- name: Publish to Cloudflare Pages (stable)
|
||||
if: ${{ inputs.release-track == 'stable' }}
|
||||
working-directory: ./apps/web
|
||||
run: npx --yes wrangler pages deploy --project-name=notesnook-app ./build/
|
||||
|
||||
- name: Generate build (beta)
|
||||
if: ${{ inputs.release-track == 'beta' }}
|
||||
run: npm run build:beta:web
|
||||
|
||||
- name: Publish to Cloudflare Pages (beta)
|
||||
if: ${{ inputs.release-track == 'beta' }}
|
||||
working-directory: ./apps/web
|
||||
run: npx --yes wrangler pages deploy --branch=beta --project-name=notesnook-app-beta ./build/
|
||||
|
||||
4
.github/workflows/web.tests.yml
vendored
4
.github/workflows/web.tests.yml
vendored
@@ -19,7 +19,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
name: 🧪 Test (${{ matrix.shard }}/${{ strategy.job-total }})
|
||||
strategy:
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4]
|
||||
shard: [1, 2, 3, 4, 5]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ nx-cloud.env
|
||||
site
|
||||
node_modules.backup
|
||||
.nx
|
||||
/*.patch
|
||||
|
||||
4
apps/desktop/.gitignore
vendored
4
apps/desktop/.gitignore
vendored
@@ -2,4 +2,6 @@ node_modules
|
||||
build
|
||||
output
|
||||
dist
|
||||
_catalog
|
||||
_catalog
|
||||
test-results
|
||||
test-artifacts
|
||||
|
||||
@@ -58,13 +58,13 @@ This will compile and run the app in production mode but it won't generate any p
|
||||
npm run release -- --rebuild
|
||||
|
||||
# For macOS
|
||||
npx electron-builder --mac dmg --arm64 --x64 -publish never
|
||||
npx electron-builder --config=electron-builder.config.js --mac dmg --arm64 --x64 -publish never
|
||||
|
||||
# For Linux (AppImage)
|
||||
npx electron-builder --linux AppImage:x64 AppImage:arm64 -publish never
|
||||
npx electron-builder --config=electron-builder.config.js --linux AppImage:x64 AppImage:arm64 -publish never
|
||||
|
||||
# For Windows
|
||||
npx electron-builder --win --publish never
|
||||
npx electron-builder --config=electron-builder.config.js --win --publish never
|
||||
```
|
||||
|
||||
Feel free to play around with the `electron-builder` command to get the packages you need. `npx electron-builder --help` is a great resource to learn different commands & platforms supported by `electron-builder`.
|
||||
|
||||
246
apps/desktop/__tests__/auto-updates.test.ts
Normal file
246
apps/desktop/__tests__/auto-updates.test.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { test } from "vitest";
|
||||
import { harness } from "./utils.js";
|
||||
import { writeFile } from "fs/promises";
|
||||
import { Page } from "playwright";
|
||||
import { gt, lt } from "semver";
|
||||
|
||||
test("update starts downloading if version is outdated", async (t) => {
|
||||
await harness(
|
||||
t,
|
||||
async ({ page }) => {
|
||||
await page.waitForSelector("#authForm");
|
||||
|
||||
t.expect(
|
||||
await page.getByRole("button", { name: "Create account" }).isVisible()
|
||||
).toBe(true);
|
||||
|
||||
await page
|
||||
.getByRole("button", { name: "Skip & go directly to the app" })
|
||||
.click();
|
||||
|
||||
await skipDialog(page);
|
||||
|
||||
await page.waitForSelector(".ProseMirror");
|
||||
|
||||
await page
|
||||
.locator(".theme-scope-statusBar")
|
||||
.getByRole("button", { name: /updating/i })
|
||||
.waitFor({ state: "attached" });
|
||||
},
|
||||
{ version: "3.0.0" }
|
||||
);
|
||||
});
|
||||
|
||||
test("update is only shown if version is outdated and auto updates are disabled", async (t) => {
|
||||
await harness(
|
||||
t,
|
||||
async (ctx) => {
|
||||
await ctx.app.close();
|
||||
await writeFile(
|
||||
ctx.configPath,
|
||||
JSON.stringify({
|
||||
automaticUpdates: false
|
||||
})
|
||||
);
|
||||
|
||||
await ctx.relaunch();
|
||||
|
||||
const { page } = ctx;
|
||||
|
||||
await page.waitForSelector("#authForm");
|
||||
|
||||
t.expect(
|
||||
await page.getByRole("button", { name: "Create account" }).isVisible()
|
||||
).toBe(true);
|
||||
|
||||
await page
|
||||
.getByRole("button", { name: "Skip & go directly to the app" })
|
||||
.click();
|
||||
|
||||
await skipDialog(page);
|
||||
|
||||
await page.waitForSelector(".ProseMirror");
|
||||
|
||||
await page
|
||||
.locator(".theme-scope-statusBar")
|
||||
.getByRole("button", { name: /available/i })
|
||||
.waitFor({ state: "attached" });
|
||||
},
|
||||
{ version: "3.0.0" }
|
||||
);
|
||||
});
|
||||
|
||||
test("update to stable if it is newer", async (t) => {
|
||||
await harness(
|
||||
t,
|
||||
async (ctx) => {
|
||||
await ctx.app.close();
|
||||
await writeFile(
|
||||
ctx.configPath,
|
||||
JSON.stringify({
|
||||
automaticUpdates: false,
|
||||
releaseTrack: "beta"
|
||||
})
|
||||
);
|
||||
|
||||
await ctx.relaunch();
|
||||
|
||||
const { page } = ctx;
|
||||
|
||||
await page.waitForSelector("#authForm");
|
||||
|
||||
t.expect(
|
||||
await page.getByRole("button", { name: "Create account" }).isVisible()
|
||||
).toBe(true);
|
||||
|
||||
await page
|
||||
.getByRole("button", { name: "Skip & go directly to the app" })
|
||||
.click();
|
||||
|
||||
await skipDialog(page);
|
||||
|
||||
await page.waitForSelector(".ProseMirror");
|
||||
|
||||
const updateButton = page
|
||||
.locator(".theme-scope-statusBar")
|
||||
.getByRole("button", { name: /available/i });
|
||||
await updateButton.waitFor({ state: "visible" });
|
||||
const content = await updateButton.textContent();
|
||||
const version = content?.split(" ")?.[0] || "";
|
||||
t.expect(gt(version, "3.0.0-beta.0")).toBe(true);
|
||||
},
|
||||
{ version: "3.0.0-beta.0" }
|
||||
);
|
||||
});
|
||||
|
||||
test("update is not available if it latest stable version is older", async (t) => {
|
||||
await harness(
|
||||
t,
|
||||
async (ctx) => {
|
||||
await ctx.app.close();
|
||||
await writeFile(
|
||||
ctx.configPath,
|
||||
JSON.stringify({
|
||||
automaticUpdates: false,
|
||||
releaseTrack: "beta"
|
||||
})
|
||||
);
|
||||
|
||||
await ctx.relaunch();
|
||||
|
||||
const { page } = ctx;
|
||||
|
||||
await page.waitForSelector("#authForm");
|
||||
|
||||
t.expect(
|
||||
await page.getByRole("button", { name: "Create account" }).isVisible()
|
||||
).toBe(true);
|
||||
|
||||
await page
|
||||
.getByRole("button", { name: "Skip & go directly to the app" })
|
||||
.click();
|
||||
|
||||
await skipDialog(page);
|
||||
|
||||
await page.waitForSelector(".ProseMirror");
|
||||
|
||||
await page
|
||||
.locator(".theme-scope-statusBar")
|
||||
.getByRole("button", { name: /checking for updates/i })
|
||||
.waitFor({ state: "hidden" });
|
||||
|
||||
t.expect(
|
||||
await page
|
||||
.locator(".theme-scope-statusBar")
|
||||
.getByRole("button", { name: /available/i })
|
||||
.isHidden()
|
||||
).toBe(true);
|
||||
},
|
||||
{ version: "99.0.0-beta.0" }
|
||||
);
|
||||
});
|
||||
|
||||
test("downgrade to stable on switching to stable release track", async (t) => {
|
||||
await harness(
|
||||
t,
|
||||
async (ctx) => {
|
||||
await ctx.app.close();
|
||||
await writeFile(
|
||||
ctx.configPath,
|
||||
JSON.stringify({
|
||||
automaticUpdates: false,
|
||||
releaseTrack: "stable"
|
||||
})
|
||||
);
|
||||
|
||||
await ctx.relaunch();
|
||||
|
||||
const { page } = ctx;
|
||||
|
||||
await page.waitForSelector("#authForm");
|
||||
|
||||
t.expect(
|
||||
await page.getByRole("button", { name: "Create account" }).isVisible()
|
||||
).toBe(true);
|
||||
|
||||
await page
|
||||
.getByRole("button", { name: "Skip & go directly to the app" })
|
||||
.click();
|
||||
|
||||
await skipDialog(page);
|
||||
|
||||
await page.waitForSelector(".ProseMirror");
|
||||
|
||||
await page
|
||||
.locator(".theme-scope-statusBar")
|
||||
.getByRole("button", { name: /checking for updates/i })
|
||||
.waitFor({ state: "hidden" });
|
||||
|
||||
const updateButton = page
|
||||
.locator(".theme-scope-statusBar")
|
||||
.getByRole("button", { name: /available/i });
|
||||
await updateButton.waitFor({ state: "visible" });
|
||||
const content = await updateButton.textContent();
|
||||
const version = content?.split(" ")?.[0] || "";
|
||||
t.expect(lt(version, "99.0.0-beta.0")).toBe(true);
|
||||
},
|
||||
{ version: "99.0.0-beta.0" }
|
||||
);
|
||||
});
|
||||
|
||||
async function skipDialog(page: Page) {
|
||||
await page
|
||||
.waitForSelector(".ReactModal__Content", {
|
||||
timeout: 1000
|
||||
})
|
||||
.catch(() => {})
|
||||
.then(async () => {
|
||||
const positiveButton = page.locator(
|
||||
"button[data-role='positive-button']"
|
||||
);
|
||||
const negativeButton = page.locator(
|
||||
"button[data-role='negative-button']"
|
||||
);
|
||||
if (await positiveButton.isVisible()) await positiveButton.click();
|
||||
else if (await negativeButton.isVisible()) await negativeButton.click();
|
||||
});
|
||||
}
|
||||
@@ -17,16 +17,12 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import test from "node:test";
|
||||
import { launchApp } from "./utils.mjs";
|
||||
import { test } from "vitest";
|
||||
import { harness } from "./utils.js";
|
||||
import assert from "assert";
|
||||
import slugify from "slugify";
|
||||
import path from "path";
|
||||
import { mkdir } from "fs/promises";
|
||||
|
||||
test("make sure app loads", async (t) => {
|
||||
const { app, page } = await launchApp();
|
||||
try {
|
||||
await harness(t, async ({ page }) => {
|
||||
await page.waitForSelector("#authForm");
|
||||
|
||||
assert.ok(
|
||||
@@ -38,16 +34,5 @@ test("make sure app loads", async (t) => {
|
||||
.click();
|
||||
|
||||
await page.waitForSelector(".ProseMirror");
|
||||
} catch (e) {
|
||||
await mkdir("test-results", { recursive: true });
|
||||
await page.screenshot({
|
||||
path: path.join(
|
||||
"test-results",
|
||||
`${slugify(t.name)}-${process.platform}-${process.arch}-error.png`
|
||||
)
|
||||
});
|
||||
throw e;
|
||||
} finally {
|
||||
await app.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
172
apps/desktop/__tests__/utils.ts
Normal file
172
apps/desktop/__tests__/utils.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { execSync } from "child_process";
|
||||
import { mkdir } from "fs/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import path from "path";
|
||||
import { _electron as electron } from "playwright";
|
||||
import slugify from "slugify";
|
||||
import { TaskContext } from "vitest";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const IS_DEBUG = process.env.NN_DEBUG === "true" || process.env.CI === "true";
|
||||
|
||||
interface AppContext {
|
||||
app: import("playwright").ElectronApplication;
|
||||
page: import("playwright").Page;
|
||||
configPath: string;
|
||||
relaunch: () => Promise<void>;
|
||||
}
|
||||
|
||||
interface TestOptions {
|
||||
version: string;
|
||||
}
|
||||
|
||||
export async function harness(
|
||||
t: TaskContext,
|
||||
cb: (ctx: AppContext) => Promise<void>,
|
||||
options?: TestOptions
|
||||
) {
|
||||
const ctx = await buildAndLaunchApp(options);
|
||||
|
||||
t.onTestFinished(async (result) => {
|
||||
if (result.state === "fail") {
|
||||
await mkdir("test-results", { recursive: true });
|
||||
await ctx.page.screenshot({
|
||||
path: path.join(
|
||||
"test-results",
|
||||
`${slugify(t.task.name)}-${process.platform}-${
|
||||
process.arch
|
||||
}-error.png`
|
||||
)
|
||||
});
|
||||
}
|
||||
await ctx.app.close();
|
||||
});
|
||||
|
||||
await cb(ctx);
|
||||
}
|
||||
|
||||
async function buildAndLaunchApp(options?: TestOptions): Promise<AppContext> {
|
||||
const productName = makeid(10);
|
||||
const executablePath = await buildApp({ ...options, productName });
|
||||
const { app, page, configPath } = await launchApp(executablePath);
|
||||
const ctx: AppContext = {
|
||||
app,
|
||||
page,
|
||||
configPath,
|
||||
relaunch: async () => {
|
||||
const { app, page, configPath } = await launchApp(executablePath);
|
||||
ctx.app = app;
|
||||
ctx.page = page;
|
||||
ctx.configPath = configPath;
|
||||
}
|
||||
};
|
||||
return ctx;
|
||||
}
|
||||
|
||||
async function launchApp(executablePath: string) {
|
||||
const app = await electron.launch({
|
||||
executablePath,
|
||||
args: IS_DEBUG ? [] : ["--hidden"],
|
||||
env:
|
||||
process.platform === "linux"
|
||||
? {
|
||||
...(process.env as Record<string, string>),
|
||||
APPIMAGE: "true"
|
||||
}
|
||||
: (process.env as Record<string, string>)
|
||||
});
|
||||
|
||||
const page = await app.firstWindow();
|
||||
|
||||
const userDataDirectory = await app.evaluate((a) => {
|
||||
return a.app.getPath("userData");
|
||||
});
|
||||
const configPath = path.join(userDataDirectory, "config.json");
|
||||
return {
|
||||
app,
|
||||
page,
|
||||
configPath
|
||||
};
|
||||
}
|
||||
|
||||
async function buildApp({
|
||||
version,
|
||||
productName
|
||||
}: {
|
||||
version?: string;
|
||||
productName: string;
|
||||
}) {
|
||||
const buildRoot = path.join("test-artifacts", `${productName}-build`);
|
||||
const output = path.join("test-artifacts", `${productName}-output`);
|
||||
execSync(`npm run release -- --root ${buildRoot} --skip-tsc-build`, {
|
||||
stdio: IS_DEBUG ? "inherit" : "ignore"
|
||||
});
|
||||
|
||||
const args = [
|
||||
`--config electron-builder.config.js`,
|
||||
`--c.extraMetadata.productName=${productName}`,
|
||||
"--publish=never"
|
||||
];
|
||||
if (version) args.push(`--c.extraMetadata.version=${version}`);
|
||||
|
||||
execSync(`npx electron-builder --dir --${process.arch} ${args.join(" ")}`, {
|
||||
stdio: IS_DEBUG ? "inherit" : "ignore",
|
||||
env: {
|
||||
...process.env,
|
||||
NOTESNOOK_STAGING: "true",
|
||||
NN_BUILD_ROOT: buildRoot,
|
||||
NN_PRODUCT_NAME: productName,
|
||||
NN_APP_ID: `com.notesnook.test.${productName}`,
|
||||
NN_OUTPUT_DIR: output
|
||||
}
|
||||
});
|
||||
|
||||
return path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
output,
|
||||
process.platform === "linux"
|
||||
? process.arch === "arm64"
|
||||
? "linux-arm64-unpacked"
|
||||
: "linux-unpacked"
|
||||
: process.platform === "darwin"
|
||||
? process.arch === "arm64"
|
||||
? `mac-arm64/${productName}.app/Contents/MacOS/`
|
||||
: `mac/${productName}.app/Contents/MacOS/`
|
||||
: "win-unpacked",
|
||||
process.platform === "win32" ? `${productName}.exe` : productName
|
||||
);
|
||||
}
|
||||
|
||||
function makeid(length: number) {
|
||||
let result = "";
|
||||
const characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
const charactersLength = characters.length;
|
||||
let counter = 0;
|
||||
while (counter < length) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
counter += 1;
|
||||
}
|
||||
return result.toLowerCase();
|
||||
}
|
||||
Binary file not shown.
215
apps/desktop/electron-builder.config.js
Normal file
215
apps/desktop/electron-builder.config.js
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
const pkg = require("./package.json");
|
||||
|
||||
const buildRoot = process.env.NN_BUILD_ROOT || ".";
|
||||
const buildFiles = [
|
||||
`${buildRoot}/build/`,
|
||||
`!${buildRoot}/build/screenshots\${/*}`,
|
||||
`!${buildRoot}/build/banner.jpg`,
|
||||
`!${buildRoot}/build/*.ico`,
|
||||
`!${buildRoot}/build/*.png`
|
||||
];
|
||||
|
||||
const productName = process.env.NN_PRODUCT_NAME || "Notesnook";
|
||||
const appId = process.env.NN_APP_ID || "org.streetwriters.notesnook";
|
||||
const outputDir = process.env.NN_OUTPUT_DIR || "output";
|
||||
const linuxExecutableName = process.env.NN_PRODUCT_NAME
|
||||
? process.env.NN_PRODUCT_NAME.toLowerCase().replace(/\s+/g, "-")
|
||||
: "notesnook";
|
||||
const year = new Date().getFullYear();
|
||||
const isBeta = pkg.version.includes("-beta");
|
||||
|
||||
module.exports = {
|
||||
appId: appId,
|
||||
productName: productName,
|
||||
copyright: `Copyright © ${year} Streetwriters (Private) Limited`,
|
||||
artifactName: "notesnook_${os}_${arch}.${ext}",
|
||||
generateUpdatesFilesForAllChannels: true,
|
||||
asar: false,
|
||||
files: [
|
||||
"!*.chunk.js.map",
|
||||
"!*.chunk.js.LICENSE.txt",
|
||||
...buildFiles,
|
||||
"!node_modules${/*}",
|
||||
"node_modules/better-sqlite3-multiple-ciphers/build/Release/better_sqlite3.node",
|
||||
"node_modules/better-sqlite3-multiple-ciphers/lib",
|
||||
"node_modules/better-sqlite3-multiple-ciphers/package.json",
|
||||
"node_modules/file-uri-to-path",
|
||||
"node_modules/bindings",
|
||||
"node_modules/node-gyp-build",
|
||||
"node_modules/sqlite-better-trigram",
|
||||
"node_modules/sqlite3-fts5-html",
|
||||
"node_modules/sodium-native/prebuilds/${platform}-${arch}",
|
||||
{
|
||||
from: "node_modules/sqlite-better-trigram-linux-${arch}",
|
||||
to: "node_modules/sqlite-better-trigram-linux-${arch}"
|
||||
},
|
||||
{
|
||||
from: "node_modules/sqlite-better-trigram-darwin-${arch}",
|
||||
to: "node_modules/sqlite-better-trigram-darwin-${arch}"
|
||||
},
|
||||
{
|
||||
from: "node_modules/sqlite-better-trigram-windows-${arch}",
|
||||
to: "node_modules/sqlite-better-trigram-windows-${arch}"
|
||||
},
|
||||
|
||||
{
|
||||
from: "node_modules/sqlite3-fts5-html-linux-${arch}",
|
||||
to: "node_modules/sqlite3-fts5-html-linux-${arch}"
|
||||
},
|
||||
{
|
||||
from: "node_modules/sqlite3-fts5-html-darwin-${arch}",
|
||||
to: "node_modules/sqlite3-fts5-html-darwin-${arch}"
|
||||
},
|
||||
{
|
||||
from: "node_modules/sqlite3-fts5-html-windows-${arch}",
|
||||
to: "node_modules/sqlite3-fts5-html-windows-${arch}"
|
||||
},
|
||||
|
||||
"node_modules/sodium-native/index.js",
|
||||
"node_modules/sodium-native/package.json"
|
||||
],
|
||||
afterPack: "./scripts/removeLocales.js",
|
||||
mac: {
|
||||
bundleVersion: "240",
|
||||
minimumSystemVersion: "10.12.0",
|
||||
target: [
|
||||
{
|
||||
target: "dmg",
|
||||
arch: ["arm64", "x64"]
|
||||
},
|
||||
{
|
||||
target: "zip",
|
||||
arch: ["arm64", "x64"]
|
||||
}
|
||||
],
|
||||
category: "public.app-category.productivity",
|
||||
darkModeSupport: true,
|
||||
type: "distribution",
|
||||
hardenedRuntime: true,
|
||||
entitlements: "assets/entitlements.mac.plist",
|
||||
entitlementsInherit: "assets/entitlements.mac.plist",
|
||||
gatekeeperAssess: false,
|
||||
icon: "assets/icons/app.icns",
|
||||
notarize: true
|
||||
},
|
||||
dmg: {
|
||||
contents: [
|
||||
{
|
||||
x: 130,
|
||||
y: 220
|
||||
},
|
||||
{
|
||||
x: 410,
|
||||
y: 220,
|
||||
type: "link",
|
||||
path: "/Applications"
|
||||
}
|
||||
],
|
||||
icon: "assets/icons/app.icns",
|
||||
title: "Install Notesnook"
|
||||
},
|
||||
mas: {
|
||||
entitlements: "assets/entitlements.mas.plist",
|
||||
entitlementsInherit: "assets/entitlements.mas.inherit.plist",
|
||||
entitlementsLoginHelper: "assets/entitlements.mas.loginhelper.plist",
|
||||
hardenedRuntime: true
|
||||
},
|
||||
win: {
|
||||
target: [
|
||||
{
|
||||
target: "nsis",
|
||||
arch: ["x64", "arm64"]
|
||||
},
|
||||
{
|
||||
target: "portable",
|
||||
arch: ["x64", "arm64"]
|
||||
}
|
||||
],
|
||||
signtoolOptions: {
|
||||
signingHashAlgorithms: ["sha256"],
|
||||
sign: "./scripts/sign.js"
|
||||
},
|
||||
icon: "assets/icons/app.ico"
|
||||
},
|
||||
portable: {
|
||||
artifactName: "notesnook_${os}_${arch}_portable.${ext}"
|
||||
},
|
||||
nsis: {
|
||||
oneClick: true,
|
||||
createDesktopShortcut: "always",
|
||||
deleteAppDataOnUninstall: true
|
||||
},
|
||||
linux: {
|
||||
target: [
|
||||
{
|
||||
target: "AppImage",
|
||||
arch: ["x64", "arm64"]
|
||||
},
|
||||
{
|
||||
target: "snap",
|
||||
arch: ["x64", "arm64"]
|
||||
}
|
||||
],
|
||||
category: "Office",
|
||||
icon: "assets/icons/app.icns",
|
||||
description: "Your private note taking space",
|
||||
executableName: linuxExecutableName,
|
||||
desktop: {
|
||||
desktopActions: {
|
||||
"new-note": {
|
||||
Name: "New note",
|
||||
Exec: `${linuxExecutableName} new note`
|
||||
},
|
||||
"new-notebook": {
|
||||
Name: "New notebook",
|
||||
Exec: `${linuxExecutableName} new notebook`
|
||||
},
|
||||
"new-reminder": {
|
||||
Name: "New reminder",
|
||||
Exec: `${linuxExecutableName} new reminder`
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
snap: {
|
||||
autoStart: false,
|
||||
confinement: "strict",
|
||||
allowNativeWayland: true
|
||||
},
|
||||
extraResources: ["app-update.yml", "./assets/**"],
|
||||
extraMetadata: {
|
||||
main: path.join(buildRoot, "build", "electron.js")
|
||||
},
|
||||
directories: {
|
||||
buildResources: "assets",
|
||||
output: outputDir
|
||||
},
|
||||
publish: [
|
||||
{
|
||||
provider: "github",
|
||||
repo: "notesnook",
|
||||
owner: "streetwriters",
|
||||
channel: isBeta ? "beta" : "latest"
|
||||
}
|
||||
]
|
||||
};
|
||||
2312
apps/desktop/package-lock.json
generated
2312
apps/desktop/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.0.23",
|
||||
"version": "3.2.0-beta.8",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/cjs/index.js",
|
||||
@@ -25,241 +25,57 @@
|
||||
"repository": "https://github.com/streetwriters/notesnook",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@lingui/core": "4.11.4",
|
||||
"@lingui/core": "5.1.2",
|
||||
"@notesnook/intl": "file:../../packages/intl",
|
||||
"@notesnook/ui": "file:../../packages/ui",
|
||||
"@trpc/client": "10.45.2",
|
||||
"@trpc/server": "10.45.2",
|
||||
"better-sqlite3-multiple-ciphers": "11.5.0",
|
||||
"electron-trpc": "0.6.1",
|
||||
"electron-updater": "^6.3.4",
|
||||
"icojs": "^0.19.4",
|
||||
"sqlite-better-trigram": "^0.0.2",
|
||||
"better-sqlite3-multiple-ciphers": "11.3.0",
|
||||
"electron-trpc": "0.7.1",
|
||||
"electron-updater": "^6.6.2",
|
||||
"icojs": "^0.19.5",
|
||||
"sqlite-better-trigram": "0.0.3",
|
||||
"sqlite3-fts5-html": "^0.0.3",
|
||||
"typed-emitter": "^2.1.0",
|
||||
"yargs": "^17.7.2",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@streetwriters/kysely": "^0.27.4",
|
||||
"@types/node": "22.7.0",
|
||||
"@types/node": "22.15.3",
|
||||
"@types/yargs": "^17.0.33",
|
||||
"chokidar": "^4.0.1",
|
||||
"electron": "^31.7.4",
|
||||
"electron-builder": "^25.1.8",
|
||||
"esbuild": "^0.24.0",
|
||||
"node-abi": "^3.68.0",
|
||||
"node-gyp-build": "^4.8.2",
|
||||
"playwright": "^1.48.2",
|
||||
"chokidar": "^4.0.3",
|
||||
"electron": "^34.0.0",
|
||||
"electron-builder": "^26.0.12",
|
||||
"esbuild": "0.21.5",
|
||||
"node-abi": "^4.5.0",
|
||||
"node-gyp-build": "^4.8.4",
|
||||
"playwright": "1.48.2",
|
||||
"prebuildify": "^6.0.1",
|
||||
"slugify": "^1.6.6",
|
||||
"slugify": "1.6.6",
|
||||
"tree-kill": "^1.2.2",
|
||||
"undici": "^6.19.8",
|
||||
"vitest": "^2.1.5"
|
||||
"undici": "^7.8.0",
|
||||
"vitest": "2.1.8"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"dmg-license": "^1.0.11"
|
||||
},
|
||||
"overrides": {
|
||||
"@electron/node-gyp": "^10.2.0-electron.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node scripts/dev.mjs",
|
||||
"staging": "node scripts/build.mjs --run",
|
||||
"release": "node scripts/build.mjs",
|
||||
"build": "node ../../scripts/build.mjs",
|
||||
"bundle": "esbuild electron=./src/main.ts ./src/preload.ts --external:electron --external:fsevents --external:better-sqlite3-multiple-ciphers --external:sodium-native --minify --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=false --define:RELEASE=true",
|
||||
"bundle": "esbuild electron=./src/main.ts ./src/preload.ts --external:electron --external:fsevents --external:better-sqlite3-multiple-ciphers --external:sodium-native --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=false --define:RELEASE=true",
|
||||
"bundle:mas": "esbuild electron=./src/main.ts ./src/preload.ts --minify --external:electron --external:fsevents --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=true --define:RELEASE=true",
|
||||
"postinstall": "patch-package",
|
||||
"test": "node --test --test-timeout 30000 --test-force-exit"
|
||||
"test": "vitest run"
|
||||
},
|
||||
"author": {
|
||||
"name": "Streetwriters (Private) Limited",
|
||||
"email": "support@streetwriters.co",
|
||||
"url": "https://streetwriters.co"
|
||||
},
|
||||
"build": {
|
||||
"appId": "org.streetwriters.notesnook",
|
||||
"productName": "Notesnook",
|
||||
"copyright": "Copyright © 2023 Streetwriters (Private) Limited",
|
||||
"artifactName": "notesnook_${os}_${arch}.${ext}",
|
||||
"generateUpdatesFilesForAllChannels": true,
|
||||
"asar": false,
|
||||
"files": [
|
||||
"!*.chunk.js.map",
|
||||
"!*.chunk.js.LICENSE.txt",
|
||||
"build/",
|
||||
"!build/screenshots${/*}",
|
||||
"!build/banner.jpg",
|
||||
"!build/*.ico",
|
||||
"!build/*.png",
|
||||
"!node_modules${/*}",
|
||||
"node_modules/better-sqlite3-multiple-ciphers/build/Release/better_sqlite3.node",
|
||||
"node_modules/better-sqlite3-multiple-ciphers/lib",
|
||||
"node_modules/better-sqlite3-multiple-ciphers/package.json",
|
||||
"node_modules/file-uri-to-path",
|
||||
"node_modules/bindings",
|
||||
"node_modules/node-gyp-build",
|
||||
"node_modules/sqlite-better-trigram",
|
||||
"node_modules/sodium-native/prebuilds/${platform}-${arch}",
|
||||
{
|
||||
"from": "node_modules/sqlite-better-trigram-linux-${arch}",
|
||||
"to": "node_modules/sqlite-better-trigram-linux-${arch}"
|
||||
},
|
||||
{
|
||||
"from": "node_modules/sqlite-better-trigram-darwin-${arch}",
|
||||
"to": "node_modules/sqlite-better-trigram-darwin-${arch}"
|
||||
},
|
||||
{
|
||||
"from": "node_modules/sqlite-better-trigram-windows-${arch}",
|
||||
"to": "node_modules/sqlite-better-trigram-windows-${arch}"
|
||||
},
|
||||
"node_modules/sodium-native/index.js",
|
||||
"node_modules/sodium-native/package.json"
|
||||
],
|
||||
"afterPack": "./scripts/removeLocales.js",
|
||||
"mac": {
|
||||
"bundleVersion": "240",
|
||||
"minimumSystemVersion": "10.12.0",
|
||||
"target": [
|
||||
{
|
||||
"target": "dmg",
|
||||
"arch": [
|
||||
"arm64",
|
||||
"x64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "zip",
|
||||
"arch": [
|
||||
"arm64",
|
||||
"x64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"category": "public.app-category.productivity",
|
||||
"darkModeSupport": true,
|
||||
"type": "distribution",
|
||||
"hardenedRuntime": true,
|
||||
"entitlements": "assets/entitlements.mac.plist",
|
||||
"entitlementsInherit": "assets/entitlements.mac.plist",
|
||||
"gatekeeperAssess": false,
|
||||
"icon": "assets/icons/app.icns",
|
||||
"notarize": true
|
||||
},
|
||||
"dmg": {
|
||||
"contents": [
|
||||
{
|
||||
"x": 130,
|
||||
"y": 220
|
||||
},
|
||||
{
|
||||
"x": 410,
|
||||
"y": 220,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
}
|
||||
],
|
||||
"icon": "assets/icons/app.icns",
|
||||
"title": "Install Notesnook"
|
||||
},
|
||||
"mas": {
|
||||
"entitlements": "assets/entitlements.mas.plist",
|
||||
"entitlementsInherit": "assets/entitlements.mas.inherit.plist",
|
||||
"entitlementsLoginHelper": "assets/entitlements.mas.loginhelper.plist",
|
||||
"hardenedRuntime": true
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
{
|
||||
"target": "nsis",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "portable",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"signtoolOptions": {
|
||||
"signingHashAlgorithms": [
|
||||
"sha256"
|
||||
],
|
||||
"sign": "./scripts/sign.js"
|
||||
},
|
||||
"icon": "assets/icons/app.ico"
|
||||
},
|
||||
"portable": {
|
||||
"artifactName": "notesnook_${os}_${arch}_portable.${ext}"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": true,
|
||||
"createDesktopShortcut": "always",
|
||||
"deleteAppDataOnUninstall": true
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
{
|
||||
"target": "AppImage",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "snap",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"category": "Office",
|
||||
"icon": "assets/icons/app.icns",
|
||||
"description": "Your private note taking space",
|
||||
"executableName": "notesnook",
|
||||
"desktop": {
|
||||
"actions": [
|
||||
{
|
||||
"id": "new-note",
|
||||
"name": "New note",
|
||||
"args": "new note"
|
||||
},
|
||||
{
|
||||
"id": "new-notebook",
|
||||
"name": "New notebook",
|
||||
"args": "new notebook"
|
||||
},
|
||||
{
|
||||
"id": "new-reminder",
|
||||
"name": "New reminder",
|
||||
"args": "new reminder"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"snap": {
|
||||
"autoStart": false,
|
||||
"confinement": "strict",
|
||||
"allowNativeWayland": true
|
||||
},
|
||||
"extraResources": [
|
||||
"app-update.yml",
|
||||
"./assets/**"
|
||||
],
|
||||
"extraMetadata": {
|
||||
"main": "./build/electron.js"
|
||||
},
|
||||
"directories": {
|
||||
"buildResources": "assets",
|
||||
"output": "./output/"
|
||||
},
|
||||
"publish": [
|
||||
{
|
||||
"provider": "github",
|
||||
"repo": "notesnook",
|
||||
"owner": "streetwriters"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
diff --git a/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js b/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js
|
||||
index 739a48c..7a7b4af 100644
|
||||
--- a/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js
|
||||
+++ b/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js
|
||||
@@ -117,6 +117,7 @@ class LinuxTargetHelper {
|
||||
StartupWMClass: appInfo.productName,
|
||||
...extra,
|
||||
...targetSpecificOptions.desktop,
|
||||
+ actions: undefined
|
||||
};
|
||||
const description = this.getDescription(targetSpecificOptions);
|
||||
if (!(0, builder_util_1.isEmptyOrSpaces)(description)) {
|
||||
@@ -160,6 +161,23 @@ class LinuxTargetHelper {
|
||||
data += `\n${name}=${desktopMeta[name]}`;
|
||||
}
|
||||
data += "\n";
|
||||
+
|
||||
+ if (targetSpecificOptions.desktop.actions) {
|
||||
+ let actionsData = "";
|
||||
+ const validActions = [];
|
||||
+ for (const action of targetSpecificOptions.desktop.actions) {
|
||||
+ if (!action.id || !action.name || !action.args) continue;
|
||||
+ actionsData += "\n";
|
||||
+ actionsData += `[Desktop Action ${action.id}]
|
||||
+Name=${action.name}
|
||||
+Exec=${desktopMeta.Exec} ${action.args}`;
|
||||
+ actionsData += "\n";
|
||||
+
|
||||
+ validActions.push(action.id);
|
||||
+ }
|
||||
+
|
||||
+ data += `Actions=${validActions.join(";")};\n${actionsData}`
|
||||
+ }
|
||||
return Promise.resolve(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
diff --git a/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi b/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi
|
||||
index 1a14ecd..dabcce2 100644
|
||||
--- a/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi
|
||||
+++ b/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi
|
||||
@@ -38,5 +38,6 @@
|
||||
'SQLITE_TRACE_SIZE_LIMIT=32',
|
||||
'SQLITE_USER_AUTHENTICATION=0',
|
||||
'SQLITE_USE_URI=0',
|
||||
+ 'SQLITE_ENABLE_REGEXP',
|
||||
],
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
diff --git a/node_modules/electron-trpc/dist/main.mjs b/node_modules/electron-trpc/dist/main.mjs
|
||||
index 07971db..b67a3cf 100644
|
||||
--- a/node_modules/electron-trpc/dist/main.mjs
|
||||
+++ b/node_modules/electron-trpc/dist/main.mjs
|
||||
@@ -537,8 +537,15 @@ class be {
|
||||
attachWindow(r) {
|
||||
F(this, w).includes(r) || (y("Attaching window", r.id), F(this, w).push(r), v(this, N, Z).call(this, r));
|
||||
}
|
||||
- detachWindow(r) {
|
||||
- y("Detaching window", r.id), L(this, w, F(this, w).filter((e) => e !== r)), v(this, R, M).call(this, { webContentsId: r.webContents.id });
|
||||
+ detachWindow(r, webContentsId) {
|
||||
+ y("Detaching window", r.id);
|
||||
+
|
||||
+ if (r.isDestroyed() && webContentsId === undefined) {
|
||||
+ throw new Error('webContentsId is required when calling detachWindow on a destroyed window');
|
||||
+ }
|
||||
+
|
||||
+ L(this, w, F(this, w).filter((e) => e !== r));
|
||||
+ v(this, R, M).call(this, { webContentsId: webContentsId ?? r.webContents.id });
|
||||
}
|
||||
}
|
||||
w = new WeakMap(), E = new WeakMap(), R = new WeakSet(), M = function({
|
||||
@@ -548,6 +555,7 @@ w = new WeakMap(), E = new WeakMap(), R = new WeakSet(), M = function({
|
||||
for (const [i, a] of F(this, E).entries())
|
||||
i.startsWith(`${r}-${e ?? ""}`) && (y("Closing subscription", i), a.unsubscribe(), F(this, E).delete(i));
|
||||
}, N = new WeakSet(), Z = function(r) {
|
||||
+ const webContentsId = r.webContents.id;
|
||||
r.webContents.on("did-start-navigation", ({ frame: e }) => {
|
||||
y(
|
||||
"Handling webContents `did-start-navigation` event",
|
||||
@@ -558,7 +566,7 @@ w = new WeakMap(), E = new WeakMap(), R = new WeakSet(), M = function({
|
||||
frameRoutingId: e.routingId
|
||||
});
|
||||
}), r.webContents.on("destroyed", () => {
|
||||
- y("Handling webContents `destroyed` event"), this.detachWindow(r);
|
||||
+ y("Handling webContents `destroyed` event"), this.detachWindow(r, webContentsId);
|
||||
});
|
||||
};
|
||||
const me = ({
|
||||
@@ -29,40 +29,47 @@ import { patchBetterSQLite3 } from "./patch-better-sqlite3.mjs";
|
||||
const args = yargs(process.argv);
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const root = args.root || path.join(__dirname, "..");
|
||||
const skipTscBuild = args.skipTscBuild || false;
|
||||
|
||||
const webAppPath = path.resolve(path.join(__dirname, "..", "..", "web"));
|
||||
|
||||
await fs.rm("./build/", { force: true, recursive: true });
|
||||
await fs.rm(path.join(root, "build"), { force: true, recursive: true });
|
||||
|
||||
if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
|
||||
await exec(
|
||||
"yarn nx build:desktop @notesnook/web",
|
||||
"npx nx build:desktop @notesnook/web",
|
||||
path.join(__dirname, "..", "..", "..")
|
||||
);
|
||||
}
|
||||
|
||||
// temporary until there's support for prebuilt binaries for linux ARM
|
||||
if (os.platform() === "linux") await patchBetterSQLite3();
|
||||
await patchBetterSQLite3();
|
||||
|
||||
await fs.cp(path.join(webAppPath, "build"), "build", {
|
||||
await fs.cp(path.join(webAppPath, "build"), path.join(root, "build"), {
|
||||
recursive: true,
|
||||
force: true
|
||||
});
|
||||
|
||||
if (args.variant === "mas") {
|
||||
await exec(`yarn run bundle:mas`);
|
||||
await exec(`yarn run bundle:mas --outdir=${path.join(root, "build")}`);
|
||||
} else {
|
||||
await exec(`yarn run bundle`);
|
||||
await exec(`yarn run bundle --outdir=${path.join(root, "build")}`);
|
||||
}
|
||||
|
||||
await exec(`yarn run build`);
|
||||
if (!skipTscBuild) {
|
||||
await exec(`yarn run build`);
|
||||
}
|
||||
|
||||
if (args.run) {
|
||||
await exec(`yarn electron-builder --dir --x64`);
|
||||
await exec(
|
||||
`yarn electron-builder --dir --${process.arch} --config=electron-builder.config.js`
|
||||
);
|
||||
if (process.platform === "win32") {
|
||||
await exec(`.\\output\\win-unpacked\\Notesnook.exe`);
|
||||
} else if (process.platform === "darwin") {
|
||||
await exec(`./output/mac/Notesnook.app/Contents/MacOS/Notesnook`);
|
||||
if (process.arch === "arm64")
|
||||
await exec(`./output/mac-arm64/Notesnook.app/Contents/MacOS/Notesnook`);
|
||||
else await exec(`./output/mac/Notesnook.app/Contents/MacOS/Notesnook`);
|
||||
} else {
|
||||
await exec(`./output/linux-unpacked/Notesnook`);
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ async function onChange(first) {
|
||||
|
||||
if (first) {
|
||||
await spawnAndWaitUntil(
|
||||
["yarn", "nx", "start:desktop", "@notesnook/web"],
|
||||
path.join(__dirname, "..", "..", ".."),
|
||||
["npm", "run", "start:desktop"],
|
||||
path.join(__dirname, "..", "..", "web"),
|
||||
(data) => data.includes("Network: use --host to expose")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { readFile, writeFile } from "fs/promises";
|
||||
import { readFile, rm, writeFile } from "fs/promises";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
@@ -25,6 +25,8 @@ const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
export async function patchBetterSQLite3() {
|
||||
console.log("Patching better-sqlite3");
|
||||
|
||||
const jsonPath = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
@@ -34,15 +36,23 @@ export async function patchBetterSQLite3() {
|
||||
);
|
||||
const json = JSON.parse(await readFile(jsonPath, "utf-8"));
|
||||
|
||||
json.version = "11.5.1";
|
||||
json.homepage = "https://github.com/thecodrr/better-sqlite3-multiple-ciphers";
|
||||
json.repository.url =
|
||||
"git://github.com/thecodrr/better-sqlite3-multiple-ciphers.git";
|
||||
delete json.homepage;
|
||||
delete json.repository;
|
||||
|
||||
await writeFile(jsonPath, JSON.stringify(json));
|
||||
|
||||
await rm(
|
||||
path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"node_modules",
|
||||
"better-sqlite3-multiple-ciphers",
|
||||
"build"
|
||||
),
|
||||
{ force: true, recursive: true }
|
||||
);
|
||||
}
|
||||
|
||||
if (process.argv[1] === __filename) {
|
||||
console.log("Patching better-sqlite3");
|
||||
patchBetterSQLite3();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { initTRPC } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
import { app, dialog, nativeTheme, Notification, shell } from "electron";
|
||||
import {
|
||||
app,
|
||||
dialog,
|
||||
Menu,
|
||||
MenuItem,
|
||||
nativeImage,
|
||||
nativeTheme,
|
||||
Notification,
|
||||
shell
|
||||
} from "electron";
|
||||
import { AutoLaunch } from "../utils/autolaunch";
|
||||
import { config, DesktopIntegration } from "../utils/config";
|
||||
import { bringToFront } from "../utils/bring-to-front";
|
||||
@@ -29,10 +38,11 @@ import { dirname } from "path";
|
||||
import { resolvePath } from "../utils/resolve-path";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
import { AssetManager } from "../utils/asset-manager";
|
||||
import { isFlatpak } from "../utils";
|
||||
import { isFlatpak, isSnap } from "../utils";
|
||||
import { setupDesktopIntegration } from "../utils/desktop-integration";
|
||||
import { rm } from "fs/promises";
|
||||
import { disableCustomDns, enableCustomDns } from "../utils/custom-dns";
|
||||
import type { MenuItem as NNMenuItem } from "@notesnook/ui";
|
||||
|
||||
const t = initTRPC.create();
|
||||
|
||||
@@ -49,6 +59,7 @@ const NotificationOptions = z.object({
|
||||
|
||||
export const osIntegrationRouter = t.router({
|
||||
isFlatpak: t.procedure.query(() => isFlatpak()),
|
||||
isSnap: t.procedure.query(() => isSnap()),
|
||||
|
||||
zoomFactor: t.procedure.query(() => config.zoomFactor),
|
||||
setZoomFactor: t.procedure.input(z.number()).mutation(({ input: factor }) => {
|
||||
@@ -224,5 +235,63 @@ export const osIntegrationRouter = t.router({
|
||||
nativeTheme.off("updated", updated);
|
||||
};
|
||||
})
|
||||
)
|
||||
),
|
||||
|
||||
showMenu: t.procedure
|
||||
.input(
|
||||
z.object({
|
||||
menuItems: z.array(z.any())
|
||||
})
|
||||
)
|
||||
.subscription(({ input: { menuItems } }) =>
|
||||
observable<string[]>((emit) => {
|
||||
const items = menuItems as NNMenuItem[];
|
||||
const menu = new Menu();
|
||||
for (const item of items) {
|
||||
const menuItem = toMenuItem(item, (id) => emit.next(id));
|
||||
if (menuItem) menu.append(menuItem);
|
||||
}
|
||||
if (menu.items.length > 0) menu.popup();
|
||||
return () => {
|
||||
menu.removeAllListeners();
|
||||
menu.closePopup();
|
||||
};
|
||||
})
|
||||
)
|
||||
});
|
||||
|
||||
function toMenuItem(
|
||||
item: NNMenuItem,
|
||||
onClick: (id: string[]) => void,
|
||||
parentKey?: string
|
||||
): MenuItem | undefined {
|
||||
switch (item.type) {
|
||||
case "lazy-loader":
|
||||
return undefined;
|
||||
case "separator":
|
||||
return new MenuItem({ type: "separator" });
|
||||
case "button": {
|
||||
const submenu = item.menu ? new Menu() : undefined;
|
||||
if (submenu && item.menu) {
|
||||
for (const subitem of item.menu.items) {
|
||||
const subMenuItem = toMenuItem(subitem, onClick, item.key);
|
||||
if (subMenuItem) submenu.append(subMenuItem);
|
||||
}
|
||||
}
|
||||
|
||||
return new MenuItem({
|
||||
label: item.title,
|
||||
enabled: !item.isDisabled,
|
||||
visible: !item.isHidden,
|
||||
toolTip: item.tooltip,
|
||||
sublabel: item.tooltip,
|
||||
checked: item.isChecked,
|
||||
type: submenu ? "submenu" : item.isChecked ? "checkbox" : "normal",
|
||||
id: item.key,
|
||||
submenu,
|
||||
click: () => onClick(parentKey ? [parentKey, item.key] : [item.key]),
|
||||
accelerator: item.modifier?.replace("Mod", "CommandOrControl")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,9 @@ export class SQLite {
|
||||
filePath
|
||||
).unsafeMode(true);
|
||||
const betterTrigram = require("sqlite-better-trigram");
|
||||
const fts5Html = require("sqlite3-fts5-html");
|
||||
betterTrigram.load(this.sqlite);
|
||||
fts5Html.load(this.sqlite);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,7 @@ import { CancellationToken, autoUpdater } from "electron-updater";
|
||||
import type { AppUpdaterEvents } from "electron-updater/out/AppUpdater";
|
||||
import { z } from "zod";
|
||||
import { config } from "../utils/config";
|
||||
import { app } from "electron";
|
||||
|
||||
type UpdateInfo = { version: string };
|
||||
type Progress = { percent: number };
|
||||
@@ -33,6 +34,7 @@ let downloadTimeout: NodeJS.Timeout | undefined = undefined;
|
||||
|
||||
export const updaterRouter = t.router({
|
||||
autoUpdates: t.procedure.query(() => config.automaticUpdates),
|
||||
releaseTrack: t.procedure.query(() => config.releaseTrack),
|
||||
install: t.procedure.query(() => autoUpdater.quitAndInstall()),
|
||||
download: t.procedure.query(async () => {
|
||||
if (cancellationToken) return;
|
||||
@@ -67,7 +69,13 @@ export const updaterRouter = t.router({
|
||||
.mutation(({ input: { enabled } }) => {
|
||||
config.automaticUpdates = enabled;
|
||||
}),
|
||||
|
||||
changeReleaseTrack: t.procedure
|
||||
.input(z.object({ track: z.string() }))
|
||||
.mutation(({ input: { track } }) => {
|
||||
config.releaseTrack = track;
|
||||
app.relaunch();
|
||||
app.exit();
|
||||
}),
|
||||
onChecking: createSubscription("checking-for-update"),
|
||||
onDownloaded: createSubscription<"update-downloaded", UpdateInfo>(
|
||||
"update-downloaded"
|
||||
|
||||
@@ -85,6 +85,8 @@ async function createWindow() {
|
||||
const mainWindowState = new WindowState({});
|
||||
const mainWindow = new BrowserWindow({
|
||||
show: !cliOptions.hidden,
|
||||
paintWhenInitiallyHidden: cliOptions.hidden,
|
||||
skipTaskbar: cliOptions.hidden,
|
||||
x: mainWindowState.x,
|
||||
y: mainWindowState.y,
|
||||
width: mainWindowState.width,
|
||||
@@ -101,7 +103,10 @@ async function createWindow() {
|
||||
...(config.desktopSettings.nativeTitlebar
|
||||
? {}
|
||||
: {
|
||||
titleBarStyle: process.platform === "win32" || process.platform === "darwin" ? "hidden" : "default",
|
||||
titleBarStyle:
|
||||
process.platform === "win32" || process.platform === "darwin"
|
||||
? "hidden"
|
||||
: "default",
|
||||
frame: process.platform === "win32" || process.platform === "darwin",
|
||||
titleBarOverlay: {
|
||||
height: 37,
|
||||
@@ -142,6 +147,11 @@ async function createWindow() {
|
||||
await AssetManager.loadIcons();
|
||||
setupDesktopIntegration(config.desktopSettings);
|
||||
|
||||
mainWindow.webContents.session.setPermissionRequestHandler(
|
||||
(webContents, permission, callback) => {
|
||||
callback(permission === "geolocation" ? false : true);
|
||||
}
|
||||
);
|
||||
mainWindow.webContents.session.setSpellCheckerDictionaryDownloadURL(
|
||||
"http://dictionaries.notesnook.com/"
|
||||
);
|
||||
|
||||
@@ -20,19 +20,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { autoUpdater } from "electron-updater";
|
||||
import { config } from "./config";
|
||||
|
||||
const CHANNEL = autoUpdater.currentVersion.raw.endsWith("-beta")
|
||||
? "beta"
|
||||
: "latest";
|
||||
async function configureAutoUpdater() {
|
||||
const releaseTrack =
|
||||
config.releaseTrack === "stable" ? "latest" : config.releaseTrack;
|
||||
autoUpdater.setFeedURL({
|
||||
provider: "generic",
|
||||
url: `https://notesnook.com/api/v1/releases/${process.platform}/${CHANNEL}`,
|
||||
url: `https://notesnook.com/api/v1/releases/${process.platform}/${releaseTrack}`,
|
||||
useMultipleRangeRequest: false,
|
||||
channel: CHANNEL
|
||||
channel: releaseTrack
|
||||
});
|
||||
|
||||
autoUpdater.autoDownload = config.automaticUpdates;
|
||||
autoUpdater.allowDowngrade = false;
|
||||
autoUpdater.allowDowngrade =
|
||||
// only allow downgrade if the current version is a prerelease
|
||||
// and the user has changed the release track to stable
|
||||
config.releaseTrack === "stable" &&
|
||||
autoUpdater.currentVersion.prerelease.length > 0;
|
||||
autoUpdater.allowPrerelease = false;
|
||||
autoUpdater.autoInstallOnAppQuit = true;
|
||||
autoUpdater.disableWebInstaller = true;
|
||||
|
||||
@@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { nativeTheme } from "electron";
|
||||
import { JSONStorage } from "./json-storage";
|
||||
import { z } from "zod";
|
||||
import { autoUpdater } from "electron-updater";
|
||||
|
||||
export const DesktopIntegration = z.object({
|
||||
autoStart: z.boolean().optional(),
|
||||
@@ -46,6 +47,9 @@ export const config = {
|
||||
automaticUpdates: true,
|
||||
proxyRules: "",
|
||||
customDns: true,
|
||||
releaseTrack: autoUpdater.currentVersion.raw.includes("-beta")
|
||||
? "beta"
|
||||
: "stable",
|
||||
|
||||
backgroundColor: nativeTheme.themeSource === "dark" ? "#0f0f0f" : "#ffffff",
|
||||
windowControlsIconColor:
|
||||
|
||||
@@ -29,3 +29,7 @@ export function isDevelopment() {
|
||||
export function isFlatpak() {
|
||||
return existsSync("/.flatpak-info");
|
||||
}
|
||||
|
||||
export function isSnap() {
|
||||
return process.env.SNAP !== undefined;
|
||||
}
|
||||
|
||||
@@ -143,16 +143,44 @@ function setupMenu() {
|
||||
})
|
||||
);
|
||||
|
||||
if (params.isEditable)
|
||||
if (params.isEditable) {
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: strings.paste(),
|
||||
role: "pasteAndMatchStyle",
|
||||
role: "paste",
|
||||
enabled: clipboard.readText("clipboard").length > 0,
|
||||
accelerator: "CommandOrControl+V"
|
||||
})
|
||||
);
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label:
|
||||
process.platform === "darwin"
|
||||
? strings.pasteAndMatchStyle()
|
||||
: strings.pasteWithoutFormatting(),
|
||||
role: "pasteAndMatchStyle",
|
||||
enabled: clipboard.readText("clipboard").length > 0,
|
||||
accelerator:
|
||||
process.platform === "darwin"
|
||||
? "Option+Shift+Command+V"
|
||||
: "Shift+CommandOrControl+V"
|
||||
})
|
||||
);
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
type: "separator"
|
||||
})
|
||||
);
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: strings.spellCheck(),
|
||||
role: "toggleSpellChecker"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (menu.items.length > 0) menu.popup();
|
||||
});
|
||||
}
|
||||
|
||||
40
apps/desktop/vitest.config.ts
Normal file
40
apps/desktop/vitest.config.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
testTimeout: process.env.CI ? 120 * 1000 : 120 * 1000,
|
||||
sequence: {
|
||||
concurrent: true,
|
||||
shuffle: true
|
||||
},
|
||||
dir: "./__tests__/",
|
||||
exclude: [
|
||||
"**/node_modules/**",
|
||||
"**/dist/**",
|
||||
"**/cypress/**",
|
||||
"**/.{idea,git,cache,output,temp}/**",
|
||||
"**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*",
|
||||
"**/test-results/**",
|
||||
"**/test-artifacts/**"
|
||||
]
|
||||
}
|
||||
});
|
||||
1
apps/mobile/.gitignore
vendored
1
apps/mobile/.gitignore
vendored
@@ -69,6 +69,7 @@ buck-out/
|
||||
*/fastlane/report.xml
|
||||
*/fastlane/Preview.html
|
||||
*/fastlane/screenshots
|
||||
native/vendor
|
||||
|
||||
# Bundle artifact
|
||||
*.jsbundle
|
||||
|
||||
@@ -19,25 +19,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { i18n } from "@lingui/core";
|
||||
import { I18nProvider } from "@lingui/react";
|
||||
import {
|
||||
ScopedThemeProvider,
|
||||
THEME_COMPATIBILITY_VERSION,
|
||||
useThemeEngineStore
|
||||
} from "@notesnook/theme";
|
||||
import React, { useEffect } from "react";
|
||||
import { I18nManager, View } from "react-native";
|
||||
import { Appearance, I18nManager, StatusBar } from "react-native";
|
||||
import "react-native-gesture-handler";
|
||||
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
||||
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||
import AppLockedOverlay from "./components/app-lock-overlay";
|
||||
import DialogProvider from "./components/dialog-provider";
|
||||
import { withErrorBoundry } from "./components/exception-handler";
|
||||
import GlobalSafeAreaProvider from "./components/globalsafearea";
|
||||
import { Toast } from "./components/toast";
|
||||
import { useAppEvents } from "./hooks/use-app-events";
|
||||
import { ApplicationHolder } from "./navigation";
|
||||
import { NotePreviewConfigure } from "./screens/note-preview-configure";
|
||||
import { RootNavigation } from "./navigation/navigation-stack";
|
||||
import { themeTrpcClient } from "./screens/settings/theme-selector";
|
||||
import Notifications from "./services/notifications";
|
||||
import SettingsService from "./services/settings";
|
||||
import { TipManager } from "./services/tip-manager";
|
||||
import { useThemeStore } from "./stores/use-theme-store";
|
||||
import { changeSystemBarColors, useThemeStore } from "./stores/use-theme-store";
|
||||
import { useUserStore } from "./stores/use-user-store";
|
||||
import RNBootSplash from "react-native-bootsplash";
|
||||
import AppLocked from "./components/app-lock";
|
||||
|
||||
I18nManager.allowRTL(false);
|
||||
I18nManager.forceRTL(false);
|
||||
@@ -47,11 +52,13 @@ if (appLockEnabled || appLockMode !== "none") {
|
||||
useUserStore.getState().lockApp(true);
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
RNBootSplash.hide();
|
||||
const App = (props: { configureMode: "note-preview" }) => {
|
||||
useAppEvents();
|
||||
//@ts-ignore
|
||||
globalThis["IS_MAIN_APP_RUNNING"] = true;
|
||||
useEffect(() => {
|
||||
changeSystemBarColors();
|
||||
SettingsService.onFirstLaunch();
|
||||
setTimeout(async () => {
|
||||
await Notifications.get();
|
||||
@@ -61,39 +68,29 @@ const App = () => {
|
||||
TipManager.init();
|
||||
}, 100);
|
||||
}, []);
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
zIndex: -1
|
||||
}}
|
||||
pointerEvents="none"
|
||||
>
|
||||
<SafeAreaProvider>
|
||||
<GlobalSafeAreaProvider />
|
||||
</SafeAreaProvider>
|
||||
</View>
|
||||
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
<StatusBar translucent={true} backgroundColor="transparent" />
|
||||
<GestureHandlerRootView
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<ApplicationHolder />
|
||||
<GlobalSafeAreaProvider />
|
||||
{props.configureMode === "note-preview" ? (
|
||||
<NotePreviewConfigure />
|
||||
) : (
|
||||
<RootNavigation />
|
||||
)}
|
||||
<ScopedThemeProvider value="dialog">
|
||||
<Toast />
|
||||
</ScopedThemeProvider>
|
||||
<DialogProvider />
|
||||
<AppLocked />
|
||||
</GestureHandlerRootView>
|
||||
<AppLockedOverlay />
|
||||
</View>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -103,8 +100,8 @@ let currTheme =
|
||||
: SettingsService.getProperty("lighTheme");
|
||||
useThemeEngineStore.getState().setTheme(currTheme);
|
||||
|
||||
export const withTheme = (Element: () => JSX.Element) => {
|
||||
return function AppWithThemeProvider() {
|
||||
export const withTheme = (Element: (props: any) => JSX.Element) => {
|
||||
return function AppWithThemeProvider(props: any) {
|
||||
const [colorScheme, darkTheme, lightTheme] = useThemeStore((state) => [
|
||||
state.colorScheme,
|
||||
state.darkTheme,
|
||||
@@ -123,28 +120,40 @@ export const withTheme = (Element: () => JSX.Element) => {
|
||||
})
|
||||
.then((theme) => {
|
||||
if (theme) {
|
||||
console.log(theme.version, "theme updated");
|
||||
theme.colorScheme === "dark"
|
||||
? useThemeStore.getState().setDarkTheme(theme)
|
||||
: useThemeStore.getState().setLightTheme(theme);
|
||||
}
|
||||
})
|
||||
.catch(console.log);
|
||||
.catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
const listener = Appearance.addChangeListener(({ colorScheme }) => {
|
||||
if (colorScheme && SettingsService.getProperty("useSystemTheme")) {
|
||||
useThemeStore.getState().setColorScheme(colorScheme);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
listener.remove();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const nextTheme = colorScheme === "dark" ? darkTheme : lightTheme;
|
||||
if (JSON.stringify(nextTheme) !== JSON.stringify(currTheme)) {
|
||||
useThemeEngineStore
|
||||
.getState()
|
||||
.setTheme(colorScheme === "dark" ? darkTheme : lightTheme);
|
||||
currTheme = nextTheme;
|
||||
}
|
||||
useEffect(() => {
|
||||
const nextTheme = colorScheme === "dark" ? darkTheme : lightTheme;
|
||||
if (JSON.stringify(nextTheme) !== JSON.stringify(currTheme)) {
|
||||
useThemeEngineStore
|
||||
.getState()
|
||||
.setTheme(colorScheme === "dark" ? darkTheme : lightTheme);
|
||||
currTheme = nextTheme;
|
||||
}
|
||||
}, [colorScheme, darkTheme, lightTheme]);
|
||||
|
||||
return (
|
||||
<I18nProvider i18n={i18n}>
|
||||
<Element />
|
||||
<Element {...props} />
|
||||
</I18nProvider>
|
||||
);
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -126,7 +126,6 @@ export async function setAppLockVerificationCipher(appLockPassword: string) {
|
||||
DatabaseLogger.info("setAppLockVerificationCipher");
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e);
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,79 +158,77 @@ export function clearDatabaseKey() {
|
||||
|
||||
export async function getDatabaseKey(appLockPassword?: string) {
|
||||
if (DB_KEY) return DB_KEY;
|
||||
try {
|
||||
if (appLockPassword) {
|
||||
const databaseKeyCipher: Cipher =
|
||||
CipherStorage.getMap("databaseKeyCipher");
|
||||
const databaseKey = await decrypt(
|
||||
{
|
||||
password: appLockPassword
|
||||
},
|
||||
databaseKeyCipher
|
||||
);
|
||||
DatabaseLogger.info("Getting database key from cipher");
|
||||
DB_KEY = databaseKey;
|
||||
}
|
||||
if (appLockPassword) {
|
||||
const databaseKeyCipher: Cipher = CipherStorage.getMap("databaseKeyCipher");
|
||||
const databaseKey = await decrypt(
|
||||
{
|
||||
password: appLockPassword
|
||||
},
|
||||
databaseKeyCipher
|
||||
);
|
||||
DatabaseLogger.info("Getting database key from cipher");
|
||||
DB_KEY = databaseKey;
|
||||
}
|
||||
|
||||
if (!DB_KEY) {
|
||||
const hasKey = await Keychain.hasInternetCredentials(
|
||||
if (!DB_KEY) {
|
||||
const hasKey = await Keychain.hasInternetCredentials(KEYCHAIN_SERVER_DBKEY);
|
||||
if (hasKey) {
|
||||
const credentials = await Keychain.getInternetCredentials(
|
||||
KEYCHAIN_SERVER_DBKEY
|
||||
);
|
||||
if (hasKey) {
|
||||
const credentials = await Keychain.getInternetCredentials(
|
||||
KEYCHAIN_SERVER_DBKEY
|
||||
);
|
||||
|
||||
DatabaseLogger.info("Getting database key from Keychain");
|
||||
DB_KEY = (credentials as Keychain.UserCredentials).password;
|
||||
}
|
||||
DatabaseLogger.info("Getting database key from Keychain");
|
||||
DB_KEY = (credentials as Keychain.UserCredentials).password;
|
||||
}
|
||||
|
||||
if (!DB_KEY) {
|
||||
DatabaseLogger.info("Generating new database key");
|
||||
const password = generatePassword();
|
||||
const derivedDatabaseKey = await Sodium.deriveKey(
|
||||
password,
|
||||
NOTESNOOK_DB_KEY_SALT
|
||||
);
|
||||
|
||||
DB_KEY = derivedDatabaseKey.key as string;
|
||||
|
||||
await Keychain.setInternetCredentials(
|
||||
KEYCHAIN_SERVER_DBKEY,
|
||||
"notesnook",
|
||||
DB_KEY,
|
||||
KEYSTORE_CONFIG
|
||||
);
|
||||
}
|
||||
|
||||
if (await Keychain.hasInternetCredentials("notesnook")) {
|
||||
const userKeyCredentials = await Keychain.getInternetCredentials(
|
||||
"notesnook"
|
||||
);
|
||||
|
||||
if (userKeyCredentials) {
|
||||
const userKeyCipher: Cipher = (await encrypt(
|
||||
{
|
||||
key: DB_KEY,
|
||||
salt: NOTESNOOK_DB_KEY_SALT
|
||||
},
|
||||
userKeyCredentials.password
|
||||
)) as Cipher;
|
||||
// Store encrypted user key in MMKV
|
||||
MMKV.setMap(USER_KEY_CIPHER, userKeyCipher);
|
||||
await Keychain.resetInternetCredentials("notesnook");
|
||||
}
|
||||
DatabaseLogger.info("Migrated user credentials to cipher storage");
|
||||
}
|
||||
|
||||
return DB_KEY;
|
||||
} catch (e) {
|
||||
ToastManager.error(e as Error, "Error getting database key");
|
||||
console.log(e, "error");
|
||||
DatabaseLogger.error(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!DB_KEY) {
|
||||
DatabaseLogger.info("Generating new database key");
|
||||
const password = generatePassword();
|
||||
const derivedDatabaseKey = await Sodium.deriveKey(
|
||||
password,
|
||||
NOTESNOOK_DB_KEY_SALT
|
||||
);
|
||||
|
||||
DB_KEY = derivedDatabaseKey.key as string;
|
||||
|
||||
await Keychain.setInternetCredentials(
|
||||
KEYCHAIN_SERVER_DBKEY,
|
||||
"notesnook",
|
||||
DB_KEY,
|
||||
KEYSTORE_CONFIG
|
||||
);
|
||||
}
|
||||
|
||||
if (await Keychain.hasInternetCredentials("notesnook")) {
|
||||
const userKeyCredentials = await Keychain.getInternetCredentials(
|
||||
"notesnook"
|
||||
);
|
||||
|
||||
if (userKeyCredentials) {
|
||||
const userKeyCipher: Cipher = (await encrypt(
|
||||
{
|
||||
key: DB_KEY,
|
||||
salt: NOTESNOOK_DB_KEY_SALT
|
||||
},
|
||||
userKeyCredentials.password
|
||||
)) as Cipher;
|
||||
// Store encrypted user key in MMKV
|
||||
MMKV.setMap(USER_KEY_CIPHER, userKeyCipher);
|
||||
await Keychain.resetInternetCredentials("notesnook");
|
||||
}
|
||||
DatabaseLogger.info("Migrated user credentials to cipher storage");
|
||||
}
|
||||
|
||||
if (!DB_KEY) {
|
||||
throw new Error(
|
||||
`Failed to get database key, ${await Keychain.hasInternetCredentials(
|
||||
KEYCHAIN_SERVER_DBKEY
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
return DB_KEY;
|
||||
}
|
||||
|
||||
export async function deriveCryptoKeyFallback(data: SerializedKey) {
|
||||
@@ -312,7 +309,6 @@ export async function getCryptoKey() {
|
||||
|
||||
return key;
|
||||
} catch (e) {
|
||||
console.log("getCryptoKey", e);
|
||||
DatabaseLogger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@ export async function setupDatabase(password?: string) {
|
||||
const key = await getDatabaseKey(password);
|
||||
if (!key) throw new Error(strings.databaseSetupFailed());
|
||||
|
||||
console.log("Opening database with key:", !!key);
|
||||
|
||||
database.host({
|
||||
API_HOST: "https://api.notesnook.com",
|
||||
AUTH_HOST: "https://auth.streetwriters.co",
|
||||
|
||||
@@ -27,13 +27,9 @@ import RNFetchBlob from "react-native-blob-util";
|
||||
*/
|
||||
export async function compressToBase64(path, type) {
|
||||
const { width: screenWidth, scale } = Dimensions.get("window");
|
||||
console.log("COMPRESSING TO BASE64...");
|
||||
|
||||
return new Promise((resolve) => {
|
||||
console.log(path, "image path...");
|
||||
|
||||
Image.getSize(path, async (width) => {
|
||||
console.log("image width", width);
|
||||
|
||||
const response = await ImageResizer.createResizedImage(
|
||||
path,
|
||||
screenWidth * scale,
|
||||
@@ -55,10 +51,12 @@ export async function compressToBase64(path, type) {
|
||||
: response.uri,
|
||||
"base64"
|
||||
);
|
||||
RNFetchBlob.fs.unlink(path.replace("file://", "")).catch(console.log);
|
||||
RNFetchBlob.fs
|
||||
.unlink(response.uri.replace("file://", ""))
|
||||
.catch(console.log);
|
||||
RNFetchBlob.fs.unlink(path.replace("file://", "")).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
RNFetchBlob.fs.unlink(response.uri.replace("file://", "")).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
|
||||
resolve(base64);
|
||||
});
|
||||
@@ -80,6 +78,8 @@ export async function compressToFile(path, type) {
|
||||
onlyScaleDown: true
|
||||
}
|
||||
);
|
||||
RNFetchBlob.fs.unlink(path.replace("file://", "")).catch(console.log);
|
||||
RNFetchBlob.fs.unlink(path.replace("file://", "")).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
return response.uri;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,9 @@ export async function downloadAttachments(attachmentIds: string[]) {
|
||||
|
||||
const isCancelled = () => {
|
||||
if (useAttachmentStore.getState().downloading?.[groupId]?.canceled) {
|
||||
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
|
||||
RNFetchBlob.fs.unlink(zipSourceFolder).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
useAttachmentStore.getState().setDownloading({
|
||||
groupId,
|
||||
current: 0,
|
||||
@@ -188,9 +190,13 @@ export async function downloadAttachments(attachmentIds: string[]) {
|
||||
ToastManager.error(e as Error, "Error zipping attachments");
|
||||
}
|
||||
// Remove source & zip file from cache.
|
||||
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
|
||||
RNFetchBlob.fs.unlink(zipSourceFolder).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
if (Platform.OS === "android") {
|
||||
RNFetchBlob.fs.unlink(zipOutputFile).catch(console.log);
|
||||
RNFetchBlob.fs.unlink(zipOutputFile).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,10 +327,14 @@ export default async function downloadAttachment(
|
||||
if (attachment.dateUploaded) {
|
||||
RNFetchBlob.fs
|
||||
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}`)
|
||||
.catch(console.log);
|
||||
.catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
RNFetchBlob.fs
|
||||
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}_dcache`)
|
||||
.catch(console.log);
|
||||
.catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}
|
||||
|
||||
useAttachmentStore.getState().setDownloading({
|
||||
|
||||
@@ -123,7 +123,9 @@ export async function downloadFile(
|
||||
cancelToken.cancel = async (reason) => {
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
request.cancel();
|
||||
RNFetchBlob.fs.unlink(tempFilePath).catch(console.log);
|
||||
RNFetchBlob.fs.unlink(tempFilePath).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
DatabaseLogger.log(`Download cancelled: ${reason} ${filename}`);
|
||||
};
|
||||
|
||||
@@ -142,10 +144,14 @@ export async function downloadFile(
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
|
||||
if (await exists(originalFilePath)) {
|
||||
await RNFetchBlob.fs.unlink(originalFilePath).catch(console.log);
|
||||
await RNFetchBlob.fs.unlink(originalFilePath).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}
|
||||
|
||||
await RNFetchBlob.fs.mv(tempFilePath, originalFilePath);
|
||||
await RNFetchBlob.fs.mv(tempFilePath, originalFilePath).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
|
||||
if (!(await exists(filename))) {
|
||||
throw new Error("File size mismatch");
|
||||
@@ -169,8 +175,12 @@ export async function downloadFile(
|
||||
}
|
||||
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
RNFetchBlob.fs.unlink(tempFilePath).catch(console.log);
|
||||
RNFetchBlob.fs.unlink(originalFilePath).catch(console.log);
|
||||
RNFetchBlob.fs.unlink(tempFilePath).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
RNFetchBlob.fs.unlink(originalFilePath).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
DatabaseLogger.error(e, "Download failed: ", {
|
||||
url
|
||||
});
|
||||
@@ -188,7 +198,7 @@ export async function checkAttachment(hash: string) {
|
||||
|
||||
try {
|
||||
const size = await getUploadedFileSize(hash);
|
||||
console.log("File Size", size);
|
||||
|
||||
if (size === -1) return { success: true };
|
||||
|
||||
if (size === 0)
|
||||
|
||||
@@ -60,7 +60,9 @@ export async function readEncrypted<TOutputFormat extends DataFormat>(
|
||||
|
||||
return output as Output<TOutputFormat>;
|
||||
} catch (e) {
|
||||
RNFetchBlob.fs.unlink(path).catch(console.log);
|
||||
RNFetchBlob.fs.unlink(path).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
DatabaseLogger.error(e);
|
||||
}
|
||||
}
|
||||
@@ -90,8 +92,9 @@ export async function writeEncryptedBase64(
|
||||
type: "url"
|
||||
});
|
||||
|
||||
RNFetchBlob.fs.unlink(filepath).catch(console.log);
|
||||
console.log("encrypted file output: ", output);
|
||||
RNFetchBlob.fs.unlink(filepath).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
|
||||
return {
|
||||
...output,
|
||||
@@ -106,7 +109,9 @@ export async function deleteFile(
|
||||
await createCacheDir();
|
||||
const localFilePath = cacheDir + `/${filename}`;
|
||||
if (!requestOptions) {
|
||||
RNFetchBlob.fs.unlink(localFilePath).catch(console.log);
|
||||
RNFetchBlob.fs.unlink(localFilePath).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -117,7 +122,9 @@ export async function deleteFile(
|
||||
const status = response.info().status;
|
||||
const ok = status >= 200 && status < 300;
|
||||
if (ok) {
|
||||
RNFetchBlob.fs.unlink(localFilePath).catch(console.log);
|
||||
RNFetchBlob.fs.unlink(localFilePath).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}
|
||||
return ok;
|
||||
} catch (e) {
|
||||
@@ -134,10 +141,14 @@ export async function clearFileStorage() {
|
||||
const oldCache = await RNFetchBlob.fs.ls(cacheDirOld);
|
||||
|
||||
for (const file of files) {
|
||||
await RNFetchBlob.fs.unlink(cacheDir + `/${file}`).catch(console.log);
|
||||
await RNFetchBlob.fs.unlink(cacheDir + `/${file}`).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}
|
||||
for (const file of oldCache) {
|
||||
await RNFetchBlob.fs.unlink(cacheDirOld + `/${file}`).catch(console.log);
|
||||
await RNFetchBlob.fs.unlink(cacheDirOld + `/${file}`).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e, "clearFileStorage");
|
||||
@@ -161,20 +172,19 @@ export async function migrateFilesFromCache() {
|
||||
const migratedFilesPath = cacheDir + "/.migrated_1";
|
||||
const migrated = await RNFetchBlob.fs.exists(migratedFilesPath);
|
||||
if (migrated) {
|
||||
console.log("Files migrated already");
|
||||
return;
|
||||
}
|
||||
|
||||
const files = await RNFetchBlob.fs.ls(cacheDir);
|
||||
console.log("Files to migrate:", files.join(","));
|
||||
|
||||
const oldCache = await RNFetchBlob.fs.ls(cacheDirOld);
|
||||
for (const file of oldCache) {
|
||||
if (file.startsWith("org.") || file.startsWith("com.")) continue;
|
||||
RNFetchBlob.fs
|
||||
.mv(cacheDirOld + `/${file}`, cacheDir + `/${file}`)
|
||||
.catch(console.log);
|
||||
console.log("Moved", file);
|
||||
.catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}
|
||||
await RNFetchBlob.fs.createFile(migratedFilesPath, "1", "utf8");
|
||||
} catch (e) {
|
||||
@@ -183,13 +193,17 @@ export async function migrateFilesFromCache() {
|
||||
}
|
||||
|
||||
export async function clearCache() {
|
||||
await RNFetchBlob.fs.unlink(cacheDir).catch(console.log);
|
||||
await RNFetchBlob.fs.unlink(cacheDir).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
await createCacheDir();
|
||||
eSendEvent("cache-cleared");
|
||||
}
|
||||
|
||||
export async function deleteCacheFileByPath(path: string) {
|
||||
await RNFetchBlob.fs.unlink(path).catch(console.log);
|
||||
await RNFetchBlob.fs.unlink(path).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteCacheFileByName(name: string) {
|
||||
@@ -198,19 +212,45 @@ export async function deleteCacheFileByName(name: string) {
|
||||
? await (RNFetchBlob.fs as any).pathForAppGroup(IOS_APPGROUPID)
|
||||
: null;
|
||||
const appGroupPath = `${iosAppGroup}/${name}`;
|
||||
await RNFetchBlob.fs.unlink(appGroupPath).catch(console.log);
|
||||
await RNFetchBlob.fs.unlink(`${cacheDir}/${name}`).catch(console.log);
|
||||
await RNFetchBlob.fs.unlink(appGroupPath).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
await RNFetchBlob.fs.unlink(`${cacheDir}/${name}`).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteDCacheFiles() {
|
||||
const files = await RNFetchBlob.fs.ls(cacheDir);
|
||||
for (const file of files) {
|
||||
if (file.includes("_dcache")) {
|
||||
await RNFetchBlob.fs.unlink(file).catch(console.log);
|
||||
if (file.includes("_dcache") || file.startsWith("NN_")) {
|
||||
await RNFetchBlob.fs.unlink(file).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCachePathForFile(filename: string) {
|
||||
const path = `${cacheDir}/${filename}`;
|
||||
|
||||
const iosAppGroup =
|
||||
Platform.OS === "ios"
|
||||
? await (RNFetchBlob.fs as any).pathForAppGroup(IOS_APPGROUPID)
|
||||
: null;
|
||||
const appGroupPath = `${iosAppGroup}/${filename}`;
|
||||
|
||||
const exists = await RNFetchBlob.fs.exists(path);
|
||||
|
||||
// Check if file is present in app group path.
|
||||
let existsInAppGroup = false;
|
||||
if (!exists && Platform.OS === "ios") {
|
||||
existsInAppGroup = await RNFetchBlob.fs.exists(appGroupPath);
|
||||
}
|
||||
|
||||
return existsInAppGroup ? appGroupPath : path;
|
||||
}
|
||||
|
||||
export async function exists(filename: string) {
|
||||
const path = `${cacheDir}/${filename}`;
|
||||
|
||||
@@ -245,7 +285,9 @@ export async function exists(filename: string) {
|
||||
);
|
||||
RNFetchBlob.fs
|
||||
.unlink(existsInAppGroup ? appGroupPath : path)
|
||||
.catch(console.log);
|
||||
.catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -281,7 +323,7 @@ export async function getCacheSize() {
|
||||
await createCacheDir();
|
||||
const stat = await RNFetchBlob.fs.lstat(`file://` + cacheDir);
|
||||
let total = 0;
|
||||
console.log("Total files", stat.length);
|
||||
|
||||
stat.forEach((file) => {
|
||||
total += parseInt(file.size as unknown as string);
|
||||
});
|
||||
|
||||
@@ -115,7 +115,7 @@ export const FileSizeResult = {
|
||||
Error: -1
|
||||
};
|
||||
|
||||
export async function getUploadedFileSize(hash: string) {
|
||||
export async function getUploadedFileSize(hash: string, retry = 0) {
|
||||
try {
|
||||
const url = `${hosts.API_HOST}/s3?name=${hash}`;
|
||||
const token = await db.tokenManager.getAccessToken();
|
||||
@@ -123,8 +123,24 @@ export async function getUploadedFileSize(hash: string) {
|
||||
method: "HEAD",
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
|
||||
if (
|
||||
!attachmentInfo.ok ||
|
||||
attachmentInfo.headers?.get("content-length") === null
|
||||
) {
|
||||
if (retry < 3) {
|
||||
DatabaseLogger.log(`Retrying file size check: ${hash}, ${retry}`);
|
||||
return getUploadedFileSize(hash, retry + 1);
|
||||
}
|
||||
throw new Error(
|
||||
`File size check failed: ${hash}, ${
|
||||
attachmentInfo.status
|
||||
}, ${attachmentInfo.headers?.get("content-length")}`
|
||||
);
|
||||
}
|
||||
|
||||
const contentLength = parseInt(
|
||||
attachmentInfo.headers?.get("content-length") || "0"
|
||||
attachmentInfo.headers?.get("content-length") as string
|
||||
);
|
||||
return isNaN(contentLength) ? FileSizeResult.Empty : contentLength;
|
||||
} catch (e) {
|
||||
|
||||
@@ -17,9 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
function info(context: string, ...logs: unknown[]) {
|
||||
console.log(`${new Date().toLocaleDateString()}::info::${context}:`, ...logs);
|
||||
}
|
||||
function info(context: string, ...logs: unknown[]) {}
|
||||
|
||||
function error(context: string, ...logs: unknown[]) {
|
||||
console.log(
|
||||
|
||||
@@ -24,6 +24,7 @@ import { useMessageStore } from "../../stores/use-message-store";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { allowedOnPlatform, renderItem } from "./functions";
|
||||
import { getContainerBorder } from "../../utils/colors";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const Announcement = ({ color }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -36,9 +37,8 @@ export const Announcement = ({ color }) => {
|
||||
style={{
|
||||
backgroundColor: colors.primary.background,
|
||||
width: "100%",
|
||||
paddingHorizontal: 12,
|
||||
paddingTop: 12,
|
||||
paddingBottom: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -54,7 +54,7 @@ export const Announcement = ({ color }) => {
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
marginTop: 12
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
{announcement?.body
|
||||
|
||||
@@ -20,12 +20,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React from "react";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { getStyle } from "./functions";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const Body = ({ text, style = {} }) => {
|
||||
return (
|
||||
<Paragraph
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
...getStyle(style)
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -23,12 +23,13 @@ import { Linking, View } from "react-native";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { eSendEvent, presentSheet } from "../../services/event-manager";
|
||||
import { eCloseAnnouncementDialog } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { PricingPlans } from "../premium/pricing-plans";
|
||||
import SheetProvider from "../sheet-provider";
|
||||
import { Button } from "../ui/button";
|
||||
import { allowedOnPlatform, getStyle } from "./functions";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const Cta = ({ actions, style = {}, color, inline }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -41,7 +42,9 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
|
||||
await sleep(500);
|
||||
}
|
||||
if (item.type === "link") {
|
||||
Linking.openURL(item.data).catch(console.log);
|
||||
Linking.openURL(item.data).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
} else if (item.type === "promo") {
|
||||
presentSheet({
|
||||
component: (
|
||||
@@ -59,7 +62,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
...getStyle(style),
|
||||
flexDirection: inline ? "row" : "column"
|
||||
}}
|
||||
@@ -73,7 +76,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
|
||||
<Button
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
fontSize={SIZE.sm}
|
||||
fontSize={AppFontSize.sm}
|
||||
type="transparent"
|
||||
textStyle={{
|
||||
textDecorationLine: "underline"
|
||||
@@ -81,7 +84,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
|
||||
onPress={() => onPress(item)}
|
||||
bold
|
||||
style={{
|
||||
height: 30,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
alignSelf: "flex-start",
|
||||
paddingHorizontal: 0
|
||||
}}
|
||||
@@ -93,7 +96,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
|
||||
<Button
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
fontSize={SIZE.sm}
|
||||
fontSize={AppFontSize.sm}
|
||||
type="plain"
|
||||
onPress={() => onPress(item)}
|
||||
width={null}
|
||||
@@ -116,7 +119,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
|
||||
<Button
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
fontSize={SIZE.md}
|
||||
fontSize={AppFontSize.md}
|
||||
buttonType={{
|
||||
color: color ? color : colors.primary.accent,
|
||||
text: color
|
||||
@@ -128,7 +131,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
|
||||
onPress={() => onPress(item)}
|
||||
width={250}
|
||||
style={{
|
||||
marginBottom: 5,
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL,
|
||||
borderRadius: 100
|
||||
}}
|
||||
/>
|
||||
@@ -139,14 +142,14 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
|
||||
<Button
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
fontSize={SIZE.xs}
|
||||
fontSize={AppFontSize.xs}
|
||||
type="plain"
|
||||
onPress={() => onPress(item)}
|
||||
width={null}
|
||||
height={30}
|
||||
style={{
|
||||
minWidth: "50%",
|
||||
marginTop: 5
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
}}
|
||||
textStyle={{
|
||||
textDecorationLine: "underline"
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { getStyle } from "./functions";
|
||||
|
||||
@@ -30,7 +30,7 @@ export const Description = ({ text, style = {}, inline }) => {
|
||||
...getStyle(style),
|
||||
textAlign: inline ? "left" : style?.textAlign
|
||||
}}
|
||||
size={inline ? SIZE.sm : SIZE.md}
|
||||
size={inline ? AppFontSize.sm : AppFontSize.md}
|
||||
>
|
||||
{text}
|
||||
</Paragraph>
|
||||
|
||||
@@ -28,6 +28,7 @@ import { List } from "./list";
|
||||
import { Photo } from "./photo";
|
||||
import { SubHeading } from "./subheading";
|
||||
import { Title } from "./title";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export function allowedOnPlatform(platforms) {
|
||||
if (!platforms) return true;
|
||||
@@ -53,7 +54,7 @@ const Features = () => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
alignItems: "center",
|
||||
width: "100%"
|
||||
}}
|
||||
|
||||
@@ -22,12 +22,13 @@ import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { getStyle } from "./functions";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const List = ({ items, listType, style = {} }) => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingLeft: listType === "ordered" ? 25 : 25,
|
||||
...getStyle(style)
|
||||
}}
|
||||
@@ -36,7 +37,7 @@ export const List = ({ items, listType, style = {} }) => {
|
||||
<View
|
||||
key={item.text}
|
||||
style={{
|
||||
paddingVertical: 6,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -18,17 +18,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import { getStyle } from "./functions";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const SubHeading = ({ text, style = {} }) => {
|
||||
return (
|
||||
<Heading
|
||||
size={SIZE.md + 2}
|
||||
size={AppFontSize.md + 2}
|
||||
style={{
|
||||
marginHorizontal: 12,
|
||||
marginTop: 12,
|
||||
marginHorizontal: DefaultAppStyles.GAP,
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
...getStyle(style)
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -20,10 +20,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { useMessageStore } from "../../stores/use-message-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import { getStyle } from "./functions";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const Title = ({ text, style = {}, inline }) => {
|
||||
const announcements = useMessageStore((state) => state.announcements);
|
||||
@@ -36,19 +37,19 @@ export const Title = ({ text, style = {}, inline }) => {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: inline ? 5 : 0
|
||||
marginBottom: inline ? DefaultAppStyles.GAP_VERTICAL_SMALL : 0
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
marginHorizontal: 12,
|
||||
marginTop: 12,
|
||||
marginHorizontal: DefaultAppStyles.GAP,
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
...getStyle(style),
|
||||
textAlign: inline ? "left" : style?.textAlign,
|
||||
flexShrink: 1
|
||||
}}
|
||||
numberOfLines={1}
|
||||
size={inline ? SIZE.md : SIZE.xl}
|
||||
size={inline ? AppFontSize.md : AppFontSize.xl}
|
||||
>
|
||||
{inline ? text?.toUpperCase() : text}
|
||||
</Heading>
|
||||
@@ -67,7 +68,7 @@ export const Title = ({ text, style = {}, inline }) => {
|
||||
right: 0
|
||||
}}
|
||||
iconSize={24}
|
||||
fontSize={SIZE.xs}
|
||||
fontSize={AppFontSize.xs}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingVertical: 0,
|
||||
@@ -80,9 +81,9 @@ export const Title = ({ text, style = {}, inline }) => {
|
||||
) : (
|
||||
<Heading
|
||||
style={{
|
||||
marginHorizontal: 12,
|
||||
marginHorizontal: DefaultAppStyles.GAP,
|
||||
...getStyle(style),
|
||||
marginTop: style?.marginTop || 12
|
||||
marginTop: style?.marginTop || DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
|
||||
@@ -44,7 +44,6 @@ import SettingsService from "../../services/settings";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { NotesnookModule } from "../../utils/notesnook-module";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { Toast } from "../toast";
|
||||
import { Button } from "../ui/button";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
@@ -54,6 +53,9 @@ import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { editorController } from "../../screens/editor/tiptap/utils";
|
||||
import { useTabStore } from "../../screens/editor/tiptap/use-tab-store";
|
||||
|
||||
const getUser = () => {
|
||||
const user = MMKV.getString("user");
|
||||
@@ -82,7 +84,7 @@ const verifyUserPassword = async (password: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const AppLockedOverlay = () => {
|
||||
const AppLocked = () => {
|
||||
const initialLaunchBiometricRequest = useRef(true);
|
||||
const { colors } = useThemeColors();
|
||||
const user = getUser();
|
||||
@@ -109,17 +111,25 @@ const AppLockedOverlay = () => {
|
||||
);
|
||||
|
||||
const onUnlockAppRequested = useCallback(async () => {
|
||||
editorController.current?.commands.blur(useTabStore.getState().currentTab);
|
||||
editorController.current?.commands.blurPassInput();
|
||||
|
||||
if (
|
||||
!biometricsAuthEnabled ||
|
||||
!(await BiometricService.isBiometryAvailable())
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
biometricUnlockAwaitingUserInput.current = true;
|
||||
|
||||
if (Platform.OS === "android") {
|
||||
const activityName = await NotesnookModule.getActivityName();
|
||||
if (activityName !== "MainActivity") return;
|
||||
if (
|
||||
activityName !== "MainActivity" &&
|
||||
activityName !== "NotePreviewConfigureActivity"
|
||||
)
|
||||
return;
|
||||
}
|
||||
useSettingStore.getState().setRequestBiometrics(true);
|
||||
|
||||
@@ -177,10 +187,15 @@ const AppLockedOverlay = () => {
|
||||
(prevState === "background" || initialLaunchBiometricRequest.current)
|
||||
) {
|
||||
if (SettingsService.shouldLockAppOnEnterForeground()) {
|
||||
if (useSettingStore.getState().appDidEnterBackgroundForAction) {
|
||||
useSettingStore.getState().setAppDidEnterBackgroundForAction(false);
|
||||
return;
|
||||
}
|
||||
|
||||
DatabaseLogger.info("Locking app on entering foreground");
|
||||
|
||||
useUserStore.getState().lockApp(true);
|
||||
}
|
||||
|
||||
if (
|
||||
!(
|
||||
biometricUnlockAwaitingUserInput.current ||
|
||||
@@ -279,6 +294,16 @@ const AppLockedOverlay = () => {
|
||||
keyboardType={
|
||||
appLockHasPasswordSecurity ? keyboardType : "default"
|
||||
}
|
||||
onLayout={async () => {
|
||||
if (
|
||||
!biometricsAuthEnabled ||
|
||||
!(await BiometricService.isBiometryAvailable())
|
||||
) {
|
||||
setTimeout(() => {
|
||||
passwordInputRef.current?.focus();
|
||||
}, 32);
|
||||
}
|
||||
}}
|
||||
placeholder={
|
||||
appLockHasPasswordSecurity
|
||||
? keyboardType === "numeric"
|
||||
@@ -311,7 +336,7 @@ const AppLockedOverlay = () => {
|
||||
borderRadius: 150,
|
||||
marginBottom: 10
|
||||
}}
|
||||
fontSize={SIZE.md}
|
||||
fontSize={AppFontSize.md}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
@@ -332,4 +357,4 @@ const AppLockedOverlay = () => {
|
||||
) : null;
|
||||
};
|
||||
|
||||
export default AppLockedOverlay;
|
||||
export default AppLocked;
|
||||
@@ -45,7 +45,7 @@ import {
|
||||
eDBItemUpdate,
|
||||
eOnLoadNote
|
||||
} from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { Dialog } from "../dialog";
|
||||
import { presentDialog } from "../dialog/functions";
|
||||
@@ -58,6 +58,7 @@ import { Pressable } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
const Actions = ({
|
||||
attachment,
|
||||
@@ -182,18 +183,17 @@ const Actions = ({
|
||||
relations
|
||||
.map((relation) => relation.fromId)
|
||||
.forEach(async (id) => {
|
||||
const tab = useTabStore.getState().getTabForNote(id);
|
||||
if (tab !== undefined) {
|
||||
const isFocused = useTabStore.getState().currentTab === tab;
|
||||
useTabStore.getState().forEachNoteTab(id, async (tab) => {
|
||||
const isFocused = useTabStore.getState().currentTab === tab.id;
|
||||
if (isFocused) {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: await db.notes.note(id),
|
||||
forced: true
|
||||
});
|
||||
} else {
|
||||
editorController.current.commands.setLoading(true, tab);
|
||||
editorController.current.commands.setLoading(true, tab.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
close?.();
|
||||
},
|
||||
@@ -227,14 +227,15 @@ const Actions = ({
|
||||
style={{
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
marginBottom: notes && notes.length > 0 ? 0 : 12
|
||||
marginBottom:
|
||||
notes && notes.length > 0 ? 0 : DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
>
|
||||
{filename}
|
||||
</Heading>
|
||||
@@ -242,21 +243,21 @@ const Actions = ({
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
marginBottom: 10,
|
||||
paddingHorizontal: 12,
|
||||
marginTop: 6,
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
gap: 10
|
||||
}}
|
||||
>
|
||||
<Paragraph size={SIZE.xs} color={colors.secondary.paragraph}>
|
||||
<Paragraph size={AppFontSize.xs} color={colors.secondary.paragraph}>
|
||||
{attachment.mimeType}
|
||||
</Paragraph>
|
||||
<Paragraph size={SIZE.xs} color={colors.secondary.paragraph}>
|
||||
<Paragraph size={AppFontSize.xs} color={colors.secondary.paragraph}>
|
||||
{formatBytes(attachment.size)}
|
||||
</Paragraph>
|
||||
|
||||
{notes.length ? (
|
||||
<Paragraph size={SIZE.xs} color={colors.secondary.paragraph}>
|
||||
<Paragraph size={AppFontSize.xs} color={colors.secondary.paragraph}>
|
||||
{strings.notes(notes.length)}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
@@ -269,7 +270,7 @@ const Actions = ({
|
||||
context: "local"
|
||||
});
|
||||
}}
|
||||
size={SIZE.xs}
|
||||
size={AppFontSize.xs}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
{attachment.hash}
|
||||
@@ -284,16 +285,16 @@ const Actions = ({
|
||||
style={{
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
marginBottom: 12,
|
||||
paddingVertical: 12
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<Heading
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
size={SIZE.sm}
|
||||
size={AppFontSize.sm}
|
||||
>
|
||||
{strings.listOf()} {strings.dataTypesPlural.note()}:
|
||||
</Heading>
|
||||
@@ -308,14 +309,14 @@ const Actions = ({
|
||||
openNote(item, (item as any).type === "trash");
|
||||
}}
|
||||
style={{
|
||||
paddingVertical: 12,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
alignItems: "flex-start",
|
||||
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
key={item.id}
|
||||
>
|
||||
<Paragraph size={SIZE.xs}>{item.title}</Paragraph>
|
||||
<Paragraph size={AppFontSize.xs}>{item.title}</Paragraph>
|
||||
</Pressable>
|
||||
))}
|
||||
</>
|
||||
@@ -336,7 +337,7 @@ const Actions = ({
|
||||
icon={item.icon}
|
||||
loading={loading?.name === item.name}
|
||||
type="plain"
|
||||
fontSize={SIZE.sm}
|
||||
fontSize={AppFontSize.sm}
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
justifyContent: "flex-start",
|
||||
@@ -348,7 +349,7 @@ const Actions = ({
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
{failed ? (
|
||||
|
||||
@@ -25,13 +25,14 @@ import { TouchableOpacity, View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
|
||||
import { useDBItem } from "../../hooks/use-db-item";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { defaultBorderRadius, AppFontSize } from "../../utils/size";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import { ProgressCircleComponent } from "../ui/svg/lazy";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import Actions from "./actions";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
function getFileExtension(filename: string) {
|
||||
const ext = /^.+\.([^.]+)$/.exec(filename);
|
||||
@@ -76,8 +77,8 @@ export const AttachmentItem = ({
|
||||
flexDirection: "row",
|
||||
marginVertical: 5,
|
||||
justifyContent: "space-between",
|
||||
padding: 12,
|
||||
paddingVertical: 6,
|
||||
padding: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
minHeight: 45
|
||||
}}
|
||||
>
|
||||
@@ -101,7 +102,7 @@ export const AttachmentItem = ({
|
||||
paddingHorizontal: 2,
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 5
|
||||
borderRadius: defaultBorderRadius
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
@@ -124,7 +125,7 @@ export const AttachmentItem = ({
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
size={SIZE.sm}
|
||||
size={AppFontSize.sm}
|
||||
style={{
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
@@ -136,7 +137,10 @@ export const AttachmentItem = ({
|
||||
</Paragraph>
|
||||
|
||||
{!hideWhenNotDownloading ? (
|
||||
<Paragraph color={colors.secondary.paragraph} size={SIZE.xxs}>
|
||||
<Paragraph
|
||||
color={colors.secondary.paragraph}
|
||||
size={AppFontSize.xxs}
|
||||
>
|
||||
{strings.fileSize()}: {formatBytes(attachment.size)}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
@@ -159,7 +163,7 @@ export const AttachmentItem = ({
|
||||
}}
|
||||
>
|
||||
<ProgressCircleComponent
|
||||
size={SIZE.xxl}
|
||||
size={AppFontSize.xxl}
|
||||
progress={
|
||||
currentProgress?.value ? currentProgress?.value / 100 : 0
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import { downloadAttachments } from "../../common/filesystem/download-attachment
|
||||
import { AttachmentGroupProgress } from "../../screens/settings/attachment-group-progress";
|
||||
import { presentSheet, ToastManager } from "../../services/event-manager";
|
||||
import { useAttachmentStore } from "../../stores/use-attachment-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { Dialog } from "../dialog";
|
||||
import { presentDialog } from "../dialog/functions";
|
||||
import { Header } from "../header";
|
||||
@@ -50,6 +50,7 @@ import Seperator from "../ui/seperator";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { AttachmentItem } from "./attachment-item";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
const DEFAULT_SORTING: SortOptions = {
|
||||
sortBy: "dateEdited",
|
||||
@@ -325,30 +326,31 @@ export const AttachmentDialog = ({
|
||||
title={strings.manageAttachments()}
|
||||
renderedInRoute="SettingsGroup"
|
||||
canGoBack
|
||||
headerRightButtons={[
|
||||
{
|
||||
onPress() {
|
||||
onCheck();
|
||||
},
|
||||
title: strings.recheckAll()
|
||||
},
|
||||
{
|
||||
onPress() {
|
||||
if (!attachments) return;
|
||||
presentDialog({
|
||||
title: strings.doActions.download.attachment(
|
||||
attachments.placeholders.length
|
||||
),
|
||||
positiveText: strings.network.download(),
|
||||
positivePress: async () => {
|
||||
downloadAttachments(await attachments.ids());
|
||||
},
|
||||
negativeText: strings.cancel()
|
||||
});
|
||||
},
|
||||
title: strings.downloadAllAttachments()
|
||||
}
|
||||
]}
|
||||
// TODO: Add headerRightButtons
|
||||
// headerRightButtons={[
|
||||
// {
|
||||
// onPress() {
|
||||
// onCheck();
|
||||
// },
|
||||
// title: strings.recheckAll()
|
||||
// },
|
||||
// {
|
||||
// onPress() {
|
||||
// if (!attachments) return;
|
||||
// presentDialog({
|
||||
// title: strings.doActions.download.attachment(
|
||||
// attachments.placeholders.length
|
||||
// ),
|
||||
// positiveText: strings.network.download(),
|
||||
// positivePress: async () => {
|
||||
// downloadAttachments(await attachments.ids());
|
||||
// },
|
||||
// negativeText: strings.cancel()
|
||||
// });
|
||||
// },
|
||||
// title: strings.downloadAllAttachments()
|
||||
// }
|
||||
// ]}
|
||||
/>
|
||||
) : (
|
||||
<View
|
||||
@@ -356,7 +358,7 @@ export const AttachmentDialog = ({
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Heading>{strings.dataTypesPluralCamelCase.attachment()}</Heading>
|
||||
@@ -369,21 +371,15 @@ export const AttachmentDialog = ({
|
||||
<IconButton
|
||||
name="check-all"
|
||||
style={{
|
||||
height: 40,
|
||||
width: 40,
|
||||
marginRight: 10
|
||||
}}
|
||||
color={colors.primary.paragraph}
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
onPress={onCheck}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
name="download"
|
||||
style={{
|
||||
height: 40,
|
||||
width: 40
|
||||
}}
|
||||
color={colors.primary.paragraph}
|
||||
onPress={() => {
|
||||
if (!attachments) return;
|
||||
@@ -398,7 +394,7 @@ export const AttachmentDialog = ({
|
||||
negativeText: strings.cancel()
|
||||
});
|
||||
}}
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
@@ -408,7 +404,7 @@ export const AttachmentDialog = ({
|
||||
style={{
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
height: "100%"
|
||||
}}
|
||||
>
|
||||
@@ -428,7 +424,7 @@ export const AttachmentDialog = ({
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
borderRadius: 10,
|
||||
padding: 10,
|
||||
padding: DefaultAppStyles.GAP_SMALL,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.primary.border,
|
||||
gap: 12,
|
||||
@@ -473,7 +469,7 @@ export const AttachmentDialog = ({
|
||||
<IconButton
|
||||
type={rechecker.isWorking ? "errorShade" : "plain"}
|
||||
name={rechecker.isWorking ? "close" : "check"}
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
color={
|
||||
rechecker.isWorking ? colors.error.icon : colors.primary.accent
|
||||
}
|
||||
@@ -494,7 +490,7 @@ export const AttachmentDialog = ({
|
||||
backgroundColor: colors.primary.background,
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
paddingVertical: 12
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
contentContainerStyle={{
|
||||
alignItems: "center"
|
||||
@@ -511,10 +507,10 @@ export const AttachmentDialog = ({
|
||||
}
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
fontSize={SIZE.sm}
|
||||
fontSize={AppFontSize.sm}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
height: 40,
|
||||
minWidth: 80
|
||||
}}
|
||||
@@ -528,7 +524,9 @@ export const AttachmentDialog = ({
|
||||
setAttachments(results);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(console.log);
|
||||
.catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -37,6 +37,7 @@ import { hideAuth, initialAuthMode } from "./common";
|
||||
import { Login } from "./login";
|
||||
import { Signup } from "./signup";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const AuthMode = {
|
||||
login: 0,
|
||||
@@ -133,7 +134,7 @@ const AuthModal = () => {
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
width: "100%",
|
||||
height: 50,
|
||||
justifyContent:
|
||||
@@ -167,7 +168,7 @@ const AuthModal = () => {
|
||||
marginTop: 2
|
||||
}}
|
||||
style={{
|
||||
paddingHorizontal: 6
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -36,6 +36,7 @@ import { Dialog } from "../dialog";
|
||||
import BackupService from "../../services/backup";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const ChangePassword = () => {
|
||||
const passwordInputRef = useRef();
|
||||
@@ -88,7 +89,6 @@ export const ChangePassword = () => {
|
||||
await sleep(300);
|
||||
eSendEvent(eOpenRecoveryKeyDialog);
|
||||
} catch (e) {
|
||||
console.log(e.stack);
|
||||
setLoading(false);
|
||||
ToastManager.show({
|
||||
heading: strings.passwordChangeFailed(),
|
||||
@@ -104,7 +104,7 @@ export const ChangePassword = () => {
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: 12
|
||||
padding: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Dialog context="change-password-dialog" />
|
||||
@@ -149,7 +149,7 @@ export const ChangePassword = () => {
|
||||
|
||||
<Button
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
width: "100%"
|
||||
}}
|
||||
loading={loading}
|
||||
|
||||
@@ -20,23 +20,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { createRef } from "react";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import { eCloseLoginDialog } from "../../utils/events";
|
||||
import Navigation from "../../services/navigation";
|
||||
export const AuthMode = {
|
||||
login: 0,
|
||||
signup: 1,
|
||||
welcomeSignup: 2,
|
||||
trialSignup: 3
|
||||
};
|
||||
|
||||
export const initialAuthMode = createRef(0);
|
||||
export function hideAuth() {
|
||||
eSendEvent(eCloseLoginDialog);
|
||||
|
||||
// if (initialAuthMode.current === 2) {
|
||||
// Navigation.replace(
|
||||
// {
|
||||
// name: 'Notes'
|
||||
// },
|
||||
// {
|
||||
// menu: true
|
||||
// }
|
||||
// );
|
||||
// } else {
|
||||
// Navigation.goBack();
|
||||
// }
|
||||
|
||||
// tabBarRef.current?.unlock();
|
||||
if (initialAuthMode.current === AuthMode.welcomeSignup) {
|
||||
Navigation.replace("FluidPanelsView");
|
||||
} else {
|
||||
Navigation.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import Seperator from "../ui/seperator";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const ForgotPassword = () => {
|
||||
const { colors } = useThemeColors("sheet");
|
||||
@@ -107,7 +108,7 @@ export const ForgotPassword = () => {
|
||||
{sent ? (
|
||||
<View
|
||||
style={{
|
||||
padding: 12,
|
||||
padding: DefaultAppStyles.GAP,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
paddingBottom: 50
|
||||
@@ -138,7 +139,7 @@ export const ForgotPassword = () => {
|
||||
backgroundColor: colors.primary.background,
|
||||
zIndex: 10,
|
||||
width: "100%",
|
||||
padding: 12
|
||||
padding: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<DialogHeader title={strings.accountRecovery()} />
|
||||
@@ -164,7 +165,7 @@ export const ForgotPassword = () => {
|
||||
|
||||
<Button
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
width: "100%"
|
||||
}}
|
||||
loading={loading}
|
||||
|
||||
@@ -17,35 +17,98 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { Toast } from "../toast";
|
||||
import { initialAuthMode } from "./common";
|
||||
import { Button } from "../ui/button";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import { AuthMode, initialAuthMode } from "./common";
|
||||
import { Login } from "./login";
|
||||
import { Signup } from "./signup";
|
||||
|
||||
export const AuthMode = {
|
||||
login: 0,
|
||||
signup: 1,
|
||||
welcomeSignup: 2,
|
||||
trialSignup: 3
|
||||
};
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
|
||||
const Auth = ({ navigation, route }) => {
|
||||
const [currentAuthMode, setCurrentAuthMode] = useState(
|
||||
route?.params?.mode || AuthMode.login
|
||||
);
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const { colors } = useThemeColors();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
initialAuthMode.current = route?.params.mode || AuthMode.login;
|
||||
useNavigationFocus(navigation, {
|
||||
onFocus: () => {
|
||||
//tabBarRef?.current.lock();
|
||||
initialAuthMode.current = route?.params.mode || AuthMode.login;
|
||||
}
|
||||
});
|
||||
useNavigationFocus(navigation, {});
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
paddingTop: insets.top,
|
||||
top: 0,
|
||||
zIndex: 999,
|
||||
backgroundColor:
|
||||
deviceMode === "mobile" ? colors.secondary.background : null,
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
width: "100%",
|
||||
height: 50,
|
||||
justifyContent:
|
||||
initialAuthMode.current !== AuthMode.welcomeSignup
|
||||
? "space-between"
|
||||
: "flex-end"
|
||||
}}
|
||||
>
|
||||
{initialAuthMode.current === AuthMode.welcomeSignup ? null : (
|
||||
<IconButton
|
||||
name="arrow-left"
|
||||
onPress={() => {
|
||||
if (initialAuthMode.current === 2) {
|
||||
Navigation.replace("FluidPanelsView");
|
||||
} else {
|
||||
Navigation.goBack();
|
||||
}
|
||||
}}
|
||||
color={colors.primary.paragraph}
|
||||
/>
|
||||
)}
|
||||
|
||||
{initialAuthMode.current !== AuthMode.welcomeSignup ? null : (
|
||||
<Button
|
||||
title={strings.skip()}
|
||||
onPress={() => {
|
||||
if (initialAuthMode.current === 2) {
|
||||
Navigation.replace("FluidPanelsView");
|
||||
} else {
|
||||
Navigation.goBack();
|
||||
}
|
||||
}}
|
||||
iconSize={16}
|
||||
type="plain"
|
||||
iconPosition="right"
|
||||
icon="chevron-right"
|
||||
height={25}
|
||||
iconStyle={{
|
||||
marginTop: 2
|
||||
}}
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{currentAuthMode !== AuthMode.login ? (
|
||||
<Signup
|
||||
changeMode={(mode) => setCurrentAuthMode(mode)}
|
||||
|
||||
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
|
||||
@@ -24,10 +25,9 @@ import { SheetManager } from "react-native-actions-sheet";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import Sync from "../../services/sync";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { eUserLoggedIn } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import SheetProvider from "../sheet-provider";
|
||||
import { Progress } from "../sheets/progress";
|
||||
@@ -38,7 +38,7 @@ import Paragraph from "../ui/typography/paragraph";
|
||||
import { hideAuth } from "./common";
|
||||
import { ForgotPassword } from "./forgot-password";
|
||||
import { useLogin } from "./use-login";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
const LoginSteps = {
|
||||
emailAuth: 1,
|
||||
@@ -71,8 +71,8 @@ export const Login = ({ changeMode }) => {
|
||||
}
|
||||
}, 5000);
|
||||
});
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const { width, height } = useWindowDimensions();
|
||||
const isTablet = width > 600;
|
||||
useEffect(() => {
|
||||
async () => {
|
||||
setStep(LoginSteps.emailAuth);
|
||||
@@ -96,23 +96,24 @@ export const Login = ({ changeMode }) => {
|
||||
backgroundColor: colors.primary.background,
|
||||
zIndex: 10,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
alignSelf: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "flex-end",
|
||||
paddingHorizontal: 20,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderBottomWidth: 0.8,
|
||||
marginBottom: 12,
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL,
|
||||
borderBottomColor: colors.primary.border,
|
||||
alignSelf: deviceMode !== "mobile" ? "center" : undefined,
|
||||
borderWidth: deviceMode !== "mobile" ? 1 : null,
|
||||
borderColor: deviceMode !== "mobile" ? colors.primary.border : null,
|
||||
borderRadius: deviceMode !== "mobile" ? 20 : null,
|
||||
marginTop: deviceMode !== "mobile" ? 50 : null,
|
||||
width: deviceMode === "mobile" ? null : "50%",
|
||||
alignSelf: isTablet ? "center" : undefined,
|
||||
borderWidth: isTablet ? 1 : null,
|
||||
borderColor: isTablet ? colors.primary.border : null,
|
||||
borderRadius: isTablet ? 20 : null,
|
||||
marginTop: isTablet ? 50 : null,
|
||||
width: !isTablet ? null : "70%",
|
||||
minHeight: height * 0.4
|
||||
}}
|
||||
>
|
||||
@@ -143,10 +144,10 @@ export const Login = ({ changeMode }) => {
|
||||
<Heading
|
||||
style={{
|
||||
marginBottom: 25,
|
||||
marginTop: 10
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
extraBold
|
||||
size={SIZE.xxl}
|
||||
size={AppFontSize.xxl}
|
||||
>
|
||||
{strings.loginToYourAccount()}
|
||||
</Heading>
|
||||
@@ -163,7 +164,7 @@ export const Login = ({ changeMode }) => {
|
||||
: "99.9%",
|
||||
backgroundColor: colors.primary.background,
|
||||
alignSelf: "center",
|
||||
paddingHorizontal: 20
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
@@ -216,7 +217,7 @@ export const Login = ({ changeMode }) => {
|
||||
title={strings.forgotPassword()}
|
||||
style={{
|
||||
alignSelf: "flex-end",
|
||||
height: 30,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
paddingHorizontal: 0
|
||||
}}
|
||||
onPress={() => {
|
||||
@@ -226,7 +227,7 @@ export const Login = ({ changeMode }) => {
|
||||
textStyle={{
|
||||
textDecorationLine: "underline"
|
||||
}}
|
||||
fontSize={SIZE.xs}
|
||||
fontSize={AppFontSize.xs}
|
||||
type="plain"
|
||||
/>
|
||||
</>
|
||||
@@ -244,11 +245,8 @@ export const Login = ({ changeMode }) => {
|
||||
login();
|
||||
}}
|
||||
style={{
|
||||
width: 250,
|
||||
borderRadius: 100
|
||||
width: 250
|
||||
}}
|
||||
height={50}
|
||||
fontSize={SIZE.md}
|
||||
type="accent"
|
||||
title={!loading ? strings.continue() : null}
|
||||
/>
|
||||
@@ -258,19 +256,16 @@ export const Login = ({ changeMode }) => {
|
||||
title={strings.cancelLogin()}
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
height: 30,
|
||||
marginTop: 10
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
width: 250
|
||||
}}
|
||||
onPress={() => {
|
||||
if (loading) return;
|
||||
setStep(LoginSteps.emailAuth);
|
||||
setLoading(false);
|
||||
}}
|
||||
textStyle={{
|
||||
textDecorationLine: "underline"
|
||||
}}
|
||||
fontSize={SIZE.xs}
|
||||
type="errorShade"
|
||||
fontSize={AppFontSize.xs}
|
||||
type="secondaryAccented"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -283,17 +278,17 @@ export const Login = ({ changeMode }) => {
|
||||
activeOpacity={0.8}
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
marginTop: 12,
|
||||
paddingVertical: 12
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
size={SIZE.xs + 1}
|
||||
size={AppFontSize.xs}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
{strings.dontHaveAccount()}{" "}
|
||||
<Paragraph
|
||||
size={SIZE.xs + 1}
|
||||
size={AppFontSize.xs}
|
||||
style={{ color: colors.primary.accent }}
|
||||
>
|
||||
{strings.signUp()}
|
||||
|
||||
@@ -34,7 +34,7 @@ import Sync from "../../services/sync";
|
||||
import { clearAllStores } from "../../stores";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { eLoginSessionExpired, eUserLoggedIn } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { Dialog } from "../dialog";
|
||||
import BaseDialog from "../dialog/base-dialog";
|
||||
@@ -48,18 +48,8 @@ import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { LoginSteps, useLogin } from "./use-login";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
function getObfuscatedEmail(email) {
|
||||
if (!email) return "";
|
||||
const [username, provider] = email.split("@");
|
||||
if (username.length === 1) return `****@${provider}`;
|
||||
return email.replace(/(.{1})(.*)(?=@)/, function (gp1, gp2, gp3) {
|
||||
for (let i = 0; i < gp3.length; i++) {
|
||||
gp2 += "*";
|
||||
}
|
||||
return gp2;
|
||||
});
|
||||
}
|
||||
import { getObfuscatedEmail } from "../../utils/functions";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const SessionExpired = () => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -168,7 +158,7 @@ export const SessionExpired = () => {
|
||||
<View
|
||||
style={{
|
||||
width: focused ? "100%" : "99.9%",
|
||||
padding: 12,
|
||||
padding: DefaultAppStyles.GAP,
|
||||
justifyContent: "center",
|
||||
flex: 1,
|
||||
backgroundColor: colors.primary.background
|
||||
@@ -193,7 +183,7 @@ export const SessionExpired = () => {
|
||||
color={colors.error.icon}
|
||||
size={50}
|
||||
/>
|
||||
<Heading size={SIZE.xxxl} color={colors.primary.heading}>
|
||||
<Heading size={AppFontSize.xxxl} color={colors.primary.heading}>
|
||||
{strings.sessionExpired()}
|
||||
</Heading>
|
||||
<Paragraph
|
||||
@@ -224,7 +214,7 @@ export const SessionExpired = () => {
|
||||
|
||||
<Button
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
width: 250,
|
||||
borderRadius: 100
|
||||
}}
|
||||
@@ -236,7 +226,7 @@ export const SessionExpired = () => {
|
||||
|
||||
<Button
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
width: "100%"
|
||||
}}
|
||||
onPress={() => {
|
||||
|
||||
@@ -17,6 +17,8 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
@@ -24,19 +26,17 @@ import { DDS } from "../../services/device-detection";
|
||||
import { ToastManager } from "../../services/event-manager";
|
||||
import { clearMessage, setEmailVerifyMessage } from "../../services/message";
|
||||
import PremiumService from "../../services/premium";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { Button } from "../ui/button";
|
||||
import Input from "../ui/input";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { hideAuth } from "./common";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const Signup = ({ changeMode, trial }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -50,8 +50,8 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const setUser = useUserStore((state) => state.setUser);
|
||||
const setLastSynced = useUserStore((state) => state.setLastSynced);
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const { width, height } = useWindowDimensions();
|
||||
const isTablet = width > 600;
|
||||
const validateInfo = () => {
|
||||
if (!password.current || !email.current || !confirmPassword.current) {
|
||||
ToastManager.show({
|
||||
@@ -105,23 +105,24 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
backgroundColor: colors.primary.background,
|
||||
zIndex: 10,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
alignSelf: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "flex-end",
|
||||
paddingHorizontal: 20,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
backgroundColor: colors.secondary.background,
|
||||
marginBottom: 20,
|
||||
borderBottomWidth: 0.8,
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL,
|
||||
borderBottomColor: colors.primary.border,
|
||||
alignSelf: deviceMode !== "mobile" ? "center" : undefined,
|
||||
borderWidth: deviceMode !== "mobile" ? 1 : null,
|
||||
borderColor: deviceMode !== "mobile" ? colors.primary.border : null,
|
||||
borderRadius: deviceMode !== "mobile" ? 20 : null,
|
||||
marginTop: deviceMode !== "mobile" ? 50 : null,
|
||||
width: deviceMode === "mobile" ? null : "50%",
|
||||
alignSelf: isTablet ? "center" : undefined,
|
||||
borderWidth: isTablet ? 1 : null,
|
||||
borderColor: isTablet ? colors.primary.border : null,
|
||||
borderRadius: isTablet ? 20 : null,
|
||||
marginTop: isTablet ? 50 : null,
|
||||
width: !isTablet ? null : "70%",
|
||||
minHeight: height * 0.4
|
||||
}}
|
||||
>
|
||||
@@ -153,9 +154,9 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
extraBold
|
||||
style={{
|
||||
marginBottom: 25,
|
||||
marginTop: 10
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
size={SIZE.xxl}
|
||||
size={AppFontSize.xxl}
|
||||
>
|
||||
{strings.createYourAccount()}
|
||||
</Heading>
|
||||
@@ -164,7 +165,7 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
<View
|
||||
style={{
|
||||
width: DDS.isTab ? "50%" : "100%",
|
||||
paddingHorizontal: 20,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
backgroundColor: colors.primary.background,
|
||||
alignSelf: "center"
|
||||
}}
|
||||
@@ -233,12 +234,12 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
style={{
|
||||
marginBottom: 25
|
||||
}}
|
||||
size={SIZE.xs}
|
||||
size={AppFontSize.xxs}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
{strings.signupAgreement[0]()}
|
||||
<Paragraph
|
||||
size={SIZE.xs}
|
||||
size={AppFontSize.xxs}
|
||||
onPress={() => {
|
||||
openLinkInBrowser("https://notesnook.com/tos", colors);
|
||||
}}
|
||||
@@ -247,11 +248,12 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
}}
|
||||
color={colors.primary.accent}
|
||||
>
|
||||
{" "}
|
||||
{strings.signupAgreement[1]()}
|
||||
</Paragraph>
|
||||
</Paragraph>{" "}
|
||||
{strings.signupAgreement[2]()}
|
||||
<Paragraph
|
||||
size={SIZE.xs}
|
||||
size={AppFontSize.xxs}
|
||||
onPress={() => {
|
||||
openLinkInBrowser("https://notesnook.com/privacy", colors);
|
||||
}}
|
||||
@@ -260,8 +262,9 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
}}
|
||||
color={colors.primary.accent}
|
||||
>
|
||||
{" "}
|
||||
{strings.signupAgreement[3]()}
|
||||
</Paragraph>
|
||||
</Paragraph>{" "}
|
||||
{strings.signupAgreement[4]()}
|
||||
</Paragraph>
|
||||
|
||||
@@ -270,11 +273,8 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
type="accent"
|
||||
loading={loading}
|
||||
onPress={signup}
|
||||
fontSize={SIZE.md}
|
||||
style={{
|
||||
marginRight: 12,
|
||||
width: 250,
|
||||
borderRadius: 100
|
||||
width: 250
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -286,14 +286,14 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
activeOpacity={0.8}
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
marginTop: 12,
|
||||
paddingVertical: 12
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<Paragraph size={SIZE.xs + 1} color={colors.secondary.paragraph}>
|
||||
<Paragraph size={AppFontSize.xs} color={colors.secondary.paragraph}>
|
||||
{strings.alreadyHaveAccount()}{" "}
|
||||
<Paragraph
|
||||
size={SIZE.xs + 1}
|
||||
size={AppFontSize.xs}
|
||||
style={{ color: colors.primary.accent }}
|
||||
>
|
||||
{strings.login()}
|
||||
|
||||
@@ -30,13 +30,14 @@ import {
|
||||
ToastManager
|
||||
} from "../../services/event-manager";
|
||||
import { eCloseSheet } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import Input from "../ui/input";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -204,9 +205,9 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
|
||||
}}
|
||||
onSubmitEditing={onNext}
|
||||
caretHidden
|
||||
height={60}
|
||||
inputStyle={{
|
||||
fontSize: SIZE.lg,
|
||||
height: 60,
|
||||
fontSize: AppFontSize.lg,
|
||||
textAlign: "center",
|
||||
letterSpacing: 10,
|
||||
width: 250
|
||||
@@ -216,11 +217,13 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
|
||||
}
|
||||
enablesReturnKeyAutomatically
|
||||
containerStyle={{
|
||||
height: 60,
|
||||
borderWidth: 0,
|
||||
width: undefined,
|
||||
minWidth: "50%"
|
||||
}}
|
||||
wrapperStyle={{
|
||||
height: 60
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
@@ -229,9 +232,12 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
|
||||
width={250}
|
||||
loading={loading}
|
||||
onPress={onNext}
|
||||
style={{
|
||||
borderRadius: 100
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title={strings.cancel()}
|
||||
type="secondaryAccented"
|
||||
onPress={onCancel}
|
||||
width={250}
|
||||
/>
|
||||
|
||||
<Button
|
||||
@@ -242,13 +248,6 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
|
||||
onPress={onRequestSecondaryMethod}
|
||||
height={30}
|
||||
/>
|
||||
|
||||
<Button
|
||||
title={strings.cancel()}
|
||||
type="plain"
|
||||
onPress={onCancel}
|
||||
height={30}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
@@ -262,8 +261,8 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
marginTop: 0,
|
||||
flexDirection: "row",
|
||||
borderRadius: 0,
|
||||
@@ -275,8 +274,6 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
|
||||
<IconButton
|
||||
type="secondaryAccented"
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
marginRight: 10
|
||||
}}
|
||||
size={15}
|
||||
@@ -288,7 +285,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
<Paragraph size={SIZE.md}>{item.title}</Paragraph>
|
||||
<Paragraph size={AppFontSize.md}>{item.title}</Paragraph>
|
||||
</View>
|
||||
</Pressable>
|
||||
))}
|
||||
|
||||
@@ -70,7 +70,7 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
|
||||
switch (step) {
|
||||
case LoginSteps.emailAuth: {
|
||||
const mfaInfo = await db.user.authenticateEmail(email.current);
|
||||
console.log("email auth", mfaInfo);
|
||||
|
||||
if (mfaInfo) {
|
||||
TwoFactorVerification.present(
|
||||
async (mfa, callback) => {
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { Keyboard, View } from "react-native";
|
||||
import { Keyboard, TouchableOpacity, View, ViewStyle } from "react-native";
|
||||
import Animated, {
|
||||
Easing,
|
||||
useAnimatedStyle,
|
||||
@@ -33,20 +33,31 @@ import { editorState } from "../../screens/editor/tiptap/utils";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { SIZE, normalize } from "../../utils/size";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import { AppFontSize, normalize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { hexToRGBA, RGB_Linear_Shade } from "../../utils/colors";
|
||||
|
||||
interface FloatingButtonProps {
|
||||
onPress: () => void;
|
||||
color?: string;
|
||||
shouldShow?: boolean;
|
||||
alwaysVisible?: boolean;
|
||||
icon?: string;
|
||||
testID?: string;
|
||||
position?: "left" | "right";
|
||||
size?: "small" | "large";
|
||||
style?: ViewStyle;
|
||||
}
|
||||
|
||||
const FloatingButton = ({
|
||||
onPress,
|
||||
color,
|
||||
alwaysVisible = false
|
||||
alwaysVisible = false,
|
||||
icon,
|
||||
testID,
|
||||
position = "right",
|
||||
size = "large",
|
||||
style
|
||||
}: FloatingButtonProps) => {
|
||||
const { colors } = useThemeColors();
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
@@ -108,20 +119,26 @@ const FloatingButton = ({
|
||||
style={[
|
||||
{
|
||||
position: "absolute",
|
||||
right: 12,
|
||||
right: position === "right" ? DefaultAppStyles.GAP : undefined,
|
||||
left: position === "left" ? DefaultAppStyles.GAP : undefined,
|
||||
bottom: 20,
|
||||
zIndex: 10
|
||||
zIndex: 10,
|
||||
...style
|
||||
},
|
||||
animatedStyle
|
||||
]}
|
||||
>
|
||||
<Pressable
|
||||
testID={notesnook.buttons.add}
|
||||
type="accent"
|
||||
accentColor={color}
|
||||
<TouchableOpacity
|
||||
testID={testID || notesnook.buttons.add}
|
||||
activeOpacity={0.95}
|
||||
style={{
|
||||
...getElevationStyle(5),
|
||||
borderRadius: 100
|
||||
borderRadius: size === "small" ? 15 : 20,
|
||||
borderTopWidth: 0,
|
||||
borderBottomWidth: 0,
|
||||
borderLeftWidth: 0,
|
||||
borderRightWidth: 0,
|
||||
backgroundColor: colors.primary.background
|
||||
}}
|
||||
onPress={onPress}
|
||||
>
|
||||
@@ -129,23 +146,29 @@ const FloatingButton = ({
|
||||
style={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: normalize(60),
|
||||
width: normalize(60)
|
||||
height: normalize(size === "small" ? 40 : 60),
|
||||
width: normalize(size === "small" ? 40 : 60),
|
||||
backgroundColor: color
|
||||
? RGB_Linear_Shade(0.87, hexToRGBA(color))
|
||||
: colors.primary.shade,
|
||||
borderRadius: size === "small" ? 15 : 20
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name={
|
||||
route.name === "Notebooks"
|
||||
icon
|
||||
? icon
|
||||
: route.name === "Notebooks"
|
||||
? "notebook-plus"
|
||||
: route.name === "Trash"
|
||||
? "delete"
|
||||
: "plus"
|
||||
}
|
||||
color={colors.primary.accentForeground}
|
||||
size={SIZE.xxl}
|
||||
color={color || colors.primary.accent}
|
||||
size={size === "small" ? AppFontSize.xl : AppFontSize.xxxl}
|
||||
/>
|
||||
</View>
|
||||
</Pressable>
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,6 +23,8 @@ import { useMessageStore } from "../../stores/use-message-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { ColorValues } from "../../utils/colors";
|
||||
import { hexToRGBA } from "../../utils/colors";
|
||||
import { defaultBorderRadius } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -40,7 +42,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
{hasAnnoucements ? (
|
||||
@@ -51,7 +53,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
borderRadius: 10,
|
||||
marginBottom: 20,
|
||||
backgroundColor: colors.secondary.background,
|
||||
padding: 12
|
||||
padding: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -59,7 +61,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
width: 150,
|
||||
height: 20,
|
||||
backgroundColor: colors.primary.hover,
|
||||
borderRadius: 5,
|
||||
borderRadius: defaultBorderRadius,
|
||||
marginBottom: 10
|
||||
}}
|
||||
/>
|
||||
@@ -68,7 +70,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
width: 250,
|
||||
height: 14,
|
||||
backgroundColor: colors.primary.hover,
|
||||
borderRadius: 5
|
||||
borderRadius: defaultBorderRadius
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -78,7 +80,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
height: 15,
|
||||
backgroundColor: shadeColor,
|
||||
borderRadius: 3,
|
||||
marginTop: 10
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -93,7 +95,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
marginBottom: 20,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 20
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -111,7 +113,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
width: 150,
|
||||
height: 12,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5,
|
||||
borderRadius: defaultBorderRadius,
|
||||
marginBottom: 10
|
||||
}}
|
||||
/>
|
||||
@@ -120,7 +122,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
width: 250,
|
||||
height: 16,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5
|
||||
borderRadius: defaultBorderRadius
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -134,7 +136,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 10,
|
||||
marginBottom: 20,
|
||||
padding: 5,
|
||||
padding: DefaultAppStyles.GAP_SMALL,
|
||||
justifyContent: "space-between",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
@@ -179,7 +181,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
width: 200,
|
||||
height: 16,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5
|
||||
borderRadius: defaultBorderRadius
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
@@ -187,15 +189,15 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
width: "85%",
|
||||
height: 13,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5,
|
||||
marginTop: 10
|
||||
borderRadius: defaultBorderRadius,
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
marginTop: 10
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -203,7 +205,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
width: 50,
|
||||
height: 10,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5
|
||||
borderRadius: defaultBorderRadius
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
@@ -211,7 +213,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
|
||||
width: 60,
|
||||
height: 10,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5,
|
||||
borderRadius: defaultBorderRadius,
|
||||
marginLeft: 10
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -17,11 +17,10 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { ViewProps } from "react-native";
|
||||
import Animated, { FadeOutUp } from "react-native-reanimated";
|
||||
import { useDelayLayout } from "../../hooks/use-delay-layout";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { View, ViewProps } from "react-native";
|
||||
import { useDelayLayout } from "../../hooks/use-delay-layout";
|
||||
import { DefaultPlaceholder } from "./default-placeholder";
|
||||
import { SettingsPlaceholder } from "./settings-placeholder";
|
||||
|
||||
@@ -49,8 +48,7 @@ export default function DelayLayout({
|
||||
const Placeholder = placeholder[props.type || "default"];
|
||||
|
||||
return loading || props.wait ? (
|
||||
<Animated.View
|
||||
exiting={animated ? FadeOutUp : undefined}
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.primary.background,
|
||||
flex: 1,
|
||||
@@ -58,7 +56,7 @@ export default function DelayLayout({
|
||||
}}
|
||||
>
|
||||
<Placeholder color={props.color || colors.primary.accent} />
|
||||
</Animated.View>
|
||||
</View>
|
||||
) : (
|
||||
<>{props.children}</>
|
||||
);
|
||||
|
||||
@@ -20,6 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { defaultBorderRadius } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const SettingsPlaceholder = () => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -31,7 +33,7 @@ export const SettingsPlaceholder = () => {
|
||||
width: 100,
|
||||
height: 12,
|
||||
backgroundColor: colors.primary.shade,
|
||||
borderRadius: 5,
|
||||
borderRadius: defaultBorderRadius,
|
||||
marginLeft: 12,
|
||||
marginBottom: 12
|
||||
}}
|
||||
@@ -44,7 +46,7 @@ export const SettingsPlaceholder = () => {
|
||||
marginBottom: 20,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 16
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -62,7 +64,7 @@ export const SettingsPlaceholder = () => {
|
||||
width: 150,
|
||||
height: 12,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5,
|
||||
borderRadius: defaultBorderRadius,
|
||||
marginBottom: 10
|
||||
}}
|
||||
/>
|
||||
@@ -71,7 +73,7 @@ export const SettingsPlaceholder = () => {
|
||||
width: 250,
|
||||
height: 16,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5
|
||||
borderRadius: defaultBorderRadius
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -85,7 +87,7 @@ export const SettingsPlaceholder = () => {
|
||||
marginBottom: 20,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 16,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
@@ -104,7 +106,7 @@ export const SettingsPlaceholder = () => {
|
||||
width: 150,
|
||||
height: 12,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5,
|
||||
borderRadius: defaultBorderRadius,
|
||||
marginBottom: 10
|
||||
}}
|
||||
/>
|
||||
@@ -113,7 +115,7 @@ export const SettingsPlaceholder = () => {
|
||||
width: 250,
|
||||
height: 16,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5
|
||||
borderRadius: defaultBorderRadius
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ScopedThemeProvider } from "@notesnook/theme";
|
||||
import React, { PropsWithChildren, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
ColorValue,
|
||||
@@ -30,11 +31,8 @@ import {
|
||||
} from "react-native";
|
||||
import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { BouncingView } from "../ui/transitions/bouncing-view";
|
||||
import { ScopedThemeProvider } from "@notesnook/theme";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { useAppState } from "../../hooks/use-app-state";
|
||||
import { BouncingView } from "../ui/transitions/bouncing-view";
|
||||
|
||||
export interface BaseDialogProps extends PropsWithChildren {
|
||||
animation?: "fade" | "none" | "slide" | undefined;
|
||||
@@ -75,9 +73,9 @@ const BaseDialog = ({
|
||||
background
|
||||
}: BaseDialogProps) => {
|
||||
const floating = useIsFloatingKeyboard();
|
||||
const appState = useAppState();
|
||||
const lockEvents = useRef(false);
|
||||
const [internalVisible, setIntervalVisible] = useState(true);
|
||||
const locked = useUserStore((state) => state.appLocked);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -86,26 +84,20 @@ const BaseDialog = ({
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (useUserStore.getState().disableAppLockRequests) return;
|
||||
|
||||
if (SettingsService.canLockAppInBackground()) {
|
||||
if (appState === "background") {
|
||||
setIntervalVisible(false);
|
||||
if (useUserStore.getState().appLocked) {
|
||||
lockEvents.current = true;
|
||||
const unsub = useUserStore.subscribe((state) => {
|
||||
if (!state.appLocked) {
|
||||
setIntervalVisible(true);
|
||||
unsub();
|
||||
setTimeout(() => {
|
||||
lockEvents.current = false;
|
||||
});
|
||||
}
|
||||
if (locked) {
|
||||
setIntervalVisible(false);
|
||||
lockEvents.current = true;
|
||||
const unsub = useUserStore.subscribe((state) => {
|
||||
if (!state.appLocked) {
|
||||
setIntervalVisible(true);
|
||||
unsub();
|
||||
setTimeout(() => {
|
||||
lockEvents.current = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [appState]);
|
||||
}, [locked]);
|
||||
|
||||
const Wrapper = useSafeArea ? SafeAreaView : View;
|
||||
|
||||
|
||||
@@ -17,16 +17,17 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { ActivityIndicator, StyleSheet, View } from "react-native";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { getColorLinearShade } from "../../utils/colors";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Button } from "../ui/button";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { getColorLinearShade } from "../../utils/colors";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
const DialogButtons = ({
|
||||
onPressPositive,
|
||||
@@ -46,9 +47,7 @@ const DialogButtons = ({
|
||||
{
|
||||
backgroundColor: colors.secondary.background,
|
||||
height: 60,
|
||||
borderBottomRightRadius: 5,
|
||||
borderBottomLeftRadius: 5,
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
borderTopWidth: 0.7,
|
||||
borderTopColor: getColorLinearShade(
|
||||
colors.secondary.background,
|
||||
@@ -58,9 +57,7 @@ const DialogButtons = ({
|
||||
}
|
||||
]}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator color={colors.primary.accent} size={SIZE.lg} />
|
||||
) : doneText ? (
|
||||
{doneText ? (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
@@ -70,7 +67,7 @@ const DialogButtons = ({
|
||||
<Icon
|
||||
color={colors.primary.accent}
|
||||
name="check-circle-outline"
|
||||
size={SIZE.md}
|
||||
size={AppFontSize.md}
|
||||
/>
|
||||
<Paragraph color={colors.primary.accent}>{" " + doneText}</Paragraph>
|
||||
</View>
|
||||
@@ -86,7 +83,7 @@ const DialogButtons = ({
|
||||
>
|
||||
<Button
|
||||
onPress={onPressNegative}
|
||||
fontSize={SIZE.md}
|
||||
fontSize={AppFontSize.sm}
|
||||
testID={notesnook.ids.default.dialog.no}
|
||||
type="plain"
|
||||
bold
|
||||
@@ -95,11 +92,12 @@ const DialogButtons = ({
|
||||
{onPressPositive ? (
|
||||
<Button
|
||||
onPress={onPressPositive}
|
||||
fontSize={SIZE.md}
|
||||
fontSize={AppFontSize.sm}
|
||||
testID={notesnook.ids.default.dialog.yes}
|
||||
style={{
|
||||
marginLeft: 10
|
||||
}}
|
||||
loading={loading}
|
||||
bold
|
||||
type={positiveType || "transparent"}
|
||||
title={positiveTitle}
|
||||
@@ -117,6 +115,6 @@ const styles = StyleSheet.create({
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
marginTop: 10
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}
|
||||
});
|
||||
|
||||
@@ -20,11 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React from "react";
|
||||
import { Text, View, ViewStyle } from "react-native";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import { PressableProps } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
type DialogHeaderProps = {
|
||||
icon?: string;
|
||||
@@ -82,7 +83,7 @@ const DialogHeader = ({
|
||||
>
|
||||
<Heading
|
||||
style={{ textAlign: centered ? "center" : "left" }}
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
>
|
||||
{title}{" "}
|
||||
{titlePart ? (
|
||||
@@ -97,7 +98,7 @@ const DialogHeader = ({
|
||||
onPress={button.onPress}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
loading={button.loading}
|
||||
fontSize={13}
|
||||
|
||||
@@ -17,33 +17,35 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { getContainerBorder } from "../../utils/colors";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { eCloseSimpleDialog, eOpenSimpleDialog } from "../../utils/events";
|
||||
import { defaultBorderRadius } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { Toast } from "../toast";
|
||||
import { Button } from "../ui/button";
|
||||
import Input from "../ui/input";
|
||||
import { Notice } from "../ui/notice";
|
||||
import Seperator from "../ui/seperator";
|
||||
import BaseDialog from "./base-dialog";
|
||||
import DialogButtons from "./dialog-buttons";
|
||||
import DialogHeader from "./dialog-header";
|
||||
import { useCallback } from "react";
|
||||
import { Button } from "../ui/button";
|
||||
import { getContainerBorder } from "../../utils/colors";
|
||||
import { Notice } from "../ui/notice";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
export const Dialog = ({ context = "global" }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const values = useRef({
|
||||
inputValue: undefined
|
||||
});
|
||||
@@ -72,16 +74,26 @@ export const Dialog = ({ context = "global" }) => {
|
||||
const onPressPositive = async () => {
|
||||
if (dialogInfo.positivePress) {
|
||||
inputRef.current?.blur();
|
||||
let result = await dialogInfo.positivePress(
|
||||
values.current.inputValue || dialogInfo.defaultValue,
|
||||
checked
|
||||
);
|
||||
setLoading(true);
|
||||
let result;
|
||||
try {
|
||||
result = await dialogInfo.positivePress(
|
||||
values.current.inputValue || dialogInfo.defaultValue,
|
||||
checked
|
||||
);
|
||||
} catch (e) {
|
||||
/** Empty */
|
||||
}
|
||||
setLoading(false);
|
||||
|
||||
if (result === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hide();
|
||||
setChecked(false);
|
||||
values.current.inputValue = undefined;
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const show = useCallback(
|
||||
@@ -104,13 +116,14 @@ export const Dialog = ({ context = "global" }) => {
|
||||
eUnSubscribeEvent(eOpenSimpleDialog, show);
|
||||
eUnSubscribeEvent(eCloseSimpleDialog, hide);
|
||||
};
|
||||
}, [show]);
|
||||
}, [hide, show]);
|
||||
|
||||
const hide = () => {
|
||||
const hide = React.useCallback(() => {
|
||||
setChecked(false);
|
||||
values.current.inputValue = undefined;
|
||||
setVisible(false);
|
||||
};
|
||||
dialogInfo.onClose?.();
|
||||
}, [dialogInfo]);
|
||||
|
||||
const onNegativePress = async () => {
|
||||
if (dialogInfo.onClose) {
|
||||
@@ -123,10 +136,11 @@ export const Dialog = ({ context = "global" }) => {
|
||||
...getElevationStyle(5),
|
||||
width: DDS.isTab ? 400 : "85%",
|
||||
maxHeight: 450,
|
||||
borderRadius: 5,
|
||||
borderRadius: defaultBorderRadius,
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: 12,
|
||||
...getContainerBorder(colors.primary.border, 0.5)
|
||||
...getContainerBorder(colors.primary.border, 0.5),
|
||||
overflow: "hidden"
|
||||
};
|
||||
|
||||
return visible ? (
|
||||
@@ -162,7 +176,7 @@ export const Dialog = ({ context = "global" }) => {
|
||||
{dialogInfo.input ? (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
@@ -186,7 +200,7 @@ export const Dialog = ({ context = "global" }) => {
|
||||
{dialogInfo?.notice ? (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Notice
|
||||
@@ -207,6 +221,7 @@ export const Dialog = ({ context = "global" }) => {
|
||||
? "check-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
iconColor={checked ? colors.secondary.icon : colors.primary.icon}
|
||||
style={{
|
||||
justifyContent: "flex-start"
|
||||
}}
|
||||
@@ -222,6 +237,7 @@ export const Dialog = ({ context = "global" }) => {
|
||||
<DialogButtons
|
||||
onPressNegative={onNegativePress}
|
||||
onPressPositive={dialogInfo.positivePress && onPressPositive}
|
||||
loading={loading}
|
||||
positiveTitle={dialogInfo.positiveText}
|
||||
negativeTitle={dialogInfo.negativeText}
|
||||
positiveType={dialogInfo.positiveType}
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
eCloseAppLocKPasswordDailog,
|
||||
eOpenAppLockPasswordDialog
|
||||
} from "../../../utils/events";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
import DialogButtons from "../../dialog/dialog-buttons";
|
||||
@@ -50,6 +50,7 @@ import { IconButton } from "../../ui/icon-button";
|
||||
import Input from "../../ui/input";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
export const AppLockPassword = () => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -128,7 +129,7 @@ export const AppLockPassword = () => {
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
{mode === "change" ? (
|
||||
@@ -192,7 +193,7 @@ export const AppLockPassword = () => {
|
||||
height: 25,
|
||||
marginRight: 5
|
||||
}}
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -248,7 +249,7 @@ export const AppLockPassword = () => {
|
||||
setSecureTextEntry(true);
|
||||
});
|
||||
}}
|
||||
iconSize={SIZE.lg}
|
||||
iconSize={AppFontSize.lg}
|
||||
type="plain"
|
||||
iconColor={
|
||||
accountPass ? colors.primary.accent : colors.primary.icon
|
||||
@@ -259,7 +260,7 @@ export const AppLockPassword = () => {
|
||||
alignSelf: "flex-start",
|
||||
justifyContent: "flex-start",
|
||||
paddingHorizontal: 0,
|
||||
height: 30
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -27,12 +27,13 @@ import {
|
||||
import { Image as ImageType } from "react-native-image-crop-picker";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { presentSheet } from "../../../services/event-manager";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { defaultBorderRadius, AppFontSize } from "../../../utils/size";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { Notice } from "../../ui/notice";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
export default function AttachImage({
|
||||
response,
|
||||
@@ -50,21 +51,26 @@ export default function AttachImage({
|
||||
<View
|
||||
style={{
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.secondary.background,
|
||||
marginBottom: 12,
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL,
|
||||
height: 140,
|
||||
width: "100%",
|
||||
borderRadius: 10,
|
||||
padding: 5,
|
||||
paddingHorizontal: 12
|
||||
padding: DefaultAppStyles.GAP_SMALL,
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
<Paragraph style={{ color: colors.primary.paragraph, marginBottom: 6 }}>
|
||||
<Paragraph
|
||||
style={{
|
||||
color: colors.primary.paragraph,
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
}}
|
||||
>
|
||||
{strings.attachImageHeading(response?.length || 1)}
|
||||
</Paragraph>
|
||||
<ScrollView horizontal>
|
||||
@@ -80,7 +86,7 @@ export default function AttachImage({
|
||||
style={{
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: 5,
|
||||
borderRadius: defaultBorderRadius,
|
||||
backgroundColor: "black",
|
||||
marginRight: 6
|
||||
}}
|
||||
@@ -96,7 +102,7 @@ export default function AttachImage({
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignSelf: "center",
|
||||
marginBottom: 12,
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL,
|
||||
alignItems: "center",
|
||||
width: "100%"
|
||||
}}
|
||||
@@ -105,7 +111,7 @@ export default function AttachImage({
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
name={compress ? "checkbox-marked" : "checkbox-blank-outline"}
|
||||
color={compress ? colors.primary.accent : colors.primary.icon}
|
||||
style={{
|
||||
@@ -122,7 +128,7 @@ export default function AttachImage({
|
||||
flexShrink: 1,
|
||||
marginLeft: 3
|
||||
}}
|
||||
size={SIZE.sm}
|
||||
size={AppFontSize.sm}
|
||||
>
|
||||
{strings.compress()} ({strings.recommended().toLowerCase()})
|
||||
</Paragraph>
|
||||
@@ -135,7 +141,7 @@ export default function AttachImage({
|
||||
size="small"
|
||||
style={{
|
||||
width: "100%",
|
||||
marginBottom: 12
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@@ -145,7 +151,7 @@ export default function AttachImage({
|
||||
size="small"
|
||||
style={{
|
||||
width: "100%",
|
||||
marginBottom: 12
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -180,7 +186,7 @@ AttachImage.present = (response: ImageType[], context?: string) => {
|
||||
close={close}
|
||||
onAttach={(result) => {
|
||||
resolved = true;
|
||||
console.log("closing");
|
||||
|
||||
resolve(result);
|
||||
close?.();
|
||||
}}
|
||||
|
||||
@@ -34,6 +34,7 @@ import { Button } from "../../ui/button";
|
||||
import Input from "../../ui/input";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
const HEX_COLOR_REGEX_ALPHA =
|
||||
/^#(?:(?:[\da-fA-F]{3}){1,2}|(?:[\da-fA-F]{4}){1,2})$/;
|
||||
@@ -105,16 +106,16 @@ const ColorPicker = ({
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingTop: 10,
|
||||
columnGap: 10,
|
||||
marginBottom: 10
|
||||
paddingTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
columnGap: DefaultAppStyles.GAP_VERTICAL,
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
@@ -155,7 +156,7 @@ const ColorPicker = ({
|
||||
<Button
|
||||
title={strings.addColor()}
|
||||
style={{
|
||||
marginBottom: 10
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
onPress={async () => {
|
||||
if (!selectedColor)
|
||||
|
||||
@@ -39,10 +39,11 @@ import {
|
||||
eOpenJumpToDialog,
|
||||
eScrollEvent
|
||||
} from "../../../utils/events";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
const JumpToSectionDialog = () => {
|
||||
const scrollRef = useRef<RefObject<FlatList>>();
|
||||
@@ -152,13 +153,13 @@ const JumpToSectionDialog = () => {
|
||||
maxHeight: "65%",
|
||||
borderRadius: 10,
|
||||
alignSelf: "center",
|
||||
padding: 10,
|
||||
padding: DefaultAppStyles.GAP_SMALL,
|
||||
paddingTop: 30
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
color={colors.primary.accent}
|
||||
style={{
|
||||
marginBottom: 20
|
||||
@@ -188,15 +189,15 @@ const JumpToSectionDialog = () => {
|
||||
style={{
|
||||
minWidth: "20%",
|
||||
width: null,
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
margin: 5,
|
||||
borderRadius: 100,
|
||||
height: 30,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
marginVertical: 10
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
size={SIZE.sm}
|
||||
size={AppFontSize.sm}
|
||||
color={
|
||||
currentIndex === index
|
||||
? colors.static.white
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
} from "../../../services/event-manager";
|
||||
import { useState } from "react";
|
||||
import { eCloseLoading, eOpenLoading } from "../../../utils/events";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
export const LoadingDialog = () => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -67,7 +68,7 @@ export const LoadingDialog = () => {
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: 100,
|
||||
marginTop: 15
|
||||
marginTop: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<ProgressBarComponent
|
||||
|
||||
@@ -19,10 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Dimensions, TextInput, View } from "react-native";
|
||||
import {
|
||||
addOrientationListener,
|
||||
removeOrientationListener
|
||||
} from "react-native-orientation";
|
||||
import Orientation from "react-native-orientation-locker";
|
||||
import Pdf from "react-native-pdf";
|
||||
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
|
||||
import downloadAttachment from "../../../common/filesystem/download-attachment";
|
||||
@@ -34,7 +31,7 @@ import {
|
||||
eUnSubscribeEvent
|
||||
} from "../../../services/event-manager";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import { Dialog } from "../../dialog";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
@@ -46,6 +43,7 @@ import { sleep } from "../../../utils/time";
|
||||
import { MMKV } from "../../../common/database/mmkv";
|
||||
import { deleteCacheFileByPath } from "../../../common/filesystem/io";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
const WIN_WIDTH = Dimensions.get("window").width;
|
||||
const WIN_HEIGHT = Dimensions.get("window").height;
|
||||
@@ -98,9 +96,9 @@ const PDFPreview = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
addOrientationListener(onOrientationChange);
|
||||
Orientation.addDeviceOrientationListener(onOrientationChange);
|
||||
return () => {
|
||||
removeOrientationListener(onOrientationChange);
|
||||
Orientation.removeDeviceOrientationListener(onOrientationChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@@ -184,7 +182,7 @@ const PDFPreview = () => {
|
||||
/>
|
||||
<Paragraph
|
||||
style={{
|
||||
marginTop: 10
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
color={colors.static.white}
|
||||
>
|
||||
@@ -200,7 +198,7 @@ const PDFPreview = () => {
|
||||
marginTop: insets.top,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingLeft: 6
|
||||
}}
|
||||
>
|
||||
@@ -216,7 +214,7 @@ const PDFPreview = () => {
|
||||
style={{
|
||||
marginRight: 12
|
||||
}}
|
||||
size={SIZE.xxl}
|
||||
size={AppFontSize.xxl}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -307,9 +305,7 @@ const PDFPreview = () => {
|
||||
password={password}
|
||||
maxScale={6}
|
||||
onError={onError}
|
||||
onPressLink={(uri) => {
|
||||
console.log(`Link pressed: ${uri}`);
|
||||
}}
|
||||
onPressLink={(uri) => {}}
|
||||
style={{
|
||||
flex: 1,
|
||||
width: width,
|
||||
|
||||
@@ -21,7 +21,7 @@ import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect } from "react";
|
||||
import { View } from "react-native";
|
||||
import { eSendEvent, eSubscribeEvent } from "../../../services/event-manager";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import { Dialog } from "../../dialog";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
import DialogContainer from "../../dialog/dialog-container";
|
||||
@@ -29,6 +29,7 @@ import { Button } from "../../ui/button";
|
||||
import { ProgressBarComponent } from "../../ui/svg/lazy";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
export type ProgressOptions = {
|
||||
progress?: string;
|
||||
@@ -62,7 +63,7 @@ export default function Progress() {
|
||||
eSubscribeEvent(PROGRESS_EVENTS.start, (options: ProgressOptions) => {
|
||||
setProgress(options.progress);
|
||||
cancelCallback.current = options.cancelCallback;
|
||||
console.log("options", options.fillBackground);
|
||||
|
||||
setData({
|
||||
title: options.title,
|
||||
paragraph: options.paragraph,
|
||||
@@ -111,7 +112,7 @@ export default function Progress() {
|
||||
>
|
||||
<DialogContainer
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingBottom: 10
|
||||
}}
|
||||
noBorder={data?.fillBackground ? true : false}
|
||||
@@ -121,7 +122,7 @@ export default function Progress() {
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
gap: 10,
|
||||
paddingBottom: 20
|
||||
}}
|
||||
@@ -131,7 +132,7 @@ export default function Progress() {
|
||||
textAlign: "center"
|
||||
}}
|
||||
color={colors.primary.paragraph}
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
>
|
||||
{data?.title}
|
||||
</Heading>
|
||||
@@ -148,7 +149,7 @@ export default function Progress() {
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: 100,
|
||||
paddingVertical: 12
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<ProgressBarComponent
|
||||
|
||||
@@ -27,13 +27,14 @@ import {
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import { eCloseResultDialog, eOpenResultDialog } from "../../../utils/events";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
import { Button } from "../../ui/button";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { ProFeatures } from "./pro-features";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
const ResultDialog = () => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -78,15 +79,15 @@ const ResultDialog = () => {
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
textBreakStrategy="balanced"
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
textAlign: "center",
|
||||
marginTop: 10,
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
maxWidth: "100%",
|
||||
marginBottom: 10,
|
||||
paddingHorizontal: 12
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL,
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
{dialogData.title}
|
||||
@@ -94,12 +95,12 @@ const ResultDialog = () => {
|
||||
|
||||
<Paragraph
|
||||
color={colors.secondary.paragraph}
|
||||
size={SIZE.md}
|
||||
size={AppFontSize.md}
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
textAlign: "center",
|
||||
maxWidth: "80%",
|
||||
lineHeight: SIZE.sm + 5
|
||||
lineHeight: AppFontSize.sm + 5
|
||||
}}
|
||||
>
|
||||
{dialogData.paragraph}
|
||||
@@ -109,7 +110,7 @@ const ResultDialog = () => {
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
alignItems: "center",
|
||||
width: "100%"
|
||||
}}
|
||||
@@ -124,17 +125,17 @@ const ResultDialog = () => {
|
||||
width: "100%",
|
||||
borderBottomRightRadius: 10,
|
||||
borderBottomLeftRadius: 10,
|
||||
paddingVertical: 10
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
title={dialogData.button}
|
||||
width={null}
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
onPress={close}
|
||||
fontSize={SIZE.md + 2}
|
||||
fontSize={AppFontSize.md + 2}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -27,9 +27,10 @@ import {
|
||||
eCloseResultDialog,
|
||||
eOpenPremiumDialog
|
||||
} from "../../../utils/events";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { defaultBorderRadius, AppFontSize } from "../../../utils/size";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
export const ProFeatures = ({ count = 6 }) => {
|
||||
const { colors } = useThemeColors();
|
||||
|
||||
@@ -67,13 +68,17 @@ export const ProFeatures = ({ count = 6 }) => {
|
||||
width: "100%",
|
||||
height: 40,
|
||||
paddingHorizontal: 0,
|
||||
marginBottom: 10,
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL,
|
||||
alignItems: "center",
|
||||
borderRadius: 5,
|
||||
borderRadius: defaultBorderRadius,
|
||||
justifyContent: "flex-start"
|
||||
}}
|
||||
>
|
||||
<Icon size={SIZE.lg} color={colors.primary.accent} name="check" />
|
||||
<Icon
|
||||
size={AppFontSize.lg}
|
||||
color={colors.primary.accent}
|
||||
name="check"
|
||||
/>
|
||||
<Paragraph style={{ marginLeft: 5, flexShrink: 1 }}>
|
||||
{item.content}
|
||||
</Paragraph>
|
||||
@@ -86,7 +91,7 @@ export const ProFeatures = ({ count = 6 }) => {
|
||||
await sleep(300);
|
||||
eSendEvent(eOpenPremiumDialog);
|
||||
}}
|
||||
size={SIZE.xs}
|
||||
size={AppFontSize.xs}
|
||||
style={{
|
||||
textDecorationLine: "underline",
|
||||
color: colors.secondary.paragraph
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
eUpdateNoteInEditor
|
||||
} from "../../../utils/events";
|
||||
import { deleteItems } from "../../../utils/functions";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../../utils/global-refs";
|
||||
import { convertNoteToText } from "../../../utils/note-to-text";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
@@ -53,6 +53,7 @@ import Input from "../../ui/input";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
export class VaultDialog extends Component {
|
||||
constructor(props) {
|
||||
@@ -336,7 +337,7 @@ export class VaultDialog extends Component {
|
||||
);
|
||||
});
|
||||
}
|
||||
console.log("VAULT UPDATED EVENT");
|
||||
|
||||
eSendEvent("vaultUpdated");
|
||||
this.setState({
|
||||
loading: false
|
||||
@@ -406,7 +407,6 @@ export class VaultDialog extends Component {
|
||||
} else {
|
||||
await db.vault.add(this.state.note.id);
|
||||
|
||||
console.log("update note event...");
|
||||
eSendEvent(eUpdateNoteInEditor, this.state.note, true);
|
||||
|
||||
this.close();
|
||||
@@ -543,7 +543,6 @@ export class VaultDialog extends Component {
|
||||
this.close();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log("Error", e);
|
||||
this._takeErrorAction(e);
|
||||
});
|
||||
}
|
||||
@@ -555,7 +554,7 @@ export class VaultDialog extends Component {
|
||||
item: note
|
||||
});
|
||||
if (!DDS.isTab) {
|
||||
tabBarRef.current?.goToPage(1);
|
||||
fluidTabsRef.current?.goToPage("editor");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -679,7 +678,7 @@ export class VaultDialog extends Component {
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
{(novault ||
|
||||
@@ -752,7 +751,7 @@ export class VaultDialog extends Component {
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
style={{
|
||||
marginTop: 10
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
width="100%"
|
||||
title={strings.deleteAllNotes()}
|
||||
@@ -849,7 +848,7 @@ export class VaultDialog extends Component {
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
marginTop: 10
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
icon="fingerprint"
|
||||
width="100%"
|
||||
|
||||
@@ -78,10 +78,10 @@ class ExceptionHandler extends React.Component<{
|
||||
}
|
||||
|
||||
export const withErrorBoundry = (Element: React.ElementType, name: string) => {
|
||||
return function ErrorBoundary() {
|
||||
return function ErrorBoundary(props: any) {
|
||||
return (
|
||||
<ExceptionHandler component={name}>
|
||||
<Element />
|
||||
<Element {...props} />
|
||||
</ExceptionHandler>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -37,12 +37,13 @@ import Animated, {
|
||||
WithSpringConfig,
|
||||
withTiming
|
||||
} from "react-native-reanimated";
|
||||
import { useTabStore } from "../../screens/editor/tiptap/use-tab-store";
|
||||
import { getAppState } from "../../screens/editor/tiptap/utils";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { eClearEditor } from "../../utils/events";
|
||||
import { useSideBarDraggingStore } from "../side-menu/dragging-store";
|
||||
import { useTabStore } from "../../screens/editor/tiptap/use-tab-store";
|
||||
import { rootNavigatorRef } from "../../utils/global-refs";
|
||||
|
||||
interface TabProps extends ViewProps {
|
||||
dimensions: { width: number; height: number };
|
||||
@@ -53,20 +54,22 @@ interface TabProps extends ViewProps {
|
||||
onDrawerStateChange: (state: boolean) => void;
|
||||
}
|
||||
|
||||
type FluidTabPage = "home" | "editor";
|
||||
|
||||
export interface TabsRef {
|
||||
goToPage: (page: number, animated?: boolean) => void;
|
||||
goToPage: (page: FluidTabPage, animated?: boolean) => void;
|
||||
goToIndex: (index: number, animated?: boolean) => 0 | undefined;
|
||||
unlock: () => boolean;
|
||||
lock: () => boolean;
|
||||
openDrawer: (animated?: boolean) => void;
|
||||
closeDrawer: (animated?: boolean) => void;
|
||||
page: () => number;
|
||||
page: () => FluidTabPage;
|
||||
setScrollEnabled: () => true;
|
||||
isDrawerOpen: () => boolean;
|
||||
node: RefObject<Animated.View>;
|
||||
}
|
||||
|
||||
export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
{
|
||||
children,
|
||||
dimensions,
|
||||
@@ -81,9 +84,6 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
const appState = useMemo(() => getAppState(), []);
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const fullscreen = useSettingStore((state) => state.fullscreen);
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
const translateX = useSharedValue(
|
||||
widths
|
||||
? appState &&
|
||||
@@ -118,26 +118,40 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
const isIPhone = Platform.OS === "ios";
|
||||
|
||||
useEffect(() => {
|
||||
if (introCompleted) {
|
||||
if (deviceMode === "tablet" || fullscreen) {
|
||||
translateX.value = 0;
|
||||
} else {
|
||||
if (prevWidths.current?.sidebar !== widths.sidebar) {
|
||||
translateX.value =
|
||||
appState && appState?.movedAway === false
|
||||
? editorPosition
|
||||
: widths.sidebar;
|
||||
if (translateX.value === editorPosition) {
|
||||
onChangeTab?.({ i: 2, from: 1 });
|
||||
}
|
||||
if (deviceMode === "tablet" || fullscreen) {
|
||||
translateX.value = 0;
|
||||
} else {
|
||||
if (prevWidths.current?.sidebar !== widths.sidebar) {
|
||||
translateX.value =
|
||||
appState && appState?.movedAway === false
|
||||
? editorPosition
|
||||
: widths.sidebar;
|
||||
if (translateX.value === editorPosition) {
|
||||
onChangeTab?.({ i: 2, from: 1 });
|
||||
}
|
||||
}
|
||||
isLoaded.current = true;
|
||||
prevWidths.current = widths;
|
||||
}
|
||||
isLoaded.current = true;
|
||||
prevWidths.current = widths;
|
||||
}, [
|
||||
deviceMode,
|
||||
widths,
|
||||
fullscreen,
|
||||
translateX,
|
||||
editorPosition,
|
||||
appState,
|
||||
onChangeTab
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const sub = BackHandler.addEventListener("hardwareBackPress", () => {
|
||||
if (isDrawerOpen.value) {
|
||||
if (isDrawerOpen.value && !forcedLock.value) {
|
||||
const routeNames = rootNavigatorRef.current?.getState().routes;
|
||||
if (routeNames) {
|
||||
const currentRoute = routeNames[routeNames.length - 1].name;
|
||||
if (currentRoute !== "FluidPanelsView") return false;
|
||||
}
|
||||
|
||||
translateX.value = withTiming(homePosition);
|
||||
onDrawerStateChange(false);
|
||||
isDrawerOpen.value = false;
|
||||
@@ -149,34 +163,28 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
sub && sub.remove();
|
||||
};
|
||||
}, [
|
||||
introCompleted,
|
||||
deviceMode,
|
||||
widths,
|
||||
fullscreen,
|
||||
translateX,
|
||||
isDrawerOpen,
|
||||
forcedLock.value,
|
||||
homePosition,
|
||||
isDrawerOpen,
|
||||
onDrawerStateChange,
|
||||
editorPosition,
|
||||
appState
|
||||
translateX
|
||||
]);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
(): TabsRef => ({
|
||||
goToPage: (page: number, animated = true) => {
|
||||
goToPage: (page: FluidTabPage, animated = true) => {
|
||||
if (deviceMode === "tablet") {
|
||||
translateX.value = animated ? withTiming(0) : 0;
|
||||
return;
|
||||
}
|
||||
page = page + 1;
|
||||
if (page === 1) {
|
||||
if (page === "home") {
|
||||
onDrawerStateChange(false);
|
||||
translateX.value = !animated
|
||||
? homePosition
|
||||
: withTiming(homePosition);
|
||||
currentTab.value = 1;
|
||||
} else if (page === 2) {
|
||||
} else if (page === "editor") {
|
||||
onDrawerStateChange(false);
|
||||
translateX.value = !animated
|
||||
? editorPosition
|
||||
@@ -242,7 +250,7 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
onDrawerStateChange(false);
|
||||
isDrawerOpen.value = false;
|
||||
},
|
||||
page: () => currentTab.value,
|
||||
page: () => (currentTab.value === 1 ? "home" : "editor"),
|
||||
setScrollEnabled: () => true,
|
||||
node: node
|
||||
}),
|
||||
@@ -295,7 +303,7 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
.maxPointers(1)
|
||||
.enabled(enabled && !disabled)
|
||||
.activeOffsetX([-5, 5])
|
||||
.failOffsetY([-15, 15])
|
||||
.failOffsetY([-10, 10])
|
||||
.onBegin((event) => {
|
||||
locked.value = false;
|
||||
gestureStartValue.value = {
|
||||
@@ -17,60 +17,52 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Platform, StyleSheet, View } from "react-native";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { View } from "react-native";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import useNavigationStore, {
|
||||
RouteName
|
||||
} from "../../stores/use-navigation-store";
|
||||
import { RouteName } from "../../stores/use-navigation-store";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { eScrollEvent } from "../../utils/events";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { IconButtonProps } from "../ui/icon-button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { LeftMenus } from "./left-menus";
|
||||
import { RightMenus } from "./right-menus";
|
||||
import { Title } from "./title";
|
||||
|
||||
type HeaderRightButton = {
|
||||
title: string;
|
||||
onPress: () => void;
|
||||
};
|
||||
|
||||
export const Header = ({
|
||||
renderedInRoute,
|
||||
onLeftMenuButtonPress,
|
||||
title,
|
||||
titleHiddenOnRender,
|
||||
headerRightButtons,
|
||||
id,
|
||||
accentColor,
|
||||
isBeta,
|
||||
canGoBack,
|
||||
onPressDefaultRightButton,
|
||||
hasSearch,
|
||||
onSearch
|
||||
onSearch,
|
||||
rightButton
|
||||
}: {
|
||||
onLeftMenuButtonPress?: () => void;
|
||||
renderedInRoute: RouteName;
|
||||
renderedInRoute?: RouteName;
|
||||
id?: string;
|
||||
title: string;
|
||||
headerRightButtons?: HeaderRightButton[];
|
||||
titleHiddenOnRender?: boolean;
|
||||
accentColor?: string;
|
||||
isBeta?: boolean;
|
||||
canGoBack?: boolean;
|
||||
onPressDefaultRightButton?: () => void;
|
||||
hasSearch?: boolean;
|
||||
onSearch?: () => void;
|
||||
rightButton?: IconButtonProps;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const [borderHidden, setBorderHidden] = useState(true);
|
||||
const selectionMode = useSelectionStore((state) => state.selectionMode);
|
||||
const isFocused = useNavigationStore((state) => state.focusedRouteId === id);
|
||||
const [selectedItemsList, selectionMode] = useSelectionStore((state) => [
|
||||
state.selectedItemsList,
|
||||
state.selectionMode
|
||||
]);
|
||||
|
||||
const onScroll = useCallback(
|
||||
(data: { x: number; y: number; id?: string; route: string }) => {
|
||||
@@ -93,88 +85,48 @@ export const Header = ({
|
||||
};
|
||||
}, [borderHidden, onScroll]);
|
||||
|
||||
return selectionMode && isFocused ? null : (
|
||||
<>
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
{
|
||||
marginTop: Platform.OS === "android" ? insets.top : null,
|
||||
backgroundColor: colors.primary.background,
|
||||
overflow: "hidden",
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: borderHidden
|
||||
? "transparent"
|
||||
: colors.secondary.background,
|
||||
justifyContent: "space-between"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<>
|
||||
<View style={styles.leftBtnContainer}>
|
||||
<LeftMenus
|
||||
canGoBack={canGoBack}
|
||||
onLeftButtonPress={onLeftMenuButtonPress}
|
||||
/>
|
||||
const HeaderWrapper = hasSearch ? Pressable : View;
|
||||
|
||||
<Title
|
||||
isHiddenOnRender={titleHiddenOnRender}
|
||||
renderedInRoute={renderedInRoute}
|
||||
id={id}
|
||||
accentColor={accentColor}
|
||||
title={title}
|
||||
isBeta={isBeta}
|
||||
/>
|
||||
</View>
|
||||
<RightMenus
|
||||
renderedInRoute={renderedInRoute}
|
||||
id={id}
|
||||
headerRightButtons={headerRightButtons}
|
||||
onPressDefaultRightButton={onPressDefaultRightButton}
|
||||
search={hasSearch}
|
||||
onSearch={onSearch}
|
||||
/>
|
||||
</>
|
||||
</View>
|
||||
</>
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<HeaderWrapper
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
marginTop: DefaultAppStyles.GAP_SMALL,
|
||||
borderRadius: 10,
|
||||
paddingVertical: 3,
|
||||
borderWidth: hasSearch ? 1 : 0,
|
||||
borderColor: colors.primary.border,
|
||||
paddingHorizontal: !hasSearch ? 0 : DefaultAppStyles.GAP_SMALL,
|
||||
alignItems: "center"
|
||||
}}
|
||||
testID="search-header"
|
||||
onPress={() => {
|
||||
onSearch?.();
|
||||
}}
|
||||
>
|
||||
<LeftMenus
|
||||
canGoBack={canGoBack}
|
||||
onLeftButtonPress={onLeftMenuButtonPress}
|
||||
/>
|
||||
|
||||
{hasSearch ? (
|
||||
<Paragraph>
|
||||
{selectionMode
|
||||
? `${selectedItemsList.length} selected`
|
||||
: strings.searchInRoute(title)}
|
||||
</Paragraph>
|
||||
) : (
|
||||
<Heading size={AppFontSize.lg}>{title}</Heading>
|
||||
)}
|
||||
|
||||
<RightMenus rightButton={rightButton} />
|
||||
</HeaderWrapper>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: "row",
|
||||
zIndex: 11,
|
||||
height: 50,
|
||||
maxHeight: 50,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12,
|
||||
width: "100%"
|
||||
},
|
||||
leftBtnContainer: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
flexShrink: 1
|
||||
},
|
||||
leftBtn: {
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 100,
|
||||
marginLeft: -5,
|
||||
marginRight: 25
|
||||
},
|
||||
rightBtnContainer: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
},
|
||||
rightBtn: {
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
height: 40,
|
||||
width: 40,
|
||||
paddingRight: 0
|
||||
}
|
||||
});
|
||||
|
||||
@@ -20,10 +20,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
|
||||
export const LeftMenus = ({
|
||||
@@ -41,7 +40,7 @@ export const LeftMenus = ({
|
||||
if (onLeftButtonPress) return onLeftButtonPress();
|
||||
|
||||
if (!canGoBack) {
|
||||
if (tabBarRef.current?.isDrawerOpen()) {
|
||||
if (fluidTabsRef.current?.isDrawerOpen()) {
|
||||
Navigation.closeDrawer();
|
||||
} else {
|
||||
Navigation.openDrawer();
|
||||
@@ -54,23 +53,14 @@ export const LeftMenus = ({
|
||||
return isTablet && !canGoBack ? null : (
|
||||
<IconButton
|
||||
testID={notesnook.ids.default.header.buttons.left}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 100,
|
||||
marginRight: DDS.isLargeTablet() ? 10 : 7
|
||||
}}
|
||||
left={40}
|
||||
top={40}
|
||||
right={DDS.isLargeTablet() ? 10 : 10}
|
||||
onPress={_onLeftButtonPress}
|
||||
onLongPress={() => {
|
||||
Navigation.popToTop();
|
||||
}}
|
||||
name={canGoBack ? "arrow-left" : "menu"}
|
||||
color={colors.primary.paragraph}
|
||||
color={colors.primary.icon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,127 +17,30 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useRef } from "react";
|
||||
import { Platform, StyleSheet, View } from "react-native";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { Menu } from "react-native-material-menu";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import {
|
||||
HeaderRightButton,
|
||||
RouteName
|
||||
} from "../../stores/use-navigation-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { Button } from "../ui/button";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import React from "react";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import { IconButton, IconButtonProps } from "../ui/icon-button";
|
||||
|
||||
export const RightMenus = ({
|
||||
headerRightButtons,
|
||||
renderedInRoute,
|
||||
id,
|
||||
onPressDefaultRightButton,
|
||||
search,
|
||||
onSearch
|
||||
rightButton
|
||||
}: {
|
||||
headerRightButtons?: HeaderRightButton[];
|
||||
renderedInRoute: RouteName;
|
||||
id?: string;
|
||||
onPressDefaultRightButton?: () => void;
|
||||
search?: boolean;
|
||||
onSearch?: () => void;
|
||||
rightButton?: IconButtonProps;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const { colors: contextMenuColors } = useThemeColors("contextMenu");
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const menuRef = useRef<Menu>(null);
|
||||
|
||||
return (
|
||||
<View style={styles.rightBtnContainer}>
|
||||
{search ? (
|
||||
<IconButton
|
||||
onPress={onSearch}
|
||||
testID="icon-search"
|
||||
name="magnify"
|
||||
color={colors.primary.paragraph}
|
||||
style={styles.rightBtn}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{deviceMode !== "mobile" ? (
|
||||
<Button
|
||||
onPress={onPressDefaultRightButton}
|
||||
testID={notesnook.ids.default.addBtn}
|
||||
icon={renderedInRoute === "Trash" ? "delete" : "plus"}
|
||||
iconSize={SIZE.xl}
|
||||
type="accent"
|
||||
hitSlop={{
|
||||
top: 10,
|
||||
right: 10,
|
||||
bottom: 10,
|
||||
left: 0
|
||||
}}
|
||||
{rightButton ? (
|
||||
<IconButton {...rightButton} color={colors.primary.icon} />
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
marginLeft: 10,
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 0,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.primary.accent
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{headerRightButtons && headerRightButtons.length > 0 ? (
|
||||
<Menu
|
||||
ref={menuRef}
|
||||
animationDuration={200}
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
backgroundColor: contextMenuColors.primary.background,
|
||||
marginTop: 35
|
||||
}}
|
||||
onRequestClose={() => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.hide();
|
||||
}}
|
||||
anchor={
|
||||
<IconButton
|
||||
onPress={() => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.show();
|
||||
}}
|
||||
name="dots-vertical"
|
||||
color={colors.primary.paragraph}
|
||||
style={styles.rightBtn}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{headerRightButtons.map((item) => (
|
||||
<Button
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
borderRadius: 0,
|
||||
alignSelf: "flex-start",
|
||||
width: "100%"
|
||||
}}
|
||||
type="plain"
|
||||
buttonType={{
|
||||
text: contextMenuColors.primary.paragraph
|
||||
}}
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
onPress={async () => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.hide();
|
||||
if (Platform.OS === "ios") await sleep(300);
|
||||
item.onPress();
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Menu>
|
||||
) : null}
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -149,10 +52,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
rightBtn: {
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 40,
|
||||
width: 40,
|
||||
marginLeft: 10,
|
||||
paddingRight: 0
|
||||
alignItems: "center"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { eScrollEvent } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import Tag from "../ui/tag";
|
||||
import Heading from "../ui/typography/heading";
|
||||
|
||||
@@ -41,7 +41,7 @@ export const Title = ({
|
||||
isHiddenOnRender?: boolean;
|
||||
accentColor?: string;
|
||||
isBeta?: boolean;
|
||||
renderedInRoute: string;
|
||||
renderedInRoute?: string;
|
||||
id?: string;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -75,7 +75,7 @@ export const Title = ({
|
||||
{!visible ? (
|
||||
<Heading
|
||||
numberOfLines={1}
|
||||
size={SIZE.lg}
|
||||
size={AppFontSize.lg}
|
||||
style={{
|
||||
flexWrap: "wrap",
|
||||
marginTop: Platform.OS === "ios" ? -1 : 0
|
||||
@@ -83,7 +83,7 @@ export const Title = ({
|
||||
color={accentColor || colors.primary.heading}
|
||||
>
|
||||
{isTag ? (
|
||||
<Heading size={SIZE.xl} color={colors.primary.accent}>
|
||||
<Heading size={AppFontSize.xl} color={colors.primary.accent}>
|
||||
#
|
||||
</Heading>
|
||||
) : null}
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Sodium from "@ammarahmed/react-native-sodium";
|
||||
import { DataURL } from "@notesnook/core";
|
||||
import { DataURL, getFileNameWithExtension } from "@notesnook/core";
|
||||
import type { ImageAttributes } from "@notesnook/editor";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
@@ -34,6 +34,12 @@ import {
|
||||
import BaseDialog from "../dialog/base-dialog";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import { ProgressBarComponent } from "../ui/svg/lazy";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import Share from "react-native-share";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
const ImagePreview = () => {
|
||||
const { colors } = useThemeColors("dialog");
|
||||
|
||||
@@ -41,6 +47,8 @@ const ImagePreview = () => {
|
||||
const [image, setImage] = useState<string>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const imageRef = useRef<ImageAttributes>();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const [showHeader, setShowHeader] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
eSubscribeEvent("ImagePreview", open);
|
||||
@@ -73,16 +81,86 @@ const ImagePreview = () => {
|
||||
silent: true,
|
||||
cache: true
|
||||
});
|
||||
const path = `${cacheDir}/${uri}`;
|
||||
|
||||
if (!uri) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const attachment = await db.attachments.attachment(hash);
|
||||
const path = `${cacheDir}/${
|
||||
"NN_" + (await getFileNameWithExtension(hash, attachment?.mimeType))
|
||||
}`;
|
||||
await RNFetchBlob.fs.mv(`${cacheDir}/${uri}`, path).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
setImage("file://" + path);
|
||||
setLoading(false);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
const close = React.useCallback(() => {
|
||||
image &&
|
||||
RNFetchBlob.fs.unlink(image.replace("file://", "")).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
setImage(undefined);
|
||||
setVisible(false);
|
||||
};
|
||||
}, [image]);
|
||||
|
||||
const renderHeader = React.useCallback(
|
||||
() => (
|
||||
<View
|
||||
style={{
|
||||
paddingTop: insets.top,
|
||||
backgroundColor: "rgba(0,0,0,0.3)",
|
||||
position: "absolute",
|
||||
zIndex: 999,
|
||||
display: showHeader ? "flex" : "none"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
height: 50,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
gap: 10
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
name="share"
|
||||
color="white"
|
||||
style={{
|
||||
borderWidth: 0
|
||||
}}
|
||||
onPress={async () => {
|
||||
useSettingStore
|
||||
.getState()
|
||||
.setAppDidEnterBackgroundForAction(true);
|
||||
await Share.open({
|
||||
url: image
|
||||
}).catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
useSettingStore
|
||||
.getState()
|
||||
.setAppDidEnterBackgroundForAction(false);
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
name="close"
|
||||
color="white"
|
||||
onPress={() => {
|
||||
close();
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
),
|
||||
[close, image, insets.top, showHeader]
|
||||
);
|
||||
|
||||
return (
|
||||
visible && (
|
||||
@@ -90,6 +168,7 @@ const ImagePreview = () => {
|
||||
background="black"
|
||||
animation="slide"
|
||||
visible={true}
|
||||
useSafeArea={false}
|
||||
onRequestClose={close}
|
||||
transparent
|
||||
>
|
||||
@@ -130,43 +209,25 @@ const ImagePreview = () => {
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<ImageViewer
|
||||
enableImageZoom={true}
|
||||
renderIndicator={() => <></>}
|
||||
enableSwipeDown
|
||||
useNativeDriver
|
||||
onSwipeDown={close}
|
||||
saveToLocalByLongPress={false}
|
||||
renderHeader={() => (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
height: 50,
|
||||
paddingHorizontal: 24,
|
||||
position: "absolute",
|
||||
zIndex: 999,
|
||||
backgroundColor: "rgba(0,0,0,0.3)",
|
||||
marginTop: Platform.OS === "android" ? 30 : 0
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
name="close"
|
||||
color="white"
|
||||
onPress={() => {
|
||||
close();
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
imageUrls={[
|
||||
{
|
||||
url: image as string
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<>
|
||||
<ImageViewer
|
||||
enableImageZoom={true}
|
||||
renderIndicator={() => <></>}
|
||||
enableSwipeDown
|
||||
useNativeDriver
|
||||
onSwipeDown={close}
|
||||
saveToLocalByLongPress={false}
|
||||
onClick={() => {
|
||||
setShowHeader(!showHeader);
|
||||
}}
|
||||
renderHeader={renderHeader}
|
||||
imageUrls={[
|
||||
{
|
||||
url: image as string
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -23,31 +23,28 @@ import React from "react";
|
||||
import { Linking, ScrollView, useWindowDimensions, View } from "react-native";
|
||||
import { SwiperFlatList } from "react-native-swiper-flatlist";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import Navigation from "../../services/navigation";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { eOpenLoginDialog } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AuthMode } from "../auth";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { AuthMode } from "../auth/common";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
const Intro = ({ navigation }) => {
|
||||
const Intro = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const { width, height } = useWindowDimensions();
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const isTablet = width > 600;
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
({ item }) => (
|
||||
({ item }: { item: (typeof strings.introData)[0] }) => (
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
width: deviceMode !== "mobile" ? width / 2 : width,
|
||||
paddingHorizontal:
|
||||
deviceMode !== "mobile" ? (width / 2) * 0.05 : width * 0.05
|
||||
width: isTablet ? width / 2 : width,
|
||||
paddingHorizontal: isTablet ? (width / 2) * 0.05 : width * 0.05
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -76,7 +73,7 @@ const Intro = ({ navigation }) => {
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL,
|
||||
maxWidth: "90%",
|
||||
width: "100%"
|
||||
}}
|
||||
@@ -88,21 +85,21 @@ const Intro = ({ navigation }) => {
|
||||
marginBottom: 5
|
||||
}}
|
||||
extraBold
|
||||
size={SIZE.xxl}
|
||||
size={AppFontSize.xxl}
|
||||
>
|
||||
{heading()}
|
||||
</Heading>
|
||||
))}
|
||||
|
||||
{item.body ? (
|
||||
<Paragraph size={SIZE.sm}>{item.body()}</Paragraph>
|
||||
<Paragraph size={AppFontSize.sm}>{item.body()}</Paragraph>
|
||||
) : null}
|
||||
|
||||
{item.tesimonial ? (
|
||||
<Paragraph
|
||||
style={{
|
||||
fontStyle: "italic",
|
||||
fontSize: SIZE.lg
|
||||
fontSize: AppFontSize.lg
|
||||
}}
|
||||
onPress={() => {
|
||||
Linking.openURL(item.link);
|
||||
@@ -114,31 +111,37 @@ const Intro = ({ navigation }) => {
|
||||
</View>
|
||||
</View>
|
||||
),
|
||||
[colors.primary.accent, colors.secondary.background, deviceMode, width]
|
||||
[colors.primary.accent, colors.secondary.background, isTablet, width]
|
||||
);
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
testID="notesnook.splashscreen"
|
||||
style={{
|
||||
width: "100%"
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: deviceMode !== "mobile" ? width / 2 : "100%",
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
alignSelf: deviceMode !== "mobile" ? "center" : undefined,
|
||||
borderWidth: deviceMode !== "mobile" ? 1 : null,
|
||||
borderColor: deviceMode !== "mobile" ? colors.primary.border : null,
|
||||
borderRadius: deviceMode !== "mobile" ? 20 : null,
|
||||
marginTop: deviceMode !== "mobile" ? 50 : null,
|
||||
paddingTop: insets.top + 10,
|
||||
paddingBottom: insets.top + 10,
|
||||
minHeight: height * 0.7 - (insets.top + insets.bottom)
|
||||
}}
|
||||
style={[
|
||||
{
|
||||
width: "100%",
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
paddingTop: insets.top + 10,
|
||||
paddingBottom: insets.top + 10,
|
||||
minHeight: height * 0.7 - (insets.top + insets.bottom)
|
||||
},
|
||||
isTablet && {
|
||||
width: width / 2,
|
||||
alignSelf: "center",
|
||||
borderWidth: 1,
|
||||
borderColor: colors.primary.border,
|
||||
borderRadius: 20,
|
||||
marginTop: 50
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SwiperFlatList
|
||||
autoplay
|
||||
@@ -170,23 +173,12 @@ const Intro = ({ navigation }) => {
|
||||
<Button
|
||||
width={250}
|
||||
onPress={async () => {
|
||||
eSendEvent(eOpenLoginDialog, AuthMode.welcomeSignup);
|
||||
setTimeout(() => {
|
||||
SettingsService.set({
|
||||
introCompleted: true
|
||||
});
|
||||
Navigation.replace("Notes", {
|
||||
canGoBack: false
|
||||
});
|
||||
}, 1000);
|
||||
SettingsService.set({ introCompleted: true });
|
||||
Navigation.push("Auth", {
|
||||
mode: AuthMode.welcomeSignup
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
paddingHorizontal: 24,
|
||||
alignSelf: "center",
|
||||
...getElevationStyle(5),
|
||||
borderRadius: 100
|
||||
}}
|
||||
fontSize={SIZE.md}
|
||||
fontSize={AppFontSize.md}
|
||||
type="accent"
|
||||
title={strings.getStarted()}
|
||||
/>
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user