Compare commits
644 Commits
v0.1.40
...
backup-in-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c832e6af6c | ||
|
|
fc7f46618f | ||
|
|
b2949b2dd9 | ||
|
|
9478f7d0f5 | ||
|
|
2e0ff5a10c | ||
|
|
85826d8068 | ||
|
|
9b0af72d74 | ||
|
|
eb088a259a | ||
|
|
dcd157274a | ||
|
|
19ed0ba3ab | ||
|
|
b8fe2833b6 | ||
|
|
c39bbe5984 | ||
|
|
9950d8ff6c | ||
|
|
99b467488f | ||
|
|
8dfaa4d8c6 | ||
|
|
000e4d0d61 | ||
|
|
0132408eec | ||
|
|
b4d46caee4 | ||
|
|
b2d4d90c9c | ||
|
|
f92063dfe9 | ||
|
|
ce07e467f3 | ||
|
|
f6adc99187 | ||
|
|
950645cc96 | ||
|
|
cb71b6074d | ||
|
|
bb87dc2e14 | ||
|
|
ce49e4afc6 | ||
|
|
ea89f90edb | ||
|
|
8cba22024a | ||
|
|
9044193aa3 | ||
|
|
b145bb422b | ||
|
|
5a4f67d70a | ||
|
|
09c3dbbebc | ||
|
|
ad1f85ffcc | ||
|
|
ec8eb39599 | ||
|
|
adca4c3af9 | ||
|
|
f5da57d7f7 | ||
|
|
a133b271d0 | ||
|
|
e0257445ef | ||
|
|
65c774d0d0 | ||
|
|
2e5f0a3725 | ||
|
|
ebae9b78b1 | ||
|
|
b73586f81b | ||
|
|
74d658f928 | ||
|
|
24a1572752 | ||
|
|
80099635be | ||
|
|
cd28526ccc | ||
|
|
277e10daf1 | ||
|
|
a6fb3c87c4 | ||
|
|
bf6474a488 | ||
|
|
08f8d6ac6a | ||
|
|
0c80efc686 | ||
|
|
e1b6561b70 | ||
|
|
56e91db0b4 | ||
|
|
ce3b6fdc37 | ||
|
|
885427fa85 | ||
|
|
b77365e60f | ||
|
|
b3d601aa35 | ||
|
|
f1aca45ce3 | ||
|
|
46b1902af2 | ||
|
|
96cee7e5c2 | ||
|
|
8a32811226 | ||
|
|
18dbaee4b5 | ||
|
|
9c7cea23a7 | ||
|
|
519b085dd2 | ||
|
|
66d3bc6c7b | ||
|
|
40270f219a | ||
|
|
08fba15fee | ||
|
|
379e1a354b | ||
|
|
eacfca715c | ||
|
|
4bfff4b7e9 | ||
|
|
bbad583e53 | ||
|
|
359202a594 | ||
|
|
7f68f7dd9d | ||
|
|
77a9de570c | ||
|
|
a02b193b90 | ||
|
|
789eb30d2b | ||
|
|
7a653a5bb7 | ||
|
|
c929977773 | ||
|
|
d21ec9d727 | ||
|
|
5332d0ac62 | ||
|
|
6508eb57fc | ||
|
|
b7cc19b719 | ||
|
|
925ebf3210 | ||
|
|
b1ff6805eb | ||
|
|
1f4646dbdc | ||
|
|
6612e77579 | ||
|
|
2fd6d5cd2e | ||
|
|
0531b97e19 | ||
|
|
7336b52e14 | ||
|
|
cc03275ad3 | ||
|
|
09909514d5 | ||
|
|
a3dd8cb00d | ||
|
|
d207f7e2b2 | ||
|
|
cc348e5d35 | ||
|
|
aeed19532f | ||
|
|
b52f870e29 | ||
|
|
7889c32be0 | ||
|
|
1ca91d684f | ||
|
|
648f76bd22 | ||
|
|
f8d772149b | ||
|
|
181156de10 | ||
|
|
a038a3366f | ||
|
|
c43dc175d0 | ||
|
|
fd3f7f4a37 | ||
|
|
20e5c0e292 | ||
|
|
768c639d2b | ||
|
|
9bd7f8347b | ||
|
|
ae57022102 | ||
|
|
12d95dd4b1 | ||
|
|
57ac5cb9bf | ||
|
|
24508be4d4 | ||
|
|
9136ff3ea3 | ||
|
|
4460d80b72 | ||
|
|
eb8c178603 | ||
|
|
ad08cfaad0 | ||
|
|
96845ad7cf | ||
|
|
16412821bf | ||
|
|
3c0f4402a8 | ||
|
|
260087d1fa | ||
|
|
3361ef949a | ||
|
|
dfe0e960dc | ||
|
|
98e0623cb7 | ||
|
|
97084db4de | ||
|
|
165c76bb69 | ||
|
|
e0e4145481 | ||
|
|
8c4e7e0e43 | ||
|
|
dee41dad9e | ||
|
|
47d596af08 | ||
|
|
4019a7c225 | ||
|
|
db0c199674 | ||
|
|
f11a1fd9cc | ||
|
|
892754bbae | ||
|
|
a0de0c2005 | ||
|
|
6b5f5c54dd | ||
|
|
fdac632b1c | ||
|
|
43f804c7a8 | ||
|
|
e85220d727 | ||
|
|
489d46e922 | ||
|
|
b2fdb049e2 | ||
|
|
878031cae8 | ||
|
|
aeb0b90281 | ||
|
|
83eb04eed3 | ||
|
|
aeb3916741 | ||
|
|
c42b0677d1 | ||
|
|
08f7e4900c | ||
|
|
8e5c31f7f3 | ||
|
|
23e777cd94 | ||
|
|
fe22bb1035 | ||
|
|
0da9c7f965 | ||
|
|
48adb0b256 | ||
|
|
b2eeeeddfb | ||
|
|
5ee2b6de48 | ||
|
|
4e0b5d79df | ||
|
|
027fac90b4 | ||
|
|
24118e0dc3 | ||
|
|
faf478a092 | ||
|
|
f4a7b00b1b | ||
|
|
df7680e06c | ||
|
|
bbe68974b2 | ||
|
|
aad300bd9c | ||
|
|
12ce568d3c | ||
|
|
25d3f76f9e | ||
|
|
aece93d63d | ||
|
|
45e667abf7 | ||
|
|
5869b39035 | ||
|
|
d981d57a8f | ||
|
|
1f9d1edc5c | ||
|
|
4944ee6078 | ||
|
|
c4b2b909e0 | ||
|
|
08f3928ebc | ||
|
|
350c177be8 | ||
|
|
d3103cb966 | ||
|
|
eec3721e9d | ||
|
|
8058f8f376 | ||
|
|
eae67fd482 | ||
|
|
d10727b87d | ||
|
|
52d6dc9376 | ||
|
|
6ff38e8068 | ||
|
|
b54f4a846c | ||
|
|
b69fa34b9f | ||
|
|
e1f050ac7e | ||
|
|
76668b65fc | ||
|
|
7524cc31c1 | ||
|
|
a7f832107d | ||
|
|
674b0d7285 | ||
|
|
d618a09f6d | ||
|
|
995b1fe63c | ||
|
|
6200f4f790 | ||
|
|
a16c312292 | ||
|
|
1d907bc2d4 | ||
|
|
d267ba4cde | ||
|
|
07c31ebde1 | ||
|
|
2c353628a3 | ||
|
|
3d170d88fb | ||
|
|
90171b2ebd | ||
|
|
36298893db | ||
|
|
ca7d71eed4 | ||
|
|
d0e2771a2f | ||
|
|
6b80bc5b5e | ||
|
|
016a613fd5 | ||
|
|
c6865f48f5 | ||
|
|
3935bd67f8 | ||
|
|
917993a4fa | ||
|
|
1d807002f8 | ||
|
|
f95e1138a0 | ||
|
|
1d16d74383 | ||
|
|
4713e99c7b | ||
|
|
1e122b36a3 | ||
|
|
c87335ff56 | ||
|
|
b8dd0e2fb1 | ||
|
|
3a754d5703 | ||
|
|
e39fa172cb | ||
|
|
8c0d2b67e4 | ||
|
|
12cba1f8fb | ||
|
|
bf5ae30bda | ||
|
|
2973b47555 | ||
|
|
65cba31306 | ||
|
|
629920c2b3 | ||
|
|
e0f00fd4f5 | ||
|
|
083680b563 | ||
|
|
9a637c484b | ||
|
|
175ef04c70 | ||
|
|
1d11f8316c | ||
|
|
bb8027d663 | ||
|
|
bfa5c66006 | ||
|
|
0a606f6d23 | ||
|
|
e4611508ca | ||
|
|
3a8ac8298d | ||
|
|
6ab6d0d205 | ||
|
|
30d398f5f1 | ||
|
|
85de7d08fd | ||
|
|
9f9724ce36 | ||
|
|
264acd0532 | ||
|
|
7a495c9bd6 | ||
|
|
42549bf1a6 | ||
|
|
56c97001fa | ||
|
|
4ad8b1aef8 | ||
|
|
92e8160505 | ||
|
|
0b19cdc95c | ||
|
|
c4b6674492 | ||
|
|
cd2fa7943b | ||
|
|
6245f25b7c | ||
|
|
3c29d4a19f | ||
|
|
f917952083 | ||
|
|
6fe22a5fa4 | ||
|
|
8ea12399d3 | ||
|
|
2947d9aeec | ||
|
|
17551f813b | ||
|
|
8022a0cd92 | ||
|
|
e5edaeb147 | ||
|
|
4b1bd48487 | ||
|
|
980a539ad2 | ||
|
|
cef329376b | ||
|
|
230546d57f | ||
|
|
b418ef0f99 | ||
|
|
343245f290 | ||
|
|
5ec2db5dca | ||
|
|
c21f4e1d2e | ||
|
|
a49404b877 | ||
|
|
1982408881 | ||
|
|
4e352e1430 | ||
|
|
4fa0f5ffeb | ||
|
|
fc91894336 | ||
|
|
176f66b437 | ||
|
|
0a0a5bd60f | ||
|
|
e5504e0bdf | ||
|
|
220c068822 | ||
|
|
1214c17389 | ||
|
|
a82f6b16b7 | ||
|
|
7f74574a7f | ||
|
|
6d6185dc5e | ||
|
|
21e0b08716 | ||
|
|
651a6d6b3d | ||
|
|
658054b13d | ||
|
|
58ebfb71d1 | ||
|
|
842fb976cd | ||
|
|
8c28183410 | ||
|
|
5ae1036f4d | ||
|
|
cf70854af5 | ||
|
|
da1f7bea4f | ||
|
|
a8180a04ab | ||
|
|
55acdfba2a | ||
|
|
4b4498e84d | ||
|
|
f483653506 | ||
|
|
6e3def2c13 | ||
|
|
6416378b78 | ||
|
|
3591bce1b5 | ||
|
|
174a00c469 | ||
|
|
f07cb350d4 | ||
|
|
3d8fbe05f3 | ||
|
|
1fc1d69dae | ||
|
|
af401e7b27 | ||
|
|
bc274b2cf7 | ||
|
|
488c732f3c | ||
|
|
4d58e52729 | ||
|
|
0b7d7bbe7f | ||
|
|
75ac9fd38d | ||
|
|
d8d7b6a766 | ||
|
|
aaa7bae55f | ||
|
|
1786a5e910 | ||
|
|
d9d93ffad3 | ||
|
|
032b9f1f17 | ||
|
|
eba629e5a7 | ||
|
|
41fb8b6bf4 | ||
|
|
430e6025fa | ||
|
|
57a16c4904 | ||
|
|
4525aee9a1 | ||
|
|
e99aae3ed4 | ||
|
|
a9cff1f49a | ||
|
|
f5b0f06bba | ||
|
|
f65e72fdc1 | ||
|
|
979565691f | ||
|
|
9c115efecb | ||
|
|
1ce7749826 | ||
|
|
ef232aba88 | ||
|
|
0c9619a550 | ||
|
|
8b9d20d908 | ||
|
|
1006254090 | ||
|
|
b784f94a64 | ||
|
|
34c382daea | ||
|
|
1b57be1d64 | ||
|
|
25676cff8c | ||
|
|
476c9f5840 | ||
|
|
d663f047bd | ||
|
|
7663a53e1c | ||
|
|
117d8b99df | ||
|
|
bec69b4706 | ||
|
|
0beb876cb4 | ||
|
|
666eb75779 | ||
|
|
b31bcc2a9c | ||
|
|
2c1458e026 | ||
|
|
8d65b5117e | ||
|
|
1e0c8a594f | ||
|
|
b05da7d27c | ||
|
|
00e6cf537a | ||
|
|
c2c1c6dfcc | ||
|
|
753d4cd981 | ||
|
|
86cfc10741 | ||
|
|
492e2f2e7e | ||
|
|
560cceca9b | ||
|
|
f19f8ca2a7 | ||
|
|
925cf982ca | ||
|
|
30a7e3b429 | ||
|
|
f978c6f7d7 | ||
|
|
facbf8ec02 | ||
|
|
ac338e9299 | ||
|
|
d18e6412f0 | ||
|
|
373096b339 | ||
|
|
bb4bff50b3 | ||
|
|
c9baf04505 | ||
|
|
938fec1ab3 | ||
|
|
ed9f04332e | ||
|
|
c20e8d1aea | ||
|
|
3a13f111eb | ||
|
|
6c95c683ba | ||
|
|
d666f59c1b | ||
|
|
28869498ae | ||
|
|
81104a3f38 | ||
|
|
48cdbf6b6b | ||
|
|
7e9ce00166 | ||
|
|
c3431b0921 | ||
|
|
27210317fd | ||
|
|
312b51a5fd | ||
|
|
96a649ca9c | ||
|
|
445d8168e1 | ||
|
|
24a219853e | ||
|
|
4271159294 | ||
|
|
9ff94a4402 | ||
|
|
448b8fa160 | ||
|
|
aa46eb4c6d | ||
|
|
7e35fa8710 | ||
|
|
66b9e9b5c1 | ||
|
|
ad45bafd62 | ||
|
|
4cb2b34d11 | ||
|
|
2b8482cb06 | ||
|
|
57bc6ad98e | ||
|
|
8ec8816e45 | ||
|
|
801bc2ab66 | ||
|
|
5fec87e688 | ||
|
|
9490c00f9f | ||
|
|
a14dde8183 | ||
|
|
b742cda635 | ||
|
|
2e92acbfd5 | ||
|
|
7af904d860 | ||
|
|
59289a2f40 | ||
|
|
b08d0a76ee | ||
|
|
cc8fdbe4a6 | ||
|
|
64eb477f4f | ||
|
|
68f368dc94 | ||
|
|
219d9b2a59 | ||
|
|
a7e1506ea0 | ||
|
|
f61cfaa781 | ||
|
|
66de395549 | ||
|
|
242f382da3 | ||
|
|
a879bd09b4 | ||
|
|
ed273fbfce | ||
|
|
31cb8f9fab | ||
|
|
2aeb6598dc | ||
|
|
9810083304 | ||
|
|
23fd0482c8 | ||
|
|
39f9e16a61 | ||
|
|
c3a5e759b7 | ||
|
|
f9d5aeee91 | ||
|
|
371631856a | ||
|
|
73f33bc0a3 | ||
|
|
ed595a3815 | ||
|
|
7c8fa27aaf | ||
|
|
b326b1b3d7 | ||
|
|
f527064e55 | ||
|
|
3a074ef6c5 | ||
|
|
7fbae16a53 | ||
|
|
98018ca590 | ||
|
|
7d5724d8d5 | ||
|
|
ba17a5592c | ||
|
|
7332e81e6e | ||
|
|
5aeacef511 | ||
|
|
8f3fd3b37e | ||
|
|
0fd58860a8 | ||
|
|
8edd3774d2 | ||
|
|
dd1a4cba76 | ||
|
|
1123fb3ca6 | ||
|
|
5bfd340176 | ||
|
|
7cb2f203b1 | ||
|
|
412d0a19e7 | ||
|
|
f2a50141b7 | ||
|
|
7f65ce0e84 | ||
|
|
8f577a4368 | ||
|
|
09f89afe2a | ||
|
|
d1dcdc2f50 | ||
|
|
e31a5f705c | ||
|
|
04314d507b | ||
|
|
409c5a0ba3 | ||
|
|
68f09125d4 | ||
|
|
ac2d4911fe | ||
|
|
26c2eaddff | ||
|
|
d41d6bd758 | ||
|
|
86b8bbfb5f | ||
|
|
7658b4c9fe | ||
|
|
f1384eef34 | ||
|
|
c7c4a011ef | ||
|
|
bdd73135f0 | ||
|
|
b367410aaf | ||
|
|
c27404cc3a | ||
|
|
9ff2359b4c | ||
|
|
d28ecb509e | ||
|
|
048e7d0ef6 | ||
|
|
1b49a6f3f7 | ||
|
|
fd6e4880d2 | ||
|
|
f27bbd318f | ||
|
|
79dfb11fa4 | ||
|
|
3bf9126595 | ||
|
|
a5a90dfbdd | ||
|
|
ac767f63ae | ||
|
|
782862294d | ||
|
|
eded4b2e16 | ||
|
|
707ba55e1d | ||
|
|
b71a533024 | ||
|
|
36093b9199 | ||
|
|
bd14a35e23 | ||
|
|
406da9732c | ||
|
|
219aa06835 | ||
|
|
43decb21f4 | ||
|
|
b152da9d21 | ||
|
|
14ce2305ce | ||
|
|
a65cb92f37 | ||
|
|
4a54115e31 | ||
|
|
3a80838b4c | ||
|
|
bb16da0f4a | ||
|
|
714ae7b9f0 | ||
|
|
f5f60cc9eb | ||
|
|
057c5f7ec8 | ||
|
|
d704588624 | ||
|
|
c82a6cc0e1 | ||
|
|
603d83ba1b | ||
|
|
fd15235124 | ||
|
|
0c1e5a9628 | ||
|
|
6403d135ea | ||
|
|
a7b33c48b3 | ||
|
|
4e31190291 | ||
|
|
e99a06654e | ||
|
|
cf4d0565c5 | ||
|
|
6b5a874db5 | ||
|
|
defc7a8fa4 | ||
|
|
06d568e61d | ||
|
|
6064c88f2c | ||
|
|
77b3430339 | ||
|
|
0f125cd63f | ||
|
|
65f84b116f | ||
|
|
2d008a5cc3 | ||
|
|
4dcd4483e9 | ||
|
|
0102ce2144 | ||
|
|
71d5887bcc | ||
|
|
76ce301d91 | ||
|
|
692ccd7838 | ||
|
|
bf97b1c115 | ||
|
|
6eda3c68a8 | ||
|
|
ca9a53bde9 | ||
|
|
9b9327c25e | ||
|
|
a2be4c73a1 | ||
|
|
720987197d | ||
|
|
c52e83fe10 | ||
|
|
e97e03b814 | ||
|
|
ac9b85e10b | ||
|
|
8e80af657f | ||
|
|
93f9afd98d | ||
|
|
c9178db87d | ||
|
|
18743f66e8 | ||
|
|
72ae2ec5c4 | ||
|
|
0a407d869e | ||
|
|
8c8b684cb4 | ||
|
|
63d1b86b5e | ||
|
|
49a9055a73 | ||
|
|
4f444bd3c9 | ||
|
|
1efab98f91 | ||
|
|
b8677b4c02 | ||
|
|
e6400d8305 | ||
|
|
7bdfdcc1f8 | ||
|
|
9d9c12a52d | ||
|
|
1aa9da57aa | ||
|
|
0b57e42652 | ||
|
|
9a988e82c4 | ||
|
|
9e83448cd0 | ||
|
|
8d9a3cbb1e | ||
|
|
fc5a40ace0 | ||
|
|
9f2d0b5b07 | ||
|
|
7e98e98ad0 | ||
|
|
f6085aa89b | ||
|
|
32905aa16b | ||
|
|
8a240d506b | ||
|
|
b6f07fdf85 | ||
|
|
9a7ab172ac | ||
|
|
3d6c70629d | ||
|
|
c8d13a6fa1 | ||
|
|
b24ac01493 | ||
|
|
be21da5e3b | ||
|
|
b6fea7dc9b | ||
|
|
adc2e96bf2 | ||
|
|
807dc612a7 | ||
|
|
1612de8231 | ||
|
|
4576ddf09c | ||
|
|
bb96de47be | ||
|
|
ccfc56e629 | ||
|
|
e6cde49247 | ||
|
|
1de41a8d24 | ||
|
|
953c62cc66 | ||
|
|
c2f5569010 | ||
|
|
baef35d263 | ||
|
|
8b8345d0e6 | ||
|
|
3bab677607 | ||
|
|
79362162d2 | ||
|
|
ca537c6321 | ||
|
|
79382ce6f8 | ||
|
|
ea70aa21a9 | ||
|
|
e9f1b2a9b4 | ||
|
|
981513086b | ||
|
|
9f4bfe75d1 | ||
|
|
dcd8bbbd01 | ||
|
|
cadfa5e034 | ||
|
|
76ea0c4978 | ||
|
|
c1321203c1 | ||
|
|
a8388db1c3 | ||
|
|
f0589c2f33 | ||
|
|
fbe7d64d5d | ||
|
|
a5722b2445 | ||
|
|
18e57e20ed | ||
|
|
5b83bf35d0 | ||
|
|
97fb8d8561 | ||
|
|
fe5a72ae90 | ||
|
|
643a48afec | ||
|
|
e50598dfa3 | ||
|
|
46fb1f0ac0 | ||
|
|
eaaeee6ee7 | ||
|
|
522c5562eb | ||
|
|
ef8c63c176 | ||
|
|
12537ec7f3 | ||
|
|
8b791ed19e | ||
|
|
a1fdd0c0d2 | ||
|
|
0b7097082d | ||
|
|
c903192d7c | ||
|
|
d8b5e0f5a1 | ||
|
|
fb8d476c34 | ||
|
|
b4652e2486 | ||
|
|
b0d37dc997 | ||
|
|
778930015e | ||
|
|
fb8c45a86d | ||
|
|
13b0533277 | ||
|
|
6cb6b1534d | ||
|
|
4098f2e498 | ||
|
|
19093a4ead | ||
|
|
c7e2cb9d06 | ||
|
|
6be22170d8 | ||
|
|
2077b0ab4c | ||
|
|
bc2ca86a95 | ||
|
|
a443582ca9 | ||
|
|
2162397575 | ||
|
|
6cfb32570b | ||
|
|
a8d66fd0a4 | ||
|
|
b46d3d28a9 | ||
|
|
44372059cd | ||
|
|
64668147d9 | ||
|
|
e21e71b442 | ||
|
|
10f646f125 | ||
|
|
187b76e04c | ||
|
|
f2c36cae37 | ||
|
|
77a6357ae4 | ||
|
|
6c1be53f86 | ||
|
|
59e02f047b | ||
|
|
321250f30e | ||
|
|
7559036abe | ||
|
|
6080b57f8a | ||
|
|
ddf654a641 | ||
|
|
bf8e187962 | ||
|
|
cf252c3ec4 | ||
|
|
d65541fb3e | ||
|
|
20ef99cfc7 | ||
|
|
87a003918d | ||
|
|
f5c3c90f37 | ||
|
|
ee690092ba | ||
|
|
34a386abce | ||
|
|
05f4c42bf9 | ||
|
|
1510dc9bc2 | ||
|
|
2a85b621f5 | ||
|
|
97abf2f230 | ||
|
|
1f2d38730d | ||
|
|
5c000f50aa | ||
|
|
581931b679 | ||
|
|
709a91b1db | ||
|
|
6ddb8d01ec | ||
|
|
81d3144da2 | ||
|
|
be2e8b7169 | ||
|
|
a21774031b | ||
|
|
de4dc9b9a0 | ||
|
|
7ae2859c78 | ||
|
|
8228472031 | ||
|
|
7ca5a2bd02 | ||
|
|
8858c7f619 | ||
|
|
992d7e3303 | ||
|
|
bce135cba8 | ||
|
|
8c30bedefb | ||
|
|
e26f2910f3 | ||
|
|
0b1208a11c | ||
|
|
261919f17a | ||
|
|
4206cbd557 | ||
|
|
edc203c9ed |
2
.bundle/config
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
BUNDLE_PATH: "vendor/bundle"
|
||||||
9
.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
2
.env
@@ -1,4 +1,4 @@
|
|||||||
VITE_APP_API_URL=http://localhost:2021
|
VITE_APP_API_URL=http://localhost:2021
|
||||||
VITE_IS_LANDING_DEFAULT=false
|
VITE_IS_LANDING_DEFAULT=true
|
||||||
VITE_OPENREPLAY_PROJECT_KEY=
|
VITE_OPENREPLAY_PROJECT_KEY=
|
||||||
VITE_IS_SELF_HOSTED=false
|
VITE_IS_SELF_HOSTED=false
|
||||||
@@ -2,3 +2,4 @@ VITE_APP_API_URL=http://localhost:2021
|
|||||||
VITE_APP_REDIRECT_URL=http://localhost:5173
|
VITE_APP_REDIRECT_URL=http://localhost:5173
|
||||||
VITE_APP_GOOGLE_CLIENT_ID=USE_YOUR_OWN_CLIENT_ID
|
VITE_APP_GOOGLE_CLIENT_ID=USE_YOUR_OWN_CLIENT_ID
|
||||||
VITE_IS_SELF_HOSTED=true
|
VITE_IS_SELF_HOSTED=true
|
||||||
|
VITE_IS_LANDING_DEFAULT=true
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: { browser: true, es2020: true },
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:react/recommended',
|
|
||||||
'plugin:react/jsx-runtime',
|
|
||||||
'plugin:react-hooks/recommended',
|
|
||||||
'plugin:prettier/recommended',
|
|
||||||
'plugin:tailwindcss/recommended',
|
|
||||||
],
|
|
||||||
ignorePatterns: [
|
|
||||||
'dist',
|
|
||||||
'.eslintrc.cjs',
|
|
||||||
'tailwind.config.js',
|
|
||||||
'postcss.config.js',
|
|
||||||
],
|
|
||||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
|
||||||
settings: { react: { version: '18.2' } },
|
|
||||||
plugins: [
|
|
||||||
'react-refresh',
|
|
||||||
'simple-import-sort',
|
|
||||||
'sort-destructure-keys',
|
|
||||||
'sort-keys-fix',
|
|
||||||
'prettier',
|
|
||||||
|
|
||||||
'tailwindcss',
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
'react-refresh/only-export-components': [
|
|
||||||
'warn',
|
|
||||||
{ allowConstantExport: true },
|
|
||||||
],
|
|
||||||
'react/prop-types': 'off',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
25
.github/workflows/build.yml
vendored
@@ -2,39 +2,20 @@ name: Build validation
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main", "develop" ]
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main", "develop" ]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [20.x, 22.x]
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js 20
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: 20.x
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm i
|
run: npm i
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
|
- run: npm run lint:ci
|
||||||
lint:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Use Node.js 22
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 22
|
|
||||||
cache: 'npm'
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm i
|
|
||||||
# Lint currently reporting lots of preexisting issues
|
|
||||||
# - run: npm run lint
|
|
||||||
|
|||||||
26
.gitignore
vendored
@@ -23,4 +23,28 @@ dist-ssr
|
|||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
resources/android/**/*
|
resources/android/**/*
|
||||||
|
resources/ios/**/*
|
||||||
|
|
||||||
|
# Secrets (pulled via scripts/pull-secrets.sh)
|
||||||
|
.env.production
|
||||||
|
android/app/google-services.json
|
||||||
|
android/app/release/*.jks
|
||||||
|
android/keystore.properties
|
||||||
|
android/local.properties
|
||||||
|
android/play-service-account.json
|
||||||
|
ios/App/App/GoogleService-Info.plist
|
||||||
|
ios/*.p8
|
||||||
|
|
||||||
|
# Release build outputs
|
||||||
|
build/android
|
||||||
|
build/ios
|
||||||
|
fastlane/report.xml
|
||||||
|
fastlane/Preview.html
|
||||||
|
fastlane/.env
|
||||||
|
fastlane/*.log
|
||||||
|
vendor/bundle
|
||||||
|
|
||||||
|
# Claude Code skills cache
|
||||||
|
.agents/
|
||||||
|
skills-lock.json
|
||||||
2
.prettierignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
LICENSE.md
|
||||||
|
dist/
|
||||||
13
.prettierrc
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"arrowParens": "avoid",
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"endOfLine": "auto",
|
|
||||||
"jsxBracketSameLine": false,
|
|
||||||
"printWidth": 80,
|
|
||||||
"semi": false,
|
|
||||||
"singleQuote": true,
|
|
||||||
"jsxSingleQuote": true,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"plugins": ["prettier-plugin-tailwindcss"]
|
|
||||||
}
|
|
||||||
85
AUTOMATED_RELEASE.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Automated Release Pipeline
|
||||||
|
|
||||||
|
Builds and (optionally) uploads signed Android and iOS release artifacts from the CLI — no Android Studio or Xcode GUI needed for routine releases.
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./scripts/pull-secrets.sh # pulls all signing secrets from Vaultwarden
|
||||||
|
./scripts/release.sh # bump patch version, build + sign both platforms
|
||||||
|
./scripts/release.sh --upload # also upload: Android → Play internal track, iOS → TestFlight
|
||||||
|
```
|
||||||
|
|
||||||
|
Common flags on `release.sh`:
|
||||||
|
|
||||||
|
| Flag | Effect |
|
||||||
|
|---|---|
|
||||||
|
| `--android` / `--ios` | Build only one platform |
|
||||||
|
| `--bump minor\|major` | Bump type (default: `patch`) |
|
||||||
|
| `--skip-bump` | Build with the current version, don't bump |
|
||||||
|
| `--upload` | Upload after building |
|
||||||
|
| `--track TRACK` | Play Store track for `--upload` (default: `internal`) |
|
||||||
|
|
||||||
|
Outputs:
|
||||||
|
- Android: `android/app/build/outputs/bundle/release/app-release.aab`
|
||||||
|
- iOS: `build/ios/Donetick.ipa`
|
||||||
|
|
||||||
|
## One-time machine setup
|
||||||
|
|
||||||
|
1. `bundle install` — installs fastlane (Ruby gems, local to the repo via `vendor/bundle`)
|
||||||
|
2. `./scripts/pull-secrets.sh` — pulls all secrets below from Vaultwarden
|
||||||
|
3. `android/local.properties` needs `sdk.dir=<path to Android SDK>` — not pulled from vault, machine-specific. Android Studio creates this automatically; if building headless-only, create it manually.
|
||||||
|
4. Export `ASC_ISSUER_ID` (App Store Connect API key issuer ID) — either in your shell profile, or add it to the `Donetick Env Production` vault note so `pull-secrets.sh` delivers it into `.env.production` (which `release.sh` auto-sources).
|
||||||
|
5. A working Apple Distribution certificate + the `Donetick App Store(fastline)` provisioning profile must be installed in your keychain / `~/Library/MobileDevice/Provisioning Profiles/` (see "iOS signing model" below) — **not** currently pulled from vault, machine-specific one-time setup.
|
||||||
|
|
||||||
|
## Secrets pulled by `scripts/pull-secrets.sh`
|
||||||
|
|
||||||
|
All secrets live in Vaultwarden (`https://www.bitwarden.com`) as Secure Notes, named exactly as below:
|
||||||
|
|
||||||
|
| Vault item name | Written to | Encoding |
|
||||||
|
|---|---|---|
|
||||||
|
| Donetick Google Services Android | `android/app/google-services.json` | raw |
|
||||||
|
| Donetick Android Keystore | `android/app/release/donetick.jks` | base64 |
|
||||||
|
| Donetick Keystore Password | (used inline for `android/keystore.properties`) | raw |
|
||||||
|
| Donetick Google Play Service Account | `android/play-service-account.json` | raw |
|
||||||
|
| Donetick Google Services iOS | `ios/App/App/GoogleService-Info.plist` | raw |
|
||||||
|
| Donetick App Store Connect Key | `ios/AuthKey_84F695CDQ3.p8` | base64 |
|
||||||
|
| Donetick Env Production | `.env.production` | raw |
|
||||||
|
|
||||||
|
None of these files are committed to git — all covered by `.gitignore`.
|
||||||
|
|
||||||
|
## iOS signing model (important — don't relitigate this)
|
||||||
|
|
||||||
|
The App target uses **manual signing for Release**, not Automatic. This was a deliberate choice, not the default:
|
||||||
|
|
||||||
|
- The project has **two** Apple Distribution-type certificates on the Apple Developer account: an Xcode Cloud–managed one (private key never leaves Apple, can't be used for local CLI signing) and a plain one created via Xcode → Settings → Accounts → Manage Certificates (private key is in the local keychain, usable). Automatic signing kept binding to the wrong (Managed) one.
|
||||||
|
- Fix: `ios/App/App.xcodeproj/project.pbxproj` → App target → Release config has `CODE_SIGN_STYLE = Manual`, `CODE_SIGN_IDENTITY = "Apple Distribution"`, `PROVISIONING_PROFILE_SPECIFIER = "Donetick App Store(fastline)"`. Debug config is untouched (still Automatic, for local dev builds in Xcode).
|
||||||
|
- `fastlane/Fastfile`'s `ios release` lane passes explicit `export_options: { signingStyle: "manual", provisioningProfiles: {...} }` to `build_app` — do **not** switch this back to `-allowProvisioningUpdates`/automatic without a good reason, it re-triggers the ambiguous cert selection.
|
||||||
|
|
||||||
|
**If you ever add/change an entitlement** (new Capability in Signing & Capabilities, e.g. a new permission), the existing provisioning profile becomes stale and exports will fail with an error like `"App.app" requires a provisioning profile with the X feature`. Fix:
|
||||||
|
1. developer.apple.com → Identifiers → `com.donetick.app` → confirm the new capability is checked
|
||||||
|
2. developer.apple.com → Profiles → find `Donetick App Store(fastline)` → regenerate it → download
|
||||||
|
3. Install it locally: get its UUID (`security cms -D -i <file>.mobileprovision | plutil -extract UUID xml1 -o - -`) and copy to `~/Library/MobileDevice/Provisioning Profiles/<uuid>.mobileprovision`
|
||||||
|
|
||||||
|
**If the Distribution certificate ever expires/is revoked**, redo the one-time Xcode step: Settings → Accounts → Manage Certificates → `+` → Apple Distribution, then regenerate the profile as above.
|
||||||
|
|
||||||
|
## Android signing model
|
||||||
|
|
||||||
|
Standard keystore signing via `android/keystore.properties`, read by `android/app/build.gradle`. One historical gotcha already fixed: `storeFile` must be `release/donetick.jks` (relative to the `android/app` module dir), **not** `app/release/donetick.jks` — that extra `app/` silently resolved to a double-nested wrong path and broke CLI builds (Android Studio may have masked this before).
|
||||||
|
|
||||||
|
## Play Store upload gotchas
|
||||||
|
|
||||||
|
- The service account (`fastline-471@donetick-5f910.iam.gserviceaccount.com`) needs the **Google Play Android Developer API enabled** in its GCP project (`donetick-5f910`), **and** explicit per-app release permission granted in Play Console → Users and permissions (not just account-level access) — **and you have to actually click Save**, the UI will silently not persist it otherwise.
|
||||||
|
- Google Play Console permission changes can take time to propagate on Google's backend even after saving (historically documented as up to ~24h, though in practice it was near-instant once actually saved).
|
||||||
|
- The Play Developer API requires the app to have had **at least one manual release** uploaded through the Play Console website before API uploads work at all — not an issue for Donetick (already satisfied) but relevant if this is ever set up for a brand-new app.
|
||||||
|
|
||||||
|
## Fastlane lanes reference
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bundle exec fastlane android release # build signed .aab
|
||||||
|
bundle exec fastlane android apk # build signed .apk (sideloading/testing)
|
||||||
|
bundle exec fastlane android upload [track:X] # upload .aab to Play Store (default track: internal)
|
||||||
|
|
||||||
|
bundle exec fastlane ios release # build signed .ipa
|
||||||
|
bundle exec fastlane ios upload # upload .ipa to TestFlight
|
||||||
|
```
|
||||||
233
Gemfile.lock
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
CFPropertyList (3.0.8)
|
||||||
|
abbrev (0.1.2)
|
||||||
|
addressable (2.9.0)
|
||||||
|
public_suffix (>= 2.0.2, < 8.0)
|
||||||
|
artifactory (3.0.17)
|
||||||
|
atomos (0.1.3)
|
||||||
|
aws-eventstream (1.3.2)
|
||||||
|
aws-partitions (1.1109.0)
|
||||||
|
aws-sdk-core (3.224.1)
|
||||||
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
|
aws-partitions (~> 1, >= 1.992.0)
|
||||||
|
aws-sigv4 (~> 1.9)
|
||||||
|
base64
|
||||||
|
jmespath (~> 1, >= 1.6.1)
|
||||||
|
logger
|
||||||
|
aws-sdk-kms (1.101.0)
|
||||||
|
aws-sdk-core (~> 3, >= 3.216.0)
|
||||||
|
aws-sigv4 (~> 1.5)
|
||||||
|
aws-sdk-s3 (1.188.0)
|
||||||
|
aws-sdk-core (~> 3, >= 3.224.1)
|
||||||
|
aws-sdk-kms (~> 1)
|
||||||
|
aws-sigv4 (~> 1.5)
|
||||||
|
aws-sigv4 (1.11.0)
|
||||||
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
|
babosa (1.0.4)
|
||||||
|
base64 (0.2.0)
|
||||||
|
claide (1.1.0)
|
||||||
|
colored (1.2)
|
||||||
|
colored2 (3.1.2)
|
||||||
|
commander (4.6.0)
|
||||||
|
highline (~> 2.0.0)
|
||||||
|
csv (3.3.5)
|
||||||
|
declarative (0.0.20)
|
||||||
|
digest-crc (0.7.0)
|
||||||
|
rake (>= 12.0.0, < 14.0.0)
|
||||||
|
domain_name (0.5.20190701)
|
||||||
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
|
dotenv (2.8.1)
|
||||||
|
emoji_regex (3.2.3)
|
||||||
|
excon (0.109.0)
|
||||||
|
faraday (1.10.6)
|
||||||
|
faraday-em_http (~> 1.0)
|
||||||
|
faraday-em_synchrony (~> 1.0)
|
||||||
|
faraday-excon (~> 1.1)
|
||||||
|
faraday-httpclient (~> 1.0)
|
||||||
|
faraday-multipart (~> 1.0)
|
||||||
|
faraday-net_http (~> 1.0)
|
||||||
|
faraday-net_http_persistent (~> 1.0)
|
||||||
|
faraday-patron (~> 1.0)
|
||||||
|
faraday-rack (~> 1.0)
|
||||||
|
faraday-retry (~> 1.0)
|
||||||
|
ruby2_keywords (>= 0.0.4)
|
||||||
|
faraday-cookie_jar (0.0.8)
|
||||||
|
faraday (>= 0.8.0)
|
||||||
|
http-cookie (>= 1.0.0)
|
||||||
|
faraday-em_http (1.0.0)
|
||||||
|
faraday-em_synchrony (1.0.1)
|
||||||
|
faraday-excon (1.1.0)
|
||||||
|
faraday-httpclient (1.0.1)
|
||||||
|
faraday-multipart (1.2.0)
|
||||||
|
multipart-post (~> 2.0)
|
||||||
|
faraday-net_http (1.0.2)
|
||||||
|
faraday-net_http_persistent (1.2.0)
|
||||||
|
faraday-patron (1.0.0)
|
||||||
|
faraday-rack (1.0.0)
|
||||||
|
faraday-retry (1.0.4)
|
||||||
|
faraday_middleware (1.2.1)
|
||||||
|
faraday (~> 1.0)
|
||||||
|
fastimage (2.4.1)
|
||||||
|
fastlane (2.230.0)
|
||||||
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
|
abbrev (~> 0.1.2)
|
||||||
|
addressable (>= 2.8, < 3.0.0)
|
||||||
|
artifactory (~> 3.0)
|
||||||
|
aws-sdk-s3 (~> 1.0)
|
||||||
|
babosa (>= 1.0.3, < 2.0.0)
|
||||||
|
base64 (~> 0.2.0)
|
||||||
|
bundler (>= 1.12.0, < 3.0.0)
|
||||||
|
colored (~> 1.2)
|
||||||
|
commander (~> 4.6)
|
||||||
|
csv (~> 3.3)
|
||||||
|
dotenv (>= 2.1.1, < 3.0.0)
|
||||||
|
emoji_regex (>= 0.1, < 4.0)
|
||||||
|
excon (>= 0.71.0, < 1.0.0)
|
||||||
|
faraday (~> 1.0)
|
||||||
|
faraday-cookie_jar (~> 0.0.6)
|
||||||
|
faraday_middleware (~> 1.0)
|
||||||
|
fastimage (>= 2.1.0, < 3.0.0)
|
||||||
|
fastlane-sirp (>= 1.0.0)
|
||||||
|
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||||
|
google-apis-androidpublisher_v3 (~> 0.3)
|
||||||
|
google-apis-playcustomapp_v1 (~> 0.1)
|
||||||
|
google-cloud-env (>= 1.6.0, < 2.0.0)
|
||||||
|
google-cloud-storage (~> 1.31)
|
||||||
|
highline (~> 2.0)
|
||||||
|
http-cookie (~> 1.0.5)
|
||||||
|
json (< 3.0.0)
|
||||||
|
jwt (>= 2.1.0, < 3)
|
||||||
|
logger (>= 1.6, < 2.0)
|
||||||
|
mini_magick (>= 4.9.4, < 5.0.0)
|
||||||
|
multipart-post (>= 2.0.0, < 3.0.0)
|
||||||
|
mutex_m (~> 0.3.0)
|
||||||
|
naturally (~> 2.2)
|
||||||
|
nkf (~> 0.2.0)
|
||||||
|
optparse (>= 0.1.1, < 1.0.0)
|
||||||
|
plist (>= 3.1.0, < 4.0.0)
|
||||||
|
rubyzip (>= 2.0.0, < 3.0.0)
|
||||||
|
security (= 0.1.5)
|
||||||
|
simctl (~> 1.6.3)
|
||||||
|
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||||
|
terminal-table (~> 3)
|
||||||
|
tty-screen (>= 0.6.3, < 1.0.0)
|
||||||
|
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||||
|
word_wrap (~> 1.0.0)
|
||||||
|
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||||
|
xcpretty (~> 0.4.1)
|
||||||
|
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
|
||||||
|
fastlane-sirp (1.1.0)
|
||||||
|
gh_inspector (1.1.3)
|
||||||
|
google-apis-androidpublisher_v3 (0.54.0)
|
||||||
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
|
google-apis-core (0.11.3)
|
||||||
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
|
httpclient (>= 2.8.1, < 3.a)
|
||||||
|
mini_mime (~> 1.0)
|
||||||
|
representable (~> 3.0)
|
||||||
|
retriable (>= 2.0, < 4.a)
|
||||||
|
rexml
|
||||||
|
google-apis-iamcredentials_v1 (0.17.0)
|
||||||
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
|
google-apis-playcustomapp_v1 (0.13.0)
|
||||||
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
|
google-apis-storage_v1 (0.29.0)
|
||||||
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
|
google-cloud-core (1.6.1)
|
||||||
|
google-cloud-env (>= 1.0, < 3.a)
|
||||||
|
google-cloud-errors (~> 1.0)
|
||||||
|
google-cloud-env (1.6.0)
|
||||||
|
faraday (>= 0.17.3, < 3.0)
|
||||||
|
google-cloud-errors (1.3.1)
|
||||||
|
google-cloud-storage (1.45.0)
|
||||||
|
addressable (~> 2.8)
|
||||||
|
digest-crc (~> 0.4)
|
||||||
|
google-apis-iamcredentials_v1 (~> 0.1)
|
||||||
|
google-apis-storage_v1 (~> 0.29.0)
|
||||||
|
google-cloud-core (~> 1.6)
|
||||||
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
|
mini_mime (~> 1.0)
|
||||||
|
googleauth (1.8.1)
|
||||||
|
faraday (>= 0.17.3, < 3.a)
|
||||||
|
jwt (>= 1.4, < 3.0)
|
||||||
|
multi_json (~> 1.11)
|
||||||
|
os (>= 0.9, < 2.0)
|
||||||
|
signet (>= 0.16, < 2.a)
|
||||||
|
highline (2.0.3)
|
||||||
|
http-cookie (1.0.8)
|
||||||
|
domain_name (~> 0.5)
|
||||||
|
httpclient (2.9.0)
|
||||||
|
mutex_m
|
||||||
|
jmespath (1.6.2)
|
||||||
|
json (2.7.6)
|
||||||
|
jwt (2.10.3)
|
||||||
|
base64
|
||||||
|
logger (1.7.0)
|
||||||
|
mini_magick (4.13.2)
|
||||||
|
mini_mime (1.1.5)
|
||||||
|
multi_json (1.15.0)
|
||||||
|
multipart-post (2.4.1)
|
||||||
|
mutex_m (0.3.0)
|
||||||
|
nanaimo (0.4.0)
|
||||||
|
naturally (2.3.0)
|
||||||
|
nkf (0.2.0)
|
||||||
|
optparse (0.8.1)
|
||||||
|
os (1.1.4)
|
||||||
|
plist (3.7.2)
|
||||||
|
public_suffix (5.1.1)
|
||||||
|
rake (13.4.2)
|
||||||
|
representable (3.2.0)
|
||||||
|
declarative (< 0.1.0)
|
||||||
|
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||||
|
uber (< 0.2.0)
|
||||||
|
retriable (3.8.0)
|
||||||
|
rexml (3.4.4)
|
||||||
|
rouge (3.28.0)
|
||||||
|
ruby2_keywords (0.0.5)
|
||||||
|
rubyzip (2.4.1)
|
||||||
|
security (0.1.5)
|
||||||
|
signet (0.18.0)
|
||||||
|
addressable (~> 2.8)
|
||||||
|
faraday (>= 0.17.5, < 3.a)
|
||||||
|
jwt (>= 1.5, < 3.0)
|
||||||
|
multi_json (~> 1.10)
|
||||||
|
simctl (1.6.10)
|
||||||
|
CFPropertyList
|
||||||
|
naturally
|
||||||
|
terminal-notifier (2.0.0)
|
||||||
|
terminal-table (3.0.2)
|
||||||
|
unicode-display_width (>= 1.1.1, < 3)
|
||||||
|
trailblazer-option (0.1.2)
|
||||||
|
tty-cursor (0.7.1)
|
||||||
|
tty-screen (0.8.2)
|
||||||
|
tty-spinner (0.9.3)
|
||||||
|
tty-cursor (~> 0.7)
|
||||||
|
uber (0.1.0)
|
||||||
|
unf (0.2.0)
|
||||||
|
unicode-display_width (2.6.0)
|
||||||
|
word_wrap (1.0.0)
|
||||||
|
xcodeproj (1.28.1)
|
||||||
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
|
atomos (~> 0.1.3)
|
||||||
|
base64
|
||||||
|
claide (>= 1.0.2, < 2.0)
|
||||||
|
colored2 (~> 3.1)
|
||||||
|
nanaimo (~> 0.4.0)
|
||||||
|
nkf
|
||||||
|
rexml (>= 3.3.6, < 4.0)
|
||||||
|
xcpretty (0.4.1)
|
||||||
|
rouge (~> 3.28.0)
|
||||||
|
xcpretty-travis-formatter (1.0.1)
|
||||||
|
xcpretty (~> 0.2, >= 0.0.7)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
fastlane
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.4.10
|
||||||
@@ -20,12 +20,9 @@ As an avid for open-source, I was eager to create a solution that could benefit
|
|||||||
- Recurring Tasks: Schedule tasks to repeat daily, weekly, monthly, or yearly, with flexible customization options.
|
- Recurring Tasks: Schedule tasks to repeat daily, weekly, monthly, or yearly, with flexible customization options.
|
||||||
- Progress Tracking: Track the completion status of tasks and view historical data.
|
- Progress Tracking: Track the completion status of tasks and view historical data.
|
||||||
|
|
||||||
## Installation
|
## Development Environment
|
||||||
|
|
||||||
1. Clone the repository:
|
Follow the full instructions here: https://github.com/donetick/donetick?tab=readme-ov-file#development-environment
|
||||||
2. Navigate to the project directory: `cd frontend`
|
|
||||||
3. Download dependency `npm install`
|
|
||||||
4. Run locally `npm start`
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
@@ -41,7 +38,7 @@ Contributions are welcome! If you would like to contribute to Donetick, please f
|
|||||||
|
|
||||||
Donetick is a work in progress and has been a fantastic learning experience for me as I've honed my React skills,I'm looking for collaborators to help improve and refine the Donetick. Feel free to open PR or suggest changes.
|
Donetick is a work in progress and has been a fantastic learning experience for me as I've honed my React skills,I'm looking for collaborators to help improve and refine the Donetick. Feel free to open PR or suggest changes.
|
||||||
|
|
||||||
## Plans :
|
## Plans:
|
||||||
|
|
||||||
My goal is to expand Donetick by offering a hosted infrastructure option. This will make it even easier for users to access and utilize Donetick's features without the need for self-hosting.
|
My goal is to expand Donetick by offering a hosted infrastructure option. This will make it even easier for users to access and utilize Donetick's features without the need for self-hosting.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
def keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace "com.donetick.app"
|
namespace "com.donetick.app"
|
||||||
compileSdk rootProject.ext.compileSdkVersion
|
compileSdk rootProject.ext.compileSdkVersion
|
||||||
@@ -7,8 +13,8 @@ android {
|
|||||||
applicationId "com.donetick.app"
|
applicationId "com.donetick.app"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 1
|
versionCode 55
|
||||||
versionName "1.0"
|
versionName "1.2.34"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
@@ -16,8 +22,19 @@ android {
|
|||||||
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
|
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
storeFile file(keystoreProperties['storeFile'])
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
signingConfig keystorePropertiesFile.exists() ? signingConfigs.release : signingConfigs.debug
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,25 @@ android {
|
|||||||
|
|
||||||
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation project(':capacitor-community-in-app-review')
|
||||||
|
implementation project(':capacitor-community-speech-recognition')
|
||||||
|
implementation project(':capacitor-community-sqlite')
|
||||||
implementation project(':capacitor-app')
|
implementation project(':capacitor-app')
|
||||||
|
implementation project(':capacitor-browser')
|
||||||
implementation project(':capacitor-device')
|
implementation project(':capacitor-device')
|
||||||
|
implementation project(':capacitor-haptics')
|
||||||
|
implementation project(':capacitor-local-llm')
|
||||||
implementation project(':capacitor-local-notifications')
|
implementation project(':capacitor-local-notifications')
|
||||||
|
implementation project(':capacitor-network')
|
||||||
implementation project(':capacitor-preferences')
|
implementation project(':capacitor-preferences')
|
||||||
implementation project(':capacitor-push-notifications')
|
implementation project(':capacitor-push-notifications')
|
||||||
implementation project(':capacitor-status-bar')
|
implementation project(':capacitor-status-bar')
|
||||||
|
implementation project(':capgo-capacitor-document-scanner')
|
||||||
|
implementation project(':capgo-capacitor-nfc')
|
||||||
implementation project(':capgo-capacitor-social-login')
|
implementation project(':capgo-capacitor-social-login')
|
||||||
|
implementation project(':jcesarmobile-capacitor-ocr')
|
||||||
|
implementation project(':revenuecat-purchases-capacitor')
|
||||||
|
implementation project(':revenuecat-purchases-capacitor-ui')
|
||||||
implementation project(':capacitor-plugin-safe-area')
|
implementation project(':capacitor-plugin-safe-area')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
37
android/app/release/output-metadata.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"artifactType": {
|
||||||
|
"type": "APK",
|
||||||
|
"kind": "Directory"
|
||||||
|
},
|
||||||
|
"applicationId": "com.donetick.app",
|
||||||
|
"variantName": "release",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "SINGLE",
|
||||||
|
"filters": [],
|
||||||
|
"attributes": [],
|
||||||
|
"versionCode": 25,
|
||||||
|
"versionName": "1.2.3",
|
||||||
|
"outputFile": "app-release.apk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"elementType": "File",
|
||||||
|
"baselineProfiles": [
|
||||||
|
{
|
||||||
|
"minApi": 28,
|
||||||
|
"maxApi": 30,
|
||||||
|
"baselineProfiles": [
|
||||||
|
"baselineProfiles/1/app-release.dm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minApi": 31,
|
||||||
|
"maxApi": 2147483647,
|
||||||
|
"baselineProfiles": [
|
||||||
|
"baselineProfiles/0/app-release.dm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minSdkVersionForDexing": 28
|
||||||
|
}
|
||||||
@@ -1,14 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
android:usesCleartextTraffic="true">
|
||||||
<activity
|
<activity
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation"
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/title_activity_main"
|
android:label="@string/title_activity_main"
|
||||||
android:theme="@style/AppTheme.NoActionBarLaunch"
|
android:theme="@style/AppTheme.NoActionBarLaunch"
|
||||||
@@ -18,6 +21,79 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Deep link intent filter for OAuth and other donetick:// URLs -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="donetick" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- NFC NDEF dispatch: open app directly when a donetick:// tag is scanned -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:scheme="donetick" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<!-- Home-screen widgets -->
|
||||||
|
<receiver
|
||||||
|
android:name=".widget.TodayWidgetProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/widget_today_label">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/widget_today_info" />
|
||||||
|
</receiver>
|
||||||
|
<receiver
|
||||||
|
android:name=".widget.WeekWidgetProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/widget_week_label">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/widget_week_info" />
|
||||||
|
</receiver>
|
||||||
|
<receiver
|
||||||
|
android:name=".widget.PeopleWidgetProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/widget_people_label">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/widget_people_info" />
|
||||||
|
</receiver>
|
||||||
|
<receiver
|
||||||
|
android:name=".widget.QuickCaptureWidgetProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/widget_quick_label">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/widget_quick_capture_info" />
|
||||||
|
</receiver>
|
||||||
|
<service
|
||||||
|
android:name=".widget.WidgetListService"
|
||||||
|
android:exported="false"
|
||||||
|
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||||
|
<activity
|
||||||
|
android:name=".widget.WidgetConfigActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
@@ -27,9 +103,44 @@
|
|||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
|
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<!-- Push Notifications Icon -->
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||||
|
android:resource="@mipmap/ic_launcher" />
|
||||||
|
|
||||||
|
<!-- Push Notifications Color -->
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.firebase.messaging.default_notification_color"
|
||||||
|
android:resource="@android:color/white" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<!-- Permissions -->
|
<!-- Permissions -->
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
|
||||||
|
<!-- Android 14+ partial photo access via system photo picker; no READ_MEDIA_IMAGES needed -->
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
|
||||||
|
<!-- Explicitly block READ_MEDIA_IMAGES if any library injects it -->
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" tools:node="remove" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" tools:node="remove" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
<!-- Remove advertising ID / Privacy Sandbox ad permissions if transitive SDKs inject them. -->
|
||||||
|
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" tools:node="remove" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" tools:node="remove" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE" tools:node="remove" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_ADSERVICES_TOPICS" tools:node="remove" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
|
|
||||||
|
<!-- Voice task capture -->
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||||
|
<!-- Android 11+ package visibility for the system speech recognition service -->
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.speech.RecognitionService" />
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -2,4 +2,57 @@ package com.donetick.app;
|
|||||||
|
|
||||||
import com.getcapacitor.BridgeActivity;
|
import com.getcapacitor.BridgeActivity;
|
||||||
|
|
||||||
public class MainActivity extends BridgeActivity {}
|
import ee.forgr.capacitor.social.login.GoogleProvider;
|
||||||
|
import ee.forgr.capacitor.social.login.SocialLoginPlugin;
|
||||||
|
import ee.forgr.capacitor.social.login.ModifiedMainActivityForSocialLoginPlugin;
|
||||||
|
import com.getcapacitor.PluginHandle;
|
||||||
|
import com.getcapacitor.Plugin;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.nfc.NfcAdapter;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.donetick.app.widget.WidgetBridgePlugin;
|
||||||
|
|
||||||
|
|
||||||
|
public class MainActivity extends BridgeActivity implements ModifiedMainActivityForSocialLoginPlugin {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
registerPlugin(WidgetBridgePlugin.class);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capacitor only forwards ACTION_VIEW deep links, so normalize Donetick NFC intents before dispatch.
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())
|
||||||
|
&& intent.getData() != null
|
||||||
|
&& "donetick".equals(intent.getData().getScheme())) {
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
}
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
if (requestCode >= GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MIN && requestCode < GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MAX) {
|
||||||
|
PluginHandle pluginHandle = getBridge().getPlugin("SocialLogin");
|
||||||
|
if (pluginHandle == null) {
|
||||||
|
Log.i("Google Activity Result", "SocialLogin login handle is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Plugin plugin = pluginHandle.getInstance();
|
||||||
|
if (!(plugin instanceof SocialLoginPlugin)) {
|
||||||
|
Log.i("Google Activity Result", "SocialLogin plugin instance is not SocialLoginPlugin");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
((SocialLoginPlugin) plugin).handleGoogleLoginIntent(requestCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void IHaveModifiedTheMainActivityForTheUseWithSocialLoginPlugin() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package com.donetick.app.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.BitmapShader;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Shader;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Member avatars for the widgets: downloads and circle-crops profile photos
|
||||||
|
* (cached on disk for a day) and falls back to a colored initials disc.
|
||||||
|
* Downloads must only happen from a background/binder thread —
|
||||||
|
* RemoteViewsFactory.onDataSetChanged qualifies.
|
||||||
|
*/
|
||||||
|
final class AvatarCache {
|
||||||
|
private static final String TAG = "DonetickWidget";
|
||||||
|
private static final String DIR = "widget_avatars";
|
||||||
|
private static final long MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
||||||
|
private static final int SIZE_PX = 96;
|
||||||
|
|
||||||
|
// Joy-ish palette for initials discs; picked by hashing the member id so
|
||||||
|
// each person keeps a stable color.
|
||||||
|
private static final int[] PALETTE = {
|
||||||
|
0xFF0B6BCB, 0xFF147D57, 0xFF9C4DD3, 0xFFC2410C,
|
||||||
|
0xFF0E7490, 0xFFB02A5B, 0xFF5B21B6, 0xFF937800,
|
||||||
|
};
|
||||||
|
|
||||||
|
private AvatarCache() {}
|
||||||
|
|
||||||
|
/** Photo avatar or null; never throws, never touches the network on failure loops. */
|
||||||
|
static Bitmap photo(Context context, WidgetStore.Member member) {
|
||||||
|
if (member == null || member.image == null || !member.image.startsWith("http")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
File dir = new File(context.getCacheDir(), DIR);
|
||||||
|
if (!dir.exists()) dir.mkdirs();
|
||||||
|
File file = new File(dir, member.id + ".png");
|
||||||
|
if (file.exists()
|
||||||
|
&& System.currentTimeMillis() - file.lastModified() < MAX_AGE_MS) {
|
||||||
|
return BitmapFactory.decodeFile(file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
Bitmap downloaded = download(member.image);
|
||||||
|
if (downloaded == null) {
|
||||||
|
// Keep serving a stale copy rather than nothing.
|
||||||
|
return file.exists() ? BitmapFactory.decodeFile(file.getAbsolutePath()) : null;
|
||||||
|
}
|
||||||
|
Bitmap circled = circleCrop(downloaded);
|
||||||
|
try (FileOutputStream out = new FileOutputStream(file)) {
|
||||||
|
circled.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||||
|
}
|
||||||
|
return circled;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Avatar load failed for member " + member.id, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Colored disc with the member's first initial — the no-photo fallback. */
|
||||||
|
static Bitmap initials(WidgetStore.Member member) {
|
||||||
|
String name = member != null && member.name != null ? member.name.trim() : "";
|
||||||
|
String letter = name.isEmpty()
|
||||||
|
? "?"
|
||||||
|
: new String(Character.toChars(name.codePointAt(0))).toUpperCase(Locale.getDefault());
|
||||||
|
int color = PALETTE[Math.abs((member != null ? member.id : "?").hashCode()) % PALETTE.length];
|
||||||
|
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(SIZE_PX, SIZE_PX, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
paint.setColor(color);
|
||||||
|
canvas.drawCircle(SIZE_PX / 2f, SIZE_PX / 2f, SIZE_PX / 2f, paint);
|
||||||
|
|
||||||
|
Paint text = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
text.setColor(Color.WHITE);
|
||||||
|
text.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
|
||||||
|
text.setTextSize(SIZE_PX * 0.42f);
|
||||||
|
text.setTextAlign(Paint.Align.CENTER);
|
||||||
|
Rect bounds = new Rect();
|
||||||
|
text.getTextBounds(letter, 0, letter.length(), bounds);
|
||||||
|
canvas.drawText(letter, SIZE_PX / 2f, SIZE_PX / 2f + bounds.height() / 2f, text);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Best avatar for a member: photo when available, initials otherwise. */
|
||||||
|
static Bitmap get(Context context, WidgetStore.Member member) {
|
||||||
|
Bitmap photo = photo(context, member);
|
||||||
|
return photo != null ? photo : initials(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap download(String imageUrl) {
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
try {
|
||||||
|
connection = (HttpURLConnection) new URL(imageUrl).openConnection();
|
||||||
|
connection.setConnectTimeout(8000);
|
||||||
|
connection.setReadTimeout(10000);
|
||||||
|
if (connection.getResponseCode() != 200) return null;
|
||||||
|
try (InputStream stream = connection.getInputStream()) {
|
||||||
|
Bitmap raw = BitmapFactory.decodeStream(stream);
|
||||||
|
if (raw == null) return null;
|
||||||
|
return Bitmap.createScaledBitmap(raw, SIZE_PX, SIZE_PX, true);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Avatar download failed", e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (connection != null) connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap circleCrop(Bitmap source) {
|
||||||
|
Bitmap output = Bitmap.createBitmap(SIZE_PX, SIZE_PX, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(output);
|
||||||
|
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
|
||||||
|
canvas.drawCircle(SIZE_PX / 2f, SIZE_PX / 2f, SIZE_PX / 2f, paint);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.donetick.app.widget;
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.appwidget.AppWidgetProvider;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.donetick.app.R;
|
||||||
|
|
||||||
|
/** "People" home-screen widget: every circle member with their task load. */
|
||||||
|
public class PeopleWidgetProvider extends AppWidgetProvider {
|
||||||
|
@Override
|
||||||
|
public void onUpdate(Context context, AppWidgetManager manager, int[] appWidgetIds) {
|
||||||
|
for (int id : appWidgetIds) {
|
||||||
|
manager.updateAppWidget(id, WidgetUi.build(context, WidgetUi.MODE_PEOPLE, id));
|
||||||
|
}
|
||||||
|
// Reload rows; the factory refreshes from the server when stale, which
|
||||||
|
// is what keeps the widget current while the app stays closed.
|
||||||
|
manager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.donetick.app.widget;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.appwidget.AppWidgetProvider;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
import com.donetick.app.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Quick Capture" home-screen widget: three shortcuts straight into the
|
||||||
|
* add-task flow (type, scan, speak). Purely a launcher — it shows no task
|
||||||
|
* data, so it never needs a refresh cycle.
|
||||||
|
*/
|
||||||
|
public class QuickCaptureWidgetProvider extends AppWidgetProvider {
|
||||||
|
// Handled in src/CapacitorListener.js → /chores?add_task=1[&mode=…]
|
||||||
|
private static final String URI_TYPE = "donetick://chores/add";
|
||||||
|
private static final String URI_SCAN = "donetick://chores/add?mode=scan";
|
||||||
|
private static final String URI_VOICE = "donetick://chores/add?mode=voice";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdate(Context context, AppWidgetManager manager, int[] appWidgetIds) {
|
||||||
|
for (int id : appWidgetIds) {
|
||||||
|
manager.updateAppWidget(id, build(context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RemoteViews build(Context context) {
|
||||||
|
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_quick_capture);
|
||||||
|
views.setOnClickPendingIntent(R.id.quick_type, deepLink(context, 10, URI_TYPE));
|
||||||
|
views.setOnClickPendingIntent(R.id.quick_scan, deepLink(context, 11, URI_SCAN));
|
||||||
|
views.setOnClickPendingIntent(R.id.quick_voice, deepLink(context, 12, URI_VOICE));
|
||||||
|
return views;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingIntent deepLink(Context context, int requestCode, String uri) {
|
||||||
|
Intent intent = new Intent(context, com.donetick.app.MainActivity.class);
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
// Distinct data per tile so the three PendingIntents aren't collapsed.
|
||||||
|
intent.setData(Uri.parse(uri));
|
||||||
|
return PendingIntent.getActivity(context, requestCode, intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.donetick.app.widget;
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.appwidget.AppWidgetProvider;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.donetick.app.R;
|
||||||
|
|
||||||
|
/** "Today" home-screen widget: tasks due today plus anything awaiting approval. */
|
||||||
|
public class TodayWidgetProvider extends AppWidgetProvider {
|
||||||
|
@Override
|
||||||
|
public void onUpdate(Context context, AppWidgetManager manager, int[] appWidgetIds) {
|
||||||
|
for (int id : appWidgetIds) {
|
||||||
|
manager.updateAppWidget(id, WidgetUi.build(context, WidgetUi.MODE_TODAY, id));
|
||||||
|
}
|
||||||
|
// Reload rows; the factory refreshes from the server when stale, which
|
||||||
|
// is what keeps the widget current while the app stays closed.
|
||||||
|
manager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleted(Context context, int[] appWidgetIds) {
|
||||||
|
for (int id : appWidgetIds) {
|
||||||
|
WidgetStore.removeWidgetOptions(context, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.donetick.app.widget;
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.appwidget.AppWidgetProvider;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.donetick.app.R;
|
||||||
|
|
||||||
|
/** "Next 7 Days" home-screen widget: upcoming tasks grouped by day. */
|
||||||
|
public class WeekWidgetProvider extends AppWidgetProvider {
|
||||||
|
@Override
|
||||||
|
public void onUpdate(Context context, AppWidgetManager manager, int[] appWidgetIds) {
|
||||||
|
for (int id : appWidgetIds) {
|
||||||
|
manager.updateAppWidget(id, WidgetUi.build(context, WidgetUi.MODE_WEEK, id));
|
||||||
|
}
|
||||||
|
manager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleted(Context context, int[] appWidgetIds) {
|
||||||
|
for (int id : appWidgetIds) {
|
||||||
|
WidgetStore.removeWidgetOptions(context, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.donetick.app.widget;
|
||||||
|
|
||||||
|
import com.getcapacitor.Plugin;
|
||||||
|
import com.getcapacitor.PluginCall;
|
||||||
|
import com.getcapacitor.PluginMethod;
|
||||||
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS bridge for the home-screen widgets (see src/service/WidgetService.js).
|
||||||
|
* Persists the task snapshot + API config and redraws the widgets.
|
||||||
|
*/
|
||||||
|
@CapacitorPlugin(name = "WidgetBridge")
|
||||||
|
public class WidgetBridgePlugin extends Plugin {
|
||||||
|
|
||||||
|
@PluginMethod
|
||||||
|
public void update(PluginCall call) {
|
||||||
|
String data = call.getString("data");
|
||||||
|
String config = call.getString("config");
|
||||||
|
if (data != null) WidgetStore.saveData(getContext(), data);
|
||||||
|
if (config != null) WidgetStore.saveConfig(getContext(), config);
|
||||||
|
WidgetUi.refreshAll(getContext());
|
||||||
|
call.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PluginMethod
|
||||||
|
public void clear(PluginCall call) {
|
||||||
|
WidgetStore.clear(getContext());
|
||||||
|
WidgetUi.refreshAll(getContext());
|
||||||
|
call.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.donetick.app.widget;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Switch;
|
||||||
|
|
||||||
|
import com.donetick.app.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Placement / long-press configuration for the Today and Next 7 Days widgets.
|
||||||
|
* One option for now: include tasks assigned to other circle members
|
||||||
|
* (stored per appWidgetId so mixed setups work side by side).
|
||||||
|
*/
|
||||||
|
public class WidgetConfigActivity extends Activity {
|
||||||
|
private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
appWidgetId = getIntent().getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
|
||||||
|
AppWidgetManager.INVALID_APPWIDGET_ID);
|
||||||
|
// Cancelled result until Save, so backing out never adds a half-configured widget.
|
||||||
|
setResult(RESULT_CANCELED, resultIntent());
|
||||||
|
if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentView(R.layout.widget_config);
|
||||||
|
|
||||||
|
Switch includeOthers = findViewById(R.id.config_include_others);
|
||||||
|
includeOthers.setChecked(WidgetStore.includeOthers(this, appWidgetId));
|
||||||
|
|
||||||
|
findViewById(R.id.config_save).setOnClickListener(v -> {
|
||||||
|
WidgetStore.setIncludeOthers(this, appWidgetId, includeOthers.isChecked());
|
||||||
|
WidgetUi.refreshAll(this);
|
||||||
|
setResult(RESULT_OK, resultIntent());
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent resultIntent() {
|
||||||
|
return new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
package com.donetick.app.widget;
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
import android.widget.RemoteViewsService;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import com.donetick.app.R;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Feeds task rows, day-group headers, and people rows to the widget ListView. */
|
||||||
|
public class WidgetListService extends RemoteViewsService {
|
||||||
|
@Override
|
||||||
|
public RemoteViewsFactory onGetViewFactory(Intent intent) {
|
||||||
|
String mode = intent.getStringExtra(WidgetUi.EXTRA_MODE);
|
||||||
|
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
|
||||||
|
AppWidgetManager.INVALID_APPWIDGET_ID);
|
||||||
|
return new WidgetListFactory(getApplicationContext(),
|
||||||
|
mode != null ? mode : WidgetUi.MODE_TODAY, appWidgetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Row {
|
||||||
|
final String header; // non-null for day-group headers
|
||||||
|
final WidgetStore.Task task; // non-null for task rows
|
||||||
|
final WidgetStore.Member person; // non-null for people rows
|
||||||
|
int todayCount;
|
||||||
|
int weekCount;
|
||||||
|
|
||||||
|
Row(String header, WidgetStore.Task task, WidgetStore.Member person) {
|
||||||
|
this.header = header;
|
||||||
|
this.task = task;
|
||||||
|
this.person = person;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class WidgetListFactory implements RemoteViewsFactory {
|
||||||
|
private final Context context;
|
||||||
|
private final String mode;
|
||||||
|
private final int appWidgetId;
|
||||||
|
private List<Row> rows = new ArrayList<>();
|
||||||
|
private final Map<String, Bitmap> avatars = new HashMap<>();
|
||||||
|
private String myUserId;
|
||||||
|
private boolean includeOthers;
|
||||||
|
|
||||||
|
WidgetListFactory(Context context, String mode, int appWidgetId) {
|
||||||
|
this.context = context;
|
||||||
|
this.mode = mode;
|
||||||
|
this.appWidgetId = appWidgetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDataSetChanged() {
|
||||||
|
// Runs on a binder thread, so synchronous network work is allowed
|
||||||
|
// here. This is the background-refresh path: the 30-minute
|
||||||
|
// updatePeriodMillis cycle lands here via notifyAppWidgetViewDataChanged.
|
||||||
|
boolean refreshed = WidgetStore.refreshFromServerIfStale(context);
|
||||||
|
|
||||||
|
myUserId = WidgetStore.userId(context);
|
||||||
|
includeOthers = WidgetStore.includeOthers(context, appWidgetId);
|
||||||
|
List<WidgetStore.Task> allTasks = WidgetStore.loadTasks(context);
|
||||||
|
List<WidgetStore.Member> members = WidgetStore.loadMembers(context);
|
||||||
|
|
||||||
|
if (WidgetUi.MODE_PEOPLE.equals(mode)) {
|
||||||
|
rows = buildPeopleRows(allTasks, members);
|
||||||
|
loadAvatars(members);
|
||||||
|
} else {
|
||||||
|
List<WidgetStore.Task> tasks =
|
||||||
|
WidgetStore.visibleTasks(context, allTasks, includeOthers);
|
||||||
|
rows = WidgetUi.MODE_TODAY.equals(mode)
|
||||||
|
? buildTodayRows(tasks)
|
||||||
|
: buildWeekRows(tasks);
|
||||||
|
if (includeOthers) loadAvatars(members);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshed) {
|
||||||
|
// Counts and "Updated …" line live outside the list.
|
||||||
|
WidgetUi.updateHeaders(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resolve avatars for everyone up front — getViewAt must not block. */
|
||||||
|
private void loadAvatars(List<WidgetStore.Member> members) {
|
||||||
|
avatars.clear();
|
||||||
|
for (WidgetStore.Member member : members) {
|
||||||
|
avatars.put(member.id, AvatarCache.get(context, member));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Row> buildTodayRows(List<WidgetStore.Task> tasks) {
|
||||||
|
List<Row> result = new ArrayList<>();
|
||||||
|
for (WidgetStore.Task task : WidgetStore.todaySubset(tasks)) {
|
||||||
|
result.add(new Row(null, task, null));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Row> buildWeekRows(List<WidgetStore.Task> tasks) {
|
||||||
|
List<Row> result = new ArrayList<>();
|
||||||
|
long startOfToday = WidgetStore.endOfDay(-1) + 1;
|
||||||
|
SimpleDateFormat dayFormat = new SimpleDateFormat("EEE, MMM d", Locale.getDefault());
|
||||||
|
|
||||||
|
List<WidgetStore.Task> approvals = new ArrayList<>();
|
||||||
|
List<WidgetStore.Task> scheduled = new ArrayList<>();
|
||||||
|
for (WidgetStore.Task task : tasks) {
|
||||||
|
if (task.approval) approvals.add(task);
|
||||||
|
else if (task.dueDate != null) scheduled.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!approvals.isEmpty()) {
|
||||||
|
result.add(new Row(context.getString(R.string.widget_group_approval), null, null));
|
||||||
|
for (WidgetStore.Task task : approvals) result.add(new Row(null, task, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
String currentGroup = null;
|
||||||
|
for (WidgetStore.Task task : scheduled) {
|
||||||
|
String group;
|
||||||
|
if (task.dueDate < startOfToday) {
|
||||||
|
group = context.getString(R.string.widget_group_overdue);
|
||||||
|
} else if (task.dueDate <= WidgetStore.endOfDay(0)) {
|
||||||
|
group = context.getString(R.string.widget_group_today);
|
||||||
|
} else if (task.dueDate <= WidgetStore.endOfDay(1)) {
|
||||||
|
group = context.getString(R.string.widget_group_tomorrow);
|
||||||
|
} else {
|
||||||
|
group = dayFormat.format(new Date(task.dueDate));
|
||||||
|
}
|
||||||
|
if (!group.equals(currentGroup)) {
|
||||||
|
result.add(new Row(group, null, null));
|
||||||
|
currentGroup = group;
|
||||||
|
}
|
||||||
|
result.add(new Row(null, task, null));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** One row per member with today/week workloads, busiest first. */
|
||||||
|
private List<Row> buildPeopleRows(List<WidgetStore.Task> tasks,
|
||||||
|
List<WidgetStore.Member> members) {
|
||||||
|
List<Row> result = new ArrayList<>();
|
||||||
|
List<WidgetStore.Task> todayTasks = WidgetStore.todaySubset(tasks);
|
||||||
|
for (WidgetStore.Member member : members) {
|
||||||
|
Row row = new Row(null, null, member);
|
||||||
|
for (WidgetStore.Task task : tasks) {
|
||||||
|
if (member.id.equals(task.assignedTo)) row.weekCount++;
|
||||||
|
}
|
||||||
|
for (WidgetStore.Task task : todayTasks) {
|
||||||
|
if (member.id.equals(task.assignedTo)) row.todayCount++;
|
||||||
|
}
|
||||||
|
result.add(row);
|
||||||
|
}
|
||||||
|
java.util.Collections.sort(result, (a, b) -> {
|
||||||
|
if (a.todayCount != b.todayCount) return b.todayCount - a.todayCount;
|
||||||
|
if (a.weekCount != b.weekCount) return b.weekCount - a.weekCount;
|
||||||
|
return a.person.name.compareToIgnoreCase(b.person.name);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteViews getViewAt(int position) {
|
||||||
|
if (position < 0 || position >= rows.size()) return null;
|
||||||
|
Row row = rows.get(position);
|
||||||
|
|
||||||
|
if (row.header != null) {
|
||||||
|
RemoteViews views = new RemoteViews(context.getPackageName(),
|
||||||
|
R.layout.widget_row_day_header);
|
||||||
|
views.setTextViewText(R.id.row_day, row.header);
|
||||||
|
return views;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row.person != null) {
|
||||||
|
return buildPersonRow(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetStore.Task task = row.task;
|
||||||
|
RemoteViews views = new RemoteViews(context.getPackageName(),
|
||||||
|
R.layout.widget_row_task);
|
||||||
|
views.setTextViewText(R.id.row_title, task.name);
|
||||||
|
|
||||||
|
boolean overdue = !task.approval && task.dueDate != null
|
||||||
|
&& task.dueDate < System.currentTimeMillis();
|
||||||
|
int secondary = ContextCompat.getColor(context, R.color.widget_text_secondary);
|
||||||
|
int warning = ContextCompat.getColor(context, R.color.widget_warning);
|
||||||
|
int danger = ContextCompat.getColor(context, R.color.widget_overdue);
|
||||||
|
|
||||||
|
String meta;
|
||||||
|
int metaColor;
|
||||||
|
if (task.approval) {
|
||||||
|
meta = context.getString(R.string.widget_meta_approval);
|
||||||
|
metaColor = warning;
|
||||||
|
} else if (overdue) {
|
||||||
|
meta = context.getString(R.string.widget_meta_overdue);
|
||||||
|
metaColor = danger;
|
||||||
|
} else {
|
||||||
|
meta = android.text.format.DateFormat.getTimeFormat(context)
|
||||||
|
.format(new Date(task.dueDate));
|
||||||
|
metaColor = secondary;
|
||||||
|
}
|
||||||
|
views.setTextViewText(R.id.row_meta, meta);
|
||||||
|
views.setTextColor(R.id.row_meta, metaColor);
|
||||||
|
|
||||||
|
int ringColor;
|
||||||
|
if (task.approval) ringColor = warning;
|
||||||
|
else if (overdue || task.priority == 1) ringColor = danger;
|
||||||
|
else if (task.priority == 2) ringColor = warning;
|
||||||
|
else ringColor = ContextCompat.getColor(context, R.color.widget_ring_neutral);
|
||||||
|
views.setInt(R.id.row_ring, "setColorFilter", ringColor);
|
||||||
|
|
||||||
|
// In "everyone" mode, show who a task belongs to (own tasks stay clean).
|
||||||
|
Bitmap avatar = includeOthers && task.assignedTo != null
|
||||||
|
&& !task.assignedTo.equals(myUserId)
|
||||||
|
? avatars.get(task.assignedTo)
|
||||||
|
: null;
|
||||||
|
if (avatar != null) {
|
||||||
|
views.setImageViewBitmap(R.id.row_avatar, avatar);
|
||||||
|
views.setViewVisibility(R.id.row_avatar, View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
views.setViewVisibility(R.id.row_avatar, View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent fillIn = new Intent();
|
||||||
|
fillIn.setData(Uri.parse("donetick://chores/" + task.id));
|
||||||
|
views.setOnClickFillInIntent(R.id.row_root, fillIn);
|
||||||
|
return views;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RemoteViews buildPersonRow(Row row) {
|
||||||
|
RemoteViews views = new RemoteViews(context.getPackageName(),
|
||||||
|
R.layout.widget_row_person);
|
||||||
|
Bitmap avatar = avatars.get(row.person.id);
|
||||||
|
if (avatar == null) avatar = AvatarCache.initials(row.person);
|
||||||
|
views.setImageViewBitmap(R.id.person_avatar, avatar);
|
||||||
|
views.setTextViewText(R.id.person_name, row.person.name);
|
||||||
|
views.setTextViewText(R.id.person_counts, context.getString(
|
||||||
|
R.string.widget_person_counts, row.todayCount, row.weekCount));
|
||||||
|
|
||||||
|
// Highlight the today-count when someone has work due today.
|
||||||
|
int accent = ContextCompat.getColor(context, R.color.widget_accent);
|
||||||
|
int secondary = ContextCompat.getColor(context, R.color.widget_text_secondary);
|
||||||
|
views.setTextColor(R.id.person_counts, row.todayCount > 0 ? accent : secondary);
|
||||||
|
|
||||||
|
Intent fillIn = new Intent();
|
||||||
|
fillIn.setData(Uri.parse("donetick://chores"));
|
||||||
|
views.setOnClickFillInIntent(R.id.person_root, fillIn);
|
||||||
|
return views;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteViews getLoadingView() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getViewTypeCount() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasStableIds() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return rows.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,346 @@
|
|||||||
|
package com.donetick.app.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared storage + refresh logic for the home-screen widgets.
|
||||||
|
*
|
||||||
|
* The web app pushes a pre-filtered snapshot through WidgetBridgePlugin
|
||||||
|
* whenever its chores cache changes. When the app has not run for a while,
|
||||||
|
* {@link #refreshFromServerIfStale} re-fetches the list directly so the
|
||||||
|
* widget stays current in the background (driven by the 30-minute
|
||||||
|
* updatePeriodMillis cycle).
|
||||||
|
*
|
||||||
|
* Snapshot JSON: {version, lastUpdated,
|
||||||
|
* tasks:[{id, name, dueDate, priority, approval, assignedTo}],
|
||||||
|
* members:[{id, name, image}]}
|
||||||
|
* Config JSON: {serverUrl, token, userId}
|
||||||
|
*
|
||||||
|
* Since v2 the snapshot holds every member's tasks; widgets narrow it down to
|
||||||
|
* the current user unless the per-widget "include others" option is on.
|
||||||
|
*/
|
||||||
|
public final class WidgetStore {
|
||||||
|
private static final String TAG = "DonetickWidget";
|
||||||
|
private static final String PREFS = "donetick_widget";
|
||||||
|
private static final String KEY_DATA = "widget_tasks";
|
||||||
|
private static final String KEY_CONFIG = "widget_config";
|
||||||
|
private static final String KEY_INCLUDE_OTHERS_PREFIX = "include_others_";
|
||||||
|
|
||||||
|
// Same filtering window as src/service/WidgetService.js
|
||||||
|
private static final int WINDOW_DAYS = 7;
|
||||||
|
private static final int MAX_TASKS = 100;
|
||||||
|
// Don't hit the network if the app (or a previous refresh) updated the
|
||||||
|
// snapshot recently; also guards against notify->refresh loops.
|
||||||
|
private static final long STALE_MS = 10 * 60 * 1000;
|
||||||
|
|
||||||
|
private static final Object REFRESH_LOCK = new Object();
|
||||||
|
|
||||||
|
private WidgetStore() {}
|
||||||
|
|
||||||
|
public static class Task {
|
||||||
|
public String id;
|
||||||
|
public String name;
|
||||||
|
public Long dueDate; // epoch millis, null when unscheduled
|
||||||
|
public int priority;
|
||||||
|
public boolean approval;
|
||||||
|
public String assignedTo; // member userId, null when unassigned
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Member {
|
||||||
|
public String id;
|
||||||
|
public String name;
|
||||||
|
public String image; // avatar URL, may be null
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SharedPreferences prefs(Context context) {
|
||||||
|
return context.getApplicationContext()
|
||||||
|
.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void saveData(Context context, String json) {
|
||||||
|
prefs(context).edit().putString(KEY_DATA, json).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void saveConfig(Context context, String json) {
|
||||||
|
prefs(context).edit().putString(KEY_CONFIG, json).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear(Context context) {
|
||||||
|
prefs(context).edit().clear().apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasConfig(Context context) {
|
||||||
|
return prefs(context).getString(KEY_CONFIG, null) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The signed-in user's id from the pushed config, or null. */
|
||||||
|
public static String userId(Context context) {
|
||||||
|
try {
|
||||||
|
String raw = prefs(context).getString(KEY_CONFIG, null);
|
||||||
|
if (raw == null) return null;
|
||||||
|
Object id = new JSONObject(raw).opt("userId");
|
||||||
|
return id == null ? null : String.valueOf(id);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Per-widget "include tasks assigned to others" option (default off). */
|
||||||
|
public static boolean includeOthers(Context context, int appWidgetId) {
|
||||||
|
return prefs(context).getBoolean(KEY_INCLUDE_OTHERS_PREFIX + appWidgetId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setIncludeOthers(Context context, int appWidgetId, boolean value) {
|
||||||
|
prefs(context).edit()
|
||||||
|
.putBoolean(KEY_INCLUDE_OTHERS_PREFIX + appWidgetId, value)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeWidgetOptions(Context context, int appWidgetId) {
|
||||||
|
prefs(context).edit()
|
||||||
|
.remove(KEY_INCLUDE_OTHERS_PREFIX + appWidgetId)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long lastUpdated(Context context) {
|
||||||
|
try {
|
||||||
|
String raw = prefs(context).getString(KEY_DATA, null);
|
||||||
|
if (raw == null) return 0;
|
||||||
|
return new JSONObject(raw).optLong("lastUpdated", 0);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Task> loadTasks(Context context) {
|
||||||
|
List<Task> tasks = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
String raw = prefs(context).getString(KEY_DATA, null);
|
||||||
|
if (raw == null) return tasks;
|
||||||
|
JSONArray arr = new JSONObject(raw).optJSONArray("tasks");
|
||||||
|
if (arr == null) return tasks;
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
JSONObject obj = arr.optJSONObject(i);
|
||||||
|
if (obj == null) continue;
|
||||||
|
Task task = new Task();
|
||||||
|
task.id = String.valueOf(obj.opt("id"));
|
||||||
|
task.name = obj.optString("name", "");
|
||||||
|
task.dueDate = obj.isNull("dueDate") ? null : obj.optLong("dueDate");
|
||||||
|
task.priority = obj.optInt("priority", 0);
|
||||||
|
task.approval = obj.optBoolean("approval", false);
|
||||||
|
// v1 snapshots carried only the user's own tasks and had no
|
||||||
|
// assignedTo — treat those rows as "mine".
|
||||||
|
task.assignedTo = obj.has("assignedTo")
|
||||||
|
? (obj.isNull("assignedTo") ? null : String.valueOf(obj.opt("assignedTo")))
|
||||||
|
: userId(context);
|
||||||
|
tasks.add(task);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Failed to parse widget snapshot", e);
|
||||||
|
}
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Member> loadMembers(Context context) {
|
||||||
|
List<Member> members = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
String raw = prefs(context).getString(KEY_DATA, null);
|
||||||
|
if (raw == null) return members;
|
||||||
|
JSONArray arr = new JSONObject(raw).optJSONArray("members");
|
||||||
|
if (arr == null) return members;
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
JSONObject obj = arr.optJSONObject(i);
|
||||||
|
if (obj == null) continue;
|
||||||
|
Member member = new Member();
|
||||||
|
member.id = String.valueOf(obj.opt("id"));
|
||||||
|
member.name = obj.optString("name", "");
|
||||||
|
member.image = obj.isNull("image") ? null : obj.optString("image", null);
|
||||||
|
members.add(member);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Failed to parse widget members", e);
|
||||||
|
}
|
||||||
|
return members;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tasks a today/week widget should render: everything when includeOthers,
|
||||||
|
* otherwise the user's own tasks plus approvals (which wait on them).
|
||||||
|
*/
|
||||||
|
public static List<Task> visibleTasks(Context context, List<Task> tasks, boolean includeOthers) {
|
||||||
|
if (includeOthers) return tasks;
|
||||||
|
String me = userId(context);
|
||||||
|
List<Task> mine = new ArrayList<>();
|
||||||
|
for (Task task : tasks) {
|
||||||
|
if (task.approval || (me != null && me.equals(task.assignedTo))) {
|
||||||
|
mine.add(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tasks the Today widget shows: awaiting approval, overdue, or due today. */
|
||||||
|
public static List<Task> todaySubset(List<Task> tasks) {
|
||||||
|
long endOfToday = endOfDay(0);
|
||||||
|
List<Task> subset = new ArrayList<>();
|
||||||
|
for (Task task : tasks) {
|
||||||
|
if (task.approval || (task.dueDate != null && task.dueDate <= endOfToday)) {
|
||||||
|
subset.add(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long endOfDay(int daysFromNow) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.add(Calendar.DAY_OF_YEAR, daysFromNow);
|
||||||
|
cal.set(Calendar.HOUR_OF_DAY, 23);
|
||||||
|
cal.set(Calendar.MINUTE, 59);
|
||||||
|
cal.set(Calendar.SECOND, 59);
|
||||||
|
cal.set(Calendar.MILLISECOND, 999);
|
||||||
|
return cal.getTimeInMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch /chores/ from the configured server and rebuild the snapshot.
|
||||||
|
* Returns true when a network refresh actually happened and succeeded.
|
||||||
|
* Safe to call from RemoteViewsFactory.onDataSetChanged (binder thread).
|
||||||
|
*/
|
||||||
|
public static boolean refreshFromServerIfStale(Context context) {
|
||||||
|
synchronized (REFRESH_LOCK) {
|
||||||
|
long age = System.currentTimeMillis() - lastUpdated(context);
|
||||||
|
if (age < STALE_MS) return false;
|
||||||
|
|
||||||
|
String rawConfig = prefs(context).getString(KEY_CONFIG, null);
|
||||||
|
if (rawConfig == null) return false;
|
||||||
|
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
try {
|
||||||
|
JSONObject config = new JSONObject(rawConfig);
|
||||||
|
String serverUrl = config.optString("serverUrl", "");
|
||||||
|
String token = config.optString("token", "");
|
||||||
|
if (serverUrl.isEmpty() || token.isEmpty()) return false;
|
||||||
|
|
||||||
|
URL url = new URL(serverUrl + "/chores/");
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setConnectTimeout(10000);
|
||||||
|
connection.setReadTimeout(15000);
|
||||||
|
connection.setRequestProperty("Authorization", "Bearer " + token);
|
||||||
|
connection.setRequestProperty("Accept", "application/json");
|
||||||
|
|
||||||
|
if (connection.getResponseCode() != 200) {
|
||||||
|
// Expired token or server trouble — keep the last snapshot,
|
||||||
|
// the UI surfaces staleness via the "Updated …" line.
|
||||||
|
Log.w(TAG, "Widget refresh got HTTP " + connection.getResponseCode());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray chores = new JSONObject(readAll(connection.getInputStream()))
|
||||||
|
.optJSONArray("res");
|
||||||
|
if (chores == null) return false;
|
||||||
|
|
||||||
|
// The chores endpoint has no member profiles, so carry the
|
||||||
|
// member list over from the previous snapshot (it changes
|
||||||
|
// rarely and the app re-pushes it on every open).
|
||||||
|
JSONArray members = null;
|
||||||
|
String previous = prefs(context).getString(KEY_DATA, null);
|
||||||
|
if (previous != null) {
|
||||||
|
members = new JSONObject(previous).optJSONArray("members");
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject snapshot = new JSONObject();
|
||||||
|
snapshot.put("version", 2);
|
||||||
|
snapshot.put("lastUpdated", System.currentTimeMillis());
|
||||||
|
snapshot.put("tasks", filterChores(chores));
|
||||||
|
snapshot.put("members", members == null ? new JSONArray() : members);
|
||||||
|
saveData(context, snapshot.toString());
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Widget background refresh failed", e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (connection != null) connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Mirror of buildWidgetTasks in src/service/WidgetService.js. */
|
||||||
|
private static JSONArray filterChores(JSONArray chores) throws Exception {
|
||||||
|
long cutoff = endOfDay(WINDOW_DAYS);
|
||||||
|
List<JSONObject> selected = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < chores.length(); i++) {
|
||||||
|
JSONObject chore = chores.optJSONObject(i);
|
||||||
|
if (chore == null || chore.opt("id") == null) continue;
|
||||||
|
|
||||||
|
boolean approval = chore.optInt("status", 0) == 3;
|
||||||
|
Long dueDate = parseDate(chore.optString("nextDueDate", null));
|
||||||
|
boolean inWindow = dueDate != null && dueDate <= cutoff;
|
||||||
|
if (!approval && !inWindow) continue;
|
||||||
|
|
||||||
|
JSONObject task = new JSONObject();
|
||||||
|
task.put("id", chore.opt("id"));
|
||||||
|
task.put("name", chore.optString("name", ""));
|
||||||
|
task.put("dueDate", dueDate == null ? JSONObject.NULL : dueDate);
|
||||||
|
task.put("priority", chore.optInt("priority", 0));
|
||||||
|
task.put("approval", approval);
|
||||||
|
task.put("assignedTo", chore.isNull("assignedTo")
|
||||||
|
? JSONObject.NULL
|
||||||
|
: String.valueOf(chore.opt("assignedTo")));
|
||||||
|
selected.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(selected, (a, b) -> {
|
||||||
|
boolean aApproval = a.optBoolean("approval");
|
||||||
|
boolean bApproval = b.optBoolean("approval");
|
||||||
|
if (aApproval != bApproval) return aApproval ? -1 : 1;
|
||||||
|
long aDue = a.isNull("dueDate") ? Long.MAX_VALUE : a.optLong("dueDate");
|
||||||
|
long bDue = b.isNull("dueDate") ? Long.MAX_VALUE : b.optLong("dueDate");
|
||||||
|
return Long.compare(aDue, bDue);
|
||||||
|
});
|
||||||
|
|
||||||
|
JSONArray result = new JSONArray();
|
||||||
|
for (int i = 0; i < selected.size() && i < MAX_TASKS; i++) {
|
||||||
|
result.put(selected.get(i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Long parseDate(String value) {
|
||||||
|
if (value == null || value.isEmpty() || "null".equals(value)) return null;
|
||||||
|
try {
|
||||||
|
return OffsetDateTime.parse(value).toInstant().toEpochMilli();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readAll(InputStream stream) throws Exception {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
try (BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(stream, StandardCharsets.UTF_8))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
builder.append(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
141
android/app/src/main/java/com/donetick/app/widget/WidgetUi.java
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package com.donetick.app.widget;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
import com.donetick.app.MainActivity;
|
||||||
|
import com.donetick.app.R;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/** Builds the shell RemoteViews shared by the widgets and pushes updates. */
|
||||||
|
public final class WidgetUi {
|
||||||
|
public static final String MODE_TODAY = "today";
|
||||||
|
public static final String MODE_WEEK = "week";
|
||||||
|
public static final String MODE_PEOPLE = "people";
|
||||||
|
public static final String EXTRA_MODE = "com.donetick.app.widget.MODE";
|
||||||
|
|
||||||
|
private WidgetUi() {}
|
||||||
|
|
||||||
|
/** Full refresh: redraw headers and reload list content (used by the JS bridge). */
|
||||||
|
public static void refreshAll(Context context) {
|
||||||
|
updateHeaders(context);
|
||||||
|
AppWidgetManager manager = AppWidgetManager.getInstance(context);
|
||||||
|
manager.notifyAppWidgetViewDataChanged(
|
||||||
|
widgetIds(context, manager, TodayWidgetProvider.class), R.id.widget_list);
|
||||||
|
manager.notifyAppWidgetViewDataChanged(
|
||||||
|
widgetIds(context, manager, WeekWidgetProvider.class), R.id.widget_list);
|
||||||
|
manager.notifyAppWidgetViewDataChanged(
|
||||||
|
widgetIds(context, manager, PeopleWidgetProvider.class), R.id.widget_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Redraw title/count/subtitle only — safe to call from the list factory. */
|
||||||
|
public static void updateHeaders(Context context) {
|
||||||
|
AppWidgetManager manager = AppWidgetManager.getInstance(context);
|
||||||
|
for (int id : widgetIds(context, manager, TodayWidgetProvider.class)) {
|
||||||
|
manager.updateAppWidget(id, build(context, MODE_TODAY, id));
|
||||||
|
}
|
||||||
|
for (int id : widgetIds(context, manager, WeekWidgetProvider.class)) {
|
||||||
|
manager.updateAppWidget(id, build(context, MODE_WEEK, id));
|
||||||
|
}
|
||||||
|
for (int id : widgetIds(context, manager, PeopleWidgetProvider.class)) {
|
||||||
|
manager.updateAppWidget(id, build(context, MODE_PEOPLE, id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] widgetIds(Context context, AppWidgetManager manager, Class<?> provider) {
|
||||||
|
return manager.getAppWidgetIds(new ComponentName(context, provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RemoteViews build(Context context, String mode, int appWidgetId) {
|
||||||
|
boolean today = MODE_TODAY.equals(mode);
|
||||||
|
boolean people = MODE_PEOPLE.equals(mode);
|
||||||
|
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_shell);
|
||||||
|
|
||||||
|
int titleRes = today ? R.string.widget_today_title
|
||||||
|
: people ? R.string.widget_people_title
|
||||||
|
: R.string.widget_week_title;
|
||||||
|
views.setTextViewText(R.id.widget_title, context.getString(titleRes));
|
||||||
|
|
||||||
|
if (people) {
|
||||||
|
views.setViewVisibility(R.id.widget_count, View.GONE);
|
||||||
|
} else {
|
||||||
|
boolean includeOthers = WidgetStore.includeOthers(context, appWidgetId);
|
||||||
|
List<WidgetStore.Task> tasks = WidgetStore.visibleTasks(
|
||||||
|
context, WidgetStore.loadTasks(context), includeOthers);
|
||||||
|
int count = (today ? WidgetStore.todaySubset(tasks) : tasks).size();
|
||||||
|
views.setTextViewText(R.id.widget_count, String.valueOf(count));
|
||||||
|
views.setViewVisibility(R.id.widget_count, count > 0 ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
views.setTextViewText(R.id.widget_subtitle, subtitle(context));
|
||||||
|
|
||||||
|
if (!WidgetStore.hasConfig(context)) {
|
||||||
|
views.setTextViewText(R.id.widget_empty,
|
||||||
|
context.getString(R.string.widget_signed_out));
|
||||||
|
} else {
|
||||||
|
int emptyRes = today ? R.string.widget_empty_today
|
||||||
|
: people ? R.string.widget_empty_people
|
||||||
|
: R.string.widget_empty_week;
|
||||||
|
views.setTextViewText(R.id.widget_empty, context.getString(emptyRes));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Today widget gets a quick-add button that deep links straight
|
||||||
|
// into the in-app AddTaskModal.
|
||||||
|
if (today) {
|
||||||
|
Intent addTask = new Intent(context, MainActivity.class);
|
||||||
|
addTask.setAction(Intent.ACTION_VIEW);
|
||||||
|
addTask.setData(Uri.parse("donetick://chores/add"));
|
||||||
|
views.setOnClickPendingIntent(R.id.widget_add, PendingIntent.getActivity(
|
||||||
|
context, 2, addTask,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
|
||||||
|
views.setViewVisibility(R.id.widget_add, View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
views.setViewVisibility(R.id.widget_add, View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// List content comes from WidgetListService; mode travels in the intent
|
||||||
|
// and the unique data URI keeps the adapters from being collapsed.
|
||||||
|
Intent adapterIntent = new Intent(context, WidgetListService.class);
|
||||||
|
adapterIntent.putExtra(EXTRA_MODE, mode);
|
||||||
|
adapterIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
||||||
|
adapterIntent.setData(Uri.parse("donetickwidget://" + mode + "/" + appWidgetId));
|
||||||
|
views.setRemoteAdapter(R.id.widget_list, adapterIntent);
|
||||||
|
views.setEmptyView(R.id.widget_list, R.id.widget_empty);
|
||||||
|
|
||||||
|
// Row taps deep link into the chore view; the row's fill-in intent
|
||||||
|
// supplies the donetick://chores/<id> data URI.
|
||||||
|
Intent rowTemplate = new Intent(context, MainActivity.class);
|
||||||
|
rowTemplate.setAction(Intent.ACTION_VIEW);
|
||||||
|
views.setPendingIntentTemplate(R.id.widget_list, PendingIntent.getActivity(
|
||||||
|
context, 1, rowTemplate,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE));
|
||||||
|
|
||||||
|
// Anywhere else on the widget simply opens the app.
|
||||||
|
Intent openApp = new Intent(context, MainActivity.class);
|
||||||
|
views.setOnClickPendingIntent(R.id.widget_container, PendingIntent.getActivity(
|
||||||
|
context, 0, openApp,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
|
||||||
|
|
||||||
|
return views;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String subtitle(Context context) {
|
||||||
|
String date = new SimpleDateFormat("EEE, MMM d", Locale.getDefault())
|
||||||
|
.format(new Date());
|
||||||
|
long lastUpdated = WidgetStore.lastUpdated(context);
|
||||||
|
if (lastUpdated <= 0) return date;
|
||||||
|
String time = android.text.format.DateFormat.getTimeFormat(context)
|
||||||
|
.format(new Date(lastUpdated));
|
||||||
|
return date + " · " + context.getString(R.string.widget_updated_at, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
1
android/app/src/main/res/.impeccable/hook.cache.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":1,"sessions":{}}
|
||||||
|
After Width: | Height: | Size: 23 KiB |
BIN
android/app/src/main/res/drawable-nodpi/widget_preview_quick.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
android/app/src/main/res/drawable-nodpi/widget_preview_today.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
android/app/src/main/res/drawable-nodpi/widget_preview_week.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||||
|
<solid android:color="@color/widget_bg" />
|
||||||
|
<!-- Match the launcher's system-wide widget corner radius on Android 12+ -->
|
||||||
|
<corners android:radius="@android:dimen/system_app_widget_background_radius" />
|
||||||
|
</shape>
|
||||||
11
android/app/src/main/res/drawable/ic_widget_add.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/widget_accent">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
||||||
|
</vector>
|
||||||
14
android/app/src/main/res/drawable/ic_widget_mic.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/widget_accent">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M17,11c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c3.39,-0.49 6,-3.39 6,-6.92H17z" />
|
||||||
|
</vector>
|
||||||
15
android/app/src/main/res/drawable/ic_widget_scan.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/widget_accent">
|
||||||
|
<!-- Framing corners plus a scan line: reads as "scan" at widget size. -->
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M4,4h5v2H6v3H4V4zM15,4h5v5h-2V6h-3V4zM4,15h2v3h3v2H4V15zM18,15h2v5h-5v-2h3V15z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,11h18v2H3V11z" />
|
||||||
|
</vector>
|
||||||
5
android/app/src/main/res/drawable/widget_background.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||||
|
<solid android:color="@color/widget_bg" />
|
||||||
|
<corners android:radius="24dp" />
|
||||||
|
</shape>
|
||||||
5
android/app/src/main/res/drawable/widget_count_bg.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||||
|
<solid android:color="@color/widget_accent_soft" />
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Initials disc stand-in for the picker previews; placed widgets get the
|
||||||
|
real cached avatars from AvatarCache. -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||||
|
<solid android:color="@color/widget_accent_soft" />
|
||||||
|
<size android:width="28dp" android:height="28dp" />
|
||||||
|
</shape>
|
||||||
6
android/app/src/main/res/drawable/widget_ring.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||||
|
<solid android:color="@android:color/transparent" />
|
||||||
|
<stroke android:width="2dp" android:color="#FFFFFF" />
|
||||||
|
<size android:width="18dp" android:height="18dp" />
|
||||||
|
</shape>
|
||||||
6
android/app/src/main/res/drawable/widget_tile_bg.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Tappable tile on the Quick Capture widget. -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||||
|
<solid android:color="@color/widget_accent_soft" />
|
||||||
|
<corners android:radius="18dp" />
|
||||||
|
</shape>
|
||||||
43
android/app/src/main/res/layout/widget_config.xml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/widget_bg"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/widget_config_title"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="22sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/config_include_others"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:text="@string/widget_config_include_others"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="@string/widget_config_include_others_hint"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/config_save"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:backgroundTint="@color/widget_accent"
|
||||||
|
android:text="@string/widget_config_save"
|
||||||
|
android:textColor="#FFFFFF" />
|
||||||
|
</LinearLayout>
|
||||||
182
android/app/src/main/res/layout/widget_preview_people.xml
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Widget-picker preview for the People widget. See widget_preview_today.xml
|
||||||
|
for why the real shell can't be previewed directly. Avatars are initials
|
||||||
|
discs here; placed widgets get the real photos from AvatarCache.
|
||||||
|
Keep in visual sync with widget_shell.xml + widget_row_person.xml. -->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/widget_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingTop="14dp"
|
||||||
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@+id/preview_count"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_people_title"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/preview_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:background="@drawable/widget_count_bg"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="22dp"
|
||||||
|
android:paddingStart="7dp"
|
||||||
|
android:paddingEnd="7dp"
|
||||||
|
android:paddingTop="2dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:text="@string/widget_preview_count_people"
|
||||||
|
android:textColor="@color/widget_accent"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="1dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_updated"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="44dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="28dp"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:background="@drawable/widget_preview_avatar"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/widget_preview_initials_1"
|
||||||
|
android:textColor="@color/widget_accent"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_person_1"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/widget_preview_counts_1"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="44dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="28dp"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:background="@drawable/widget_preview_avatar"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/widget_preview_initials_2"
|
||||||
|
android:textColor="@color/widget_accent"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_person_2"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/widget_preview_counts_2"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="44dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="28dp"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:background="@drawable/widget_preview_avatar"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/widget_preview_initials_3"
|
||||||
|
android:textColor="@color/widget_accent"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_person_3"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/widget_preview_counts_3"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
182
android/app/src/main/res/layout/widget_preview_today.xml
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Widget-picker preview for the Today widget. Mirrors widget_shell.xml, but
|
||||||
|
with literal sample rows: the real shell fills its ListView from a
|
||||||
|
RemoteViewsFactory, which the picker never binds, so it would render blank.
|
||||||
|
Keep in visual sync with widget_shell.xml + widget_row_task.xml. -->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/widget_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingTop="14dp"
|
||||||
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@+id/preview_count"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_today_title"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/preview_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@+id/preview_add"
|
||||||
|
android:background="@drawable/widget_count_bg"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="22dp"
|
||||||
|
android:paddingStart="7dp"
|
||||||
|
android:paddingEnd="7dp"
|
||||||
|
android:paddingTop="2dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:text="@string/widget_preview_count_today"
|
||||||
|
android:textColor="@color/widget_accent"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/preview_add"
|
||||||
|
android:layout_width="26dp"
|
||||||
|
android:layout_height="26dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:background="@drawable/widget_count_bg"
|
||||||
|
android:contentDescription="@string/widget_add_task"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:src="@drawable/ic_widget_add" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="1dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_updated"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="40dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/widget_ring"
|
||||||
|
android:tint="@color/widget_overdue" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_task_1"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/widget_meta_overdue"
|
||||||
|
android:textColor="@color/widget_overdue"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="40dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/widget_ring"
|
||||||
|
android:tint="@color/widget_ring_neutral" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_task_2"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/widget_preview_meta_1"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="40dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/widget_ring"
|
||||||
|
android:tint="@color/widget_ring_neutral" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_task_3"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/widget_preview_meta_2"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
193
android/app/src/main/res/layout/widget_preview_week.xml
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Widget-picker preview for the Next 7 Days widget. See widget_preview_today.xml
|
||||||
|
for why the real shell can't be previewed directly. Keep in visual sync with
|
||||||
|
widget_shell.xml + widget_row_day_header.xml + widget_row_task.xml. -->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/widget_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingTop="14dp"
|
||||||
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@+id/preview_count"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_week_title"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/preview_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:background="@drawable/widget_count_bg"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="22dp"
|
||||||
|
android:paddingStart="7dp"
|
||||||
|
android:paddingEnd="7dp"
|
||||||
|
android:paddingTop="2dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:text="@string/widget_preview_count_week"
|
||||||
|
android:textColor="@color/widget_accent"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="1dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_updated"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:letterSpacing="0.08"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:text="@string/widget_group_today"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="40dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/widget_ring"
|
||||||
|
android:tint="@color/widget_ring_neutral" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_task_1"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/widget_preview_meta_1"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="40dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/widget_ring"
|
||||||
|
android:tint="@color/widget_ring_neutral" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_task_2"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/widget_preview_meta_2"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:letterSpacing="0.08"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:text="@string/widget_group_tomorrow"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="40dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/widget_ring"
|
||||||
|
android:tint="@color/widget_ring_neutral" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_preview_task_3"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/widget_preview_meta_1"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
110
android/app/src/main/res/layout/widget_quick_capture.xml
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Quick Capture widget: three one-tap ways into the add-task flow. -->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/quick_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/widget_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingBottom="12dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:baselineAligned="false"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/quick_type"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/widget_tile_bg"
|
||||||
|
android:contentDescription="@string/widget_quick_type"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:contentDescription="@string/widget_quick_type"
|
||||||
|
android:src="@drawable/ic_widget_add" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_quick_type"
|
||||||
|
android:textColor="@color/widget_accent"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/quick_scan"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/widget_tile_bg"
|
||||||
|
android:contentDescription="@string/widget_quick_scan"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:contentDescription="@string/widget_quick_scan"
|
||||||
|
android:src="@drawable/ic_widget_scan" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_quick_scan"
|
||||||
|
android:textColor="@color/widget_accent"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/quick_voice"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/widget_tile_bg"
|
||||||
|
android:contentDescription="@string/widget_quick_voice"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:contentDescription="@string/widget_quick_voice"
|
||||||
|
android:src="@drawable/ic_widget_mic" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/widget_quick_voice"
|
||||||
|
android:textColor="@color/widget_accent"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
12
android/app/src/main/res/layout/widget_row_day_header.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/row_day"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:letterSpacing="0.08"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
37
android/app/src/main/res/layout/widget_row_person.xml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/person_root"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="44dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/person_avatar"
|
||||||
|
android:layout_width="28dp"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:importantForAccessibility="no" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/person_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/person_counts"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
46
android/app/src/main/res/layout/widget_row_task.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/row_root"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="40dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/row_ring"
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/widget_ring" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/row_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/row_meta"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<!-- Assignee avatar, shown only in "everyone" mode for others' tasks. -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/row_avatar"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
93
android/app/src/main/res/layout/widget_shell.xml
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/widget_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/widget_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingTop="14dp"
|
||||||
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/widget_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@id/widget_count"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/widget_text_primary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/widget_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toStartOf="@+id/widget_add"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:background="@drawable/widget_count_bg"
|
||||||
|
android:minWidth="22dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingStart="7dp"
|
||||||
|
android:paddingEnd="7dp"
|
||||||
|
android:paddingTop="2dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:textColor="@color/widget_accent"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<!-- Quick-add, shown on the Today widget only (deep links to the
|
||||||
|
in-app AddTaskModal). Gone by default so the other widgets keep
|
||||||
|
the count pill flush with the edge. -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/widget_add"
|
||||||
|
android:layout_width="26dp"
|
||||||
|
android:layout_height="26dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:background="@drawable/widget_count_bg"
|
||||||
|
android:contentDescription="@string/widget_add_task"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:src="@drawable/ic_widget_add"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/widget_subtitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="1dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/widget_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:divider="@null"
|
||||||
|
android:dividerHeight="0dp"
|
||||||
|
android:listSelector="@android:color/transparent"
|
||||||
|
android:scrollbars="none" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/widget_empty"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:lineSpacingExtra="3dp"
|
||||||
|
android:textColor="@color/widget_text_secondary"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
12
android/app/src/main/res/values-night/colors.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="widget_bg">#181C20</color>
|
||||||
|
<color name="widget_text_primary">#F0F4F8</color>
|
||||||
|
<color name="widget_text_secondary">#9FA6AD</color>
|
||||||
|
<color name="widget_accent">#4B9BE8</color>
|
||||||
|
<color name="widget_accent_soft">#12395F</color>
|
||||||
|
<color name="widget_overdue">#F09898</color>
|
||||||
|
<color name="widget_warning">#F3C896</color>
|
||||||
|
<color name="widget_ring_neutral">#555E68</color>
|
||||||
|
<color name="widget_divider">#252B31</color>
|
||||||
|
</resources>
|
||||||
13
android/app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Home-screen widget palette (values-night overrides for dark mode) -->
|
||||||
|
<color name="widget_bg">#FFFFFF</color>
|
||||||
|
<color name="widget_text_primary">#171A1C</color>
|
||||||
|
<color name="widget_text_secondary">#6B7681</color>
|
||||||
|
<color name="widget_accent">#0B6BCB</color>
|
||||||
|
<color name="widget_accent_soft">#E3EFFB</color>
|
||||||
|
<color name="widget_overdue">#C41C1C</color>
|
||||||
|
<color name="widget_warning">#B26A00</color>
|
||||||
|
<color name="widget_ring_neutral">#B8C0C9</color>
|
||||||
|
<color name="widget_divider">#EDF1F5</color>
|
||||||
|
</resources>
|
||||||
@@ -4,4 +4,58 @@
|
|||||||
<string name="title_activity_main">Donetick</string>
|
<string name="title_activity_main">Donetick</string>
|
||||||
<string name="package_name">com.donetick.app</string>
|
<string name="package_name">com.donetick.app</string>
|
||||||
<string name="custom_url_scheme">com.donetick.app</string>
|
<string name="custom_url_scheme">com.donetick.app</string>
|
||||||
|
|
||||||
|
<!-- Home-screen widgets -->
|
||||||
|
<string name="widget_today_label">Today</string>
|
||||||
|
<string name="widget_week_label">Next 7 Days</string>
|
||||||
|
<string name="widget_today_description">Your tasks due today, plus anything waiting on you.</string>
|
||||||
|
<string name="widget_week_description">Your tasks for the next 7 days, grouped by day.</string>
|
||||||
|
<string name="widget_today_title">Today</string>
|
||||||
|
<string name="widget_week_title">Next 7 days</string>
|
||||||
|
<string name="widget_empty_today">All caught up!\nNothing due today</string>
|
||||||
|
<string name="widget_empty_week">All caught up!\nNothing due this week</string>
|
||||||
|
<string name="widget_signed_out">Open Donetick and sign in\nto see your tasks</string>
|
||||||
|
<string name="widget_meta_overdue">Overdue</string>
|
||||||
|
<string name="widget_meta_approval">Approve</string>
|
||||||
|
<string name="widget_group_approval">Needs approval</string>
|
||||||
|
<string name="widget_group_overdue">Overdue</string>
|
||||||
|
<string name="widget_group_today">Today</string>
|
||||||
|
<string name="widget_group_tomorrow">Tomorrow</string>
|
||||||
|
<string name="widget_updated_at">Updated %s</string>
|
||||||
|
<string name="widget_add_task">Add task</string>
|
||||||
|
<string name="widget_people_label">People</string>
|
||||||
|
<string name="widget_people_description">Everyone in your circle with their tasks for today and the week ahead.</string>
|
||||||
|
<string name="widget_people_title">People</string>
|
||||||
|
<string name="widget_empty_people">No circle members yet</string>
|
||||||
|
<string name="widget_person_counts">%1$d today · %2$d this week</string>
|
||||||
|
<string name="widget_quick_label">Quick Capture</string>
|
||||||
|
<string name="widget_quick_description">Capture a task in one tap — type it, scan it, or say it.</string>
|
||||||
|
<string name="widget_quick_type">Type</string>
|
||||||
|
<string name="widget_quick_scan">Scan</string>
|
||||||
|
<string name="widget_quick_voice">Speak</string>
|
||||||
|
<string name="widget_config_title">Widget options</string>
|
||||||
|
<string name="widget_config_include_others">Show everyone\'s tasks</string>
|
||||||
|
<string name="widget_config_include_others_hint">Include tasks assigned to other members of your circle. Their avatar appears next to their tasks.</string>
|
||||||
|
<string name="widget_config_save">Save</string>
|
||||||
|
|
||||||
|
<!-- Sample data for the widget-picker previews (never shown on a placed
|
||||||
|
widget). Kept generic so it reads well in any locale. -->
|
||||||
|
<string name="widget_preview_updated">Updated just now</string>
|
||||||
|
<string name="widget_preview_task_1">Take out the bins</string>
|
||||||
|
<string name="widget_preview_task_2">Water the plants</string>
|
||||||
|
<string name="widget_preview_task_3">Book the vet</string>
|
||||||
|
<string name="widget_preview_meta_1">6:00 PM</string>
|
||||||
|
<string name="widget_preview_meta_2">8:30 PM</string>
|
||||||
|
<string name="widget_preview_count_today">3</string>
|
||||||
|
<string name="widget_preview_count_week">7</string>
|
||||||
|
<string name="widget_preview_count_people">4</string>
|
||||||
|
<string name="widget_preview_person_1">Alex</string>
|
||||||
|
<string name="widget_preview_person_2">Sam</string>
|
||||||
|
<string name="widget_preview_person_3">Jordan</string>
|
||||||
|
<string name="widget_preview_initials_1">A</string>
|
||||||
|
<string name="widget_preview_initials_2">S</string>
|
||||||
|
<string name="widget_preview_initials_3">J</string>
|
||||||
|
<string name="widget_preview_counts_1">2 today · 5 this week</string>
|
||||||
|
<string name="widget_preview_counts_2">1 today · 3 this week</string>
|
||||||
|
<string name="widget_preview_counts_3">0 today · 2 this week</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
15
android/app/src/main/res/xml/network_security_config.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config>
|
||||||
|
<!--
|
||||||
|
Trust both the system CA store and user-installed CA certificates.
|
||||||
|
This lets self-hosted Donetick instances that use custom or self-signed
|
||||||
|
certificates work once the user installs their CA on the device, without
|
||||||
|
having to modify/repackage the APK. See donetick/donetick#727.
|
||||||
|
-->
|
||||||
|
<base-config cleartextTrafficPermitted="true">
|
||||||
|
<trust-anchors>
|
||||||
|
<certificates src="system" />
|
||||||
|
<certificates src="user" />
|
||||||
|
</trust-anchors>
|
||||||
|
</base-config>
|
||||||
|
</network-security-config>
|
||||||
15
android/app/src/main/res/xml/widget_people_info.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:description="@string/widget_people_description"
|
||||||
|
android:initialLayout="@layout/widget_shell"
|
||||||
|
android:previewLayout="@layout/widget_preview_people"
|
||||||
|
android:previewImage="@drawable/widget_preview_people"
|
||||||
|
android:minWidth="180dp"
|
||||||
|
android:minHeight="110dp"
|
||||||
|
android:minResizeWidth="110dp"
|
||||||
|
android:minResizeHeight="110dp"
|
||||||
|
android:resizeMode="horizontal|vertical"
|
||||||
|
android:targetCellWidth="3"
|
||||||
|
android:targetCellHeight="2"
|
||||||
|
android:updatePeriodMillis="1800000"
|
||||||
|
android:widgetCategory="home_screen" />
|
||||||
17
android/app/src/main/res/xml/widget_quick_capture_info.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- The layout is fully static (no bound data), so it doubles as its own
|
||||||
|
API 31+ preview. -->
|
||||||
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:description="@string/widget_quick_description"
|
||||||
|
android:initialLayout="@layout/widget_quick_capture"
|
||||||
|
android:previewLayout="@layout/widget_quick_capture"
|
||||||
|
android:previewImage="@drawable/widget_preview_quick"
|
||||||
|
android:minWidth="180dp"
|
||||||
|
android:minHeight="70dp"
|
||||||
|
android:minResizeWidth="180dp"
|
||||||
|
android:minResizeHeight="60dp"
|
||||||
|
android:resizeMode="horizontal|vertical"
|
||||||
|
android:targetCellWidth="4"
|
||||||
|
android:targetCellHeight="1"
|
||||||
|
android:updatePeriodMillis="0"
|
||||||
|
android:widgetCategory="home_screen" />
|
||||||
17
android/app/src/main/res/xml/widget_today_info.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:description="@string/widget_today_description"
|
||||||
|
android:initialLayout="@layout/widget_shell"
|
||||||
|
android:previewLayout="@layout/widget_preview_today"
|
||||||
|
android:previewImage="@drawable/widget_preview_today"
|
||||||
|
android:minWidth="180dp"
|
||||||
|
android:minHeight="110dp"
|
||||||
|
android:minResizeWidth="110dp"
|
||||||
|
android:minResizeHeight="110dp"
|
||||||
|
android:resizeMode="horizontal|vertical"
|
||||||
|
android:targetCellWidth="3"
|
||||||
|
android:targetCellHeight="2"
|
||||||
|
android:updatePeriodMillis="1800000"
|
||||||
|
android:configure="com.donetick.app.widget.WidgetConfigActivity"
|
||||||
|
android:widgetFeatures="reconfigurable|configuration_optional"
|
||||||
|
android:widgetCategory="home_screen" />
|
||||||
17
android/app/src/main/res/xml/widget_week_info.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:description="@string/widget_week_description"
|
||||||
|
android:initialLayout="@layout/widget_shell"
|
||||||
|
android:previewLayout="@layout/widget_preview_week"
|
||||||
|
android:previewImage="@drawable/widget_preview_week"
|
||||||
|
android:minWidth="250dp"
|
||||||
|
android:minHeight="180dp"
|
||||||
|
android:minResizeWidth="180dp"
|
||||||
|
android:minResizeHeight="110dp"
|
||||||
|
android:resizeMode="horizontal|vertical"
|
||||||
|
android:targetCellWidth="4"
|
||||||
|
android:targetCellHeight="3"
|
||||||
|
android:updatePeriodMillis="1800000"
|
||||||
|
android:configure="com.donetick.app.widget.WidgetConfigActivity"
|
||||||
|
android:widgetFeatures="reconfigurable|configuration_optional"
|
||||||
|
android:widgetCategory="home_screen" />
|
||||||
@@ -7,8 +7,8 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.2.1'
|
classpath 'com.android.tools.build:gradle:8.10.1'
|
||||||
classpath 'com.google.gms:google-services:4.4.0'
|
classpath 'com.google.gms:google-services:4.4.2'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
@@ -22,6 +22,12 @@ allprojects {
|
|||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations.configureEach {
|
||||||
|
// The app does not use Advertising ID. Exclude RevenueCat's transitive
|
||||||
|
// ads identifier dependency so Play Console does not require an AD_ID declaration.
|
||||||
|
exclude group: 'com.google.android.gms', module: 'play-services-ads-identifier'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
|
|||||||
@@ -2,15 +2,36 @@
|
|||||||
include ':capacitor-android'
|
include ':capacitor-android'
|
||||||
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
|
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
|
||||||
|
|
||||||
|
include ':capacitor-community-in-app-review'
|
||||||
|
project(':capacitor-community-in-app-review').projectDir = new File('../node_modules/@capacitor-community/in-app-review/android')
|
||||||
|
|
||||||
|
include ':capacitor-community-speech-recognition'
|
||||||
|
project(':capacitor-community-speech-recognition').projectDir = new File('../node_modules/@capacitor-community/speech-recognition/android')
|
||||||
|
|
||||||
|
include ':capacitor-community-sqlite'
|
||||||
|
project(':capacitor-community-sqlite').projectDir = new File('../node_modules/@capacitor-community/sqlite/android')
|
||||||
|
|
||||||
include ':capacitor-app'
|
include ':capacitor-app'
|
||||||
project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
|
project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
|
||||||
|
|
||||||
|
include ':capacitor-browser'
|
||||||
|
project(':capacitor-browser').projectDir = new File('../node_modules/@capacitor/browser/android')
|
||||||
|
|
||||||
include ':capacitor-device'
|
include ':capacitor-device'
|
||||||
project(':capacitor-device').projectDir = new File('../node_modules/@capacitor/device/android')
|
project(':capacitor-device').projectDir = new File('../node_modules/@capacitor/device/android')
|
||||||
|
|
||||||
|
include ':capacitor-haptics'
|
||||||
|
project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/haptics/android')
|
||||||
|
|
||||||
|
include ':capacitor-local-llm'
|
||||||
|
project(':capacitor-local-llm').projectDir = new File('../node_modules/@capacitor/local-llm/android')
|
||||||
|
|
||||||
include ':capacitor-local-notifications'
|
include ':capacitor-local-notifications'
|
||||||
project(':capacitor-local-notifications').projectDir = new File('../node_modules/@capacitor/local-notifications/android')
|
project(':capacitor-local-notifications').projectDir = new File('../node_modules/@capacitor/local-notifications/android')
|
||||||
|
|
||||||
|
include ':capacitor-network'
|
||||||
|
project(':capacitor-network').projectDir = new File('../node_modules/@capacitor/network/android')
|
||||||
|
|
||||||
include ':capacitor-preferences'
|
include ':capacitor-preferences'
|
||||||
project(':capacitor-preferences').projectDir = new File('../node_modules/@capacitor/preferences/android')
|
project(':capacitor-preferences').projectDir = new File('../node_modules/@capacitor/preferences/android')
|
||||||
|
|
||||||
@@ -20,8 +41,23 @@ project(':capacitor-push-notifications').projectDir = new File('../node_modules/
|
|||||||
include ':capacitor-status-bar'
|
include ':capacitor-status-bar'
|
||||||
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')
|
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')
|
||||||
|
|
||||||
|
include ':capgo-capacitor-document-scanner'
|
||||||
|
project(':capgo-capacitor-document-scanner').projectDir = new File('../node_modules/@capgo/capacitor-document-scanner/android')
|
||||||
|
|
||||||
|
include ':capgo-capacitor-nfc'
|
||||||
|
project(':capgo-capacitor-nfc').projectDir = new File('../node_modules/@capgo/capacitor-nfc/android')
|
||||||
|
|
||||||
include ':capgo-capacitor-social-login'
|
include ':capgo-capacitor-social-login'
|
||||||
project(':capgo-capacitor-social-login').projectDir = new File('../node_modules/@capgo/capacitor-social-login/android')
|
project(':capgo-capacitor-social-login').projectDir = new File('../node_modules/@capgo/capacitor-social-login/android')
|
||||||
|
|
||||||
|
include ':jcesarmobile-capacitor-ocr'
|
||||||
|
project(':jcesarmobile-capacitor-ocr').projectDir = new File('../node_modules/@jcesarmobile/capacitor-ocr/android')
|
||||||
|
|
||||||
|
include ':revenuecat-purchases-capacitor'
|
||||||
|
project(':revenuecat-purchases-capacitor').projectDir = new File('../node_modules/@revenuecat/purchases-capacitor/android')
|
||||||
|
|
||||||
|
include ':revenuecat-purchases-capacitor-ui'
|
||||||
|
project(':revenuecat-purchases-capacitor-ui').projectDir = new File('../node_modules/@revenuecat/purchases-capacitor-ui/android')
|
||||||
|
|
||||||
include ':capacitor-plugin-safe-area'
|
include ':capacitor-plugin-safe-area'
|
||||||
project(':capacitor-plugin-safe-area').projectDir = new File('../node_modules/capacitor-plugin-safe-area/android')
|
project(':capacitor-plugin-safe-area').projectDir = new File('../node_modules/capacitor-plugin-safe-area/android')
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
ext {
|
ext {
|
||||||
minSdkVersion = 22
|
minSdkVersion = 28
|
||||||
compileSdkVersion = 34
|
compileSdkVersion = 36
|
||||||
targetSdkVersion = 34
|
targetSdkVersion = 36
|
||||||
androidxActivityVersion = '1.8.0'
|
androidxActivityVersion = '1.11.0'
|
||||||
androidxAppCompatVersion = '1.6.1'
|
androidxAppCompatVersion = '1.7.1'
|
||||||
androidxCoordinatorLayoutVersion = '1.2.0'
|
androidxCoordinatorLayoutVersion = '1.3.0'
|
||||||
androidxCoreVersion = '1.12.0'
|
androidxCoreVersion = '1.17.0'
|
||||||
androidxFragmentVersion = '1.6.2'
|
androidxFragmentVersion = '1.8.9'
|
||||||
coreSplashScreenVersion = '1.0.1'
|
coreSplashScreenVersion = '1.0.1'
|
||||||
androidxWebkitVersion = '1.9.0'
|
androidxWebkitVersion = '1.14.0'
|
||||||
junitVersion = '4.13.2'
|
junitVersion = '4.13.2'
|
||||||
androidxJunitVersion = '1.1.5'
|
androidxJunitVersion = '1.3.0'
|
||||||
androidxEspressoCoreVersion = '3.5.1'
|
androidxEspressoCoreVersion = '3.7.0'
|
||||||
cordovaAndroidVersion = '10.1.1'
|
cordovaAndroidVersion = '10.1.1'
|
||||||
}
|
firebaseMessagingVersion = '24.1.0'
|
||||||
|
}
|
||||||
|
|||||||
BIN
assets/ipad_dashbard_calendar.png
Normal file
|
After Width: | Height: | Size: 419 KiB |
84
bump-version.js
Executable file
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { execSync } from 'child_process'
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
|
const __dirname = path.dirname(__filename)
|
||||||
|
|
||||||
|
// Read package.json
|
||||||
|
const packageJsonPath = path.join(__dirname, 'package.json')
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
||||||
|
|
||||||
|
// Get current version
|
||||||
|
const currentVersion = packageJson.version
|
||||||
|
console.log(`Current version: ${currentVersion}`)
|
||||||
|
|
||||||
|
// Parse version (assuming semver format: major.minor.patch)
|
||||||
|
const versionParts = currentVersion.split('.').map(Number)
|
||||||
|
const [major, minor, patch] = versionParts
|
||||||
|
|
||||||
|
// Bump patch version by default (can be modified to accept arguments for major/minor)
|
||||||
|
const bumpType = process.argv[2] || 'patch'
|
||||||
|
let newVersion
|
||||||
|
|
||||||
|
switch (bumpType) {
|
||||||
|
case 'major':
|
||||||
|
newVersion = `${major + 1}.0.0`
|
||||||
|
break
|
||||||
|
case 'minor':
|
||||||
|
newVersion = `${major}.${minor + 1}.0`
|
||||||
|
break
|
||||||
|
case 'patch':
|
||||||
|
default:
|
||||||
|
newVersion = `${major}.${minor}.${patch + 1}`
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`New version: ${newVersion}`)
|
||||||
|
|
||||||
|
// Update package.json
|
||||||
|
packageJson.version = newVersion
|
||||||
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n')
|
||||||
|
console.log('✓ Updated package.json')
|
||||||
|
|
||||||
|
// Read Android build.gradle to get current versionCode
|
||||||
|
const buildGradlePath = path.join(__dirname, 'android', 'app', 'build.gradle')
|
||||||
|
let buildGradle = fs.readFileSync(buildGradlePath, 'utf8')
|
||||||
|
|
||||||
|
// Extract current versionCode
|
||||||
|
const versionCodeMatch = buildGradle.match(/versionCode\s+(\d+)/)
|
||||||
|
const currentVersionCode = versionCodeMatch ? parseInt(versionCodeMatch[1]) : 1
|
||||||
|
const newVersionCode = currentVersionCode + 1
|
||||||
|
|
||||||
|
console.log(`Android versionCode: ${currentVersionCode} → ${newVersionCode}`)
|
||||||
|
|
||||||
|
// Use capacitor-set-version to update both iOS and Android
|
||||||
|
try {
|
||||||
|
console.log('\nUpdating iOS and Android versions...')
|
||||||
|
|
||||||
|
// Set version and build number for both platforms
|
||||||
|
execSync(
|
||||||
|
`npx capacitor-set-version set -v ${newVersion} -b ${newVersionCode}`,
|
||||||
|
{
|
||||||
|
stdio: 'inherit',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log('\n✓ Successfully bumped version to', newVersion)
|
||||||
|
console.log(`✓ Build number: ${newVersionCode}`)
|
||||||
|
console.log('\nNext steps:')
|
||||||
|
console.log('1. Review the changes')
|
||||||
|
console.log(
|
||||||
|
'2. Commit: git add . && git commit -m "Bump version to ' +
|
||||||
|
newVersion +
|
||||||
|
'"',
|
||||||
|
)
|
||||||
|
console.log('3. Tag: git tag v' + newVersion)
|
||||||
|
console.log('4. Push: git push origin develop --tags')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating versions:', error.message)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
@@ -1,26 +1,44 @@
|
|||||||
import type { CapacitorConfig } from '@capacitor/cli';
|
import type { CapacitorConfig } from '@capacitor/cli'
|
||||||
|
|
||||||
const config: CapacitorConfig = {
|
const config: CapacitorConfig = {
|
||||||
appId: 'com.donetick.app',
|
appId: 'com.donetick.app',
|
||||||
appName: 'Donetick',
|
appName: 'Donetick',
|
||||||
webDir: 'dist',
|
webDir: 'dist',
|
||||||
|
android: {
|
||||||
|
allowMixedContent: true,
|
||||||
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
PushNotifications: {
|
PushNotifications: {
|
||||||
presentationOptions: ['badge', 'sound', 'alert'],
|
presentationOptions: ['badge', 'sound', 'alert'],
|
||||||
},
|
},
|
||||||
LocalNotifications: {
|
LocalNotifications: {
|
||||||
smallIcon: "ic_stat_icon_config_sample",
|
smallIcon: 'ic_stat_icon_config_sample',
|
||||||
iconColor: "#488AFF",
|
iconColor: '#488AFF',
|
||||||
sound: "beep.wav",
|
sound: 'beep.wav',
|
||||||
},
|
},
|
||||||
GoogleAuth: {
|
CapacitorSQLite: {
|
||||||
scopes: ['profile', 'email', 'openid'],
|
// The offline cache opens databases with encrypted: false / no-encryption.
|
||||||
clientId: process.env.VITE_APP_GOOGLE_CLIENT_ID,
|
// @capacitor-community/sqlite defaults native encryption to true when this
|
||||||
androidClientId: process.env.VITE_APP_ANDRIOD_CLIENT_ID,
|
// block is absent; on Android that can make plugin load fail with
|
||||||
iosClientId: process.env.VITE_APP_IOS_CLIENT_ID,
|
// "CapacitorSQLitePlugin: null" before JS gets a chance to open the DB.
|
||||||
|
iosIsEncryption: false,
|
||||||
|
androidIsEncryption: false,
|
||||||
|
},
|
||||||
|
SocialLogin: {
|
||||||
|
providers: {
|
||||||
|
google: true,
|
||||||
|
facebook: false,
|
||||||
|
apple: true,
|
||||||
|
twitter: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// GoogleAuth: {
|
||||||
|
// scopes: ['profile', 'email', 'openid'],
|
||||||
|
// clientId: process.env.VITE_APP_GOOGLE_CLIENT_ID,
|
||||||
|
// androidClientId: process.env.VITE_APP_ANDRIOD_CLIENT_ID,
|
||||||
|
// iosClientId: process.env.VITE_APP_IOS_CLIENT_ID,
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
export default config
|
||||||
|
|||||||
15
crowdin.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Crowdin configuration for Donetick
|
||||||
|
# See: https://support.crowdin.com/configuration-file/
|
||||||
|
|
||||||
|
project_id: "donetick"
|
||||||
|
api_token_env: CROWDIN_API_TOKEN
|
||||||
|
preserve_hierarchy: true
|
||||||
|
|
||||||
|
languages_mapping:
|
||||||
|
two_letters_code:
|
||||||
|
zh-CN: zh-CN
|
||||||
|
|
||||||
|
files:
|
||||||
|
- source: /public/locales/en/*.json
|
||||||
|
translation: /public/locales/%two_letters_code%/%original_file_name%
|
||||||
|
update_option: update_as_unapproved
|
||||||
187
eslint.config.mjs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
import js from '@eslint/js'
|
||||||
|
import stylistic from '@stylistic/eslint-plugin'
|
||||||
|
import queryPlugin from '@tanstack/eslint-plugin-query'
|
||||||
|
import typescriptEslint from '@typescript-eslint/eslint-plugin'
|
||||||
|
import tsParser from '@typescript-eslint/parser'
|
||||||
|
import eslintConfigPrettier from 'eslint-config-prettier/flat'
|
||||||
|
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
|
||||||
|
import react from 'eslint-plugin-react'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
import simpleImportSort from 'eslint-plugin-simple-import-sort'
|
||||||
|
import sortDestructureKeys from 'eslint-plugin-sort-destructure-keys'
|
||||||
|
import tailwind from 'eslint-plugin-tailwindcss'
|
||||||
|
import globals from 'globals'
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['dist'],
|
||||||
|
},
|
||||||
|
js.configs.recommended,
|
||||||
|
react.configs.flat.recommended,
|
||||||
|
{
|
||||||
|
...react.configs.flat['jsx-runtime'],
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reactHooks.configs.flat.recommended,
|
||||||
|
reactRefresh.configs.recommended,
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
'simple-import-sort': simpleImportSort,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'simple-import-sort/exports': 'error',
|
||||||
|
'simple-import-sort/imports': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
'sort-destructure-keys': sortDestructureKeys,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'sort-destructure-keys/sort-destructure-keys': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...tailwind.configs['flat/recommended'],
|
||||||
|
...queryPlugin.configs['flat/recommended'],
|
||||||
|
eslintConfigPrettier, // Disable any rules that conflict with prettier
|
||||||
|
eslintPluginPrettierRecommended,
|
||||||
|
{
|
||||||
|
files: ['*.{js,ts}'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: globals.node,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.config.{mjs,ts}'],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
eqeqeq: 'error',
|
||||||
|
indent: ['error', 2],
|
||||||
|
'space-infix-ops': ['error', { int32Hint: false }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['src/**/*.{js,jsx,ts,tsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
globals: globals.browser,
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'@stylistic': stylistic,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'@stylistic/brace-style': 'error',
|
||||||
|
'@stylistic/comma-dangle': ['error', 'always-multiline'],
|
||||||
|
'@stylistic/keyword-spacing': 'error',
|
||||||
|
'@stylistic/no-multiple-empty-lines': ['error', { max: 1 }],
|
||||||
|
'@stylistic/no-trailing-spaces': 'error',
|
||||||
|
'@stylistic/object-curly-spacing': ['error', 'always'],
|
||||||
|
'@stylistic/semi': ['error', 'never'],
|
||||||
|
'@stylistic/space-in-parens': 'error',
|
||||||
|
'@stylistic/space-infix-ops': ['error', { int32Hint: false }],
|
||||||
|
'@stylistic/type-annotation-spacing': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
after: true,
|
||||||
|
before: true,
|
||||||
|
overrides: {
|
||||||
|
colon: {
|
||||||
|
before: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
eqeqeq: 'warn',
|
||||||
|
'max-depth': ['error', { max: 5 }],
|
||||||
|
'max-len': ['warn', { code: 120 }],
|
||||||
|
'max-lines': [
|
||||||
|
'error',
|
||||||
|
{ max: 2000, skipBlankLines: true, skipComments: true },
|
||||||
|
],
|
||||||
|
'max-lines-per-function': ['warn', { max: 500 }],
|
||||||
|
'no-constant-condition': 'warn',
|
||||||
|
'no-loss-of-precision': 'error',
|
||||||
|
'no-undef': 'warn',
|
||||||
|
'no-unused-vars': 'off', // @typescript-eslint/no-unused-vars
|
||||||
|
'space-infix-ops': 'off', // @stylistic/space-infix-ops
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.{ts,tsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
parserOptions: {
|
||||||
|
project: 'tsconfig.json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': typescriptEslint,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
|
||||||
|
'@typescript-eslint/no-duplicate-enum-values': 'error',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'error',
|
||||||
|
'@typescript-eslint/no-inferrable-types': 'error',
|
||||||
|
'@typescript-eslint/no-mixed-enums': 'error',
|
||||||
|
'@typescript-eslint/no-require-imports': 'error',
|
||||||
|
'@typescript-eslint/no-type-alias': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
allowAliases: 'always',
|
||||||
|
allowCallbacks: 'always',
|
||||||
|
allowConditionalTypes: 'never',
|
||||||
|
allowConstructors: 'never',
|
||||||
|
allowGenerics: 'always',
|
||||||
|
allowLiterals: 'always',
|
||||||
|
allowMappedTypes: 'never',
|
||||||
|
allowTupleTypes: 'always',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
|
||||||
|
'@typescript-eslint/no-unsafe-argument': 'warn',
|
||||||
|
'@typescript-eslint/no-unsafe-assignment': 'warn',
|
||||||
|
'@typescript-eslint/no-unsafe-member-access': 'warn',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'warn',
|
||||||
|
{ argsIgnorePattern: '^_' },
|
||||||
|
],
|
||||||
|
'@typescript-eslint/prefer-as-const': 'error',
|
||||||
|
'@typescript-eslint/prefer-for-of': 'warn',
|
||||||
|
'@typescript-eslint/prefer-function-type': 'error',
|
||||||
|
'@typescript-eslint/prefer-includes': 'error',
|
||||||
|
'@typescript-eslint/prefer-literal-enum-member': 'error',
|
||||||
|
'@typescript-eslint/prefer-nullish-coalescing': 'warn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Reduce existing errors to warnings
|
||||||
|
plugins: {
|
||||||
|
'@tanstack/query': queryPlugin,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'@tanstack/query/exhaustive-deps': 'warn',
|
||||||
|
'no-case-declarations': 'warn',
|
||||||
|
'no-empty': 'warn',
|
||||||
|
'no-redeclare': 'warn',
|
||||||
|
'react-hooks/immutability': 'warn',
|
||||||
|
'react-hooks/preserve-manual-memoization': 'warn',
|
||||||
|
'react-hooks/purity': 'warn',
|
||||||
|
'react-hooks/refs': 'warn',
|
||||||
|
'react-hooks/set-state-in-effect': 'warn',
|
||||||
|
'react-hooks/static-components': 'warn',
|
||||||
|
'react-refresh/only-export-components': 'warn',
|
||||||
|
'react/jsx-no-undef': 'warn',
|
||||||
|
'react/no-unescaped-entities': 'warn',
|
||||||
|
'react/prop-types': 'warn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
4
fastlane/Appfile
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
app_identifier("com.donetick.app")
|
||||||
|
team_id("6UJJ78R3BS")
|
||||||
|
|
||||||
|
apple_id(ENV["APPLE_ID"]) if ENV["APPLE_ID"]
|
||||||
114
fastlane/Fastfile
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
require "shellwords"
|
||||||
|
|
||||||
|
default_platform(:ios)
|
||||||
|
|
||||||
|
IOS_KEY_PATH = File.expand_path("../ios/AuthKey_84F695CDQ3.p8")
|
||||||
|
IOS_OUTPUT_DIR = File.expand_path("../build/ios")
|
||||||
|
ANDROID_PROJECT_DIR = File.expand_path("../android")
|
||||||
|
PLAY_JSON_KEY_PATH = File.expand_path("../android/play-service-account.json")
|
||||||
|
|
||||||
|
platform :ios do
|
||||||
|
desc "Build a signed, App Store-ready .ipa"
|
||||||
|
lane :release do
|
||||||
|
Dir.chdir(File.expand_path("../ios/App")) do
|
||||||
|
Bundler.with_clean_env do
|
||||||
|
sh("pod install --repo-update")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
build_app(
|
||||||
|
workspace: "ios/App/App.xcworkspace",
|
||||||
|
scheme: "App",
|
||||||
|
export_method: "app-store",
|
||||||
|
output_directory: IOS_OUTPUT_DIR,
|
||||||
|
output_name: "Donetick.ipa",
|
||||||
|
export_options: {
|
||||||
|
signingStyle: "manual",
|
||||||
|
provisioningProfiles: {
|
||||||
|
"com.donetick.app" => "Donetick App Store(fastline)",
|
||||||
|
"com.donetick.app.widget" => "Donetick Widget App Store(fastline)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
UI.success("Signed .ipa at #{IOS_OUTPUT_DIR}/Donetick.ipa")
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Upload the built .ipa to TestFlight"
|
||||||
|
lane :upload do
|
||||||
|
UI.user_error!("Missing #{IOS_KEY_PATH} — run scripts/pull-secrets.sh first") unless File.exist?(IOS_KEY_PATH)
|
||||||
|
UI.user_error!("ASC_ISSUER_ID is not set (App Store Connect API key issuer id)") if ENV["ASC_ISSUER_ID"].to_s.empty?
|
||||||
|
|
||||||
|
ipa_path = File.join(IOS_OUTPUT_DIR, "Donetick.ipa")
|
||||||
|
UI.user_error!("Missing #{ipa_path} — run `fastlane ios release` first") unless File.exist?(ipa_path)
|
||||||
|
|
||||||
|
api_key = app_store_connect_api_key(
|
||||||
|
key_id: ENV["ASC_KEY_ID"] || "84F695CDQ3",
|
||||||
|
issuer_id: ENV["ASC_ISSUER_ID"],
|
||||||
|
key_filepath: IOS_KEY_PATH,
|
||||||
|
duration: 1200,
|
||||||
|
in_house: false,
|
||||||
|
)
|
||||||
|
|
||||||
|
upload_to_testflight(
|
||||||
|
api_key: api_key,
|
||||||
|
ipa: ipa_path,
|
||||||
|
skip_waiting_for_build_processing: true,
|
||||||
|
skip_submission: true,
|
||||||
|
)
|
||||||
|
|
||||||
|
UI.success("Uploaded to TestFlight")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
platform :android do
|
||||||
|
desc "Build a signed release .aab"
|
||||||
|
lane :release do
|
||||||
|
keystore_properties = File.join(ANDROID_PROJECT_DIR, "keystore.properties")
|
||||||
|
UI.user_error!("Missing #{keystore_properties} — run scripts/pull-secrets.sh first") unless File.exist?(keystore_properties)
|
||||||
|
|
||||||
|
gradle(
|
||||||
|
task: "bundle",
|
||||||
|
build_type: "Release",
|
||||||
|
project_dir: "#{ANDROID_PROJECT_DIR}/",
|
||||||
|
)
|
||||||
|
|
||||||
|
aab_path = lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH]
|
||||||
|
UI.success("Signed .aab at #{aab_path}")
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Build a signed release .apk (for sideloading/testing)"
|
||||||
|
lane :apk do
|
||||||
|
keystore_properties = File.join(ANDROID_PROJECT_DIR, "keystore.properties")
|
||||||
|
UI.user_error!("Missing #{keystore_properties} — run scripts/pull-secrets.sh first") unless File.exist?(keystore_properties)
|
||||||
|
|
||||||
|
gradle(
|
||||||
|
task: "assemble",
|
||||||
|
build_type: "Release",
|
||||||
|
project_dir: "#{ANDROID_PROJECT_DIR}/",
|
||||||
|
)
|
||||||
|
|
||||||
|
apk_path = lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]
|
||||||
|
UI.success("Signed .apk at #{apk_path}")
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Upload the built .aab to the Play Store (internal testing track by default)"
|
||||||
|
lane :upload do |options|
|
||||||
|
UI.user_error!("Missing #{PLAY_JSON_KEY_PATH} — run scripts/pull-secrets.sh first") unless File.exist?(PLAY_JSON_KEY_PATH)
|
||||||
|
|
||||||
|
aab_path = File.join(ANDROID_PROJECT_DIR, "app/build/outputs/bundle/release/app-release.aab")
|
||||||
|
UI.user_error!("Missing #{aab_path} — run `fastlane android release` first") unless File.exist?(aab_path)
|
||||||
|
|
||||||
|
upload_to_play_store(
|
||||||
|
json_key: PLAY_JSON_KEY_PATH,
|
||||||
|
package_name: "com.donetick.app",
|
||||||
|
aab: aab_path,
|
||||||
|
track: options[:track] || "internal",
|
||||||
|
skip_upload_metadata: true,
|
||||||
|
skip_upload_images: true,
|
||||||
|
skip_upload_screenshots: true,
|
||||||
|
)
|
||||||
|
|
||||||
|
UI.success("Uploaded to Play Store (#{options[:track] || 'internal'} track)")
|
||||||
|
end
|
||||||
|
end
|
||||||
69
fastlane/README.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
fastlane documentation
|
||||||
|
----
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
Make sure you have the latest version of the Xcode command line tools installed:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
xcode-select --install
|
||||||
|
```
|
||||||
|
|
||||||
|
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
|
||||||
|
|
||||||
|
# Available Actions
|
||||||
|
|
||||||
|
## iOS
|
||||||
|
|
||||||
|
### ios release
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane ios release
|
||||||
|
```
|
||||||
|
|
||||||
|
Build a signed, App Store-ready .ipa
|
||||||
|
|
||||||
|
### ios upload
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane ios upload
|
||||||
|
```
|
||||||
|
|
||||||
|
Upload the built .ipa to TestFlight
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
## Android
|
||||||
|
|
||||||
|
### android release
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android release
|
||||||
|
```
|
||||||
|
|
||||||
|
Build a signed release .aab
|
||||||
|
|
||||||
|
### android apk
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android apk
|
||||||
|
```
|
||||||
|
|
||||||
|
Build a signed release .apk (for sideloading/testing)
|
||||||
|
|
||||||
|
### android upload
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android upload
|
||||||
|
```
|
||||||
|
|
||||||
|
Upload the built .aab to the Play Store (internal testing track by default)
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||||
|
|
||||||
|
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
|
||||||
|
|
||||||
|
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
||||||
@@ -3,8 +3,13 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<!-- <link rel="icon" type="image/svg+xml" href="/logo.svg" /> -->
|
<!-- <link rel="icon" type="image/svg+xml" href="/logo.svg" /> -->
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta
|
||||||
<!-- <title>FE Template</title> -->
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<title>Donetick</title>
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||||
|
|||||||
@@ -14,8 +14,13 @@
|
|||||||
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
|
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
|
||||||
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
|
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
|
||||||
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
|
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
|
||||||
|
72FA9293C4A649D1BA0E5917 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 71866EB277374608AD02C137 /* PrivacyInfo.xcprivacy */; };
|
||||||
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
|
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
|
||||||
D1115B492C653D60004C6043 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D1115B482C653D60004C6043 /* GoogleService-Info.plist */; };
|
D1115B492C653D60004C6043 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D1115B482C653D60004C6043 /* GoogleService-Info.plist */; };
|
||||||
|
D0AC000000000000000000B1 /* WidgetBridgePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AC000000000000000000A1 /* WidgetBridgePlugin.swift */; };
|
||||||
|
D0AC000000000000000000B2 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AC000000000000000000A2 /* MainViewController.swift */; };
|
||||||
|
D0AC000000000000000000B3 /* DonetickWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AC000000000000000000A3 /* DonetickWidget.swift */; };
|
||||||
|
D0AC000000000000000000B4 /* DonetickWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D0AC000000000000000000A4 /* DonetickWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@@ -28,13 +33,44 @@
|
|||||||
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
|
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
|
||||||
|
71866EB277374608AD02C137 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||||
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
|
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
D1115B482C653D60004C6043 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
D1115B482C653D60004C6043 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||||
D1115B4A2C659D51004C6043 /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
|
D1115B4A2C659D51004C6043 /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
|
||||||
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
|
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
D0AC000000000000000000A1 /* WidgetBridgePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetBridgePlugin.swift; sourceTree = "<group>"; };
|
||||||
|
D0AC000000000000000000A2 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
|
||||||
|
D0AC000000000000000000A3 /* DonetickWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonetickWidget.swift; sourceTree = "<group>"; };
|
||||||
|
D0AC000000000000000000A4 /* DonetickWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = DonetickWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
D0AC000000000000000000A5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
D0AC000000000000000000A6 /* DonetickWidget.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DonetickWidget.entitlements; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
D0AC000000000000000000C2 /* Embed App Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
D0AC000000000000000000B4 /* DonetickWidgetExtension.appex in Embed App Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed App Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
D0AC000000000000000000E2 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 504EC2FC1FED79650016851F /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = D0AC000000000000000000D1;
|
||||||
|
remoteInfo = DonetickWidgetExtension;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
504EC3011FED79650016851F /* Frameworks */ = {
|
504EC3011FED79650016851F /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
@@ -44,6 +80,13 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
D0AC000000000000000000D3 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
@@ -59,16 +102,29 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
504EC3061FED79650016851F /* App */,
|
504EC3061FED79650016851F /* App */,
|
||||||
|
D0AC000000000000000000C1 /* DonetickWidget */,
|
||||||
504EC3051FED79650016851F /* Products */,
|
504EC3051FED79650016851F /* Products */,
|
||||||
7F8756D8B27F46E3366F6CEA /* Pods */,
|
7F8756D8B27F46E3366F6CEA /* Pods */,
|
||||||
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
|
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
|
||||||
|
71866EB277374608AD02C137 /* PrivacyInfo.xcprivacy */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D0AC000000000000000000C1 /* DonetickWidget */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D0AC000000000000000000A3 /* DonetickWidget.swift */,
|
||||||
|
D0AC000000000000000000A5 /* Info.plist */,
|
||||||
|
D0AC000000000000000000A6 /* DonetickWidget.entitlements */,
|
||||||
|
);
|
||||||
|
path = DonetickWidget;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
504EC3051FED79650016851F /* Products */ = {
|
504EC3051FED79650016851F /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
504EC3041FED79650016851F /* App.app */,
|
504EC3041FED79650016851F /* App.app */,
|
||||||
|
D0AC000000000000000000A4 /* DonetickWidgetExtension.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -79,6 +135,8 @@
|
|||||||
D1115B4A2C659D51004C6043 /* App.entitlements */,
|
D1115B4A2C659D51004C6043 /* App.entitlements */,
|
||||||
50379B222058CBB4000EE86E /* capacitor.config.json */,
|
50379B222058CBB4000EE86E /* capacitor.config.json */,
|
||||||
504EC3071FED79650016851F /* AppDelegate.swift */,
|
504EC3071FED79650016851F /* AppDelegate.swift */,
|
||||||
|
D0AC000000000000000000A2 /* MainViewController.swift */,
|
||||||
|
D0AC000000000000000000A1 /* WidgetBridgePlugin.swift */,
|
||||||
504EC30B1FED79650016851F /* Main.storyboard */,
|
504EC30B1FED79650016851F /* Main.storyboard */,
|
||||||
504EC30E1FED79650016851F /* Assets.xcassets */,
|
504EC30E1FED79650016851F /* Assets.xcassets */,
|
||||||
504EC3101FED79650016851F /* LaunchScreen.storyboard */,
|
504EC3101FED79650016851F /* LaunchScreen.storyboard */,
|
||||||
@@ -111,30 +169,53 @@
|
|||||||
504EC3011FED79650016851F /* Frameworks */,
|
504EC3011FED79650016851F /* Frameworks */,
|
||||||
504EC3021FED79650016851F /* Resources */,
|
504EC3021FED79650016851F /* Resources */,
|
||||||
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,
|
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,
|
||||||
|
D0AC000000000000000000C2 /* Embed App Extensions */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
D0AC000000000000000000E1 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = App;
|
name = App;
|
||||||
productName = App;
|
productName = App;
|
||||||
productReference = 504EC3041FED79650016851F /* App.app */;
|
productReference = 504EC3041FED79650016851F /* App.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
D0AC000000000000000000D1 /* DonetickWidgetExtension */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = D0AC000000000000000000F1 /* Build configuration list for PBXNativeTarget "DonetickWidgetExtension" */;
|
||||||
|
buildPhases = (
|
||||||
|
D0AC000000000000000000D2 /* Sources */,
|
||||||
|
D0AC000000000000000000D3 /* Frameworks */,
|
||||||
|
D0AC000000000000000000D4 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = DonetickWidgetExtension;
|
||||||
|
productName = DonetickWidgetExtension;
|
||||||
|
productReference = D0AC000000000000000000A4 /* DonetickWidgetExtension.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
504EC2FC1FED79650016851F /* Project object */ = {
|
504EC2FC1FED79650016851F /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0920;
|
LastSwiftUpdateCheck = 920;
|
||||||
LastUpgradeCheck = 0920;
|
LastUpgradeCheck = 920;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
504EC3031FED79650016851F = {
|
504EC3031FED79650016851F = {
|
||||||
CreatedOnToolsVersion = 9.2;
|
CreatedOnToolsVersion = 9.2;
|
||||||
LastSwiftMigration = 1100;
|
LastSwiftMigration = 1100;
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Automatic;
|
||||||
};
|
};
|
||||||
|
D0AC000000000000000000D1 = {
|
||||||
|
CreatedOnToolsVersion = 15.0;
|
||||||
|
ProvisioningStyle = Automatic;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */;
|
buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */;
|
||||||
@@ -153,6 +234,7 @@
|
|||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
504EC3031FED79650016851F /* App */,
|
504EC3031FED79650016851F /* App */,
|
||||||
|
D0AC000000000000000000D1 /* DonetickWidgetExtension */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@@ -169,6 +251,14 @@
|
|||||||
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
|
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
|
||||||
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
|
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
|
||||||
D1115B492C653D60004C6043 /* GoogleService-Info.plist in Resources */,
|
D1115B492C653D60004C6043 /* GoogleService-Info.plist in Resources */,
|
||||||
|
72FA9293C4A649D1BA0E5917 /* PrivacyInfo.xcprivacy in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
D0AC000000000000000000D4 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -216,11 +306,29 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
|
504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
|
||||||
|
D0AC000000000000000000B2 /* MainViewController.swift in Sources */,
|
||||||
|
D0AC000000000000000000B1 /* WidgetBridgePlugin.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
D0AC000000000000000000D2 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
D0AC000000000000000000B3 /* DonetickWidget.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
D0AC000000000000000000E1 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = D0AC000000000000000000D1 /* DonetickWidgetExtension */;
|
||||||
|
targetProxy = D0AC000000000000000000E2 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
504EC30B1FED79650016851F /* Main.storyboard */ = {
|
504EC30B1FED79650016851F /* Main.storyboard */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
@@ -270,7 +378,6 @@
|
|||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
@@ -289,7 +396,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -327,7 +434,6 @@
|
|||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
@@ -340,7 +446,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
@@ -353,18 +459,17 @@
|
|||||||
baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
|
baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 55;
|
||||||
DEVELOPMENT_TEAM = 6UJJ78R3BS;
|
DEVELOPMENT_TEAM = 6UJJ78R3BS;
|
||||||
INFOPLIST_FILE = App/Info.plist;
|
INFOPLIST_FILE = App/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.2.34;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
};
|
};
|
||||||
@@ -375,14 +480,17 @@
|
|||||||
baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
|
baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_IDENTITY = "Apple Distribution";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CODE_SIGN_STYLE = Manual;
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "Donetick App Store(fastline)";
|
||||||
|
CURRENT_PROJECT_VERSION = 55;
|
||||||
DEVELOPMENT_TEAM = 6UJJ78R3BS;
|
DEVELOPMENT_TEAM = 6UJJ78R3BS;
|
||||||
INFOPLIST_FILE = App/Info.plist;
|
INFOPLIST_FILE = App/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.2.34;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
||||||
@@ -391,6 +499,48 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
D0AC000000000000000000F2 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = DonetickWidget/DonetickWidget.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 55;
|
||||||
|
DEVELOPMENT_TEAM = 6UJJ78R3BS;
|
||||||
|
INFOPLIST_FILE = DonetickWidget/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
|
MARKETING_VERSION = 1.2.34;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app.widget;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
D0AC000000000000000000F3 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = DonetickWidget/DonetickWidget.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Distribution";
|
||||||
|
CODE_SIGN_STYLE = Manual;
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "Donetick Widget App Store(fastline)";
|
||||||
|
CURRENT_PROJECT_VERSION = 55;
|
||||||
|
DEVELOPMENT_TEAM = 6UJJ78R3BS;
|
||||||
|
INFOPLIST_FILE = DonetickWidget/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
|
MARKETING_VERSION = 1.2.34;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app.widget;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@@ -412,6 +562,15 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
D0AC000000000000000000F1 /* Build configuration list for PBXNativeTarget "DonetickWidgetExtension" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
D0AC000000000000000000F2 /* Debug */,
|
||||||
|
D0AC000000000000000000F3 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 504EC2FC1FED79650016851F /* Project object */;
|
rootObject = 504EC2FC1FED79650016851F /* Project object */;
|
||||||
|
|||||||
@@ -4,5 +4,17 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>aps-environment</key>
|
<key>aps-environment</key>
|
||||||
<string>development</string>
|
<string>development</string>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.com.donetick.app</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.developer.applesignin</key>
|
||||||
|
<array>
|
||||||
|
<string>Default</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.developer.nfc.readersession.formats</key>
|
||||||
|
<array>
|
||||||
|
<string>TAG</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Capacitor
|
import Capacitor
|
||||||
|
import FirebaseCore
|
||||||
|
import FirebaseMessaging
|
||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
@@ -8,6 +9,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
// Override point for customization after application launch.
|
// Override point for customization after application launch.
|
||||||
|
FirebaseApp.configure()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,10 +48,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
||||||
}
|
}
|
||||||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||||
NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: deviceToken)
|
Messaging.messaging().apnsToken = deviceToken
|
||||||
|
Messaging.messaging().token(completion: { (token, error) in
|
||||||
|
if let error = error {
|
||||||
|
NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
|
||||||
|
} else if let token = token {
|
||||||
|
NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: token)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
||||||
NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
|
NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 53 KiB |
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images": [
|
||||||
{
|
{
|
||||||
"filename" : "AppIcon-512@2x.png",
|
"idiom": "universal",
|
||||||
"idiom" : "universal",
|
"size": "1024x1024",
|
||||||
"platform" : "ios",
|
"filename": "AppIcon-512@2x.png",
|
||||||
"size" : "1024x1024"
|
"platform": "ios"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info": {
|
||||||
"author" : "xcode",
|
"author": "xcode",
|
||||||
"version" : 1
|
"version": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,56 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images": [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom": "universal",
|
||||||
"filename" : "splash-2732x2732-2.png",
|
"filename": "Default@1x~universal~anyany.png",
|
||||||
"scale" : "1x"
|
"scale": "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom": "universal",
|
||||||
"filename" : "splash-2732x2732-1.png",
|
"filename": "Default@2x~universal~anyany.png",
|
||||||
"scale" : "2x"
|
"scale": "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom": "universal",
|
||||||
"filename" : "splash-2732x2732.png",
|
"filename": "Default@3x~universal~anyany.png",
|
||||||
"scale" : "3x"
|
"scale": "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances": [
|
||||||
|
{
|
||||||
|
"appearance": "luminosity",
|
||||||
|
"value": "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "Default@1x~universal~anyany-dark.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances": [
|
||||||
|
{
|
||||||
|
"appearance": "luminosity",
|
||||||
|
"value": "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "Default@2x~universal~anyany-dark.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances": [
|
||||||
|
{
|
||||||
|
"appearance": "luminosity",
|
||||||
|
"value": "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x",
|
||||||
|
"filename": "Default@3x~universal~anyany-dark.png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info": {
|
||||||
"version" : 1,
|
"version": 1,
|
||||||
"author" : "xcode"
|
"author": "xcode"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
ios/App/App/Assets.xcassets/Splash.imageset/Default@1x~universal~anyany-dark.png
vendored
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
ios/App/App/Assets.xcassets/Splash.imageset/Default@1x~universal~anyany.png
vendored
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany-dark.png
vendored
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany.png
vendored
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
ios/App/App/Assets.xcassets/Splash.imageset/Default@3x~universal~anyany-dark.png
vendored
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
ios/App/App/Assets.xcassets/Splash.imageset/Default@3x~universal~anyany.png
vendored
Normal file
|
After Width: | Height: | Size: 52 KiB |
@@ -11,7 +11,7 @@
|
|||||||
<!--Bridge View Controller-->
|
<!--Bridge View Controller-->
|
||||||
<scene sceneID="tne-QT-ifu">
|
<scene sceneID="tne-QT-ifu">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="BYZ-38-t0r" customClass="CAPBridgeViewController" customModule="Capacitor" sceneMemberID="viewController"/>
|
<viewController id="BYZ-38-t0r" customClass="MainViewController" customModule="App" customModuleProvider="target" sceneMemberID="viewController"/>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
</scene>
|
</scene>
|
||||||
|
|||||||
84
ios/App/App/Info.plist
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NFCReaderUsageDescription</key>
|
||||||
|
<string>Donetick uses NFC to read and write chore tags for quick task completion.</string>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Donetick</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>This app needs access to camera to take photos to attach to task or use as profile photo</string>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>This app needs access to photo library to select images to attach to task or use as profile photo</string>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>This app uses the microphone to let you create tasks by speaking</string>
|
||||||
|
<key>NSSpeechRecognitionUsageDescription</key>
|
||||||
|
<string>This app uses on-device speech recognition to turn your voice into tasks</string>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array/>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIMainStoryboardFile</key>
|
||||||
|
<string>Main</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>armv7</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsLocalNetworking</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<true/>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>com.donetick.app</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>donetick</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>com.googleusercontent.apps.682262497914-5dacpk46qcc1494lood6ch8ul9c83kop</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
9
ios/App/App/MainViewController.swift
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import Capacitor
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
/// Registers app-local Capacitor plugins; referenced from Main.storyboard.
|
||||||
|
class MainViewController: CAPBridgeViewController {
|
||||||
|
override func capacitorDidLoad() {
|
||||||
|
bridge?.registerPluginInstance(WidgetBridgePlugin())
|
||||||
|
}
|
||||||
|
}
|
||||||
46
ios/App/App/WidgetBridgePlugin.swift
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import Capacitor
|
||||||
|
import Foundation
|
||||||
|
import WidgetKit
|
||||||
|
|
||||||
|
/// JS bridge for the home-screen widgets (see src/service/WidgetService.js).
|
||||||
|
/// Persists the task snapshot + API config in the shared App Group so the
|
||||||
|
/// widget extension can read them, then asks WidgetKit to redraw.
|
||||||
|
@objc(WidgetBridgePlugin)
|
||||||
|
public class WidgetBridgePlugin: CAPPlugin, CAPBridgedPlugin {
|
||||||
|
public let identifier = "WidgetBridgePlugin"
|
||||||
|
public let jsName = "WidgetBridge"
|
||||||
|
public let pluginMethods: [CAPPluginMethod] = [
|
||||||
|
CAPPluginMethod(name: "update", returnType: CAPPluginReturnPromise),
|
||||||
|
CAPPluginMethod(name: "clear", returnType: CAPPluginReturnPromise),
|
||||||
|
]
|
||||||
|
|
||||||
|
static let appGroup = "group.com.donetick.app"
|
||||||
|
static let dataKey = "widget_tasks"
|
||||||
|
static let configKey = "widget_config"
|
||||||
|
|
||||||
|
@objc func update(_ call: CAPPluginCall) {
|
||||||
|
guard let defaults = UserDefaults(suiteName: Self.appGroup) else {
|
||||||
|
call.reject("App Group \(Self.appGroup) unavailable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let data = call.getString("data") {
|
||||||
|
defaults.set(data, forKey: Self.dataKey)
|
||||||
|
}
|
||||||
|
if let config = call.getString("config") {
|
||||||
|
defaults.set(config, forKey: Self.configKey)
|
||||||
|
}
|
||||||
|
WidgetCenter.shared.reloadAllTimelines()
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func clear(_ call: CAPPluginCall) {
|
||||||
|
guard let defaults = UserDefaults(suiteName: Self.appGroup) else {
|
||||||
|
call.reject("App Group \(Self.appGroup) unavailable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defaults.removeObject(forKey: Self.dataKey)
|
||||||
|
defaults.removeObject(forKey: Self.configKey)
|
||||||
|
WidgetCenter.shared.reloadAllTimelines()
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
10
ios/App/DonetickWidget/DonetickWidget.entitlements
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.com.donetick.app</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
1107
ios/App/DonetickWidget/DonetickWidget.swift
Normal file
29
ios/App/DonetickWidget/Info.plist
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Donetick</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.widgetkit-extension</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
|
require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
|
||||||
|
|
||||||
platform :ios, '13.0'
|
platform :ios, '15.0'
|
||||||
use_frameworks!
|
use_frameworks!
|
||||||
|
|
||||||
# workaround to avoid Xcode caching of Pods that requires
|
# workaround to avoid Xcode caching of Pods that requires
|
||||||
@@ -11,19 +11,32 @@ install! 'cocoapods', :disable_input_output_paths => true
|
|||||||
def capacitor_pods
|
def capacitor_pods
|
||||||
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
|
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
|
||||||
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
|
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
|
||||||
|
pod 'CapacitorCommunityInAppReview', :path => '../../node_modules/@capacitor-community/in-app-review'
|
||||||
|
pod 'CapacitorCommunitySpeechRecognition', :path => '../../node_modules/@capacitor-community/speech-recognition'
|
||||||
|
pod 'CapacitorCommunitySqlite', :path => '../../node_modules/@capacitor-community/sqlite'
|
||||||
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
|
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
|
||||||
|
pod 'CapacitorBrowser', :path => '../../node_modules/@capacitor/browser'
|
||||||
pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
|
pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
|
||||||
|
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
|
||||||
|
pod 'CapacitorLocalLlm', :path => '../../node_modules/@capacitor/local-llm'
|
||||||
pod 'CapacitorLocalNotifications', :path => '../../node_modules/@capacitor/local-notifications'
|
pod 'CapacitorLocalNotifications', :path => '../../node_modules/@capacitor/local-notifications'
|
||||||
|
pod 'CapacitorNetwork', :path => '../../node_modules/@capacitor/network'
|
||||||
pod 'CapacitorPreferences', :path => '../../node_modules/@capacitor/preferences'
|
pod 'CapacitorPreferences', :path => '../../node_modules/@capacitor/preferences'
|
||||||
pod 'CapacitorPushNotifications', :path => '../../node_modules/@capacitor/push-notifications'
|
pod 'CapacitorPushNotifications', :path => '../../node_modules/@capacitor/push-notifications'
|
||||||
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
||||||
|
pod 'CapgoCapacitorDocumentScanner', :path => '../../node_modules/@capgo/capacitor-document-scanner'
|
||||||
|
pod 'CapgoCapacitorNfc', :path => '../../node_modules/@capgo/capacitor-nfc'
|
||||||
pod 'CapgoCapacitorSocialLogin', :path => '../../node_modules/@capgo/capacitor-social-login'
|
pod 'CapgoCapacitorSocialLogin', :path => '../../node_modules/@capgo/capacitor-social-login'
|
||||||
|
pod 'JcesarmobileCapacitorOcr', :path => '../../node_modules/@jcesarmobile/capacitor-ocr'
|
||||||
|
pod 'RevenuecatPurchasesCapacitor', :path => '../../node_modules/@revenuecat/purchases-capacitor'
|
||||||
|
pod 'RevenuecatPurchasesCapacitorUi', :path => '../../node_modules/@revenuecat/purchases-capacitor-ui'
|
||||||
pod 'CapacitorPluginSafeArea', :path => '../../node_modules/capacitor-plugin-safe-area'
|
pod 'CapacitorPluginSafeArea', :path => '../../node_modules/capacitor-plugin-safe-area'
|
||||||
end
|
end
|
||||||
|
|
||||||
target 'App' do
|
target 'App' do
|
||||||
capacitor_pods
|
capacitor_pods
|
||||||
# Add your Pods here
|
# Add your Pods here
|
||||||
|
pod 'FirebaseMessaging' # Add this line
|
||||||
end
|
end
|
||||||
|
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
|
|||||||
@@ -1,105 +1,193 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- Alamofire (5.10.2)
|
- Alamofire (5.10.2)
|
||||||
- AppAuth (1.7.6):
|
- AppAuth (2.1.0):
|
||||||
- AppAuth/Core (= 1.7.6)
|
- AppAuth/Core (= 2.1.0)
|
||||||
- AppAuth/ExternalUserAgent (= 1.7.6)
|
- AppAuth/ExternalUserAgent (= 2.1.0)
|
||||||
- AppAuth/Core (1.7.6)
|
- AppAuth/Core (2.1.0)
|
||||||
- AppAuth/ExternalUserAgent (1.7.6):
|
- AppAuth/ExternalUserAgent (2.1.0):
|
||||||
- AppAuth/Core
|
- AppAuth/Core
|
||||||
- AppCheckCore (11.2.0):
|
- AppCheckCore (11.3.0):
|
||||||
- GoogleUtilities/Environment (~> 8.0)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||||
- PromisesObjC (~> 2.4)
|
- PromisesObjC (~> 2.4)
|
||||||
- Capacitor (7.2.0):
|
- PromisesSwift (~> 2.4)
|
||||||
|
- RecaptchaInterop (~> 101.0)
|
||||||
|
- Capacitor (8.4.1):
|
||||||
- CapacitorCordova
|
- CapacitorCordova
|
||||||
- CapacitorApp (7.0.1):
|
- CapacitorApp (8.1.0):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorCommunitySqlite (7.0.0):
|
- CapacitorBrowser (8.0.3):
|
||||||
|
- Capacitor
|
||||||
|
- CapacitorCommunityInAppReview (8.0.0):
|
||||||
|
- Capacitor
|
||||||
|
- CapacitorCommunitySpeechRecognition (7.0.1):
|
||||||
|
- Capacitor
|
||||||
|
- CapacitorCommunitySqlite (8.1.0):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- SQLCipher
|
- SQLCipher
|
||||||
- ZIPFoundation
|
- ZIPFoundation
|
||||||
- CapacitorCordova (7.2.0)
|
- CapacitorCordova (8.4.1)
|
||||||
- CapacitorDevice (7.0.1):
|
- CapacitorDevice (8.0.2):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorLocalNotifications (7.0.1):
|
- CapacitorHaptics (8.0.2):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorNetwork (7.0.1):
|
- CapacitorLocalLlm (1.0.0):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorPluginSafeArea (4.0.0):
|
- CapacitorLocalNotifications (8.2.0):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorPreferences (7.0.1):
|
- CapacitorNetwork (8.0.1):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorPushNotifications (7.0.1):
|
- CapacitorPluginSafeArea (5.0.1):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorStatusBar (7.0.1):
|
- CapacitorPreferences (8.0.1):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapgoCapacitorSocialLogin (7.5.3):
|
- CapacitorPushNotifications (8.1.1):
|
||||||
|
- Capacitor
|
||||||
|
- CapacitorStatusBar (8.0.2):
|
||||||
|
- Capacitor
|
||||||
|
- CapgoCapacitorDocumentScanner (8.4.0):
|
||||||
|
- Capacitor
|
||||||
|
- CapgoCapacitorNfc (8.2.2):
|
||||||
|
- Capacitor
|
||||||
|
- CapgoCapacitorSocialLogin (8.3.38):
|
||||||
- Alamofire (~> 5.10.2)
|
- Alamofire (~> 5.10.2)
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- FBSDKCoreKit (= 17.4.0)
|
- GoogleSignIn (~> 9.0.0)
|
||||||
- FBSDKLoginKit (= 17.4.0)
|
- FirebaseCore (12.15.0):
|
||||||
- GoogleSignIn (~> 8.0.0)
|
- FirebaseCoreInternal (~> 12.15.0)
|
||||||
- FBAEMKit (17.4.0):
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- FBSDKCoreKit_Basics (= 17.4.0)
|
- GoogleUtilities/Logger (~> 8.1)
|
||||||
- FBSDKCoreKit (17.4.0):
|
- FirebaseCoreInternal (12.15.0):
|
||||||
- FBAEMKit (= 17.4.0)
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
- FBSDKCoreKit_Basics (= 17.4.0)
|
- FirebaseInstallations (12.15.0):
|
||||||
- FBSDKCoreKit_Basics (17.4.0)
|
- FirebaseCore (~> 12.15.0)
|
||||||
- FBSDKLoginKit (17.4.0):
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- FBSDKCoreKit (= 17.4.0)
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
- GoogleSignIn (8.0.0):
|
- PromisesObjC (~> 2.4)
|
||||||
- AppAuth (< 2.0, >= 1.7.3)
|
- FirebaseMessaging (12.15.0):
|
||||||
|
- FirebaseCore (~> 12.15.0)
|
||||||
|
- FirebaseInstallations (~> 12.15.0)
|
||||||
|
- GoogleDataTransport (~> 10.1)
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
|
- GoogleUtilities/Reachability (~> 8.1)
|
||||||
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- GoogleDataTransport (10.1.0):
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- PromisesObjC (~> 2.4)
|
||||||
|
- GoogleSignIn (9.0.0):
|
||||||
|
- AppAuth (~> 2.0)
|
||||||
- AppCheckCore (~> 11.0)
|
- AppCheckCore (~> 11.0)
|
||||||
- GTMAppAuth (< 5.0, >= 4.1.1)
|
- GTMAppAuth (~> 5.0)
|
||||||
- GTMSessionFetcher/Core (~> 3.3)
|
- GTMSessionFetcher/Core (~> 3.3)
|
||||||
- GoogleUtilities/Environment (8.0.2):
|
- GoogleUtilities/AppDelegateSwizzler (8.1.2):
|
||||||
|
- GoogleUtilities/Environment
|
||||||
|
- GoogleUtilities/Logger
|
||||||
|
- GoogleUtilities/Network
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Logger (8.0.2):
|
- GoogleUtilities/Environment (8.1.2):
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
|
- GoogleUtilities/Logger (8.1.2):
|
||||||
- GoogleUtilities/Environment
|
- GoogleUtilities/Environment
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Privacy (8.0.2)
|
- GoogleUtilities/Network (8.1.2):
|
||||||
- GoogleUtilities/UserDefaults (8.0.2):
|
- GoogleUtilities/Logger
|
||||||
|
- "GoogleUtilities/NSData+zlib"
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
|
- GoogleUtilities/Reachability
|
||||||
|
- "GoogleUtilities/NSData+zlib (8.1.2)":
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
|
- GoogleUtilities/Privacy (8.1.2)
|
||||||
|
- GoogleUtilities/Reachability (8.1.2):
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GTMAppAuth (4.1.1):
|
- GoogleUtilities/UserDefaults (8.1.2):
|
||||||
- AppAuth/Core (~> 1.7)
|
- GoogleUtilities/Logger
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
|
- GTMAppAuth (5.0.0):
|
||||||
|
- AppAuth/Core (~> 2.0)
|
||||||
- GTMSessionFetcher/Core (< 4.0, >= 3.3)
|
- GTMSessionFetcher/Core (< 4.0, >= 3.3)
|
||||||
- GTMSessionFetcher/Core (3.5.0)
|
- GTMSessionFetcher/Core (3.5.0)
|
||||||
- PromisesObjC (2.4.0)
|
- JcesarmobileCapacitorOcr (0.3.0):
|
||||||
- SQLCipher (4.7.0):
|
- Capacitor
|
||||||
- SQLCipher/standard (= 4.7.0)
|
- nanopb (3.30910.0):
|
||||||
- SQLCipher/common (4.7.0)
|
- nanopb/decode (= 3.30910.0)
|
||||||
- SQLCipher/standard (4.7.0):
|
- nanopb/encode (= 3.30910.0)
|
||||||
|
- nanopb/decode (3.30910.0)
|
||||||
|
- nanopb/encode (3.30910.0)
|
||||||
|
- PromisesObjC (2.4.1)
|
||||||
|
- PromisesSwift (2.4.1):
|
||||||
|
- PromisesObjC (= 2.4.1)
|
||||||
|
- PurchasesHybridCommon (17.55.1):
|
||||||
|
- RevenueCat (= 5.67.1)
|
||||||
|
- PurchasesHybridCommonUI (17.55.1):
|
||||||
|
- PurchasesHybridCommon (= 17.55.1)
|
||||||
|
- RevenueCatUI (= 5.67.1)
|
||||||
|
- RecaptchaInterop (101.0.0)
|
||||||
|
- RevenueCat (5.67.1)
|
||||||
|
- RevenuecatPurchasesCapacitor (12.3.2):
|
||||||
|
- Capacitor
|
||||||
|
- PurchasesHybridCommon (= 17.55.1)
|
||||||
|
- RevenuecatPurchasesCapacitorUi (12.3.2):
|
||||||
|
- Capacitor
|
||||||
|
- PurchasesHybridCommonUI (= 17.55.1)
|
||||||
|
- RevenueCatUI (5.67.1):
|
||||||
|
- RevenueCat (= 5.67.1)
|
||||||
|
- SQLCipher (4.10.0):
|
||||||
|
- SQLCipher/standard (= 4.10.0)
|
||||||
|
- SQLCipher/common (4.10.0)
|
||||||
|
- SQLCipher/standard (4.10.0):
|
||||||
- SQLCipher/common
|
- SQLCipher/common
|
||||||
- ZIPFoundation (0.9.19)
|
- ZIPFoundation (0.9.20)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
|
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
|
||||||
- "CapacitorApp (from `../../node_modules/@capacitor/app`)"
|
- "CapacitorApp (from `../../node_modules/@capacitor/app`)"
|
||||||
|
- "CapacitorBrowser (from `../../node_modules/@capacitor/browser`)"
|
||||||
|
- "CapacitorCommunityInAppReview (from `../../node_modules/@capacitor-community/in-app-review`)"
|
||||||
|
- "CapacitorCommunitySpeechRecognition (from `../../node_modules/@capacitor-community/speech-recognition`)"
|
||||||
|
- "CapacitorCommunitySqlite (from `../../node_modules/@capacitor-community/sqlite`)"
|
||||||
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
|
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
|
||||||
- "CapacitorDevice (from `../../node_modules/@capacitor/device`)"
|
- "CapacitorDevice (from `../../node_modules/@capacitor/device`)"
|
||||||
|
- "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
|
||||||
|
- "CapacitorLocalLlm (from `../../node_modules/@capacitor/local-llm`)"
|
||||||
- "CapacitorLocalNotifications (from `../../node_modules/@capacitor/local-notifications`)"
|
- "CapacitorLocalNotifications (from `../../node_modules/@capacitor/local-notifications`)"
|
||||||
- "CapacitorNetwork (from `../../node_modules/@capacitor/network`)"
|
- "CapacitorNetwork (from `../../node_modules/@capacitor/network`)"
|
||||||
- CapacitorPluginSafeArea (from `../../node_modules/capacitor-plugin-safe-area`)
|
- CapacitorPluginSafeArea (from `../../node_modules/capacitor-plugin-safe-area`)
|
||||||
- "CapacitorPreferences (from `../../node_modules/@capacitor/preferences`)"
|
- "CapacitorPreferences (from `../../node_modules/@capacitor/preferences`)"
|
||||||
- "CapacitorPushNotifications (from `../../node_modules/@capacitor/push-notifications`)"
|
- "CapacitorPushNotifications (from `../../node_modules/@capacitor/push-notifications`)"
|
||||||
- "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
|
- "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
|
||||||
|
- "CapgoCapacitorDocumentScanner (from `../../node_modules/@capgo/capacitor-document-scanner`)"
|
||||||
|
- "CapgoCapacitorNfc (from `../../node_modules/@capgo/capacitor-nfc`)"
|
||||||
- "CapgoCapacitorSocialLogin (from `../../node_modules/@capgo/capacitor-social-login`)"
|
- "CapgoCapacitorSocialLogin (from `../../node_modules/@capgo/capacitor-social-login`)"
|
||||||
|
- FirebaseMessaging
|
||||||
|
- "JcesarmobileCapacitorOcr (from `../../node_modules/@jcesarmobile/capacitor-ocr`)"
|
||||||
|
- "RevenuecatPurchasesCapacitor (from `../../node_modules/@revenuecat/purchases-capacitor`)"
|
||||||
|
- "RevenuecatPurchasesCapacitorUi (from `../../node_modules/@revenuecat/purchases-capacitor-ui`)"
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- Alamofire
|
- Alamofire
|
||||||
- AppAuth
|
- AppAuth
|
||||||
- AppCheckCore
|
- AppCheckCore
|
||||||
- FBAEMKit
|
- FirebaseCore
|
||||||
- FBSDKCoreKit
|
- FirebaseCoreInternal
|
||||||
- FBSDKCoreKit_Basics
|
- FirebaseInstallations
|
||||||
- FBSDKLoginKit
|
- FirebaseMessaging
|
||||||
|
- GoogleDataTransport
|
||||||
- GoogleSignIn
|
- GoogleSignIn
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
- GTMAppAuth
|
- GTMAppAuth
|
||||||
- GTMSessionFetcher
|
- GTMSessionFetcher
|
||||||
|
- nanopb
|
||||||
- PromisesObjC
|
- PromisesObjC
|
||||||
|
- PromisesSwift
|
||||||
|
- PurchasesHybridCommon
|
||||||
|
- PurchasesHybridCommonUI
|
||||||
|
- RecaptchaInterop
|
||||||
|
- RevenueCat
|
||||||
|
- RevenueCatUI
|
||||||
- SQLCipher
|
- SQLCipher
|
||||||
- ZIPFoundation
|
- ZIPFoundation
|
||||||
|
|
||||||
@@ -108,10 +196,22 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../../node_modules/@capacitor/ios"
|
:path: "../../node_modules/@capacitor/ios"
|
||||||
CapacitorApp:
|
CapacitorApp:
|
||||||
:path: "../../node_modules/@capacitor/app"
|
:path: "../../node_modules/@capacitor/app"
|
||||||
|
CapacitorBrowser:
|
||||||
|
:path: "../../node_modules/@capacitor/browser"
|
||||||
|
CapacitorCommunityInAppReview:
|
||||||
|
:path: "../../node_modules/@capacitor-community/in-app-review"
|
||||||
|
CapacitorCommunitySpeechRecognition:
|
||||||
|
:path: "../../node_modules/@capacitor-community/speech-recognition"
|
||||||
|
CapacitorCommunitySqlite:
|
||||||
|
:path: "../../node_modules/@capacitor-community/sqlite"
|
||||||
CapacitorCordova:
|
CapacitorCordova:
|
||||||
:path: "../../node_modules/@capacitor/ios"
|
:path: "../../node_modules/@capacitor/ios"
|
||||||
CapacitorDevice:
|
CapacitorDevice:
|
||||||
:path: "../../node_modules/@capacitor/device"
|
:path: "../../node_modules/@capacitor/device"
|
||||||
|
CapacitorHaptics:
|
||||||
|
:path: "../../node_modules/@capacitor/haptics"
|
||||||
|
CapacitorLocalLlm:
|
||||||
|
:path: "../../node_modules/@capacitor/local-llm"
|
||||||
CapacitorLocalNotifications:
|
CapacitorLocalNotifications:
|
||||||
:path: "../../node_modules/@capacitor/local-notifications"
|
:path: "../../node_modules/@capacitor/local-notifications"
|
||||||
CapacitorNetwork:
|
CapacitorNetwork:
|
||||||
@@ -124,37 +224,65 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../../node_modules/@capacitor/push-notifications"
|
:path: "../../node_modules/@capacitor/push-notifications"
|
||||||
CapacitorStatusBar:
|
CapacitorStatusBar:
|
||||||
:path: "../../node_modules/@capacitor/status-bar"
|
:path: "../../node_modules/@capacitor/status-bar"
|
||||||
|
CapgoCapacitorDocumentScanner:
|
||||||
|
:path: "../../node_modules/@capgo/capacitor-document-scanner"
|
||||||
|
CapgoCapacitorNfc:
|
||||||
|
:path: "../../node_modules/@capgo/capacitor-nfc"
|
||||||
CapgoCapacitorSocialLogin:
|
CapgoCapacitorSocialLogin:
|
||||||
:path: "../../node_modules/@capgo/capacitor-social-login"
|
:path: "../../node_modules/@capgo/capacitor-social-login"
|
||||||
|
JcesarmobileCapacitorOcr:
|
||||||
|
:path: "../../node_modules/@jcesarmobile/capacitor-ocr"
|
||||||
|
RevenuecatPurchasesCapacitor:
|
||||||
|
:path: "../../node_modules/@revenuecat/purchases-capacitor"
|
||||||
|
RevenuecatPurchasesCapacitorUi:
|
||||||
|
:path: "../../node_modules/@revenuecat/purchases-capacitor-ui"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
||||||
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
|
AppAuth: ef4da5a3fc2e10b90c09a0a94a9baeaedc0341d5
|
||||||
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
AppCheckCore: 214137f5c378d1dec88a68425c467fe65aaff637
|
||||||
Capacitor: 03bc7cbdde6a629a8b910a9d7d78c3cc7ed09ea7
|
Capacitor: 35242afe195b1e53c58ca1b827d1b444c5e6602b
|
||||||
CapacitorApp: febecbb9582cb353aed037e18ec765141f880fe9
|
CapacitorApp: 449ffe26375e96f8aaaee625ac6e01e5c57c8650
|
||||||
CapacitorCommunitySqlite: b11e556cf5d149f9e0b6fbbb47959a6cd718c3d0
|
CapacitorBrowser: c987c73d09d8bd3b5ec13f06338b1e14d5d2be69
|
||||||
CapacitorCordova: 5967b9ba03915ef1d585469d6e31f31dc49be96f
|
CapacitorCommunityInAppReview: 4492bdd34aad4d27ed87949376022cc93b294ea1
|
||||||
CapacitorDevice: c6f6d587dd310527f8a48bf09c4e7b4a4cf14329
|
CapacitorCommunitySpeechRecognition: 3e03566c44c2bb3b52391a33d4518b1adbdeb38f
|
||||||
CapacitorLocalNotifications: c2212755b33d2513b8bb325096ce3a64d4039ae3
|
CapacitorCommunitySqlite: eac6acfb852f46e7988fc59604d7f900498d354e
|
||||||
CapacitorNetwork: 15cb4385f0913a8ceb5e9a4d7af1ec554bdb8de8
|
CapacitorCordova: eebe6bcf807b1b06f3f48237650f96bbcd0eef09
|
||||||
CapacitorPluginSafeArea: 22031c3436269ca80fac90ec2c94bc7c1e59a81d
|
CapacitorDevice: 14cba6f88d1c3074cbf825fea977c8c526453ff8
|
||||||
CapacitorPreferences: 6c98117d4d7508034a4af9db64d6b26fc75d7b94
|
CapacitorHaptics: 296f771ecd89c7a1bd92a7b6826a7d268e2e70f5
|
||||||
CapacitorPushNotifications: 6a2794788c583dd89215f1805ca4bced1b13dbdf
|
CapacitorLocalLlm: a05516151a02923a9e7dae9949d3817e85e321f0
|
||||||
CapacitorStatusBar: 6e7af040d8fc4dd655999819625cae9c2d74c36f
|
CapacitorLocalNotifications: 2615aa008f608b95d3921a778ee1988abf1e6148
|
||||||
CapgoCapacitorSocialLogin: 0daa087f32775c656fbfba09b8eae7c089b85ce2
|
CapacitorNetwork: 8812ce60d11fb63d8f2e4ba51a49b2e59892ebe2
|
||||||
FBAEMKit: 58cb5f302cdd715a56d4c1d0dfdd2e423ac1421a
|
CapacitorPluginSafeArea: 874619c00586248f1694210e72038123d422c2d9
|
||||||
FBSDKCoreKit: 94d7461d0cecf441b1ba7c41acfff41daa8ccd41
|
CapacitorPreferences: cca2021f386efb75947c850334447d9ff22b14f1
|
||||||
FBSDKCoreKit_Basics: 151b43db8b834d3f0e02f95d36a44ffd36265e45
|
CapacitorPushNotifications: ec08d589c226a2c0db7c032ec1bf5b044ec85f8e
|
||||||
FBSDKLoginKit: 5c1cd53c91a2282b3a4fe6e6d3dcf2b8b0d33d55
|
CapacitorStatusBar: 01d5763b4ed720de5ce2edbc938de6a98f4c8f32
|
||||||
GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4
|
CapgoCapacitorDocumentScanner: 7ad9e8ed9c054d551bfc948660d995b9545723d8
|
||||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
CapgoCapacitorNfc: 8ea158143c441e1cf6d231a971e375511558d027
|
||||||
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
|
CapgoCapacitorSocialLogin: 5de0c9295188cbd9116d13e4581494f3bbaa3414
|
||||||
|
FirebaseCore: 2e86a4ea1684d4381707069e4a6d89ac808e901e
|
||||||
|
FirebaseCoreInternal: 6ab6a02c94446c026d2cf35cf5383842ebaa4992
|
||||||
|
FirebaseInstallations: eb29ccbf64eaedf86fd5b2ccc7fabde567660b52
|
||||||
|
FirebaseMessaging: 40017d7bc8457ee295b0f41d480a80fdabc9994e
|
||||||
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
|
GoogleSignIn: c7f09cfbc85a1abf69187be091997c317cc33b77
|
||||||
|
GoogleUtilities: 766ace00c6b10d8148408f329d10c4f051931850
|
||||||
|
GTMAppAuth: 217a876b249c3c585a54fd6f73e6b58c4f5c4238
|
||||||
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
JcesarmobileCapacitorOcr: d3668e104d7ad8968a9d00fd4766e18fea0fe305
|
||||||
SQLCipher: ba9d0076041ed767c5bd3d3f77098318d04a403c
|
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||||
ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c
|
PromisesObjC: 752c3227f599e3467650e47ea36f433eeb10c273
|
||||||
|
PromisesSwift: 217dea0fd5d2ad65222a109c48698add13cc1c5b
|
||||||
|
PurchasesHybridCommon: ecd9d7c586287625f1225b8de5e24c1021502270
|
||||||
|
PurchasesHybridCommonUI: 3056a3d261529f6adb8f74ecc3ab7f1cc0d3fab4
|
||||||
|
RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba
|
||||||
|
RevenueCat: 9357239a9f0978285e68983637b8a602e65c94f0
|
||||||
|
RevenuecatPurchasesCapacitor: c299be01b1a5c1d5eb118c30eadc84160944ecf2
|
||||||
|
RevenuecatPurchasesCapacitorUi: dc2ec0d97638ebe4ac0078452f5c212b1525ea1c
|
||||||
|
RevenueCatUI: 6f9291da540f98baf405b5bdfc46b2d5547b704a
|
||||||
|
SQLCipher: eb79c64049cb002b4e9fcb30edb7979bf4706dfc
|
||||||
|
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
|
||||||
|
|
||||||
PODFILE CHECKSUM: 54ec5ec72c1c5e3e8d03bd96cc1ec0deaf577d9e
|
PODFILE CHECKSUM: 028fdeb50d56158db0a97459bd577ed700f03c0c
|
||||||
|
|
||||||
COCOAPODS: 1.15.2
|
COCOAPODS: 1.16.2
|
||||||
|
|||||||
29
ios/App/PrivacyInfo.xcprivacy
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>85F4.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>CA92.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
0
ios/Podfile
Normal file
207
ios/Pods/Pods.xcodeproj/project.pbxproj
generated
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
93BAFDF14BA360977052E7B9CCC18E94 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
CF1408CF629C7361332E53B88F7BD30C = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9D940727FF8FB9C785EB98E56350EF41 /* Podfile */,
|
||||||
|
D89477F20FB1DE18A04690586D7808C4 /* Frameworks */,
|
||||||
|
93BAFDF14BA360977052E7B9CCC18E94 /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D89477F20FB1DE18A04690586D7808C4 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
BFDFE7DC352907FC980B868725387E98 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastSwiftUpdateCheck = 1600;
|
||||||
|
LastUpgradeCheck = 1600;
|
||||||
|
};
|
||||||
|
buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */;
|
||||||
|
compatibilityVersion = "Xcode 3.2";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
Base,
|
||||||
|
en,
|
||||||
|
);
|
||||||
|
mainGroup = CF1408CF629C7361332E53B88F7BD30C;
|
||||||
|
minimizedProjectReferenceProxies = 0;
|
||||||
|
preferredProjectObjectVersion = 77;
|
||||||
|
productRefGroup = 93BAFDF14BA360977052E7B9CCC18E94 /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
2426522C419C0DE4525883EA7AABC025 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"POD_CONFIGURATION_RELEASE=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
STRIP_INSTALLED_PRODUCT = NO;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
SYMROOT = "${SRCROOT}/../build";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
8F9FEBB7130A3DF8746ED9AA46FE33FA /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"POD_CONFIGURATION_DEBUG=1",
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
STRIP_INSTALLED_PRODUCT = NO;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
SYMROOT = "${SRCROOT}/../build";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
8F9FEBB7130A3DF8746ED9AA46FE33FA /* Debug */,
|
||||||
|
2426522C419C0DE4525883EA7AABC025 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */;
|
||||||
|
}
|
||||||
9404
package-lock.json
generated
114
package.json
@@ -1,8 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "donetick",
|
"name": "donetick",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.99",
|
"version": "1.2.34",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0",
|
||||||
|
"npm": ">=9.0.0"
|
||||||
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,jsx,ts,tsx}": [
|
"*.{js,jsx,ts,tsx}": [
|
||||||
"eslint --fix",
|
"eslint --fix",
|
||||||
@@ -13,79 +17,131 @@
|
|||||||
"start": "vite --host",
|
"start": "vite --host",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"build-selfhosted": "vite build --mode selfhosted",
|
"build-cf": "rm -rf package-lock.json && npm install && npm install --force @rollup/rollup-linux-x64-gnu@4.34.9 @swc/core-linux-x64-gnu && vite build",
|
||||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
"build-selfhosted": "rm -rf package-lock.json && npm install && npm install --force @rollup/rollup-linux-x64-gnu@4.34.9 @swc/core-linux-x64-gnu && vite build --mode selfhosted",
|
||||||
|
"build-win": "del package-lock.json && npm install && npm install --force @rollup/rollup-win32-x64-msvc @swc/core-win32-x64-msvc && vite build --mode selfhosted",
|
||||||
|
"lint": "eslint && prettier -c .",
|
||||||
|
"lint:ci": "true # TODO: eslint -f gha && prettier -c .",
|
||||||
|
"lint:fix": "eslint --fix && prettier -w .",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
"setup-m1": "rm -rf node_modules package-lock.json && npm install && npm install --force @rollup/rollup-darwin-arm64 @swc/core-darwin-arm64",
|
||||||
|
"setup-apple-silicon": "npm install --no-optional && npm install --force @rollup/rollup-darwin-arm64 @swc/core-darwin-arm64",
|
||||||
|
"test-cloudflare-build": "rm -rf node_modules package-lock.json && npm install && npm install --force @rollup/rollup-linux-x64-gnu@4.34.9 @swc/core-linux-x64-gnu && vite build",
|
||||||
"release": "npm version patch && npm run build && git push origin develop --tags",
|
"release": "npm version patch && npm run build && git push origin develop --tags",
|
||||||
"merge": "git checkout main && git merge develop && git push origin main && git checkout develop",
|
"merge": "git checkout main && git merge develop && git push origin main && git checkout develop",
|
||||||
"ionic:build": "npm run build",
|
"ionic:build": "npm run build",
|
||||||
"ionic:serve": "npm run start"
|
"ionic:serve": "npm run start",
|
||||||
|
"android": "npm run build && npx cap sync android",
|
||||||
|
"android:dev": "npx cap run android --live-reload",
|
||||||
|
"cap": "npm run build && npx cap sync",
|
||||||
|
"bump": "node bump-version.js patch",
|
||||||
|
"bump:minor": "node bump-version.js minor",
|
||||||
|
"bump:major": "node bump-version.js major",
|
||||||
|
"bump:patch": "node bump-version.js patch",
|
||||||
|
"postinstall": "patch-package"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor-community/sqlite": "^6.0.2",
|
"@capacitor-community/in-app-review": "^8.0.0",
|
||||||
"@capacitor/android": "^6.1.1",
|
"@capacitor-community/speech-recognition": "^7.0.1",
|
||||||
"@capacitor/app": "^6.0.0",
|
"@capacitor-community/sqlite": "^8.0.0",
|
||||||
"@capacitor/cli": "^6.1.1",
|
"@capacitor/android": "^8.0.0",
|
||||||
"@capacitor/core": "^6.1.1",
|
"@capacitor/app": "^8.0.0",
|
||||||
"@capacitor/device": "^6.0.2",
|
"@capacitor/browser": "^8.0.0",
|
||||||
"@capacitor/ios": "6.1.1",
|
"@capacitor/core": "^8.0.0",
|
||||||
"@capacitor/local-notifications": "^6.0.0",
|
"@capacitor/device": "^8.0.0",
|
||||||
"@capacitor/network": "^6.0.3",
|
"@capacitor/haptics": "^8.0.2",
|
||||||
"@capacitor/preferences": "^6.0.1",
|
"@capacitor/ios": "^8.0.0",
|
||||||
"@capacitor/push-notifications": "^6.0.1",
|
"@capacitor/local-llm": "^1.0.0",
|
||||||
"@codetrix-studio/capacitor-google-auth": "^3.4.0-rc.4",
|
"@capacitor/local-notifications": "^8.0.0",
|
||||||
|
"@capacitor/network": "^8.0.0",
|
||||||
|
"@capacitor/preferences": "^8.0.0",
|
||||||
|
"@capacitor/push-notifications": "^8.0.0",
|
||||||
|
"@capacitor/status-bar": "^8.0.0",
|
||||||
|
"@capgo/capacitor-document-scanner": "^8.4.0",
|
||||||
|
"@capgo/capacitor-nfc": "^8.0.0",
|
||||||
|
"@capgo/capacitor-social-login": "^8.3.38",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@emotion/react": "^11.11.3",
|
"@emotion/react": "^11.11.3",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@mui/icons-material": "^5.15.2",
|
"@hello-pangea/dnd": "^18.0.1",
|
||||||
|
"@jcesarmobile/capacitor-ocr": "^0.3.0",
|
||||||
|
"@meauxt/react-swipeable-list": "^1.0.0",
|
||||||
|
"@mui/icons-material": "^5.16.13",
|
||||||
"@mui/joy": "^5.0.0-beta.20",
|
"@mui/joy": "^5.0.0-beta.20",
|
||||||
"@mui/material": "^5.15.2",
|
"@mui/material": "^5.15.2",
|
||||||
"@openreplay/tracker": "^14.0.4",
|
"@openreplay/tracker": "^14.0.4",
|
||||||
|
"@revenuecat/purchases-capacitor": "^12.0.0",
|
||||||
|
"@revenuecat/purchases-capacitor-ui": "^12.0.0",
|
||||||
|
"@stylistic/eslint-plugin": "^5.10.0",
|
||||||
|
"@swc/core": "^1.12.5",
|
||||||
"@tanstack/react-query": "^5.17.0",
|
"@tanstack/react-query": "^5.17.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.59.4",
|
||||||
|
"@typescript-eslint/parser": "^8.59.4",
|
||||||
"aos": "^2.3.4",
|
"aos": "^2.3.4",
|
||||||
|
"browser-image-compression": "^2.0.2",
|
||||||
|
"caniuse-lite": "^1.0.30001769",
|
||||||
|
"capacitor-plugin-safe-area": "^5.0.0",
|
||||||
"chrono-node": "^2.7.7",
|
"chrono-node": "^2.7.7",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"esm": "^3.2.25",
|
"esm": "^3.2.25",
|
||||||
"farmhash": "^4.0.1",
|
"event-source-polyfill": "^1.0.31",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
|
"i18next": "^25.8.14",
|
||||||
|
"i18next-browser-languagedetector": "^8.2.1",
|
||||||
|
"i18next-http-backend": "^3.0.2",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"murmurhash": "^2.0.1",
|
"murmurhash": "^2.0.1",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
|
"quill": "^2.0.3",
|
||||||
|
"quilljs-markdown": "^1.2.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-calendar": "^5.1.0",
|
"react-calendar": "^5.1.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-easy-crop": "^5.4.2",
|
||||||
|
"react-i18next": "^16.5.6",
|
||||||
"react-router-dom": "^6.21.1",
|
"react-router-dom": "^6.21.1",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
"reactjs-social-login": "^2.6.3",
|
"reactjs-social-login": "^2.6.3",
|
||||||
"recharts": "^2.15.0",
|
"recharts": "^2.15.0",
|
||||||
"reusify": "^1.0.4",
|
"reusify": "^1.0.4",
|
||||||
|
"tesseract.js": "^7.0.0",
|
||||||
"vite-plugin-pwa": "^0.20.0"
|
"vite-plugin-pwa": "^0.20.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@capacitor/assets": "^3.0.5",
|
"@capacitor/assets": "^3.0.5",
|
||||||
|
"@capacitor/cli": "^8.0.0",
|
||||||
"@tanstack/eslint-plugin-query": "^5.14.6",
|
"@tanstack/eslint-plugin-query": "^5.14.6",
|
||||||
"@types/react": "^18.2.43",
|
"@types/react": "^18.2.43",
|
||||||
"@types/react-dom": "^18.2.17",
|
"@types/react-dom": "^18.2.17",
|
||||||
"@vite-pwa/assets-generator": "^0.2.4",
|
"@vite-pwa/assets-generator": "^0.2.4",
|
||||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
"eslint": "^8.56.0",
|
"baseline-browser-mapping": "^2.9.19",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"capacitor-set-version": "^2.2.0",
|
||||||
"eslint-plugin-prettier": "^5.1.2",
|
"eslint": "^9.39.4",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-formatter-gha": "^2.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.4.5",
|
"eslint-plugin-prettier": "^5.5.6",
|
||||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-sort-destructure-keys": "^1.5.0",
|
"eslint-plugin-react-hooks": "^7.1.1",
|
||||||
"eslint-plugin-sort-keys-fix": "^1.1.2",
|
"eslint-plugin-react-refresh": "^0.5.2",
|
||||||
"eslint-plugin-tailwindcss": "^3.13.1",
|
"eslint-plugin-simple-import-sort": "^13.0.0",
|
||||||
|
"eslint-plugin-sort-destructure-keys": "^3.0.0",
|
||||||
|
"eslint-plugin-tailwindcss": "^3.18.3",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
|
"patch-package": "^8.0.1",
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.8.3",
|
||||||
"prettier-plugin-tailwindcss": "^0.5.10",
|
"prettier-plugin-tailwindcss": "^0.5.10",
|
||||||
"tailwindcss": "^3.4.0",
|
"tailwindcss": "^3.4.0",
|
||||||
|
"typescript": "^5.8.0",
|
||||||
"vite": "^5.2.13"
|
"vite": "^5.2.13"
|
||||||
|
},
|
||||||
|
"allowScripts": {
|
||||||
|
"tesseract.js@7.0.0": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||