145 Commits

Author SHA1 Message Date
66c97dabb1 Merge pull request 'Update Rust crate serde to 1.0.228' (#111) from renovate/serde-1.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-07 00:12:52 +00:00
84e0fec3a2 Update Rust crate serde to 1.0.228
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-10-06 00:14:10 +00:00
11fb50af9a Merge pull request 'Update Rust crate log to 0.4.28' (#110) from renovate/log-0.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-05 00:14:59 +00:00
76d1430ecb Update Rust crate log to 0.4.28
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-10-04 00:37:04 +00:00
22848bcec4 Merge pull request 'Update react to ^19.2.0' (#109) from renovate/react into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-03 00:39:04 +00:00
d2f948c8fa Update react to ^19.2.0
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-10-02 00:38:17 +00:00
05aff4bbfb Merge pull request 'Update dependency @types/react to ^19.1.15' (#108) from renovate/react into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-30 00:35:25 +00:00
74b1a4dbcc Update dependency @types/react to ^19.1.15
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-29 00:36:02 +00:00
8ad7b589ce Merge pull request 'Update Rust crate clap to 4.5.48' (#107) from renovate/clap-4.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-27 00:37:25 +00:00
ff20cb7bc9 Update Rust crate clap to 4.5.48
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-26 00:35:29 +00:00
1c345127b4 Merge pull request 'Update Rust crate anyhow to 1.0.100' (#106) from renovate/anyhow-1.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-24 00:37:17 +00:00
71e54b8d72 Update Rust crate anyhow to 1.0.100
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-22 00:39:15 +00:00
13a8bbad1d Merge pull request 'Update Rust crate actix-files to 0.6.8' (#105) from renovate/actix-files-0.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-21 00:40:54 +00:00
ec212902e9 Update Rust crate actix-files to 0.6.8
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-20 00:36:36 +00:00
6786ed6075 Merge pull request 'Update dependency @fontsource/roboto to ^5.2.8' (#104) from renovate/fontsource-roboto-5.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-19 00:35:32 +00:00
c881f58cf4 Update dependency @fontsource/roboto to ^5.2.8
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-18 00:36:43 +00:00
586204dcba Merge pull request 'Update dependency @fontsource/roboto to ^5.2.7' (#103) from renovate/fontsource-roboto-5.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-17 00:35:53 +00:00
da56f99ce6 Update dependency @fontsource/roboto to ^5.2.7
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-16 00:35:52 +00:00
57d2b1bf6a Merge pull request 'Update dependency @types/react to ^19.1.13' (#102) from renovate/react into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-15 00:36:19 +00:00
831ccd073c Update dependency @types/react to ^19.1.13
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-14 00:36:35 +00:00
c5269a587b Merge pull request 'Update Rust crate chrono to 0.4.42' (#101) from renovate/chrono-0.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-13 00:37:13 +00:00
71ce57d205 Update Rust crate chrono to 0.4.42
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-12 00:36:04 +00:00
d57107cfc1 Merge pull request 'Update dependency vite to ^6.3.6' (#100) from renovate/vite-6.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-11 00:35:11 +00:00
68a3eb4218 Update dependency vite to ^6.3.6
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-10 00:35:14 +00:00
514f140527 Merge pull request 'Update Rust crate clap to 4.5.47' (#99) from renovate/clap-4.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-09 00:35:25 +00:00
4bac50b676 Update Rust crate clap to 4.5.47
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-08 00:36:10 +00:00
884b86daa9 Merge pull request 'Update Rust crate actix-files to 0.6.7' (#98) from renovate/actix-files-0.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-07 00:36:19 +00:00
64365916bd Update Rust crate actix-files to 0.6.7
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-06 00:35:57 +00:00
77c0640ec0 Merge pull request 'Update dependency ts-pattern to ^5.8.0' (#97) from renovate/ts-pattern-5.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-05 00:22:01 +00:00
96c80eb18c Update dependency ts-pattern to ^5.8.0
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-04 00:29:07 +00:00
0cde9f5635 Merge pull request 'Update dependency eslint-plugin-react-x to ^1.52.9' (#96) from renovate/eslint-plugin-react-x-1.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-03 00:27:57 +00:00
5e35dae02f Update dependency eslint-plugin-react-x to ^1.52.9
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-09-02 00:28:11 +00:00
f321376990 Merge pull request 'Update dependency dayjs to ^1.11.18' (#95) from renovate/dayjs-1.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-01 00:28:05 +00:00
86b86d4d68 Update dependency dayjs to ^1.11.18
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-31 00:26:59 +00:00
35c629a339 Merge pull request 'Update react' (#94) from renovate/react into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-30 00:27:37 +00:00
da0d5adcb9 Update react
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-29 00:28:23 +00:00
2214387010 Merge pull request 'Update dependency scanbot_sdk to ^7.0.1' (#93) from renovate/scanbot_sdk-7.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-26 00:27:16 +00:00
667ce69be8 Update dependency scanbot_sdk to ^7.0.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2025-08-26 00:27:14 +00:00
57f4ed53f6 Merge pull request 'Update dependency flutter_hooks to ^0.21.3+1' (#92) from renovate/flutter_hooks-0.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-25 00:27:49 +00:00
deb884a1f0 Update dependency flutter_hooks to ^0.21.3+1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-25 00:27:44 +00:00
0e5d878e30 Merge pull request 'Update dependency @types/react to ^19.1.11' (#91) from renovate/react into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-24 00:27:01 +00:00
b266cbcadb Update dependency @types/react to ^19.1.11
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-23 00:28:07 +00:00
91bca2b6b1 Merge pull request 'Update dependency @mui/x-charts to ^8.10.2' (#90) from renovate/mui-x-charts-8.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-22 00:27:27 +00:00
a741662251 Update dependency @mui/x-charts to ^8.10.2
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-21 00:27:57 +00:00
34f0493c51 Merge pull request 'Update dependency eslint-plugin-react-x to ^1.52.6' (#89) from renovate/eslint-plugin-react-x-1.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-20 00:27:47 +00:00
b538b6fcb3 Update dependency eslint-plugin-react-x to ^1.52.6
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-19 00:27:39 +00:00
9d18d975d0 Merge pull request 'Update dependency @mui/x-charts to ^8.10.1' (#87) from renovate/mui-x-charts-8.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-17 00:27:47 +00:00
43049bc229 Update dependency @mui/x-charts to ^8.10.1
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-16 00:26:40 +00:00
19ca17b43a Merge pull request 'Update dependency eslint-plugin-react-x to ^1.52.4' (#86) from renovate/eslint-plugin-react-x-1.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-15 00:28:03 +00:00
572046f418 Merge pull request 'Update dependency eslint-plugin-react-dom to ^1.52.4' (#85) from renovate/eslint-plugin-react-dom-1.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-15 00:28:02 +00:00
24af473dd3 Update dependency eslint-plugin-react-x to ^1.52.4
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-14 00:27:47 +00:00
8a57c57ec4 Update dependency eslint-plugin-react-dom to ^1.52.4
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-14 00:27:44 +00:00
50a5e7745f Merge pull request 'Update Rust crate anyhow to 1.0.99' (#84) from renovate/anyhow-1.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-13 00:29:06 +00:00
675e4d9ecd Merge pull request 'Update dependency @types/react to ^19.1.10' (#83) from renovate/react into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-13 00:28:05 +00:00
83c214af7d Update Rust crate anyhow to 1.0.99
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-12 00:28:57 +00:00
bfa6af5749 Update dependency @types/react to ^19.1.10
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-12 00:27:22 +00:00
6ab157504c Merge pull request 'Update dependency @mui/x-charts to ^8.10.0' (#82) from renovate/mui-x-charts-8.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-11 00:28:40 +00:00
bb98ea5e46 Merge pull request 'Update dependency @eslint/js to ^9.33.0' (#81) from renovate/eslint-js-9.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-11 00:28:37 +00:00
2d104a54b5 Update dependency @mui/x-charts to ^8.10.0
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-10 00:28:25 +00:00
3b0ff29bc8 Update dependency @eslint/js to ^9.33.0
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-10 00:28:22 +00:00
4ade72a0ee Merge pull request 'Update dependency build_runner to ^2.6.1' (#80) from renovate/build_runner-2.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-09 00:27:37 +00:00
6021b44a13 Update dependency build_runner to ^2.6.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-09 00:27:30 +00:00
eb92e8c0c5 Merge pull request 'Update dependency eslint to ^9.32.0' (#79) from renovate/eslint-9.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-08 00:16:25 +00:00
d98305908c Merge pull request 'Update Rust crate clap to 4.5.43' (#78) from renovate/clap-4.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-08 00:16:22 +00:00
ae5ef99e3a Update dependency eslint to ^9.32.0
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-07 00:17:08 +00:00
2f592183e4 Update Rust crate clap to 4.5.43
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-07 00:17:06 +00:00
74291a258c Merge pull request 'Update Rust crate serde_json to 1.0.142' (#77) from renovate/serde_json-1.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-06 00:16:05 +00:00
df8cd6a046 Merge pull request 'Update dependency @mui/x-date-pickers to ^8.9.2' (#76) from renovate/mui-x-date-pickers-8.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-06 00:15:04 +00:00
079fbbf154 Update Rust crate serde_json to 1.0.142
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-05 00:16:16 +00:00
ba443629e6 Update dependency @mui/x-date-pickers to ^8.9.2
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-05 00:14:32 +00:00
2c07a69b90 Merge pull request 'Update dependency @mui/x-data-grid to ^8.9.2' (#75) from renovate/mui-x-data-grid-8.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-04 00:15:26 +00:00
0de551f1de Update dependency @mui/x-data-grid to ^8.9.2
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-04 00:15:21 +00:00
2488ef0125 Merge pull request 'Update dependency @mui/x-charts to ^8.9.2' (#74) from renovate/mui-x-charts-8.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-03 00:17:29 +00:00
aa2e764262 Update dependency @mui/x-charts to ^8.9.2
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-08-02 00:17:07 +00:00
1202219e98 Merge pull request 'Update Rust crate clap to 4.5.42' (#73) from renovate/clap-4.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-01 00:20:44 +00:00
112597084c Merge pull request 'Update react' (#72) from renovate/react into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-08-01 00:20:43 +00:00
4f64404ffa Update Rust crate clap to 4.5.42
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-07-31 00:15:45 +00:00
c39b53c721 Update react
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-07-31 00:15:41 +00:00
bd2e343601 Show API URL when a token has been created
Some checks failed
continuous-integration/drone/push Build is failing
2025-07-30 21:39:32 +02:00
85ee2b2549 Ignore auto login variable if it is empty
Some checks failed
continuous-integration/drone/push Build is failing
2025-07-30 19:47:34 +02:00
154551aeaf Merge pull request 'Update dependency build_runner to ^2.6.0' (#71) from renovate/build_runner-2.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-07-30 00:15:47 +00:00
7b10c3508a Update dependency build_runner to ^2.6.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-07-30 00:15:41 +00:00
61c96629a1 Merge pull request 'Update dependency eslint-plugin-react-dom to ^1.52.3' (#37) from renovate/eslint-plugin-react-dom-1.x into main
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-30 00:14:59 +00:00
8644075a09 Update dependency eslint-plugin-react-dom to ^1.52.3
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-29 00:17:21 +00:00
81bfa75eec Redirect to list screens after a successful scan
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-28 21:47:00 +02:00
de0dd4e36a Merge pull request 'Update dependency @vitejs/plugin-react to ^4.7.0' (#70) from renovate/vitejs-plugin-react-4.x into main
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-28 00:16:36 +00:00
f9d7a63738 Update dependency @vitejs/plugin-react to ^4.7.0
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-07-28 00:16:33 +00:00
0ef6f8288f Merge pull request 'Update dependency @mui/x-date-pickers to ^8.9.0' (#69) from renovate/mui-x-date-pickers-8.x into main
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-27 00:17:07 +00:00
2f23e4dadb Update dependency @mui/x-date-pickers to ^8.9.0
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is passing
2025-07-27 00:17:04 +00:00
5cf5fac8f4 Merge pull request 'Update dependency @eslint/js to ^9.32.0' (#68) from renovate/eslint-js-9.x into main
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-26 00:16:48 +00:00
8e143db354 Update dependency @eslint/js to ^9.32.0
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-07-26 00:16:46 +00:00
1237c9706e Merge pull request 'Update dependency @mui/x-data-grid to ^8.9.1' (#67) from renovate/mui-x-data-grid-8.x into main
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-25 00:17:11 +00:00
1add0b4cfe Update dependency @mui/x-data-grid to ^8.9.1
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is passing
2025-07-25 00:17:07 +00:00
6920d6d9b0 Merge pull request 'Update dependency @mui/x-charts to ^8.9.0' (#66) from renovate/mui-x-charts-8.x into main
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-24 00:17:01 +00:00
27e92660f1 Update dependency @mui/x-charts to ^8.9.0
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-07-24 00:16:58 +00:00
743e5ba410 Merge pull request 'Update Rust crate rand to 0.9.2' (#65) from renovate/rand-0.x into main
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-23 00:17:12 +00:00
8039b1c807 Update Rust crate rand to 0.9.2
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is passing
2025-07-23 00:17:07 +00:00
9ef84ba63a Merge pull request 'Update dependency eslint to ^9.31.0' (#8) from renovate/eslint-9.x into main
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-23 00:16:37 +00:00
56e5ae6629 Update dependency eslint to ^9.31.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-22 00:19:56 +00:00
4443131516 Can download APK from web app
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-21 19:48:32 +02:00
365d7589b1 Force refresh of expenses editor
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-20 19:55:18 +02:00
23cc189e53 Fix date extraction
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-07-20 19:17:47 +02:00
3098d12e8a Support short dates
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-07-20 18:32:02 +02:00
0943104cc8 Can show expense in full screen
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-20 18:23:37 +02:00
3beaba806a Can clear cost value quickly 2025-07-20 18:18:20 +02:00
1788e7f184 Can disable dates extraction 2025-07-20 18:14:03 +02:00
71d32d72ef Can extract date of expenses
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-20 18:07:22 +02:00
28f61a3099 Improve regular expression
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-20 17:44:46 +02:00
f61e3541fb Perform first OCR extraction 2025-07-20 17:25:52 +02:00
fb7891d913 Merge pull request 'Update Rust crate serde_json to 1.0.141' (#63) from renovate/serde_json-1.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-07-20 00:15:22 +00:00
d9ede224cf Update Rust crate serde_json to 1.0.141
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-07-20 00:15:16 +00:00
fc9334b20b Merge pull request 'Update dependency dart to ^3.8.2' (#62) from renovate/dart-3.x into main
Some checks failed
continuous-integration/drone/push Build is failing
2025-07-19 00:18:13 +00:00
c4cbd7ec8b Update dependency dart to ^3.8.2
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-07-19 00:17:10 +00:00
a4ef3e74dc Optimize APK size
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-07-18 18:37:58 +02:00
dbb988f2b5 Add mobile application (#47)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #47
2025-07-18 16:06:21 +00:00
b2aff4902d Fix Flutter code quality issues
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-18 18:01:25 +02:00
6f578b39f9 Updated project dependencies
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-07-18 17:57:57 +02:00
de519ecb6c Fix generated APK name
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-07-18 11:25:19 +02:00
3049e545e9 Fix keystore script file
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2025-07-18 10:08:51 +02:00
1f1c01a287 Sign APK builds from CI
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2025-07-18 09:04:01 +02:00
92885b8af6 Merge pull request 'Update dependency globals to ^16.3.0' (#61) from renovate/globals-16.x into main
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-18 00:51:18 +00:00
44320db760 Update dependency globals to ^16.3.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-18 00:16:24 +00:00
1f2a28aa65 Fix signing configuration
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-17 22:58:59 +02:00
f9566315eb Create a publish flavor dedicated for public releases
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-17 22:50:12 +02:00
63bed07015 Change debug configuration
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-17 20:36:44 +02:00
4b84d926d4 Disallow app backup
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-07-17 20:32:13 +02:00
8191a28986 Fix Drone configuration
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-07-17 20:27:24 +02:00
8c30b50d0c Update
Some checks reported errors
continuous-integration/drone/pr Build was killed
continuous-integration/drone/push Build was killed
2025-07-17 20:25:31 +02:00
389b2c96ba Remove temporary comments in Drone configuration
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2025-07-17 19:51:01 +02:00
5a08b0c971 Test full APK build before release
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is passing
2025-07-17 19:31:39 +02:00
b3fd066633 Fix clone
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/pr Build is passing
2025-07-17 19:26:18 +02:00
5c987473a5 Unshallow Git clone
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2025-07-17 19:25:27 +02:00
c3d2612f9a Second test
Some checks reported errors
continuous-integration/drone/pr Build was killed
continuous-integration/drone/push Build was killed
2025-07-17 19:21:49 +02:00
130cc1ef0d Temp test
Some checks reported errors
continuous-integration/drone/pr Build was killed
continuous-integration/drone/push Build was killed
2025-07-17 19:20:44 +02:00
aebefd114a Reorganize mobile app building 2025-07-17 19:19:20 +02:00
34d3e08149 Improve pipeline
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-07-17 19:08:21 +02:00
ccd3540804 Remove mobile app dependencies
Some checks reported errors
continuous-integration/drone/pr Build was killed
continuous-integration/drone/push Build was killed
2025-07-17 18:53:12 +02:00
b9b871224b Add flutter build to CI
Some checks reported errors
continuous-integration/drone/pr Build was killed
continuous-integration/drone/push Build was killed
2025-07-17 18:52:16 +02:00
17a22d7a4c Add an option to start on scans screen instead of capture screen
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-07-17 18:04:03 +02:00
8db2cf3ece Show a message when expenses list is empty
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-07-17 17:51:13 +02:00
e45648e038 Fix: reset editor screen between captures 2025-07-17 17:49:01 +02:00
55144da943 Can upload expenses to server 2025-07-17 17:44:36 +02:00
5065f780f2 Merge pull request 'Update dependency @mui/x-date-pickers to ^8.8.0' (#60) from renovate/mui-x-date-pickers-8.x into main
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-17 00:46:19 +00:00
28d8b96ebe Update dependency @mui/x-date-pickers to ^8.8.0
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-07-17 00:16:12 +00:00
baf62aa2a5 Fix misleading comment
All checks were successful
continuous-integration/drone/push Build is passing
2025-07-16 11:30:25 +00:00
32 changed files with 2024 additions and 1587 deletions

View File

@@ -4,9 +4,17 @@ type: docker
name: default
steps:
# Needs a full git clone
- name: fetch
image: alpine/git
commands:
- git fetch --tags
# Frontend
- name: web_build
image: node:23
depends_on:
- fetch
volumes:
- name: web_app
path: /tmp/web_build
@@ -20,6 +28,8 @@ steps:
# Backend
- name: backend_fetch_deps
image: rust
depends_on:
- fetch
volumes:
- name: rust_registry
path: /usr/local/cargo/registry
@@ -54,6 +64,9 @@ steps:
- name: backend_build
image: rust
when:
event:
- tag
volumes:
- name: rust_registry
path: /usr/local/cargo/registry
@@ -72,12 +85,58 @@ steps:
- ls -lah target/release/moneymgr_backend target/release/examples/api_curl
- cp target/release/moneymgr_backend target/release/examples/api_curl /tmp/release
# Mobile app code quality
- name: mobile_app_code_quality
image: ghcr.io/cirruslabs/flutter:latest
depends_on:
- fetch
commands:
- echo "Build version:" $(git describe --tags --abbrev=0)
- echo "Build number:" $(git rev-list --count $(git describe --tags --abbrev=0))
- cd moneymgr_mobile
- flutter --disable-analytics
- flutter pub get --enforce-lockfile
- dart run build_runner build
- flutter analyze
# Mobile app build
- name: mobile_app_build
image: ghcr.io/cirruslabs/flutter:latest
depends_on:
- backend_build # prevent synchronous backend & frontend build
- mobile_app_code_quality
when:
event:
- tag
environment:
JKS_KEYSTORE:
from_secret: JKS_KEYSTORE
JKS_KEYSTORE_PASSWORD:
from_secret: JKS_KEYSTORE_PASSWORD
volumes:
- name: release
path: /tmp/release
commands:
- cd moneymgr_mobile
- flutter --disable-analytics
- bash android/ci_write_keystore.sh
- flutter pub get --enforce-lockfile
- dart run build_runner build
- flutter build apk
--release
--flavor publish
--build-name $(git describe --tags --abbrev=0)
--split-per-abi
--target-platform android-arm64
--build-number $(git rev-list --count $(git describe --tags --abbrev=0))
- cp build/app/outputs/flutter-apk/app-arm64-v8a-publish-release.apk /tmp/release/moneymgr_mobile_arm64-v8a.apk
# Release
- name: gitea_release
image: plugins/gitea-release
depends_on:
- backend_build
- mobile_app_build
when:
event:
- tag

View File

@@ -36,9 +36,9 @@ dependencies = [
[[package]]
name = "actix-files"
version = "0.6.6"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0773d59061dedb49a8aed04c67291b9d8cf2fe0b60130a381aab53c6dd86e9be"
checksum = "6c0d87f10d70e2948ad40e8edea79c8e77c6c66e0250a4c1f09b690465199576"
dependencies = [
"actix-http",
"actix-service",
@@ -46,7 +46,7 @@ dependencies = [
"actix-web",
"bitflags",
"bytes",
"derive_more 0.99.20",
"derive_more 2.0.1",
"futures-core",
"http-range",
"log",
@@ -87,7 +87,7 @@ dependencies = [
"mime",
"percent-encoding",
"pin-project-lite",
"rand 0.9.1",
"rand 0.9.2",
"sha1",
"smallvec",
"tokio",
@@ -364,12 +364,6 @@ dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
@@ -431,9 +425,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.98"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "arbitrary"
@@ -700,16 +694,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.41"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link",
"windows-link 0.2.0",
]
[[package]]
@@ -724,9 +717,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.41"
version = "4.5.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
dependencies = [
"clap_builder",
"clap_derive",
@@ -734,9 +727,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.41"
version = "4.5.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
dependencies = [
"anstream",
"anstyle",
@@ -746,9 +739,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.41"
version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
dependencies = [
"heck",
"proc-macro2",
@@ -2157,9 +2150,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.27"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "lzma-rs"
@@ -2298,7 +2291,7 @@ dependencies = [
"light-openid",
"log",
"mime_guess",
"rand 0.9.1",
"rand 0.9.2",
"rust-embed",
"rust-s3",
"rust_xlsxwriter",
@@ -2741,9 +2734,9 @@ dependencies = [
[[package]]
name = "rand"
version = "0.9.1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
@@ -3212,18 +3205,28 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
[[package]]
name = "serde"
version = "1.0.219"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -3232,9 +3235,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.140"
version = "1.0.142"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
dependencies = [
"itoa",
"memchr",
@@ -3994,7 +3997,7 @@ checksum = "46ec44dc15085cea82cf9c78f85a9114c463a369786585ad2882d1ff0b0acf40"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-link 0.1.1",
"windows-result",
"windows-strings 0.4.1",
]
@@ -4027,6 +4030,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-link"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
[[package]]
name = "windows-registry"
version = "0.4.0"
@@ -4044,7 +4053,7 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b895b5356fc36103d0f64dd1e94dfa7ac5633f1c9dd6e80fe9ec4adef69e09d"
dependencies = [
"windows-link",
"windows-link 0.1.1",
]
[[package]]
@@ -4053,7 +4062,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
dependencies = [
"windows-link",
"windows-link 0.1.1",
]
[[package]]
@@ -4062,7 +4071,7 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a7ab927b2637c19b3dbe0965e75d8f2d30bdd697a1516191cad2ec4df8fb28a"
dependencies = [
"windows-link",
"windows-link 0.1.1",
]
[[package]]

View File

@@ -5,26 +5,26 @@ edition = "2024"
[dependencies]
env_logger = "0.11.8"
log = "0.4.27"
log = "0.4.28"
diesel = { version = "2.2.12", features = ["postgres", "r2d2"] }
diesel_migrations = "2.2.0"
clap = { version = "4.5.41", features = ["env", "derive"] }
clap = { version = "4.5.48", features = ["env", "derive"] }
actix-web = "4.11.0"
actix-cors = "0.7.1"
actix-multipart = "0.7.2"
actix-remote-ip = "0.1.0"
actix-session = { version = "0.10.1", features = ["redis-session"] }
actix-files = "0.6.6"
actix-files = "0.6.8"
lazy_static = "1.5.0"
anyhow = "1.0.98"
serde = { version = "1.0.219", features = ["derive"] }
anyhow = "1.0.100"
serde = { version = "1.0.228", features = ["derive"] }
rust-s3 = "0.36.0-beta.2"
thiserror = "2.0.12"
tokio = "1.45.1"
futures-util = "0.3.31"
serde_json = "1.0.140"
serde_json = "1.0.142"
light-openid = "1.0.4"
rand = "0.9.1"
rand = "0.9.2"
ipnet = { version = "2.11.0", features = ["serde"] }
lazy-regex = "3.4.1"
jwt-simple = { version = "0.12.12", default-features = false, features = ["pure-rust"] }
@@ -33,7 +33,7 @@ rust-embed = { version = "8.7.2" }
sha2 = "0.11.0-rc.0"
base16ct = "0.2.0"
httpdate = "1.0.3"
chrono = "0.4.41"
chrono = "0.4.42"
tempfile = "3.20.0"
zip = "3.0.0"
rust_xlsxwriter = "0.87.0"

View File

@@ -29,7 +29,7 @@ pub struct AppConfig {
/// Unsecure : for development, bypass authentication, using the account with the given
/// email address by default
#[clap(long, env)]
pub unsecure_auto_login_email: Option<String>,
unsecure_auto_login_email: Option<String>,
/// PostgreSQL database host
#[clap(long, env, default_value = "localhost")]
@@ -126,6 +126,14 @@ pub struct AppConfig {
/// Redis password
#[clap(long, env, default_value = "secretredis")]
redis_password: String,
/// Application download URL
#[clap(
long,
env,
default_value = "https://gitea.communiquons.org/pierre/MoneyMgr/releases/download/latest/moneymgr_mobile_arm64-v8a.apk"
)]
pub apk_download_url: String,
}
lazy_static::lazy_static! {
@@ -140,9 +148,17 @@ impl AppConfig {
&ARGS
}
/// Get auto login email (if not empty)
pub fn unsecure_auto_login_email(&self) -> Option<&str> {
match self.unsecure_auto_login_email.as_deref() {
None | Some("") => None,
s => s,
}
}
/// Check if auth is disabled
pub fn is_auth_disabled(&self) -> bool {
self.unsecure_auto_login_email.is_some()
self.unsecure_auto_login_email().is_some()
}
/// Get auth cookie domain

View File

@@ -70,6 +70,7 @@ impl Default for ServerConstraints {
struct ServerConfig {
auth_disabled: bool,
oidc_provider_name: &'static str,
apk_download_url: &'static str,
accounts_types: &'static [AccountTypeDesc],
constraints: ServerConstraints,
}
@@ -79,6 +80,7 @@ impl Default for ServerConfig {
Self {
auth_disabled: AppConfig::get().is_auth_disabled(),
oidc_provider_name: AppConfig::get().openid_provider().name,
apk_download_url: AppConfig::get().apk_download_url.as_str(),
constraints: Default::default(),
accounts_types: &ACCOUNT_TYPES,
}

View File

@@ -56,7 +56,7 @@ impl FromRequest for AuthExtractor {
};
Box::pin(async move {
// Check for authentication using OpenID
// Check for authentication using API token
if let Some(token) = req.headers().get(constants::API_TOKEN_HEADER) {
let Ok(jwt_token) = token.to_str() else {
return Err(actix_web::error::ErrorBadRequest(
@@ -182,7 +182,7 @@ impl FromRequest for AuthExtractor {
}
// Check if login is hard-coded as program argument
if let Some(email) = &AppConfig::get().unsecure_auto_login_email {
if let Some(email) = &AppConfig::get().unsecure_auto_login_email() {
let user = users_service::get_user_by_email(email).map_err(|e| {
log::error!("Failed to retrieve dev user: {e}");
ErrorPreconditionFailed("Unable to retrieve dev user!")

View File

@@ -38,7 +38,7 @@ async fn main() -> std::io::Result<()> {
db_connection::initialize_conn().expect("Failed to connect to PostgresSQL database!");
// Auto create default account, if requested
if let Some(mail) = &AppConfig::get().unsecure_auto_login_email {
if let Some(mail) = &AppConfig::get().unsecure_auto_login_email() {
users_service::create_or_update_user(mail, "Anonymous")
.await
.expect("Failed to create default account!");

View File

@@ -1,3 +1,5 @@
publish_key.properties
gradle-wrapper.jar
/.gradle
/captures/

View File

@@ -0,0 +1,8 @@
# Android version of application
Generate keystore:
```bash
keytool -genkey -v -keystore ./keystore.jks -keyalg RSA \
-keysize 2048 -validity 20000 -alias moneymgr
```

View File

@@ -1,3 +1,6 @@
import java.util.Properties
import java.io.FileInputStream
plugins {
id("com.android.application")
id("kotlin-android")
@@ -5,6 +8,12 @@ plugins {
id("dev.flutter.flutter-gradle-plugin")
}
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("publish_key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
namespace = "org.communiquons.moneymgr"
compileSdk = flutter.compileSdkVersion
@@ -21,7 +30,6 @@ android {
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "org.communiquons.moneymgr"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
@@ -31,13 +39,34 @@ android {
versionName = flutter.versionName
}
signingConfigs {
create("publish") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = keystoreProperties["storeFile"]?.let { file(it) }
storePassword = keystoreProperties["storePassword"] as String
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
// signingConfig = signingConfigs.getByName("debug")
}
}
flavorDimensions += "default"
productFlavors {
create("development") {
dimension = "default"
applicationIdSuffix = ".debug"
signingConfig = signingConfigs.getByName("debug")
}
create("publish") {
dimension = "default"
signingConfig = signingConfigs.getByName("publish")
}
}
}
flutter {

View File

@@ -1,4 +1,5 @@
<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">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
@@ -6,5 +7,8 @@
<uses-permission android:name="android.permission.INTERNET" />
<!-- In debug mode, unsecure traffic is permitted -->
<application android:usesCleartextTraffic="true" />
<application
android:label="MoneyMgr Debug"
android:usesCleartextTraffic="true"
tools:replace="android:label" />
</manifest>

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="false" />
</manifest>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<cloud-backup>
<exclude domain="root" />
<exclude domain="file" />
<exclude domain="database" />
<exclude domain="sharedpref" />
<exclude domain="external" />
</cloud-backup>
<device-transfer>
<exclude domain="root" />
<exclude domain="file" />
<exclude domain="database" />
<exclude domain="sharedpref" />
<exclude domain="external" />
</device-transfer>
</data-extraction-rules>

View File

@@ -0,0 +1,23 @@
#!/bin/bash
SCRIPT_DIR="$(temp=$( realpath "$0" ) && dirname "$temp")"
KEYSTORE_PATH="$SCRIPT_DIR/keystore.jks"
PROPERTIES_PATH="$SCRIPT_DIR/publish_key.properties"
echo Keystore path : $KEYSTORE_PATH
echo Properties path : $PROPERTIES_PATH
[ ! -n "$JKS_KEYSTORE" ] && echo 'Missing JKS_KEYSTORE variable!'&& exit 1
[ ! -n "$JKS_KEYSTORE_PASSWORD" ] && echo 'Missing JKS_KEYSTORE_PASSWORD variable!' && exit 1
# Write keystore
echo $JKS_KEYSTORE | base64 -d > "$KEYSTORE_PATH"
# Write keystore config
cat > "$PROPERTIES_PATH" <<_EOF
storePassword=$JKS_KEYSTORE_PASSWORD
keyPassword=$JKS_KEYSTORE_PASSWORD
keyAlias=moneymgr
storeFile=$KEYSTORE_PATH
_EOF

View File

@@ -0,0 +1,4 @@
storePassword=<password-from-previous-step>
keyPassword=<password-from-previous-step>
keyAlias=upload
storeFile=<keystore-file-location>

View File

@@ -1,45 +1,31 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:logging/logging.dart';
import 'package:moneymgr_mobile/services/router/routes_list.dart';
import 'package:moneymgr_mobile/services/storage/expenses.dart';
import 'package:moneymgr_mobile/services/storage/prefs.dart';
import 'package:moneymgr_mobile/utils/ocr_utils.dart';
import 'package:moneymgr_mobile/utils/pdf_utils.dart';
import 'package:moneymgr_mobile/widgets/expense_editor.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:scanbot_sdk/scanbot_sdk.dart';
import 'package:scanbot_sdk/scanbot_sdk_ui_v2.dart' hide IconButton, EdgeInsets;
part 'scan_screen.g.dart';
/// Scan a document & return generated PDF as byte file
@riverpod
Future<Uint8List?> _scanDocument(Ref ref) async {
var configuration = DocumentScanningFlow(
appearance: DocumentFlowAppearanceConfiguration(
statusBarMode: StatusBarMode.DARK,
),
cleanScanningSession: true,
outputSettings: DocumentScannerOutputSettings(pagesScanLimit: 1),
screens: DocumentScannerScreens(
review: ReviewScreenConfiguration(enabled: false),
),
);
var documentResult = await ScanbotSdkUiV2.startDocumentScanner(configuration);
Future<(Uint8List?, BaseExpenseInfo?)> _scanDocument(Ref ref) async {
final prefs = ref.watch(prefsProvider).requireValue;
if (documentResult.status != OperationStatus.OK) {
throw Exception("Scanner failed with status ${documentResult.status}");
}
// Convert result to PDF
var result = await ScanbotSdk.document.createPDFForDocument(
PDFFromDocumentParams(
documentID: documentResult.data!.uuid,
pdfConfiguration: PdfConfiguration(),
),
final pdf = await scanDocAsPDF();
final img = await renderPdf(pdfBytes: pdf);
final amount = await extractInfoFromBill(
imgBuff: img,
extractDates: !prefs.disableExtractDates(),
);
final pdfPath = result.pdfFileUri.replaceFirst("file://", "");
return File(pdfPath).readAsBytes();
return (pdf, amount);
}
class ScanScreen extends HookConsumerWidget {
@@ -52,8 +38,8 @@ class ScanScreen extends HookConsumerWidget {
restartScan() async {
try {
final val = ref.refresh(_scanDocumentProvider);
Logger.root.info("Load again startup result: $val");
ref.invalidate(_scanDocumentProvider);
Logger.root.info("Load again startup");
} catch (e, s) {
Logger.root.shout("Failed to try again startup loading! $e $s");
}
@@ -62,21 +48,24 @@ class ScanScreen extends HookConsumerWidget {
return Padding(
padding: const EdgeInsets.all(8.0),
child: switch (scanDocProvider) {
AsyncData(:final value) when value != null => ExpenseEditor(
file: value,
AsyncData(:final value) when value.$1 != null => ExpenseEditor(
file: value.$1!,
initialData: value.$2,
onFinished: (expense) async {
await expenses.add(
info: expense,
fileContent: value,
fileContent: value.$1!,
fileMimeType: "application/pdf",
);
restartScan();
if (context.mounted) {
context.pushReplacement(scansPage);
}
},
onRescan: restartScan,
),
// No data
AsyncData(:final value) when value == null => ScanErrorScreen(
AsyncData(:final value) when value.$1 == null => ScanErrorScreen(
message: "No document scanned!",
onTryAgain: restartScan,
),

View File

@@ -48,6 +48,12 @@ class _ExpensesList extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (list.isEmpty) {
return const Center(
child: Text("There is no entry waiting for upload (yet)"),
);
}
return ListView.builder(
itemBuilder: (context, entryNum) {
final expense = list[entryNum];

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:moneymgr_mobile/providers/settings.dart';
import 'package:moneymgr_mobile/services/storage/prefs.dart';
import 'package:moneymgr_mobile/utils/extensions.dart';
class SettingsScreen extends ConsumerWidget {
@@ -8,6 +9,7 @@ class SettingsScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final prefs = ref.watch(prefsProvider).requireValue;
final themeMode = ref.watch(currentThemeModeProvider);
void onTapThemeMode() =>
@@ -15,6 +17,16 @@ class SettingsScreen extends ConsumerWidget {
void onTapLicenses() => context.showAppLicensePage();
handleToggleStartScreen(v) async {
await prefs.setStartOnScansListScreen(v);
ref.invalidate(prefsProvider);
}
handleToggleDisableExtractDate(v) async {
await prefs.setDisableExtractDates(v);
ref.invalidate(prefsProvider);
}
return Scaffold(
appBar: AppBar(title: const Text('Settings')),
body: ListView(
@@ -25,6 +37,22 @@ class SettingsScreen extends ConsumerWidget {
trailing: Text(themeMode.label),
onTap: onTapThemeMode,
),
SwitchListTile(
value: prefs.startOnScansListScreen(),
onChanged: handleToggleStartScreen,
title: Text("Start on scans screen"),
subtitle: Text(
"Do not start camera automatically on application startup",
),
),
SwitchListTile(
value: prefs.disableExtractDates(),
onChanged: handleToggleDisableExtractDate,
title: Text("Do not extract dates"),
subtitle: Text(
"Do not attempt to extract dates from scanned expenses",
),
),
const Divider(),
ListTile(
leading: const Icon(Icons.info_outline),

View File

@@ -0,0 +1,27 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'api_client.dart';
part 'inbox_api.freezed.dart';
part 'inbox_api.g.dart';
@freezed
abstract class UpdateInboxEntryRequest with _$UpdateInboxEntryRequest {
const factory UpdateInboxEntryRequest({
// ignore: non_constant_identifier_names
required int file_id,
required int time,
required String? label,
required double? amount,
}) = _UpdateInboxEntryRequest;
factory UpdateInboxEntryRequest.fromJson(Map<String, dynamic> json) =>
_$UpdateInboxEntryRequestFromJson(json);
}
extension InboxApi on ApiClient {
/// Create a new inbox entry
Future<void> createInboxEntry(UpdateInboxEntryRequest entry) async {
await execute("/inbox", method: "POST", data: entry.toJson());
}
}

View File

@@ -11,6 +11,7 @@ import 'package:moneymgr_mobile/routes/scan_details/scan_details.dart';
import 'package:moneymgr_mobile/routes/scans_list/scans_list_screen.dart';
import 'package:moneymgr_mobile/routes/settings/settings_screen.dart';
import 'package:moneymgr_mobile/services/router/routes_list.dart';
import 'package:moneymgr_mobile/services/storage/prefs.dart';
import 'package:moneymgr_mobile/widgets/load_startup_data.dart';
import 'package:moneymgr_mobile/widgets/scaffold_with_navigation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -29,6 +30,8 @@ GoRouter router(Ref ref) {
authStateNotifier.value = value;
});
final prefs = ref.read(prefsProvider).requireValue;
// This is the only place you need to define your navigation items. The items
// will be propagated automatically to the router and the navigation bar/rail
// of the scaffold.
@@ -70,22 +73,22 @@ GoRouter router(Ref ref) {
final router = GoRouter(
debugLogDiagnostics: true,
initialLocation: navigationItems.first.path,
initialLocation: prefs.startOnScansListScreen() ? scansPage : capturePage,
routes: [
GoRoute(path: homePage, builder: (_, __) => const Scaffold()),
GoRoute(path: authPage, builder: (_, __) => const LoginScreen()),
GoRoute(path: qrAuthPath, builder: (_, __) => const QrAuthScreen()),
GoRoute(path: homePage, builder: (_, _) => const Scaffold()),
GoRoute(path: authPage, builder: (_, _) => const LoginScreen()),
GoRoute(path: qrAuthPath, builder: (_, _) => const QrAuthScreen()),
GoRoute(
path: manualAuthPage,
builder: (_, __) => const ManualAuthScreen(),
builder: (_, _) => const ManualAuthScreen(),
),
GoRoute(path: settingsPage, builder: (_, __) => const SettingsScreen()),
GoRoute(path: settingsPage, builder: (_, _) => const SettingsScreen()),
// Configuration for the bottom navigation bar routes. The routes themselves
// should be defined in [navigationItems]. Modification to this [ShellRoute]
// config is rarely needed.
ShellRoute(
builder: (_, __, child) => child,
builder: (_, _, child) => child,
routes: [
for (final (index, item) in navigationItems.indexed)
GoRoute(

View File

@@ -15,6 +15,22 @@ Future<SharedPreferencesWithCache> prefs(Ref ref) =>
);
extension MoneyMgrSharedPreferences on SharedPreferencesWithCache {
bool startOnScansListScreen() {
return getBool("startOnScansListScreen") ?? false;
}
Future<void> setStartOnScansListScreen(bool start) async {
await setBool("startOnScansListScreen", start);
}
bool disableExtractDates() {
return getBool("disableExtractDates") ?? false;
}
Future<void> setDisableExtractDates(bool disable) async {
await setBool("disableExtractDates", disable);
}
ServerConfig? serverConfig() {
final json = getString("serverConfig");
if (json != null) return ServerConfig.fromJson(jsonDecode(json));

View File

@@ -0,0 +1,86 @@
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:logging/logging.dart';
import 'package:moneymgr_mobile/services/storage/expenses.dart';
/// Attempt to extract information from invoice image
Future<BaseExpenseInfo?> extractInfoFromBill({
required Uint8List imgBuff,
required bool extractDates,
}) async {
final decodedImage = await decodeImageFromList(imgBuff);
final byteData = await decodedImage.toByteData(
format: ui.ImageByteFormat.rawRgba,
);
final image = InputImage.fromBitmap(
bitmap: byteData!.buffer.asUint8List(),
width: decodedImage.width,
height: decodedImage.height,
);
final textRecognizer = TextRecognizer(script: TextRecognitionScript.latin);
final extractionResult = await textRecognizer.processImage(image);
Logger.root.fine("Expense text: ${extractionResult.text}");
// Check for highestCost amount on invoice
final costRegexp = RegExp(
r'([0-9]+([ ]*(\\.|,)[ ]*[0-9]{1,2}){0,1})([ \\t\\n]*(EUR|eur|€)|E)',
multiLine: true,
caseSensitive: false,
);
var highestCost = 0.0;
for (final match in costRegexp.allMatches(extractionResult.text)) {
if (match.groupCount == 0) continue;
// Process only numeric value
final value = (match.group(1) ?? "").replaceAll(",", ".");
highestCost = max(highestCost, double.tryParse(value) ?? 0.0);
}
// Check for highestCost amount on invoice
final dateRegexp = RegExp(
r'([0-3][0-9])(\/|-)([0-1][0-9])(\/|-)((20|)[0-9]{2})',
multiLine: false,
caseSensitive: false,
);
final currDate = DateTime.now();
DateTime? newest;
for (final match in dateRegexp.allMatches(extractionResult.text)) {
if (match.groupCount < 6) continue;
int year = int.tryParse(match.group(5)!) ?? currDate.year;
try {
final date = DateTime(
year > 99 ? year : (2000 + year),
int.tryParse(match.group(3)!) ?? currDate.month,
int.tryParse(match.group(1)!) ?? currDate.day,
);
if (newest == null) {
newest = date;
} else {
newest = DateTime.fromMillisecondsSinceEpoch(
max(newest.millisecondsSinceEpoch, date.millisecondsSinceEpoch),
);
}
} catch (e, s) {
Logger.root.warning("Failed to parse date! $e$s");
}
}
return BaseExpenseInfo(
label: null,
cost: highestCost,
time: extractDates && (newest?.isBefore(currDate) ?? false)
? newest!
: currDate,
);
}

View File

@@ -6,14 +6,40 @@ import 'package:flutter/material.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:pdf_image_renderer/pdf_image_renderer.dart';
import 'package:scanbot_sdk/scanbot_sdk.dart';
import 'package:scanbot_sdk/scanbot_sdk_ui_v2.dart' hide IconButton, EdgeInsets;
/// Scan document as PDF
Future<Uint8List> scanDocAsPDF() async {
var configuration = DocumentScanningFlow(
appearance: DocumentFlowAppearanceConfiguration(
statusBarMode: StatusBarMode.DARK,
),
cleanScanningSession: true,
outputSettings: DocumentScannerOutputSettings(pagesScanLimit: 1),
screens: DocumentScannerScreens(
review: ReviewScreenConfiguration(enabled: false),
),
);
var documentResult = await ScanbotSdkUiV2.startDocumentScanner(configuration);
if (documentResult.status != OperationStatus.OK) {
throw Exception("Scanner failed with status ${documentResult.status}");
}
// Convert result to PDF
var result = await ScanbotSdk.document.createPDFForDocument(
PDFFromDocumentParams(
documentID: documentResult.data!.uuid,
pdfConfiguration: PdfConfiguration(),
),
);
final pdfPath = result.pdfFileUri.replaceFirst("file://", "");
return File(pdfPath).readAsBytes();
}
/// Render PDF to image bits
Future<Uint8List> renderPdf(
{
String? path,
Uint8List? pdfBytes,
}) async {
Future<Uint8List> renderPdf({String? path, Uint8List? pdfBytes}) async {
assert(path != null || pdfBytes != null);
// Create temporary file if required

View File

@@ -40,6 +40,20 @@ class ExpenseEditor extends HookConsumerWidget {
final (:pending, :snapshot, :hasError) = useAsyncTask();
// Force refresh of field if required
final previousData = useState<BaseExpenseInfo?>(null);
if (initialData != previousData.value) {
previousData.value = initialData;
labelController.text = initialData?.label ?? "";
costController.text = initialData?.cost.toString() ?? "";
timeController.value = initialData?.time ?? DateTime.now();
}
// Clear cost value
handleClearCost() {
costController.text = "";
}
// Pick a new date
handlePickDate() async {
final date = await showDatePicker(
@@ -65,6 +79,11 @@ class ExpenseEditor extends HookConsumerWidget {
time: timeController.value,
),
);
// Reset screen after a scan
await pending.value;
labelController.text = "";
costController.text = "";
}
// Cancel operation
@@ -89,6 +108,19 @@ class ExpenseEditor extends HookConsumerWidget {
}
}
// Open invoice in full screen
handleFullScreenInvoice() {
showDialog(
context: context,
builder: (c) => Scaffold(
appBar: AppBar(title: Text("Expense")),
body: SingleChildScrollView(
child: PDFViewer(pdfBytes: file, fit: BoxFit.fitWidth),
),
),
);
}
return Scaffold(
appBar: AppBar(
title: Text("Expense info"),
@@ -120,8 +152,11 @@ class ExpenseEditor extends HookConsumerWidget {
children: [
// Expense preview
Expanded(
child: GestureDetector(
onTap: handleFullScreenInvoice,
child: PDFViewer(pdfBytes: file, fit: BoxFit.contain),
),
),
SizedBox(height: 10),
@@ -132,7 +167,13 @@ class ExpenseEditor extends HookConsumerWidget {
decimal: true,
signed: false,
),
decoration: const InputDecoration(labelText: 'Cost'),
decoration: InputDecoration(
labelText: 'Cost',
suffixIcon: IconButton(
onPressed: handleClearCost,
icon: const Icon(Icons.clear),
),
),
textInputAction: TextInputAction.done,
),

View File

@@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:logging/logging.dart';
import 'package:moneymgr_mobile/services/api/api_client.dart';
import 'package:moneymgr_mobile/services/api/files_api.dart';
import 'package:moneymgr_mobile/services/api/inbox_api.dart';
import 'package:moneymgr_mobile/services/storage/expenses.dart';
import 'package:moneymgr_mobile/utils/extensions.dart';
import 'package:moneymgr_mobile/utils/hooks.dart';
@@ -27,9 +28,21 @@ Future<void> _performSynchronization(Ref ref) async {
bytes: bytes,
);
// TODO continue
break;
// Then, create the inbox entry
await apiService.createInboxEntry(
UpdateInboxEntryRequest(
file_id: file.id,
time: exp.time,
label: exp.label,
amount: -1 * exp.cost,
),
);
// Lastly delete the local expense
ref.watch(expensesProvider).requireValue.deleteExpense(exp);
}
ref.invalidate(expensesProvider);
}
class SynchronizeButton extends HookConsumerWidget {

View File

@@ -141,10 +141,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
sha256: "0b1b12a0a549605e5f04476031cd0bc91ead1d7c8e830773a18ee54179b3cb62"
url: "https://pub.dev"
source: hosted
version: "8.10.1"
version: "8.11.0"
characters:
dependency: transitive
description:
@@ -370,10 +370,10 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
version: "6.0.0"
flutter_native_splash:
dependency: "direct main"
description:
@@ -452,18 +452,18 @@ packages:
dependency: "direct dev"
description:
name: freezed
sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c"
sha256: "2d399f823b8849663744d2a9ddcce01c49268fb4170d0442a655bf6a2f47be22"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
version: "3.1.0"
freezed_annotation:
dependency: "direct main"
description:
name: freezed_annotation
sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b
sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
version: "3.1.0"
frontend_server_client:
dependency: transitive
description:
@@ -484,10 +484,26 @@ packages:
dependency: "direct main"
description:
name: go_router
sha256: ac294be30ba841830cfa146e5a3b22bb09f8dc5a0fdd9ca9332b04b0bde99ebf
sha256: c489908a54ce2131f1d1b7cc631af9c1a06fac5ca7c449e959192089f9489431
url: "https://pub.dev"
source: hosted
version: "15.2.4"
version: "16.0.0"
google_mlkit_commons:
dependency: transitive
description:
name: google_mlkit_commons
sha256: "8f40fbac10685cad4715d11e6a0d86837d9ad7168684dfcad29610282a88e67a"
url: "https://pub.dev"
source: hosted
version: "0.11.0"
google_mlkit_text_recognition:
dependency: "direct main"
description:
name: google_mlkit_text_recognition
sha256: "96173ad4dd7fd06c660e22ac3f9e9f1798a517fe7e48bee68eeec83853224224"
url: "https://pub.dev"
source: hosted
version: "0.15.0"
graphs:
dependency: transitive
description:
@@ -604,10 +620,10 @@ packages:
dependency: transitive
description:
name: lints
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0
url: "https://pub.dev"
source: hosted
version: "5.1.1"
version: "6.0.0"
logging:
dependency: "direct main"
description:
@@ -921,10 +937,10 @@ packages:
dependency: transitive
description:
name: source_helper
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
sha256: "4f81479fe5194a622cdd1713fe1ecb683a6e6c85cd8cec8e2e35ee5ab3fdf2a1"
url: "https://pub.dev"
source: hosted
version: "1.3.5"
version: "1.3.6"
source_span:
dependency: transitive
description:

View File

@@ -49,10 +49,10 @@ dependencies:
riverpod_annotation: ^2.6.1
# Implement React hooks in Flutter
flutter_hooks: ^0.21.2
flutter_hooks: ^0.21.3+1
# Router
go_router: ^15.2.4
go_router: ^16.0.0
# Flutter extras widgets for columns and rows
flextras: ^1.0.0
@@ -84,7 +84,7 @@ dependencies:
# Document scanner
# flutter_doc_scanner: ^0.0.16 # no bundled support yet
# https://developers.google.com/ml-kit/tips/installation-paths
scanbot_sdk: ^7.0.0
scanbot_sdk: ^7.0.1
# Get documents path
path_provider: ^2.1.5
@@ -93,6 +93,9 @@ dependencies:
# PDF renderer
pdf_image_renderer: ^1.0.1
# Text extraction
google_mlkit_text_recognition: ^0.15.0
dev_dependencies:
flutter_test:
sdk: flutter
@@ -102,13 +105,13 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^5.0.0
flutter_lints: ^6.0.0
# Manage app icon
flutter_launcher_icons: ^0.14.4
# Generate source code
build_runner: ^2.5.4
build_runner: ^2.6.1
# Riverpod code generation
riverpod_generator: ^2.6.5
@@ -127,6 +130,9 @@ dev_dependencies:
# The following section is specific to Flutter packages.
flutter:
# Default Android flavor
default-flavor: development
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.

File diff suppressed because it is too large Load Diff

View File

@@ -12,38 +12,38 @@
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@fontsource/roboto": "^5.2.6",
"@fontsource/roboto": "^5.2.8",
"@jsonjoy.com/base64": "^1.1.2",
"@mdi/js": "^7.4.47",
"@mdi/react": "^1.6.1",
"@mui/icons-material": "^7.1.2",
"@mui/material": "^7.1.2",
"@mui/x-charts": "^8.8.0",
"@mui/x-data-grid": "^8.8.0",
"@mui/x-date-pickers": "^8.7.0",
"@mui/x-charts": "^8.10.2",
"@mui/x-data-grid": "^8.9.2",
"@mui/x-date-pickers": "^8.9.2",
"date-and-time": "^3.6.0",
"dayjs": "^1.11.13",
"dayjs": "^1.11.18",
"filesize": "^10.1.6",
"qrcode.react": "^4.2.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router": "^7.6.3",
"react-router-dom": "^7.6.3",
"ts-pattern": "^5.7.1"
"ts-pattern": "^5.8.0"
},
"devDependencies": {
"@eslint/js": "^9.31.0",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.6.0",
"eslint": "^9.26.0",
"eslint-plugin-react-dom": "^1.49.0",
"@eslint/js": "^9.33.0",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"@vitejs/plugin-react": "^4.7.0",
"eslint": "^9.32.0",
"eslint-plugin-react-dom": "^1.52.4",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^00.4.20",
"eslint-plugin-react-x": "^1.52.3",
"globals": "^16.2.0",
"eslint-plugin-react-x": "^1.52.9",
"globals": "^16.3.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.32.1",
"vite": "^6.3.5"
"vite": "^6.3.6"
}
}

View File

@@ -3,6 +3,7 @@ import { APIClient } from "./ApiClient";
export interface ServerConfig {
auth_disabled: boolean;
oidc_provider_name: string;
apk_download_url: string;
accounts_types: AccountType[];
constraints: ServerConstraints;
}

View File

@@ -280,6 +280,8 @@ function CreatedToken(p: { token: TokenWithSecret }): React.ReactElement {
The API token was successfully created. Please note the following
information as they won't be available next.
<br />
API URL : <CopyTextChip text={APIClient.ActualBackendURL()} />
<br />
Token ID: <CopyTextChip text={p.token.id.toString()} />
<br />
Token value: <CopyTextChip text={p.token.token} />

View File

@@ -1,5 +1,6 @@
import { mdiApi, mdiCash } from "@mdi/js";
import Icon from "@mdi/react";
import AndroidIcon from "@mui/icons-material/Android";
import CloudDownloadIcon from "@mui/icons-material/CloudDownload";
import LogoutIcon from "@mui/icons-material/Logout";
import SettingsIcon from "@mui/icons-material/Settings";
@@ -10,6 +11,7 @@ import MenuItem from "@mui/material/MenuItem";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";
import * as React from "react";
import { ServerApi } from "../api/ServerApi";
import { useAuthInfo } from "./BaseAuthenticatedPage";
import { DarkThemeButton } from "./DarkThemeButtonWidget";
import { PublicModeButton } from "./PublicModeButtonWidget";
@@ -100,6 +102,18 @@ export function MoneyWebAppBar(p: {
</MenuItem>
</RouterLink>
{/* APK download */}
<RouterLink to={ServerApi.Config.apk_download_url}>
<MenuItem>
<ListItemIcon>
<AndroidIcon />
</ListItemIcon>
<ListItemText secondary="Scan expenses from your smartphone">
Mobile Application
</ListItemText>
</MenuItem>
</RouterLink>
<Divider />
{/* Sign out */}