281 Commits

Author SHA1 Message Date
048fb0ef43 Updated licenses [skip ci] 2026-02-07 20:19:54 +00:00
a4bc03111d Updated version number [skip ci] 2026-02-07 20:19:24 +00:00
00abc4d1c0 Merge pull request 'NavBar optimieren' (#189) from enhancement/188-navbar-optimieren into development
All checks were successful
Push Pipeline / test (push) Successful in 33s
Push Pipeline / update_version (push) Successful in 5s
Push Pipeline / generate_licenses (push) Successful in 28s
Push Pipeline / format (push) Successful in 53s
Push Pipeline / build (push) Successful in 6m2s
Reviewed-on: #189
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
2026-02-07 20:18:46 +00:00
d4fcc8106f Removed spacing in navbar item
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 37s
Pull Request Pipeline / lint (pull_request) Successful in 44s
2026-02-07 20:21:46 +01:00
487efb4d61 Added type annotation
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 35s
Pull Request Pipeline / lint (pull_request) Successful in 46s
2026-02-06 14:07:43 +01:00
3f790cfbb1 Renamed variable 2026-02-06 14:04:16 +01:00
ee1962ef9c Implemented new nav bar 2026-02-06 14:03:57 +01:00
a4d4703069 Updated licenses [skip ci] 2026-02-06 12:32:19 +00:00
fabb7bae19 Updated version number [skip ci] 2026-02-06 12:31:47 +00:00
d1458443eb Added ref again
Some checks failed
Push Pipeline / update_version (push) Successful in 6s
Push Pipeline / build (push) Failing after 31s
Push Pipeline / generate_licenses (push) Successful in 30s
Push Pipeline / test (push) Successful in 38s
Push Pipeline / format (push) Successful in 41s
2026-01-26 14:47:26 +01:00
9c00b48de5 Updated licenses [skip ci] 2026-01-26 13:38:35 +00:00
03ce304a0a Updated version number [skip ci] 2026-01-26 13:38:01 +00:00
dde617d429 Tried new workflow fix
Some checks failed
Push Pipeline / update_version (push) Successful in 4s
Push Pipeline / generate_licenses (push) Successful in 33s
Push Pipeline / test (push) Successful in 39s
Push Pipeline / format (push) Successful in 47s
Push Pipeline / build (push) Failing after 1m42s
2026-01-26 14:37:23 +01:00
03a2df4fdf Updated version number [skip ci] 2026-01-26 08:55:12 +00:00
96ef70b209 Updated version number [skip ci] 2026-01-26 08:45:49 +00:00
4ae59ec881 Merge pull request 'README vervollständigen' (#150) from enhancement/81-readme-vervollstaendigen into development
Some checks failed
Push Pipeline / update_version (push) Successful in 5s
Push Pipeline / generate_licenses (push) Failing after 30s
Push Pipeline / format (push) Has been skipped
Push Pipeline / test (push) Successful in 38s
Push Pipeline / build (push) Successful in 5m5s
Reviewed-on: #150
2026-01-26 08:45:42 +00:00
c162e245bd Updated license name in Readme
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 37s
Pull Request Pipeline / lint (pull_request) Successful in 42s
2026-01-24 22:35:11 +01:00
481afd7533 Added example CONTRIBUTING.md
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 37s
Pull Request Pipeline / lint (pull_request) Successful in 43s
2026-01-24 19:17:33 +01:00
af42ebbf92 Merge remote-tracking branch 'origin/development' into enhancement/81-readme-vervollstaendigen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 37s
Pull Request Pipeline / lint (pull_request) Successful in 45s
2026-01-24 17:48:25 +01:00
34e442dbe9 added white space
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
2026-01-24 17:47:32 +01:00
8783d0ac44 Version badges in one row
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-24 17:30:56 +01:00
7f356b0c86 Updated licenses [skip ci] 2026-01-24 13:55:49 +00:00
e4a2ac6b47 Updated version number [skip ci] 2026-01-24 13:55:18 +00:00
6065b53ce9 Updated workflow
All checks were successful
Push Pipeline / test (push) Successful in 36s
Push Pipeline / update_version (push) Successful in 5s
Push Pipeline / generate_licenses (push) Successful in 30s
Push Pipeline / format (push) Successful in 50s
Push Pipeline / build (push) Successful in 5m21s
2026-01-24 14:53:31 +01:00
c644ccfb06 Updated version number [skip ci] 2026-01-24 12:21:35 +00:00
2dd7d0285d Pipeline Push Error (again) (#187)
Some checks failed
Push Pipeline / test (push) Successful in 34s
Push Pipeline / update_version (push) Successful in 5s
Push Pipeline / generate_licenses (push) Failing after 30s
Push Pipeline / format (push) Has been skipped
Push Pipeline / build (push) Successful in 5m23s
Pipeline Push Error (again) (#187)
Co-authored-by: Gitea Actions [bot] <actions@yannick-weigert.de>
Reviewed-on: #187
2026-01-24 12:20:55 +00:00
cf5883b430 Updated version number [skip ci] 2026-01-24 12:03:27 +00:00
7af2b43193 Merge pull request 'Hotfix: Pipeline Push Error' (#186) from hotifx/pipeline-push-error into development
Some checks failed
Push Pipeline / test (push) Successful in 36s
Push Pipeline / update_version (push) Successful in 5s
Push Pipeline / generate_licenses (push) Failing after 28s
Push Pipeline / format (push) Has been skipped
Push Pipeline / build (push) Successful in 5m15s
Reviewed-on: #186
2026-01-24 12:02:46 +00:00
90cc4d4c2a Added missing flutter step
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 36s
Pull Request Pipeline / lint (pull_request) Successful in 42s
2026-01-24 12:49:40 +01:00
db2ba2571f Added pull 2026-01-24 12:49:21 +01:00
3b01c4623c Updated version number [skip ci] 2026-01-24 11:41:50 +00:00
c5c9a43459 Update licenses in push pipeline (#184)
Some checks failed
Push Pipeline / test (push) Successful in 35s
Push Pipeline / update_version (push) Successful in 4s
Push Pipeline / generate_licenses (push) Failing after 2m1s
Push Pipeline / format (push) Has been skipped
Push Pipeline / build (push) Successful in 5m22s
Update licenses in push pipeline (#184)
Co-authored-by: Gitea Actions [bot] <actions@yannick-weigert.de>
Reviewed-on: #184
2026-01-24 11:41:07 +00:00
b2e58f2539 Updated version number [skip ci] 2026-01-24 10:33:32 +00:00
58dc5d8c2e Extend workflow to include build stage (#185)
All checks were successful
Push Pipeline / test (push) Successful in 35s
Push Pipeline / format (push) Successful in 50s
Push Pipeline / update_version (push) Successful in 4s
Push Pipeline / build (push) Successful in 5m22s
Extend workflow to include build stage (#185)
Reviewed-on: #185
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-24 10:32:00 +00:00
fd7dbd1155 Added gnu license
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m2s
Pull Request Pipeline / lint (pull_request) Successful in 2m5s
2026-01-23 22:30:54 +01:00
61b72cf595 Merge remote-tracking branch 'origin/enhancement/81-readme-vervollstaendigen' into enhancement/81-readme-vervollstaendigen
# Conflicts:
#	LICENSE
2026-01-23 22:15:01 +01:00
81c6c377d4 Added placeholder LICENSE and CONTRIBUTING.md 2026-01-23 22:14:51 +01:00
92f2b4db95 Added placeholder LICENSE and CONTRIBUTING.md
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m2s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
2026-01-23 22:14:12 +01:00
5178adf71c Changed screenshot order
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m14s
2026-01-23 22:11:33 +01:00
4a6d639f1c Updated readme
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 1m59s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-23 22:10:30 +01:00
e9cf707fca Merge branch 'refs/heads/development' into enhancement/81-readme-vervollstaendigen 2026-01-23 20:56:56 +01:00
2b7941202a Updated version number [skip ci] 2026-01-23 12:31:09 +00:00
92317bcbdf Merge pull request 'App-Name, Icon und Design' (#179) from documentation/131-app-name-icon-und-design into development
All checks were successful
Push Pipeline / test (push) Successful in 1m56s
Push Pipeline / format (push) Successful in 2m4s
Push Pipeline / update_version (push) Successful in 4s
Reviewed-on: #179
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-23 12:28:56 +00:00
ceade5cafd Fixed ios laucn screen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m0s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
2026-01-22 22:34:36 +01:00
6060afc543 Renamed package & bundle identifier
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 1m58s
Pull Request Pipeline / lint (pull_request) Successful in 2m18s
2026-01-22 22:13:12 +01:00
ac6399d707 Merge branch 'development' into documentation/131-app-name-icon-und-design 2026-01-22 21:46:11 +01:00
8ee2b6cb06 Updated version number [skip ci] 2026-01-20 14:55:05 +00:00
b14b7733ca Merge pull request 'Push Pipeline Error' (#181) from hotfix/push-pipeline-error into development
All checks were successful
Push Pipeline / test (push) Successful in 2m7s
Push Pipeline / format (push) Successful in 2m8s
Push Pipeline / update_version (push) Successful in 5s
Reviewed-on: #181
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
2026-01-20 14:52:47 +00:00
bc51b23563 Updated ref names
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m13s
Pull Request Pipeline / test (pull_request) Successful in 2m22s
2026-01-20 12:03:31 +01:00
057f8c1d58 Changed workflow back to prod mode 2026-01-20 12:00:33 +01:00
4c1c22123e Updated version number [skip ci] 2026-01-20 10:59:27 +00:00
e9929426e0 Removed development restriction
All checks were successful
Push Pipeline / test (push) Successful in 1m57s
Push Pipeline / update_version (push) Successful in 6s
Push Pipeline / format (push) Successful in 2m6s
2026-01-20 11:57:17 +01:00
eb404f3ef2 Fixed push pipeline
All checks were successful
Push Pipeline / test (push) Successful in 1m57s
Push Pipeline / update_version (push) Has been skipped
Push Pipeline / format (push) Successful in 2m17s
2026-01-20 11:54:15 +01:00
c7b4623198 Workflows um Format Stage erweitern (#175)
Some checks failed
Push Pipeline / test (push) Successful in 1m58s
Push Pipeline / update_version (push) Failing after 4s
Push Pipeline / format (push) Successful in 2m8s
Extend workflows with format stage

Co-authored-by: Gitea Actions [bot] <>
Reviewed-on: #175
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
2026-01-20 10:48:49 +00:00
ccd0c62e3c Added launchscreen
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m3s
Pull Request Pipeline / test (pull_request) Successful in 1m56s
2026-01-19 20:04:58 +01:00
9f71c22a56 Fixed pipeline
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m4s
Pull Request Pipeline / test (pull_request) Successful in 2m5s
2026-01-19 16:14:26 +01:00
87d7fbebcd Updated quick create button color
Some checks failed
Pull Request Pipeline / lint (pull_request) Failing after 2m6s
Pull Request Pipeline / test (pull_request) Successful in 2m25s
2026-01-19 16:07:20 +01:00
c625174017 Updated text color 2026-01-19 16:07:12 +01:00
53a33ca2e1 Updated app name
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
Pull Request Pipeline / test (pull_request) Successful in 1m56s
2026-01-18 22:58:28 +01:00
e1dd40a1c3 first app icon and theme update 2026-01-18 22:53:54 +01:00
45b11359b3 Merge pull request 'Maximale Input in Textfelder gefixtx' (#176) from hotifx/fix-input-length into development
Reviewed-on: #176
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-18 13:49:53 +00:00
57fb8dbcc8 Merge branch 'development' into hotifx/fix-input-length
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m11s
2026-01-18 13:46:25 +00:00
5a30538aa5 Merge pull request 'Text Overflows durch Tastatur' (#173) from bug/162-text-overflows-durch-tastatur into development
Reviewed-on: #173
2026-01-18 11:23:04 +00:00
1e18105ce0 Merge remote-tracking branch 'origin/development' into bug/162-text-overflows-durch-tastatur
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
# Conflicts:
#	pubspec.yaml
2026-01-18 11:45:50 +01:00
9ce2ca0ceb Merge pull request 'Match braucht Game' (#174) from enhancement/103-match-braucht-game into development
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m15s
Pull Request Pipeline / lint (pull_request) Successful in 2m15s
Reviewed-on: #174
2026-01-17 22:03:55 +00:00
39e6e485ac remove newline
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-17 22:54:22 +01:00
e9633a898c bump version 2026-01-17 22:54:18 +01:00
94c3bad02b Removed counter
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m11s
2026-01-17 22:41:02 +01:00
ed2d672dee Implemented maxLength attribute 2026-01-17 22:40:56 +01:00
449639df0e Merge remote-tracking branch 'origin/development' into enhancement/103-match-braucht-game
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
# Conflicts:
#	pubspec.yaml
2026-01-17 21:35:42 +01:00
a713ccb59c Merge pull request 'Gruppenprofile implementieren' (#172) from feature/169-gruppenprofile-implementieren into development
Reviewed-on: #172
2026-01-17 20:34:26 +00:00
f5924c4758 adjusted spacing & changed runAlignment to alignment
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m20s
2026-01-17 21:27:22 +01:00
5c9f44e947 bump build nr
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-17 21:12:53 +01:00
5f987806d6 remove ruleset 2026-01-17 21:12:43 +01:00
92c62000af remove margin from title_description_list_tile.dart 2026-01-17 21:12:17 +01:00
babe74f2be Merge remote-tracking branch 'origin/feature/169-gruppenprofile-implementieren' into feature/169-gruppenprofile-implementieren
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m15s
Pull Request Pipeline / lint (pull_request) Successful in 2m16s
2026-01-17 15:21:13 +01:00
8a38b9c3ea Updated dateformat to localize 2026-01-17 15:18:56 +01:00
ddd0a5d8bd Updated dateformat to localize
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m11s
Pull Request Pipeline / lint (pull_request) Successful in 2m12s
2026-01-17 15:17:33 +01:00
be9d1b52d2 Merge branch 'development' into feature/169-gruppenprofile-implementieren
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m19s
Pull Request Pipeline / lint (pull_request) Successful in 2m21s
# Conflicts:
#	pubspec.yaml
2026-01-17 14:56:26 +01:00
514e0f8064 Changed date format
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m21s
Pull Request Pipeline / lint (pull_request) Successful in 2m26s
2026-01-17 14:54:06 +01:00
ff47ef38c1 Changed alignment 2026-01-17 14:51:12 +01:00
fa2706395c Merge remote-tracking branch 'origin/development' into bug/162-text-overflows-durch-tastatur
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
# Conflicts:
#	pubspec.yaml
2026-01-17 14:44:49 +01:00
2d1ac3a17c Merge pull request 'Konstanten für Länge von Namen' (#170) from enhancement/158-konstanten-für-länge-von-namen into development
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
Reviewed-on: #170
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-17 13:42:50 +00:00
4fd8d2129b Merge remote-tracking branch 'origin/enhancement/158-konstanten-für-länge-von-namen' into enhancement/158-konstanten-für-länge-von-namen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m14s
Pull Request Pipeline / lint (pull_request) Successful in 2m18s
2026-01-17 14:35:44 +01:00
db41f40a52 Edited comment 2026-01-17 14:35:21 +01:00
0e747710ab Edited comment
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-17 14:33:24 +01:00
9f44f02a35 Changed length to 32 2026-01-17 14:32:25 +01:00
3addaa0f9d Disable resizing when using keyboard
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m7s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-17 14:22:45 +01:00
abb0fcbbd6 Updated file name
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-17 00:57:28 +01:00
fc6eb2b9cf Added comments
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m7s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-17 00:52:13 +01:00
6a0896d818 Implemented ColoredIconContainer 2026-01-17 00:52:09 +01:00
a8129eb134 Implemented first version of group profile 2026-01-16 23:34:47 +01:00
783f772da1 Implemented constants
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m41s
Pull Request Pipeline / lint (pull_request) Successful in 2m45s
2026-01-16 21:52:58 +01:00
b7930d5e2e Adjusted constant name 2026-01-16 21:41:42 +01:00
1b709707b5 Added constants 2026-01-16 21:41:20 +01:00
cef02956b1 Merge pull request 'Class Doc-Comment zu Konstruktor verschieben' (#167) from refactoring/157-class-doc-comment-zu-konstruktor-verschieben into development
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m12s
Pull Request Pipeline / lint (pull_request) Successful in 2m12s
Reviewed-on: #167
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-14 18:10:21 +00:00
a479cea5be Merge branch 'development' into refactoring/157-class-doc-comment-zu-konstruktor-verschieben
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-14 09:12:03 +00:00
bbd41a65df Merge pull request 'Neues Popup Design' (#152) from feature/129-neues-popup-design into development
Reviewed-on: #152
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
Reviewed-by: Felix Kirchner <felix.kirchner.fk@gmail.com>
2026-01-13 22:01:05 +00:00
2cadab004d Merge cleaning
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m2s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
2026-01-13 22:58:35 +01:00
6a3e184a95 Merge branch 'development' into feature/129-neues-popup-design
# Conflicts:
#	lib/presentation/views/main_menu/settings_view/settings_view.dart
2026-01-13 22:57:19 +01:00
5350113ee1 Updated doc strings 2026-01-13 22:54:19 +01:00
bb79eecdfd Updated and added comments
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-13 22:24:31 +01:00
db51990695 Revert "add background color option to AnimatedDialogButton"
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m19s
This reverts commit 4019ed083f.
2026-01-13 22:17:38 +01:00
e483fc38f3 Merge pull request 'Anzeigefehler mit Snackbar' (#166) from bug/155-anzeigefehler-mit-snackbar into development
Reviewed-on: #166
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
2026-01-13 21:09:21 +00:00
4019ed083f add background color option to AnimatedDialogButton
Some checks failed
Pull Request Pipeline / lint (pull_request) Failing after 2m47s
Pull Request Pipeline / test (pull_request) Successful in 2m38s
2026-01-13 21:53:45 +01:00
7be0b96491 Added hiding to prevent stacking snackbars
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m19s
Pull Request Pipeline / lint (pull_request) Successful in 2m23s
2026-01-13 21:45:22 +01:00
efdb5e0361 Fixed snackbar 2026-01-13 21:44:38 +01:00
82ad2b74f8 refactor: enhance documentation and fix punctuation in localization strings
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-13 21:16:59 +01:00
4161e1e88b add docs to custom_alert_dialog.dart
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-13 20:50:54 +01:00
d662680a34 fix dart analysis errors
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m9s
Pull Request Pipeline / lint (pull_request) Successful in 2m16s
2026-01-13 20:46:42 +01:00
b69d2784df Merge remote-tracking branch 'origin/development' into feature/129-neues-popup-design
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m16s
Pull Request Pipeline / lint (pull_request) Failing after 2m38s
2026-01-13 20:45:47 +01:00
d7f4b1c227 seperate button into widget & change to agreed design 2026-01-13 20:41:03 +01:00
7a1752f773 Merge pull request 'TopCenteredMessage Anzeigefehler' (#161) from bug/159-topcenteredmessage-anzeigefehler into development
Reviewed-on: #161
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-13 14:30:53 +00:00
57357f8aad Merge branch 'development' into bug/159-topcenteredmessage-anzeigefehler
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m12s
# Conflicts:
#	pubspec.yaml
2026-01-13 15:25:59 +01:00
806ed200f7 Merge pull request 'Neues NavBar Design' (#143) from enhancement/138-neues-navbar-design into development
Reviewed-on: #143
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
2026-01-13 14:11:05 +00:00
d5bd0bca5f Fixed displayment bug with TopCenteredMessage
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m11s
2026-01-12 23:19:37 +01:00
5da12939cb Updated Dart version
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m12s
Pull Request Pipeline / lint (pull_request) Successful in 2m18s
2026-01-12 20:30:57 +01:00
0d20b5847f Updated versions
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m19s
2026-01-12 20:29:18 +01:00
973e327232 Merge remote-tracking branch 'origin/development' into enhancement/81-readme-vervollstaendigen 2026-01-12 20:29:06 +01:00
8c41f6a255 Merge remote-tracking branch 'origin/development' into enhancement/138-neues-navbar-design
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
# Conflicts:
#	pubspec.yaml
2026-01-12 20:21:35 +01:00
70f570489a Merge pull request 'Einstellungen ausgestalten' (#153) from feature/151-einstellungen-ausgestalten into development
Reviewed-on: #153
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-12 19:00:26 +00:00
fa7740101b Small changes on navbar
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-12 19:34:35 +01:00
5aa2a335e3 Merge branch 'development' into enhancement/138-neues-navbar-design
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-12 18:23:26 +00:00
80e601c10e Changed formatting of link displayment
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m11s
2026-01-12 19:21:04 +01:00
2124c523bc Fixed link issue on android 2026-01-12 19:20:52 +01:00
7d0da81cf5 Updated license tile sizes 2026-01-12 19:09:31 +01:00
cd3a5c2a49 Merge branch 'development' into feature/151-einstellungen-ausgestalten
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m18s
Pull Request Pipeline / test (pull_request) Successful in 2m18s
2026-01-12 17:34:56 +00:00
4628e96456 Merge pull request 'Hotfix: Textarea in Bug Template umgestaltet' (#160) from hotifx/issue-template-fix into development
Reviewed-on: #160
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-12 17:34:48 +00:00
da61f45e8e Merge remote-tracking branch 'origin/development' into enhancement/138-neues-navbar-design
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m13s
Pull Request Pipeline / lint (pull_request) Successful in 2m21s
2026-01-12 17:56:20 +01:00
9344f8212c Updated version number
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m14s
Pull Request Pipeline / lint (pull_request) Successful in 2m22s
2026-01-12 17:55:52 +01:00
6d42d59bad Removed area
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m21s
2026-01-12 17:49:35 +01:00
46118c274c Updated textarea in template
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m14s
Pull Request Pipeline / lint (pull_request) Successful in 2m31s
2026-01-12 17:46:58 +01:00
ab06662397 Merge branch 'development' into feature/129-neues-popup-design
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m0s
Pull Request Pipeline / lint (pull_request) Successful in 2m12s
2026-01-12 16:42:05 +00:00
679f4c94d9 Updated button size
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-12 17:33:20 +01:00
8bf2b9e3dd Updated navbar item color & size 2026-01-12 17:31:30 +01:00
cde40ef293 Updated buttons in main menu
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 1m57s
Pull Request Pipeline / lint (pull_request) Successful in 2m5s
2026-01-12 17:23:43 +01:00
0fb6208345 Created new buttons for the main menu 2026-01-12 17:23:34 +01:00
ec5a686f90 Merge pull request 'Issue-Templates aktualisiert' (#154) from setup/update-issue-and-pr-templates into development
Reviewed-on: #154
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-12 16:22:04 +00:00
f0c6dd8401 Adjusted container size and padding
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m9s
Pull Request Pipeline / lint (pull_request) Successful in 2m17s
2026-01-12 16:20:36 +01:00
7bdad57cc8 Removed old code 2026-01-12 16:18:13 +01:00
5da1b6eecb Removed expanded widget 2026-01-12 16:17:18 +01:00
cdafd4bb6f Made links clickable 2026-01-12 16:16:59 +01:00
6aee055df2 Removed whitespace 2026-01-12 16:04:37 +01:00
6d9871a5f0 Removed Web
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-12 16:03:09 +01:00
4bbbcdd93f Updated label
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m11s
2026-01-12 00:47:43 +01:00
fed5c55dd4 Updated placeholder
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m7s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-12 00:18:51 +01:00
c157644b44 Folder fix 2/2
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-11 20:00:51 +01:00
5a5898787f Folder fix 1/2 2026-01-11 20:00:38 +01:00
9d3a45c01d Updated PR Template
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-11 19:57:02 +01:00
485ac87fdb Added new yaml issue templates
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m14s
2026-01-11 19:41:45 +01:00
1ebcfc9e57 implement animation
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-11 17:21:05 +01:00
758f1e6c3a .gitea/PULL_REQUEST_TEMPLATE.md aktualisiert 2026-01-11 16:14:43 +00:00
22ce742d43 change button alignment & remove InkWell Animation
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-11 17:14:28 +01:00
7fc4bbfb13 Added placeholder setting tiles for legal
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m13s
Pull Request Pipeline / lint (pull_request) Successful in 2m14s
2026-01-11 17:09:05 +01:00
857e05127d Updated arb files 2026-01-11 16:55:28 +01:00
86982ada0f Updated license file
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-11 16:48:08 +01:00
e51bf3eabb Updated spacing 2026-01-11 16:48:02 +01:00
d7f08c5f50 Updated license tile and adjusted settings tile accordingly
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m37s
Pull Request Pipeline / test (pull_request) Successful in 2m36s
2026-01-11 16:33:56 +01:00
9248284292 Removed double patterns
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m13s
Pull Request Pipeline / lint (pull_request) Successful in 2m22s
2026-01-11 16:32:19 +01:00
9a00e543d7 Implemented adaptive mail icon 2026-01-11 16:00:19 +01:00
5ea7797b3e Moved files 2026-01-11 15:44:05 +01:00
c8b76ae0ea Updated l10n config 2026-01-11 15:43:46 +01:00
c5fa540c5f Added licenses to settings 2026-01-11 15:43:36 +01:00
3ceae8341b add const
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m30s
Pull Request Pipeline / lint (pull_request) Successful in 2m31s
2026-01-11 15:26:50 +01:00
76ce3af643 implement custom alert dialog
Some checks failed
Pull Request Pipeline / lint (pull_request) Failing after 2m34s
Pull Request Pipeline / test (pull_request) Successful in 2m33s
2026-01-11 15:26:10 +01:00
ab20bd764b implement draft blur navbar
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m0s
Pull Request Pipeline / lint (pull_request) Failing after 2m6s
2026-01-11 11:30:00 +01:00
8ca4e3210e remove button in match view for testing 2026-01-11 11:28:21 +01:00
e384230a0b Updated gradle version 2026-01-10 23:37:54 +01:00
1e4fd2a164 Added ios permissions for web 2026-01-10 23:37:47 +01:00
a491427a1d Added license tile, social media icons & copyright 2026-01-10 23:37:33 +01:00
a480530919 Merge branch 'development' into enhancement/138-neues-navbar-design
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m31s
Pull Request Pipeline / lint (pull_request) Successful in 2m34s
# Conflicts:
#	pubspec.yaml
2026-01-10 22:18:54 +01:00
906c8d8450 Merge pull request 'Versions- und Build Nr in Einstellungen' (#149) from enhancement/146-versions-und-build-nr-in-einstellungen into development
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 1m58s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
Reviewed-on: #149
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-10 20:56:25 +00:00
000bdc8cbc Merge remote-tracking branch 'origin/development' into enhancement/146-versions-und-build-nr-in-einstellungen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Successful in 2m6s
2026-01-10 21:54:09 +01:00
497f30421d Updated string
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m9s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-10 21:29:45 +01:00
adedb85eb2 Merge pull request 'Gruppen nach Spielern durchsuchen' (#147) from enhancement/136-gruppen-nach-spielern-durchsuchen into development
Reviewed-on: #147
Reviewed-by: Felix Kirchner <felix.kirchner.fk@gmail.com>
2026-01-10 20:27:53 +00:00
2a72332bcd Updated error string
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-10 21:23:28 +01:00
7aa41abe61 Implemented version number 2026-01-10 16:20:21 +01:00
e1263d51ad Merge branch 'development' into enhancement/136-gruppen-nach-spielern-durchsuchen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m0s
Pull Request Pipeline / lint (pull_request) Successful in 2m3s
2026-01-10 15:06:17 +00:00
8791b5296e Merge pull request 'MatchSummaryTile durch MatchTile ersetzen' (#142) from enhancement/137-matchsummarytile-durch-matchtile-ersetzen into development
Reviewed-on: #142
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-10 15:05:42 +00:00
f2a4327166 Implemented adaptive page route
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m6s
2026-01-10 15:55:19 +01:00
d34990ed50 Removed print 2026-01-10 15:54:41 +01:00
2ef671884d New method for specific winner refreshing 2026-01-10 15:54:11 +01:00
2ef8eb6534 Made winner field non final 2026-01-10 15:53:31 +01:00
6f0e5ba5c2 add ability to search for groups members in choose_group_view.dart
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m6s
2026-01-10 15:19:18 +01:00
1c07346aaf Updated version
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m20s
Pull Request Pipeline / lint (pull_request) Successful in 2m28s
2026-01-10 15:16:09 +01:00
830a64b5dd Small changes
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m29s
Pull Request Pipeline / lint (pull_request) Successful in 2m32s
2026-01-10 15:15:52 +01:00
9221f64fa5 Merge branch 'development' into enhancement/137-matchsummarytile-durch-matchtile-ersetzen
# Conflicts:
#	lib/presentation/views/main_menu/match_view/match_view.dart
#	pubspec.yaml
2026-01-10 15:15:14 +01:00
c4f6749882 Fixed state issue in home view
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m2s
Pull Request Pipeline / lint (pull_request) Successful in 2m3s
2026-01-10 15:13:56 +01:00
14a043785e Merge pull request 'PageRoutes adaptable machen' (#145) from enhancement/128-PageRoutes-adaptable-machen into development
Reviewed-on: #145
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
Reviewed-by: Felix Kirchner <felix.kirchner.fk@gmail.com>
2026-01-10 14:06:10 +00:00
9eb9c0eb7f remove unneccessary import
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m18s
Pull Request Pipeline / lint (pull_request) Successful in 2m18s
2026-01-10 15:02:01 +01:00
54ec865f04 fix merge issues
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m14s
Pull Request Pipeline / lint (pull_request) Failing after 2m23s
2026-01-10 15:00:33 +01:00
d5e7a17127 Merge remote-tracking branch 'origin/development' into enhancement/128-PageRoutes-adaptable-machen
# Conflicts:
#	lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart
2026-01-10 14:57:33 +01:00
275f64b296 Merge pull request 'SnackBar Sichtbarkeit' (#144) from bug/130-Snackbar-Sichtbarkeit into development
Reviewed-on: #144
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
Reviewed-by: Felix Kirchner <felix.kirchner.fk@gmail.com>
2026-01-10 13:54:57 +00:00
97ca62b083 refactor: rename AdaptivePageRoute to adaptivePageRoute for lowerCamelCase
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m13s
Pull Request Pipeline / test (pull_request) Successful in 2m18s
2026-01-10 14:52:59 +01:00
32fb1550ff Merge remote-tracking branch 'origin/enhancement/128-PageRoutes-adaptable-machen' into enhancement/128-PageRoutes-adaptable-machen
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Failing after 2m10s
# Conflicts:
#	lib/main.dart
2026-01-10 14:44:53 +01:00
d67972624e replaced ternary operator solution with custom adaptive_page_route.dart 2026-01-10 14:44:33 +01:00
595cf6ead0 Changed skeleton data from 3 to 2 tiles
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m2s
Pull Request Pipeline / lint (pull_request) Successful in 2m4s
2026-01-10 14:20:09 +01:00
6faafe9fab Changed android page transition to Android U transition
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-10 14:16:16 +01:00
66b90aac25 Fixed issue with android page transition
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Successful in 2m5s
2026-01-10 14:09:08 +01:00
d22855fc1a Fixed match view tile width
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m6s
2026-01-10 13:57:54 +01:00
1be86bc3c5 Added theme box design 2026-01-10 13:57:46 +01:00
db3e8215fa Added ios swipe back gesture
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Successful in 2m6s
2026-01-10 13:42:09 +01:00
2c4cef76d8 Reimplemented info tile & match tile into it
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Successful in 2m3s
2026-01-09 23:55:29 +01:00
a4ef9705f9 made PageRoutes adapt to os, default to MaterialPageRoute
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
2026-01-09 22:22:26 +01:00
799c849570 wrap every view that uses a snackbar in ScaffoldMessenger
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Successful in 2m6s
2026-01-09 21:55:58 +01:00
799b7d8403 Implemented new nav bar with selected animation
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
2026-01-09 21:12:09 +01:00
644728a9df Removed unneccesary code
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Successful in 2m4s
2026-01-09 20:19:23 +01:00
afb7a5f1d4 MatchTiles only show when recent games are available 2026-01-09 20:15:50 +01:00
3d510d5b3d Implemented compact mode for match tiles 2026-01-09 20:04:10 +01:00
a9d2325eee Implemented match tile in home view 2026-01-09 19:50:51 +01:00
349ff948de Modified match tile 2026-01-09 19:50:44 +01:00
c2394c3733 Merge pull request 'MVP-Refactoring' (#139) from refactoring/68-mvp-refactoring into development
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
Reviewed-on: #139
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-08 20:24:01 +00:00
88766652b9 Small loc corrections
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m21s
2026-01-08 21:21:33 +01:00
2a1573ee2d Remove Whitespace
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m12s
Pull Request Pipeline / lint (pull_request) Successful in 2m15s
2026-01-08 21:15:01 +01:00
cfb07bfe28 Updated localizations
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m14s
Pull Request Pipeline / lint (pull_request) Successful in 2m18s
2026-01-08 21:13:24 +01:00
d741990f2f Fixed navbar issue
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-08 21:07:30 +01:00
76121eb4fb Updated localizations
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m18s
2026-01-07 17:30:19 +01:00
d5ee6449b0 Updated localizations
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m11s
Pull Request Pipeline / test (pull_request) Successful in 2m10s
2026-01-07 17:02:06 +01:00
7d9757abb6 Merge branch 'development' into refactoring/68-mvp-refactoring
# Conflicts:
#	lib/l10n/generated/app_localizations.dart
#	lib/l10n/generated/app_localizations_de.dart
#	lib/l10n/generated/app_localizations_en.dart
2026-01-07 16:58:15 +01:00
d94d51c6ea Merge pull request 'change settings icons to filled and rounded' (#140) from enhancement/135-settings-icons into development
Reviewed-on: #140
Reviewed-by: Felix Kirchner <felix.kirchner.fk@gmail.com>
2026-01-07 14:23:27 +00:00
gelbeinhalb
803cf8a972 update app_localizations
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m0s
Pull Request Pipeline / lint (pull_request) Successful in 2m6s
2026-01-07 15:17:13 +01:00
gelbeinhalb
13be75a65b change settings icons to filled and rounded
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m5s
2026-01-07 15:12:12 +01:00
aef12bd65a Refactored several theme settings into custom theme
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-07 14:54:53 +01:00
02d79574dd Simplified app bar logic in views 2026-01-07 14:43:13 +01:00
fdd0e7579a Updated game title process 2026-01-07 14:35:07 +01:00
cd2770be26 Removed unnessecary setStates 2026-01-07 14:33:52 +01:00
e196d6e5ef Corrected strings 2026-01-07 14:32:40 +01:00
bc3beac866 Replaced Matches with Spiele 2026-01-07 14:25:14 +01:00
aa936a938d Corrected translation 2026-01-07 14:23:47 +01:00
2811ea892e Added dispose & formatting 2026-01-07 14:15:23 +01:00
4b1d3923a0 Optimized ruleset touples 2026-01-07 14:11:14 +01:00
0f824bb30a Optimized statistics tile 2026-01-07 14:10:09 +01:00
21c74b74bc Refactored components 2026-01-07 14:05:19 +01:00
6e45e9435b Refactored views 2026-01-07 13:27:39 +01:00
a78614851b Added constants class 2026-01-07 13:02:09 +01:00
8df9a27dc7 Merge pull request 'Lokalisierung implementieren' (#112) from feature/100-lokalisierung-hinzufügen into development
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
Reviewed-on: #112
Reviewed-by: Felix Kirchner <felix.kirchner.fk@gmail.com>
2026-01-07 11:30:11 +00:00
52180eeb6d Merge branch 'development' into feature/100-lokalisierung-hinzufügen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m2s
Pull Request Pipeline / lint (pull_request) Successful in 2m4s
2026-01-07 11:28:04 +00:00
0893667e41 Merge pull request 'Konsistenzfehler im JSON Vermeiden' (#125) from enhancement/70-konsistenzfehler-im-json-vermeiden into development
Reviewed-on: #125
Reviewed-by: Felix Kirchner <felix.kirchner.fk@gmail.com>
2026-01-07 11:27:44 +00:00
1dc5286c6b change double to single quotes
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m2s
Pull Request Pipeline / lint (pull_request) Successful in 2m6s
2026-01-07 12:08:21 +01:00
gelbeinhalb
f140ebae2e Merge branch 'enhancement/70-konsistenzfehler-im-json-vermeiden' of git.yannick-weigert.de:liquid-development/game-tracker into enhancement/70-konsistenzfehler-im-json-vermeiden
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m16s
Pull Request Pipeline / lint (pull_request) Successful in 2m22s
2026-01-07 12:00:33 +01:00
a487e4071f Sort files
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m17s
Pull Request Pipeline / lint (pull_request) Failing after 2m24s
2026-01-07 12:00:25 +01:00
gelbeinhalb
99044c0ea0 remove unnecessary check 2026-01-07 12:00:25 +01:00
69effa2b7d change winner localization to not include placeholder 2026-01-07 11:49:10 +01:00
5db008c274 Merge branch 'development' into feature/100-lokalisierung-hinzufügen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m12s
2026-01-05 15:04:19 +00:00
ec94e12ed7 implement changes
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m27s
Pull Request Pipeline / lint (pull_request) Successful in 2m33s
2026-01-03 15:38:25 +01:00
9fc308554c Merge remote-tracking branch 'origin/development' into feature/100-lokalisierung-hinzufügen
# Conflicts:
#	lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart
#	lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart
#	lib/presentation/views/main_menu/match_view/create_match/choose_ruleset_view.dart
2026-01-03 14:42:53 +01:00
31abb7f1e4 Merge remote-tracking branch 'origin/development' into feature/100-lokalisierung-hinzufügen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m7s
Pull Request Pipeline / lint (pull_request) Successful in 2m12s
# Conflicts:
#	lib/presentation/widgets/player_selection.dart
2026-01-02 21:32:40 +01:00
132966f3d2 increase title_description_list_tile width to make german translation fully visible
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m9s
Pull Request Pipeline / lint (pull_request) Successful in 2m23s
2026-01-02 21:28:46 +01:00
77095725de fix game choose view highlighting not working 2026-01-02 21:26:54 +01:00
678ab90af3 removed unneccessary null assertion operators
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m6s
2026-01-02 21:16:13 +01:00
a038c22ba6 removed else in fallback 2026-01-02 21:13:00 +01:00
22fcff73bd configure en locale as fallback
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Failing after 2m7s
2026-01-02 21:11:37 +01:00
3c22b084d6 made getters non nullable and removed all null assertion operators
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m10s
Pull Request Pipeline / lint (pull_request) Failing after 2m11s
2026-01-02 19:01:44 +01:00
7f923f6dff Merge remote-tracking branch 'origin/development' into feature/100-lokalisierung-hinzufügen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m15s
Pull Request Pipeline / lint (pull_request) Successful in 2m15s
# Conflicts:
#	lib/presentation/views/main_menu/home_view.dart
#	lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart
#	lib/presentation/views/main_menu/match_view/match_view.dart
2026-01-02 18:49:34 +01:00
4451f9e2a9 Merge branch 'development' into feature/100-lokalisierung-hinzufügen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m34s
Pull Request Pipeline / lint (pull_request) Successful in 2m36s
2026-01-01 21:43:19 +00:00
f22595e678 Add localization for 'none_group' option in English and German
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-01 18:44:43 +01:00
25fe10eb9a added missing square bracket 2026-01-01 18:44:28 +01:00
5a318f9760 Merge remote-tracking branch 'origin/feature/100-lokalisierung-hinzufügen' into feature/100-lokalisierung-hinzufügen 2026-01-01 18:44:03 +01:00
0051990168 Remove auto_localize dependency from pubspec.yaml 2026-01-01 18:43:06 +01:00
7e21c65026 Merge branch 'development' into feature/100-lokalisierung-hinzufügen
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m11s
Pull Request Pipeline / lint (pull_request) Failing after 2m35s
2026-01-01 17:33:47 +00:00
cc23c03f6b Add localization for 'not available' message in English and German
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Failing after 2m15s
2026-01-01 18:32:01 +01:00
d77b5f20f9 Update statistics_view.dart to use localized 'not available' message for players 2026-01-01 18:31:53 +01:00
3c3bf506cb Add TODO for implementing quick create functionality in home_view.dart 2026-01-01 18:31:37 +01:00
8afba5680b Add localization for no statistics available message and removed dots from all messages
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m11s
2026-01-01 18:18:14 +01:00
7103765054 Update statistics_view.dart to use localized message no statistics available instead of no data available 2026-01-01 18:17:53 +01:00
bc01a6de9a Add localization for search input fields in English and German
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m26s
Pull Request Pipeline / test (pull_request) Successful in 2m15s
2026-01-01 18:10:44 +01:00
ad87dca674 add generated files
Some checks failed
Pull Request Pipeline / lint (pull_request) Failing after 2m47s
Pull Request Pipeline / test (pull_request) Successful in 2m27s
2026-01-01 18:10:24 +01:00
633a21d829 Updated search bar hint texts to use localization for group and player searches
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m38s
Pull Request Pipeline / lint (pull_request) Failing after 2m54s
2026-01-01 18:10:08 +01:00
81f63c1c07 made statistics_view.dart use localization and fix merge error
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m41s
Pull Request Pipeline / lint (pull_request) Successful in 2m42s
2026-01-01 18:09:50 +01:00
f97c341b81 added devtools_options.yaml to .gitignore
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m16s
Pull Request Pipeline / lint (pull_request) Successful in 2m23s
2026-01-01 17:43:24 +01:00
7bf03ec388 added auto-generated files to git
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m26s
Pull Request Pipeline / lint (pull_request) Successful in 2m26s
2026-01-01 17:42:38 +01:00
0bfaba4225 Refactored German localization strings by removing redundant entries
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m25s
2026-01-01 17:40:36 +01:00
86dbc9afb0 Optimized ruleset selection in CreateMatchView
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m2s
Pull Request Pipeline / lint (pull_request) Successful in 2m6s
2026-01-01 00:42:21 +01:00
73aea0d0f5 added missings consts
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Failing after 2m8s
2026-01-01 00:39:15 +01:00
ed9e3af768 merged dev into branch and fixed missing comma
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Failing after 2m5s
2026-01-01 00:34:55 +01:00
d93ead40b6 Merge remote-tracking branch 'origin/development' into feature/100-lokalisierung-hinzufügen
# Conflicts:
#	lib/presentation/views/main_menu/statistics_view.dart
2026-01-01 00:32:10 +01:00
534d19efc3 Implemented localization across the application.
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Failing after 2m22s
2026-01-01 00:21:09 +01:00
d9a26a8cf7 Added English and German localization files containing all strings used in app 2026-01-01 00:20:44 +01:00
8e05e9d61b Added l10n.yaml for localization configuration 2026-01-01 00:20:22 +01:00
179ac2fe21 Update localization dependencies and enable code generation 2026-01-01 00:20:16 +01:00
0f79495775 Implemented badges
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m25s
Pull Request Pipeline / test (pull_request) Successful in 2m27s
2025-12-06 15:42:02 +01:00
136 changed files with 13132 additions and 1782 deletions

View File

@@ -1,35 +0,0 @@
---
name: Bug report
about: Erstelle eine Meldung für etwas, das nicht Funktioniert, wie es soll.
title: ''
labels: 'Task/Bug'
assignees: ''
---
# Bug Report
## Beschreibung
[Eine klare und prägnante Beschreibung des Bugs]
## Schritte zur Reproduktion
1. Schritt 1
2. Schritt 2
3. ...
## Erwartetes Verhalten
[Was hätte passieren sollen]
## Tatsächliches Verhalten
[Was tatsächlich passiert ist]
## Screenshots/Protokolle
[Falls zutreffend, füge Screenshots, Error Logs oder Stack Traces hinzu]
## Umgebung
- Plattform: Android, iOS, Web
- OS: [z. B. iOS 18.5, Android 14]
- Flutter Version: [z.B. 3.35.6]
## Verwandte Issues
[Verweisen Sie auf ähnliche Issues oder PRs]

View File

@@ -1,22 +0,0 @@
---
name: Enhancement
about: Enhancements for current features
title: ''
labels: 'Task\Enhancement'
assignees: ''
---
# Enhancement
## Aktuelles Verhalten
[Beschreibe die bestehende Funktionalität]
## Einschränkungen/Probleme
[Was sind die aktuellen Mängel?]
## Vorgeschlagene Verbesserung
[Wie kann das Problem bzw. die Einschränkung verbessert werden?]
## Zugehörige Issues
[Links zu verwandten oder blockierenden Issues]

View File

@@ -1,19 +0,0 @@
---
name: Feature
about: Neues Feature für die App
title: ''
labels: 'Task\Feature'
assignees: ''
---
# Feature
## Beschreibung
[Ausführliche Erläuterung der vorgeschlagenen Funktion]
## Vorgeschlagene Lösung
[Beschreibe, wie die Feature funktionieren soll]
## Zugehörige Issues
[Links zu verwandten oder blockierenden Issues]

View File

@@ -1,16 +0,0 @@
# [PR Titel]
**Zugehörige Issue(s):**
Closes `<issue-no>`
## Beschreibung
*Eine klare und prägnante Übersicht über die vorgenommenen Änderungen. Erläutere nicht nur das was gemacht wurde, sondern auch warum.*
## Änderungen
- [ ] Neue Funktion X hinzugefügt
- [ ] Bug in Komponente Y behoben
- [ ] Modul Z für bessere Leistung refactored
- [ ] Dependencies aktualisiert
## Zusätzliche Anmerkungen
*Gibt es zusätzlichen Kontext, Einschränkungen oder Informationen, die Reviewer wissen sollten?*

View File

@@ -0,0 +1,53 @@
name: Bug Report
about: Erstelle eine Bug Report
labels: 'Task/Bug'
title: ''
body:
- type: textarea
id: description
attributes:
label: Beschreibung
description: Beschreibe klar und pregnant das Fehlerverhalten
placeholder: |
- Was genau ist das unerwünschte Verhalten?
- Welche Auswirkungen hat der Fehler?
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Schritte zur Reproduktion
description: Beschreibe, wie der Fehler reproduziert werden kann
placeholder: |
- 1. Schritt 1
- 2. Schritt 2
- 3. ...
- type: dropdown
id: enviroment
attributes:
label: Umgebung
description: Gebe an, auf welchen Platformen dieser Fehler auftritt
list: false
multiple: true
options: ['Android', 'iOS']
- type: textarea
id: solution
attributes:
label: Lösungsidee
description: Beschreibe, wie das Problem bzw. gelöst werden kann
placeholder: |
- Button X ändern, sodass ...
- Funktion X so erweitern, dass ...
- Design anpassen, sodass ...
- type: textarea
attributes:
label: Verwandte Issues
description: Verweise auf ähnliche Issues oder PRs
placeholder: |
- Knüpft an Issue #35 an
- Ersetzt Issue #12
- Brauch Implementierung von #43

View File

@@ -0,0 +1,36 @@
name: Enhancement
about: Erstelle ein Enhancement-Ticket
labels: 'Task/Enhancement'
title: ''
body:
- type: textarea
id: description
attributes:
label: Aktuelles Verhalten
description: Beschreibe, wie die Funktionalität aktuell gestaltet ist
placeholder: |
- Aktuell macht Button X folgendes ...
- Das Problem ist, dass ...
validations:
required: true
- type: textarea
id: solution
attributes:
label: Vorgeschlagene Verbesserung
description: Beschreibe, wie das Problem bzw. die Einschränkung verbessert werden kann
placeholder: |
- Button X ändern, sodass ...
- Funktion X so erweitern, dass ...
- Design anpassen, sodass ...
validations:
required: true
- type: textarea
attributes:
label: Zugehörige Issues
description: Links zu verwandten oder blockierenden Issues
placeholder: |
- Knüpft an Issue #35 an
- Ersetzt Issue #12
- Brauch Implementierung von #43

View File

@@ -0,0 +1,36 @@
name: Feature
about: Erstelle ein Feature-Ticket
labels: 'Task/Feature'
title: ''
body:
- type: textarea
id: description
attributes:
label: Beschreibung
description: Ausführliche Erläuterung der vorgeschlagenen Funktion
placeholder: |
- Welchen Zweck erfüllt das Feature?
- Welches Problem löst das Feature?
- Wer profitiert davon?
- Warum ist es wichtig?
validations:
required: true
- type: textarea
id: solution
attributes:
label: Vorgeschlagene Lösung
description: Beschreibe, wie das Feature funktionieren soll
placeholder: |
- Neues Widget, das folgendermaßen aussieht ...
- Neue Ansicht, die folgende Inhalte hat
- Neue Funktionsweise von Komponente XY
- type: textarea
attributes:
label: Zugehörige Issues
description: Links zu verwandten oder blockierenden Issues
placeholder: |
- Knüpft an Issue #35 an
- Ersetzt Issue #12
- Brauch Implementierung von #43

View File

@@ -0,0 +1,47 @@
name: Pull Request
about: Vorlage für Pull Requests
title: "WIP: [Name des Issues]"
body:
- type: input
id: related_issue
attributes:
label: Zugehörige Issue(s)
description: Issues welche mit diesem Pull Request geschlossen werden sollen
placeholder: "Closes #123"
validations:
required: true
- type: textarea
id: description
attributes:
label: Beschreibung
description: |
Eine klare und prägnante Zusammenfassung aller vorgenommenen Änderungen.
placeholder: |
Was wurde geändert?
validations:
required: true
- type: textarea
id: changes
attributes:
label: Änderungen
description: Liste alle Änderungen detailiert auf, die in diesem Pull Request vorgenommen wurden.
placeholder: |
- Neue Funktion X hinzugefügt
- Bug in Komponente X behoben
- Modul X für bessere Leistung refactored
- Dependencies aktualisiert
- type: textarea
id: additional_notes
attributes:
label: Zusätzliche Anmerkungen
description: |
Gibt es zusätzlichen Kontext, Einschränkungen oder Informationen,
die Reviewer wissen sollten?
placeholder: |
- Bekannte Einschränkungen
- Offene TODOs
- Hinweise für Reviewer

View File

@@ -10,22 +10,22 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
# Required for Flutter action
- name: Install jq - name: Install jq
run: | run: |
apt-get update apt-get update
apt-get install -y jq apt-get install -y jq
- name: Install Flutter (wget) - name: Set up Flutter
run: | uses: subosito/flutter-action@v2
wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.38.2-stable.tar.xz with:
tar xf flutter_linux_3.38.2-stable.tar.xz channel: stable
# Set Git safe directory for Flutter path flutter-version: 3.38.6
git config --global --add safe.directory "$(pwd)/flutter"
# Set Flutter path
echo "$(pwd)/flutter/bin" >> $GITHUB_PATH
- name: Get dependencies - name: Get dependencies
run: flutter pub get run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64
flutter pub get
- name: Analyze Formatting - name: Analyze Formatting
run: flutter analyze lib test run: flutter analyze lib test
@@ -36,22 +36,22 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install dependencies # Required for Flutter action
- name: Install jq
run: | run: |
apt-get update apt-get update
apt-get install -y jq apt-get install -y jq
- name: Install Flutter (wget) - name: Set up Flutter
run: | uses: subosito/flutter-action@v2
wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.38.2-stable.tar.xz with:
tar xf flutter_linux_3.38.2-stable.tar.xz channel: stable
# Set Git safe directory for Flutter path flutter-version: 3.38.6
git config --global --add safe.directory "$(pwd)/flutter"
# Set Flutter path
echo "$(pwd)/flutter/bin" >> $GITHUB_PATH
- name: Get dependencies - name: Get dependencies
run: flutter pub get run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64
flutter pub get
- name: Run tests - name: Run tests
run: flutter test run: flutter test

View File

@@ -7,44 +7,194 @@ on:
- "main" - "main"
jobs: jobs:
format: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: false # Needs bot user
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install dependencies - name: Setup Java (Temurin 17)
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: '17'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
# Required for Flutter action
- name: Install jq
run: | run: |
apt-get update apt-get update
apt-get install -y jq apt-get install -y jq
- name: Install Flutter (wget) - name: Set up Flutter
run: | uses: subosito/flutter-action@v2
wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.38.2-stable.tar.xz with:
tar xf flutter_linux_3.38.2-stable.tar.xz channel: stable
# Set Git safe directory for Flutter path flutter-version: 3.38.6
git config --global --add safe.directory "$(pwd)/flutter"
# Set Flutter path
echo "$(pwd)/flutter/bin" >> $GITHUB_PATH
- name: Get & upgrade dependencies - name: Get dependencies
run: | run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64
flutter pub get flutter pub get
flutter pub upgrade --major-versions
- name: Auto-format - name: Build APK
run: flutter build apk --release
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
# Required for Flutter action
- name: Install jq
run: | run: |
dart format lib apt-get update
apt-get install -y jq
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.38.6
- name: Get dependencies
run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64
flutter pub get
- name: Run tests
run: flutter test
update_version:
runs-on: ubuntu-latest
if: gitea.ref == 'refs/heads/development'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.BOT_TOKEN }}
ref: ${{ gitea.ref_name }}
- name: Increment version number
uses: stikkyapp/update-pubspec-version@v2
with:
strategy: 'patch'
path: './pubspec.yaml'
- name: Commit version update
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
git config --global user.name "Gitea Actions [bot]"
git config --global user.email "actions@yannick-weigert.de"
git config pull.rebase false
git pull origin ${{ gitea.ref_name }}
git add pubspec.yaml
git commit -m "Updated version number [skip ci]"
git push origin HEAD:${{ gitea.ref_name }}
generate_licenses:
runs-on: ubuntu-latest
needs: update_version
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.BOT_TOKEN }}
ref: ${{ gitea.ref_name }}
# Required for Flutter action
- name: Install jq
run: |
apt-get update
apt-get install -y jq
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.38.6
- name: Get dependencies
run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64
flutter pub get
- name: Generate oss_licenses.dart
run: flutter pub run dart_pubspec_licenses:generate -o lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart
- name: Commit license update
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
if [ -n "$(git status --porcelain lib test)" ]; then
git config --global user.name "Gitea Actions [bot]"
git config --global user.email "actions@yannick-weigert.de"
git config pull.rebase false
git pull origin ${{ gitea.ref_name }}
git add lib test
git commit -m "Updated licenses [skip ci]"
git push origin HEAD:${{ gitea.ref_name }}
else
echo "No changes to commit"
fi
format:
runs-on: ubuntu-latest
needs: [update_version, generate_licenses]
steps:
- name: Checkout code
uses: actions/checkout@v4
# Required for Flutter action
- name: Install jq
run: |
apt-get update
apt-get install -y jq
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.38.6
- name: Get dependencies
run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64
flutter pub get
- name: Check code format
id: check_format
continue-on-error: true
run: flutter analyze lib test
- name: Format code
if: steps.check_format.outcome == 'failure'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
git fetch origin ${{ gitea.ref_name }}
git checkout ${{ gitea.ref_name }}
dart fix --apply lib dart fix --apply lib
dart fix --apply test
# Needs credentials, push access and the right files need to be staged if [ -n "$(git status --porcelain lib test)" ]; then
- name: Commit Changes git config --global user.name "Gitea Actions [bot]"
run: | git config --global user.email "actions@yannick-weigert.de"
git config --global user.name "Gitea Actions" git config pull.rebase false
git config --global user.email "actions@gitea.com" git pull origin ${{ gitea.ref_name }}
git status git add lib test
git add lib/ git commit -m "Auto-format code [skip ci]"
git status git push origin HEAD:${{ gitea.ref_name }}
git commit -m "Actions: Auto-formatting [skip ci]" else
git push echo "No changes to commit"
fi
- name: Verify format
run: flutter analyze lib test

28
.gitignore vendored
View File

@@ -150,25 +150,9 @@ app.*.symbols
.gclient_previous_custom_vars .gclient_previous_custom_vars
.gclient_previous_sync_commits .gclient_previous_sync_commits
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/ .swiftpm/
migrate_working_dir/ migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in # The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line # VS Code which you may wish to be included in version control, so this line
@@ -176,17 +160,8 @@ migrate_working_dir/
#.vscode/ #.vscode/
# Flutter/Dart/Pub related # Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/ /build/
# Symbolication related
app.*.symbols
# Obfuscation related # Obfuscation related
app.*.map.json app.*.map.json
@@ -195,3 +170,6 @@ app.*.map.json
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /android/app/release
/devtools_options.yaml
untranslated_messages.json

13
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,13 @@
# Contributing
## Code of Conduct
`<insert link to code of conduct here>`
## Code Style
`<insert styling guidelines here>`
## Repository structure
`<insert folder structure and explanation here>`

165
LICENSE Normal file
View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@@ -1,7 +1,63 @@
# Game Tracker <p align="center">
<img alt="Tallee Logo" src="/artefacts/app-logo.png" width="200"/>
<h2 align="center">Tallee</h2>
</p>
<p align="center">
An open-source app to track card- and board games, manage players & groups and get statistics about your played games.
</p>
<p align="center">
<a href="https://apps.apple.com/">
<img src="https://tools.applemediaservices.com/api/badges/download-on-the-app-store/black/en-US"
alt="Download on the App Store"
height="48"
/>
</a>
<a href="https://play.google.com/">
<img alt="Get it on Google Play"
title="Google Play"
src="https://raw.githubusercontent.com/pd4d10/git-touch/main/assets/google-play-badge.png"
height="48"
/>
</a>
</p>
![Version](https://img.shields.io/badge/App--Version-0.0.1_Alpha-orange)
![Flutter](https://img.shields.io/badge/Flutter-3.38.6-027DFD?logo=flutter)
![iOS26](https://img.shields.io/badge/iOS-26-white?logo=apple)
![Android16](https://img.shields.io/badge/Android-16-3DDC84?logo=android)
## Screenshots
<table align="center" cellspacing="8">
<tr>
<td><img src="/artefacts/screenshot-1.png" alt="Screenshot 1" width="240" /></td>
<td><img src="/artefacts/screenshot-2.png" alt="Screenshot 2" width="240" /></td>
<td><img src="/artefacts/screenshot-3.png" alt="Screenshot 3" width="240" /></td>
<td><img src="/artefacts/screenshot-4.png" alt="Screenshot 4" width="240" /></td>
</tr>
</table>
## Contributing
Contributions are welcome! If you find a bug or have a feature request, please open an issue on GitHub. If you'd like to
contribute code, feel free to fork the repository and submit a pull request. For contribution guidelines, please refer
to [CONTRIBUTING.md](CONTRIBUTING.md).
## License
This project is licensed under the GNU LGPLv3 License. See the [LICENSE](LICENSE) file for details.
## Contributors
<a href="https://github.com/liquiddevelopmentde/game-tracker/graphs/contributors">
<img src="https://contrib.rocks/image?repo=liquiddevelopmentde/game-tracker" />
</a>
## Credits
Tallee is developed and maintained by [Liquid Development](https://liquid-dev.de). For more information or support regarding Tallee, contact us through our website or [hello@liquid-dev.de](mailto:hello@liquid-dev.de).
![Created by Liquid Development](https://img.shields.io/badge/Created_by-Liquid_Development-027DFD?logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBpZD0iRWJlbmVfMSIgZGF0YS1uYW1lPSJFYmVuZSAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3MjUuNDggODk3LjMiPgogIDxkZWZzPgogICAgPHN0eWxlPgogICAgICAuY2xzLTEgewogICAgICAgIGZpbGw6ICNmZmY7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTcwNS4yNiw3MDEuOTJsNi40LDExLjA4Yy0xLjk1LTMuODEtNC4wOS03LjUxLTYuNC0xMS4wOFoiLz4KICA8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik02MDIuMzksODk3LjI1aC03LjIxYzEuMi4wMywyLjQuMDUsMy42MS4wNXMyLjQxLS4wMiwzLjYxLS4wNVoiLz4KICA8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0wLDY5NS4zOGwyLjY4LTQuNjRjLS45MywxLjUyLTEuODIsMy4wNy0yLjY4LDQuNjRaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNjgyLjU1LDcyMy40NWw2LjA1LDEwLjQ5Yy0xLjc5LTMuNjQtMy44MS03LjE1LTYuMDUtMTAuNDlaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMzcuNzIsNzMzLjI4bDUuMy05LjE4Yy0xLjk0LDIuOTQtMy43MSw2LjAxLTUuMyw5LjE4WiIvPgogIDxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTcxMS42Niw3MTMuMDFsLTYuNC0xMS4wOC0yMjAuNDYtMzgxLjg0aDBWMTAxLjg0YzIwLjY3LTYuOTgsMzUuNTYtMjYuNTIsMzUuNTYtNDkuNTQsMC0yOC44OC0yMy40MS01Mi4zLTUyLjMtNTIuM2gtMjA5LjQ4Yy0yOC44OCwwLTUyLjMsMjMuNDEtNTIuMyw1Mi4zLDAsMjIuNzEsMTQuNDgsNDIuMDMsMzQuNyw0OS4yNXYyMTguNTRsLS4zMy41OEwxOC44OSw3MDQuNzlsLTIuNjgsNC42NGMtOS45OSwxOC4xMi0xNS42OCwzOC45Ni0xNS42OCw2MS4xMiwwLDY5Ljk3LDU2LjY0LDEyNi43LDEyNi41MSwxMjYuN2g0NzUuMzVjNjguMy0xLjkxLDEyMy4wOS01Ny44OCwxMjMuMDktMTI2LjY0LDAtMjAuNzQtNC45OS00MC4zMi0xMy44Mi01Ny42Wk02MDguNTYsODYyLjUzSDExNy40M2MtNDkuMzcsMC04OS4zOS00MC4wMi04OS4zOS04OS4zOSwwLTE0LjM2LDMuMzktMjcuOTMsOS40MS0zOS45Nmw1LjMtOS4xOCwyMzMuMi00MDMuOTJoLS4wOFYxMDQuNTloMTcuODFjOS40NywwLDE3LjE1LTcuNjgsMTcuMTUtMTcuMTVzLTcuNjgtMTcuMTUtMTcuMTUtMTcuMTVoLTM1LjU5di0uMDJjLTkuNzItLjI2LTE3LjUyLTguMi0xNy41Mi0xNy45OHM3LjgtMTcuNzIsMTcuNTItMTcuOTh2LS4wMmgyMDkuMjZjOS45NCwwLDE4LDguMDYsMTgsMThzLTguMDYsMTgtMTgsMThoLTM0LjQ4Yy05LjQ3LDAtMTcuMTUsNy42OC0xNy4xNSwxNy4xNXM3LjY4LDE3LjE1LDE3LjE1LDE3LjE1aDE3LjA0djIxNS40OWguMDdsMjMyLjgyLDQwMy4yNiw2LjA2LDEwLjVjNS44MiwxMS44Niw5LjA5LDI1LjIsOS4wOSwzOS4zLDAsNDkuMzctNDAuMDIsODkuMzktODkuMzksODkuMzlaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMzgxLjY4LDU0NS4zOGMtMy4wOCwxLjY4LTYuMTgsMy4zLTkuMzIsNC44NiwzLjA3LTEuNjcsNi4xOC0zLjI5LDkuMzItNC44NloiLz4KICA8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik01ODMuNDIsNTUxLjE5bC0yMC42Ny0zNS44Yy0xMy42OS0xLjg0LTI3LjY3LTIuNzktNDEuODYtMi43OS0xNy45OSwwLTM1LjYyLDEuNTMtNTIuNzgsNC40Ni0zMC41Niw1LjIxLTU5LjYsMTQuODktODYuNDIsMjguMzMtMy4wOCwxLjY4LTYuMTgsMy4zLTkuMzIsNC44Ni00MS44OCwyMC45OS04OS4xNiwzMi43OS0xMzkuMTksMzIuNzktMzQuODUsMC02OC4zNS01Ljc0LTk5LjYzLTE2LjMxLDAsMCwwLC4wMiwwLC4wMmwtMTYuNTIsMjguNjFjMzcuMDEsMTUuNTMsNzcuNjUsMjQuMTIsMTIwLjMsMjQuMTIsMTcuOTgsMCwzNS42MS0xLjUzLDUyLjc2LTQuNDYsMzIuNzctNS41OSw2My43OC0xNi4zMSw5Mi4yLTMxLjI5Ljg3LS40NiwxLjczLS45MiwyLjYtMS40LDQzLjI5LTIyLjgyLDkyLjYyLTM1Ljc0LDE0NC45Ni0zNS43NCwxOC4yOCwwLDM2LjE4LDEuNTksNTMuNTksNC42MWwtLjAyLS4wMloiLz4KICA8Zz4KICAgIDxjaXJjbGUgY2xhc3M9ImNscy0xIiBjeD0iNTg3LjY0IiBjeT0iODAzLjQiIHI9IjE4Ljk2Ii8+CiAgICA8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik01MTUuNTIsNzg0LjQzSDEwMy41NWMtMTAuOTIsMC0xOS43Niw4LjQ5LTE5Ljc2LDE4Ljk2czguODUsMTguOTYsMTkuNzYsMTguOTZoNDExLjk3YzEwLjkyLDAsMTkuNzYtOC40OSwxOS43Ni0xOC45NnMtOC44NS0xOC45Ni0xOS43Ni0xOC45NloiLz4KICA8L2c+CiAgPGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSIyODMuMzIiIGN5PSI0NjcuNTkiIHI9IjE4Ljk2Ii8+CiAgPGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSIzMjYuMjMiIGN5PSIzNjYuMjUiIHI9IjE4Ljk2Ii8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNDA2LjU2LDM4NS4yMmMtMjQuMDYsMC00My41NiwxOS41LTQzLjU2LDQzLjU2czE5LjUsNDMuNTYsNDMuNTYsNDMuNTYsNDMuNTYtMTkuNSw0My41Ni00My41Ni0xOS41LTQzLjU2LTQzLjU2LTQzLjU2Wk00MDYuNTYsNDQ3Ljc0Yy0xMC40NywwLTE4Ljk2LTguNDktMTguOTYtMTguOTZzOC40OS0xOC45NiwxOC45Ni0xOC45NiwxOC45Niw4LjQ5LDE4Ljk2LDE4Ljk2LTguNDksMTguOTYtMTguOTYsMTguOTZaIi8+Cjwvc3ZnPg==)
![Version](https://img.shields.io/badge/Version-0.3.0-orange)
![Flutter](https://img.shields.io/badge/Flutter-3.32.1-blue?logo=flutter)
![Dart](https://img.shields.io/badge/Dart-3.8.1-blue?logo=dart)
A all-in-one app to track card- and board games, manage players and groups and get statistics about your played games.

View File

@@ -11,3 +11,4 @@ linter:
prefer_const_literals_to_create_immutables: true prefer_const_literals_to_create_immutables: true
unnecessary_const: true unnecessary_const: true
lines_longer_than_80_chars: false lines_longer_than_80_chars: false
constant_identifier_names: false

View File

@@ -6,7 +6,7 @@ plugins {
} }
android { android {
namespace = "com.example.game_tracker" namespace = "de.liquid.tallee"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = flutter.ndkVersion
@@ -21,7 +21,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.game_tracker" applicationId = "de.liquid.tallee"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion minSdk = flutter.minSdkVersion

View File

@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:label="game_tracker" android:label="Tallee"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
@@ -42,5 +43,14 @@
<action android:name="android.intent.action.PROCESS_TEXT"/> <action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/> <data android:mimeType="text/plain"/>
</intent> </intent>
<!-- Required for url_launcher to open URLs in external browser -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries> </queries>
</manifest> </manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,4 +1,4 @@
package com.example.game_tracker package de.liquid.tallee
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity

Binary file not shown.

Before

Width:  |  Height:  |  Size: 828 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 656 B

After

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 348 B

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 824 B

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="app_icon_background">#E6F1E4</color> <color name="launch_background">#ef681f</color>
<color name="launch_background">#0B0B0B</color>
</resources> </resources>

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- Referenz unbedingt als @color/launch_background (nicht @colors/...) --> <color name="ic_launcher_background">#EF681F</color>
<color name="ic_launcher_background">@color/app_icon_background</color>
</resources> </resources>

View File

@@ -18,7 +18,7 @@ pluginManagement {
plugins { plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.3" apply false id("com.android.application") version "8.9.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false id("org.jetbrains.kotlin.android") version "2.1.0" apply false
} }

BIN
artefacts/app-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
artefacts/screenshot-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

BIN
artefacts/screenshot-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

BIN
artefacts/screenshot-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

BIN
artefacts/screenshot-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

View File

@@ -152,7 +152,6 @@
B68CF4A64F0B5E45B43D6900 /* Pods-RunnerTests.release.xcconfig */, B68CF4A64F0B5E45B43D6900 /* Pods-RunnerTests.release.xcconfig */,
E754D1191B3E54E52B6DCC49 /* Pods-RunnerTests.profile.xcconfig */, E754D1191B3E54E52B6DCC49 /* Pods-RunnerTests.profile.xcconfig */,
); );
name = Pods;
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@@ -478,7 +477,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.example.gameTracker; PRODUCT_BUNDLE_IDENTIFIER = de.liquid.tallee;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -661,7 +660,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.example.gameTracker; PRODUCT_BUNDLE_IDENTIFIER = de.liquid.tallee;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -684,7 +683,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.example.gameTracker; PRODUCT_BUNDLE_IDENTIFIER = de.liquid.tallee;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1,14 +1 @@
{ {"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"}]}
"images" : [
{
"filename" : "icon_x1024.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -5,9 +5,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "0.043", "blue" : "0.122",
"green" : "0.043", "green" : "0.408",
"red" : "0.043" "red" : "0.937"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View File

@@ -1,17 +1,8 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "icon.png", "filename" : "icon-transparent.png",
"idiom" : "universal", "idiom" : "universal"
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
} }
], ],
"info" : { "info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24412" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="light"/> <device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24405"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
<capability name="Named colors" minToolsVersion="9.0"/> <capability name="Named colors" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@@ -20,12 +20,19 @@
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/> <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Tallee" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="m4u-iU-Cmv">
<rect key="frame" x="153" y="747" width="87" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Futura-Bold" family="Futura" pointSize="28"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="LauncherIcon" translatesAutoresizingMaskIntoConstraints="NO" id="ygV-Op-Bu5"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="LauncherIcon" translatesAutoresizingMaskIntoConstraints="NO" id="ygV-Op-Bu5">
<rect key="frame" x="46" y="334" width="301" height="184"/> <rect key="frame" x="46" y="334" width="301" height="184"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView> </imageView>
</subviews> </subviews>
<color key="backgroundColor" name="LauncherBackgroundColor"/> <color key="backgroundColor" name="LauncherColor"/>
</view> </view>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -36,8 +43,8 @@
<color key="tintColor" red="0.90196078431372551" green="0.94509803921568625" blue="0.89411764705882346" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" red="0.90196078431372551" green="0.94509803921568625" blue="0.89411764705882346" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<resources> <resources>
<image name="LauncherIcon" width="1000" height="1000"/> <image name="LauncherIcon" width="1000" height="1000"/>
<namedColor name="LauncherBackgroundColor"> <namedColor name="LauncherColor">
<color red="0.90196078431372551" green="0.94509803921568625" blue="0.89411764705882346" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color red="0.93699997663497925" green="0.40799999237060547" blue="0.12200000137090683" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor> </namedColor>
</resources> </resources>
</document> </document>

View File

@@ -2,10 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Game Tracker</string> <string>Tallee</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@@ -13,7 +15,7 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>game_tracker</string> <string>tallee</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
@@ -22,8 +24,15 @@
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https</string>
<string>http</string>
</array>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
@@ -39,9 +48,5 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict> </dict>
</plist> </plist>

6
l10n.yaml Normal file
View File

@@ -0,0 +1,6 @@
arb-dir: lib/l10n/arb
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-dir: lib/l10n/generated
nullable-getter: false
untranslated-messages-file: lib/l10n/untranslated_messages.json

View File

@@ -0,0 +1,23 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// Returns a platform-adaptive page route based on the current platform.
/// - On iOS, it returns a [CupertinoPageRoute].
/// - On other platforms, it returns a [MaterialPageRoute].
Route<T> adaptivePageRoute<T>({
required Widget Function(BuildContext) builder,
bool fullscreenDialog = false,
}) {
if (Platform.isIOS) {
return CupertinoPageRoute<T>(
builder: builder,
fullscreenDialog: fullscreenDialog,
);
}
return MaterialPageRoute<T>(
builder: builder,
fullscreenDialog: fullscreenDialog,
);
}

View File

@@ -1,2 +1,22 @@
/// Minimum duration of all app skeletons /// Application-wide constants
Duration minimumSkeletonDuration = const Duration(milliseconds: 250); class Constants {
Constants._(); // Private constructor to prevent instantiation
/// Minimum duration of all app skeletons
static const Duration MINIMUM_SKELETON_DURATION = Duration(milliseconds: 250);
/// Maximum length for player names
static const int MAX_PLAYER_NAME_LENGTH = 32;
/// Maximum length for group names
static const int MAX_GROUP_NAME_LENGTH = 32;
/// Maximum length for match names
static const int MAX_MATCH_NAME_LENGTH = 32;
/// Maximum length for game names
static const int MAX_GAME_NAME_LENGTH = 32;
/// Maximum length for team names
static const int MAX_TEAM_NAME_LENGTH = 32;
}

View File

@@ -1,35 +1,83 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// Theme class that defines colors, border radius, padding, and decorations
class CustomTheme { class CustomTheme {
static Color primaryColor = const Color(0xFF7505E4); CustomTheme._(); // Private constructor to prevent instantiation
static Color secondaryColor = const Color(0xFFAFA2FF);
static Color backgroundColor = const Color(0xFF0B0B0B);
static Color boxColor = const Color(0xFF101010);
static Color onBoxColor = const Color(0xFF181818);
static Color boxBorder = const Color(0xFF272727);
// ==================== Colors ====================
/// Primary color of the app theme
static const Color primaryColor = Color(0xFFef681f);
/// Secondary color of the app theme
static const Color secondaryColor = Color(0xFFf2a981);
/// Background color of the app theme
static const Color backgroundColor = Color(0xFF0B0B0B);
/// Default color for boxes and containers
static const Color boxColor = Color(0xFF101010);
/// Default border color for boxes and containers
static const Color boxBorderColor = Color(0xFF272727);
/// Color for boxes and containers displayed on boxes
static const Color onBoxColor = Color(0xFF181818);
/// Text color used throughout the app
static const Color textColor = Color(0xFFFFFFFF);
/// Background color for the navigation bar
static const Color navBarBackgroundColor = Color(0xFF131313);
/// Selected color for the [NavbarItem]
static Color navBarItemSelectedColor = primaryColor.withGreen(100);
/// Unselected color for the [NavbarItem]
static Color navBarItemUnselectedColor = Colors.grey.shade400;
// ==================== Border Radius ====================
static const double standardBorderRadius = 12.0;
static BorderRadius get standardBorderRadiusAll =>
BorderRadius.circular(standardBorderRadius);
// ==================== Padding & Margins ====================
static const EdgeInsets standardMargin = EdgeInsets.symmetric(
horizontal: 12,
vertical: 10,
);
static const EdgeInsets tileMargin = EdgeInsets.symmetric(
horizontal: 12,
vertical: 5,
);
// ==================== Decorations ====================
static BoxDecoration standardBoxDecoration = BoxDecoration( static BoxDecoration standardBoxDecoration = BoxDecoration(
color: boxColor, color: boxColor,
border: Border.all(color: boxBorder), border: Border.all(color: boxBorderColor),
borderRadius: BorderRadius.circular(12), borderRadius: standardBorderRadiusAll,
); );
static BoxDecoration highlightedBoxDecoration = BoxDecoration( static BoxDecoration highlightedBoxDecoration = BoxDecoration(
color: boxColor, color: boxColor,
border: Border.all(color: primaryColor), border: Border.all(color: primaryColor),
borderRadius: BorderRadius.circular(12), borderRadius: standardBorderRadiusAll,
boxShadow: [BoxShadow(color: primaryColor.withAlpha(120), blurRadius: 12)], boxShadow: [BoxShadow(color: primaryColor.withAlpha(120), blurRadius: 12)],
); );
static AppBarTheme appBarTheme = AppBarTheme( // ==================== App Bar Theme ====================
static const AppBarTheme appBarTheme = AppBarTheme(
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
foregroundColor: Colors.white, foregroundColor: textColor,
elevation: 0, elevation: 0,
titleTextStyle: const TextStyle( scrolledUnderElevation: 0,
color: Colors.white, centerTitle: true,
titleTextStyle: TextStyle(
color: textColor,
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
overflow: TextOverflow.ellipsis,
), ),
iconTheme: const IconThemeData(color: Colors.white), iconTheme: IconThemeData(color: textColor),
); );
} }

View File

@@ -1,4 +1,10 @@
import 'package:flutter/material.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
/// Button types used for styling the [CustomWidthButton] /// Button types used for styling the [CustomWidthButton]
/// - [ButtonType.primary]: Primary button style.
/// - [ButtonType.secondary]: Secondary button style.
/// - [ButtonType.tertiary]: Tertiary button style.
enum ButtonType { primary, secondary, tertiary } enum ButtonType { primary, secondary, tertiary }
/// Result types for import operations in the [SettingsView] /// Result types for import operations in the [SettingsView]
@@ -30,16 +36,17 @@ enum ExportResult { success, canceled, unknownException }
/// - [Ruleset.leastPoints]: The player with the fewest points wins. /// - [Ruleset.leastPoints]: The player with the fewest points wins.
enum Ruleset { singleWinner, singleLoser, mostPoints, leastPoints } enum Ruleset { singleWinner, singleLoser, mostPoints, leastPoints }
/// Translates a [Ruleset] enum value to its corresponding string representation. /// Translates a [Ruleset] enum value to its corresponding localized string.
String translateRulesetToString(Ruleset ruleset) { String translateRulesetToString(Ruleset ruleset, BuildContext context) {
final loc = AppLocalizations.of(context);
switch (ruleset) { switch (ruleset) {
case Ruleset.singleWinner: case Ruleset.singleWinner:
return 'Single Winner'; return loc.single_winner;
case Ruleset.singleLoser: case Ruleset.singleLoser:
return 'Single Loser'; return loc.single_loser;
case Ruleset.mostPoints: case Ruleset.mostPoints:
return 'Most Points'; return loc.most_points;
case Ruleset.leastPoints: case Ruleset.leastPoints:
return 'Least Points'; return loc.least_points;
} }
} }

View File

@@ -1,9 +1,9 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:tallee/data/db/database.dart';
import 'package:game_tracker/data/db/tables/group_table.dart'; import 'package:tallee/data/db/tables/group_table.dart';
import 'package:game_tracker/data/db/tables/player_group_table.dart'; import 'package:tallee/data/db/tables/player_group_table.dart';
import 'package:game_tracker/data/dto/group.dart'; import 'package:tallee/data/dto/group.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:tallee/data/dto/player.dart';
part 'group_dao.g.dart'; part 'group_dao.g.dart';

View File

@@ -1,7 +1,7 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:tallee/data/db/database.dart';
import 'package:game_tracker/data/db/tables/group_match_table.dart'; import 'package:tallee/data/db/tables/group_match_table.dart';
import 'package:game_tracker/data/dto/group.dart'; import 'package:tallee/data/dto/group.dart';
part 'group_match_dao.g.dart'; part 'group_match_dao.g.dart';

View File

@@ -1,9 +1,9 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:tallee/data/db/database.dart';
import 'package:game_tracker/data/db/tables/match_table.dart'; import 'package:tallee/data/db/tables/match_table.dart';
import 'package:game_tracker/data/dto/group.dart'; import 'package:tallee/data/dto/group.dart';
import 'package:game_tracker/data/dto/match.dart'; import 'package:tallee/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:tallee/data/dto/player.dart';
part 'match_dao.g.dart'; part 'match_dao.g.dart';

View File

@@ -1,7 +1,7 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:tallee/data/db/database.dart';
import 'package:game_tracker/data/db/tables/player_table.dart'; import 'package:tallee/data/db/tables/player_table.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:tallee/data/dto/player.dart';
part 'player_dao.g.dart'; part 'player_dao.g.dart';

View File

@@ -1,8 +1,8 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:tallee/data/db/database.dart';
import 'package:game_tracker/data/db/tables/player_group_table.dart'; import 'package:tallee/data/db/tables/player_group_table.dart';
import 'package:game_tracker/data/db/tables/player_table.dart'; import 'package:tallee/data/db/tables/player_table.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:tallee/data/dto/player.dart';
part 'player_group_dao.g.dart'; part 'player_group_dao.g.dart';

View File

@@ -1,7 +1,7 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:tallee/data/db/database.dart';
import 'package:game_tracker/data/db/tables/player_match_table.dart'; import 'package:tallee/data/db/tables/player_match_table.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:tallee/data/dto/player.dart';
part 'player_match_dao.g.dart'; part 'player_match_dao.g.dart';

View File

@@ -1,18 +1,18 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart'; import 'package:drift_flutter/drift_flutter.dart';
import 'package:game_tracker/data/dao/group_dao.dart';
import 'package:game_tracker/data/dao/group_match_dao.dart';
import 'package:game_tracker/data/dao/match_dao.dart';
import 'package:game_tracker/data/dao/player_dao.dart';
import 'package:game_tracker/data/dao/player_group_dao.dart';
import 'package:game_tracker/data/dao/player_match_dao.dart';
import 'package:game_tracker/data/db/tables/group_match_table.dart';
import 'package:game_tracker/data/db/tables/group_table.dart';
import 'package:game_tracker/data/db/tables/match_table.dart';
import 'package:game_tracker/data/db/tables/player_group_table.dart';
import 'package:game_tracker/data/db/tables/player_match_table.dart';
import 'package:game_tracker/data/db/tables/player_table.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:tallee/data/dao/group_dao.dart';
import 'package:tallee/data/dao/group_match_dao.dart';
import 'package:tallee/data/dao/match_dao.dart';
import 'package:tallee/data/dao/player_dao.dart';
import 'package:tallee/data/dao/player_group_dao.dart';
import 'package:tallee/data/dao/player_match_dao.dart';
import 'package:tallee/data/db/tables/group_match_table.dart';
import 'package:tallee/data/db/tables/group_table.dart';
import 'package:tallee/data/db/tables/match_table.dart';
import 'package:tallee/data/db/tables/player_group_table.dart';
import 'package:tallee/data/db/tables/player_match_table.dart';
import 'package:tallee/data/db/tables/player_table.dart';
part 'database.g.dart'; part 'database.g.dart';

View File

@@ -527,6 +527,17 @@ class $MatchTableTable extends MatchTable
final GeneratedDatabase attachedDatabase; final GeneratedDatabase attachedDatabase;
final String? _alias; final String? _alias;
$MatchTableTable(this.attachedDatabase, [this._alias]); $MatchTableTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _winnerIdMeta = const VerificationMeta(
'winnerId',
);
@override
late final GeneratedColumn<String> winnerId = GeneratedColumn<String>(
'winner_id',
aliasedName,
true,
type: DriftSqlType.string,
requiredDuringInsert: false,
);
static const VerificationMeta _idMeta = const VerificationMeta('id'); static const VerificationMeta _idMeta = const VerificationMeta('id');
@override @override
late final GeneratedColumn<String> id = GeneratedColumn<String>( late final GeneratedColumn<String> id = GeneratedColumn<String>(
@@ -545,17 +556,6 @@ class $MatchTableTable extends MatchTable
type: DriftSqlType.string, type: DriftSqlType.string,
requiredDuringInsert: true, requiredDuringInsert: true,
); );
static const VerificationMeta _winnerIdMeta = const VerificationMeta(
'winnerId',
);
@override
late final GeneratedColumn<String> winnerId = GeneratedColumn<String>(
'winner_id',
aliasedName,
true,
type: DriftSqlType.string,
requiredDuringInsert: false,
);
static const VerificationMeta _createdAtMeta = const VerificationMeta( static const VerificationMeta _createdAtMeta = const VerificationMeta(
'createdAt', 'createdAt',
); );
@@ -568,7 +568,7 @@ class $MatchTableTable extends MatchTable
requiredDuringInsert: true, requiredDuringInsert: true,
); );
@override @override
List<GeneratedColumn> get $columns => [id, name, winnerId, createdAt]; List<GeneratedColumn> get $columns => [winnerId, id, name, createdAt];
@override @override
String get aliasedName => _alias ?? actualTableName; String get aliasedName => _alias ?? actualTableName;
@override @override
@@ -581,6 +581,12 @@ class $MatchTableTable extends MatchTable
}) { }) {
final context = VerificationContext(); final context = VerificationContext();
final data = instance.toColumns(true); final data = instance.toColumns(true);
if (data.containsKey('winner_id')) {
context.handle(
_winnerIdMeta,
winnerId.isAcceptableOrUnknown(data['winner_id']!, _winnerIdMeta),
);
}
if (data.containsKey('id')) { if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) { } else if (isInserting) {
@@ -594,12 +600,6 @@ class $MatchTableTable extends MatchTable
} else if (isInserting) { } else if (isInserting) {
context.missing(_nameMeta); context.missing(_nameMeta);
} }
if (data.containsKey('winner_id')) {
context.handle(
_winnerIdMeta,
winnerId.isAcceptableOrUnknown(data['winner_id']!, _winnerIdMeta),
);
}
if (data.containsKey('created_at')) { if (data.containsKey('created_at')) {
context.handle( context.handle(
_createdAtMeta, _createdAtMeta,
@@ -617,6 +617,10 @@ class $MatchTableTable extends MatchTable
MatchTableData map(Map<String, dynamic> data, {String? tablePrefix}) { MatchTableData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return MatchTableData( return MatchTableData(
winnerId: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}winner_id'],
),
id: attachedDatabase.typeMapping.read( id: attachedDatabase.typeMapping.read(
DriftSqlType.string, DriftSqlType.string,
data['${effectivePrefix}id'], data['${effectivePrefix}id'],
@@ -625,10 +629,6 @@ class $MatchTableTable extends MatchTable
DriftSqlType.string, DriftSqlType.string,
data['${effectivePrefix}name'], data['${effectivePrefix}name'],
)!, )!,
winnerId: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}winner_id'],
),
createdAt: attachedDatabase.typeMapping.read( createdAt: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime, DriftSqlType.dateTime,
data['${effectivePrefix}created_at'], data['${effectivePrefix}created_at'],
@@ -643,35 +643,35 @@ class $MatchTableTable extends MatchTable
} }
class MatchTableData extends DataClass implements Insertable<MatchTableData> { class MatchTableData extends DataClass implements Insertable<MatchTableData> {
final String? winnerId;
final String id; final String id;
final String name; final String name;
final String? winnerId;
final DateTime createdAt; final DateTime createdAt;
const MatchTableData({ const MatchTableData({
this.winnerId,
required this.id, required this.id,
required this.name, required this.name,
this.winnerId,
required this.createdAt, required this.createdAt,
}); });
@override @override
Map<String, Expression> toColumns(bool nullToAbsent) { Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{}; final map = <String, Expression>{};
map['id'] = Variable<String>(id);
map['name'] = Variable<String>(name);
if (!nullToAbsent || winnerId != null) { if (!nullToAbsent || winnerId != null) {
map['winner_id'] = Variable<String>(winnerId); map['winner_id'] = Variable<String>(winnerId);
} }
map['id'] = Variable<String>(id);
map['name'] = Variable<String>(name);
map['created_at'] = Variable<DateTime>(createdAt); map['created_at'] = Variable<DateTime>(createdAt);
return map; return map;
} }
MatchTableCompanion toCompanion(bool nullToAbsent) { MatchTableCompanion toCompanion(bool nullToAbsent) {
return MatchTableCompanion( return MatchTableCompanion(
id: Value(id),
name: Value(name),
winnerId: winnerId == null && nullToAbsent winnerId: winnerId == null && nullToAbsent
? const Value.absent() ? const Value.absent()
: Value(winnerId), : Value(winnerId),
id: Value(id),
name: Value(name),
createdAt: Value(createdAt), createdAt: Value(createdAt),
); );
} }
@@ -682,9 +682,9 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
}) { }) {
serializer ??= driftRuntimeOptions.defaultSerializer; serializer ??= driftRuntimeOptions.defaultSerializer;
return MatchTableData( return MatchTableData(
winnerId: serializer.fromJson<String?>(json['winnerId']),
id: serializer.fromJson<String>(json['id']), id: serializer.fromJson<String>(json['id']),
name: serializer.fromJson<String>(json['name']), name: serializer.fromJson<String>(json['name']),
winnerId: serializer.fromJson<String?>(json['winnerId']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']), createdAt: serializer.fromJson<DateTime>(json['createdAt']),
); );
} }
@@ -692,29 +692,29 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
Map<String, dynamic> toJson({ValueSerializer? serializer}) { Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer; serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{ return <String, dynamic>{
'winnerId': serializer.toJson<String?>(winnerId),
'id': serializer.toJson<String>(id), 'id': serializer.toJson<String>(id),
'name': serializer.toJson<String>(name), 'name': serializer.toJson<String>(name),
'winnerId': serializer.toJson<String?>(winnerId),
'createdAt': serializer.toJson<DateTime>(createdAt), 'createdAt': serializer.toJson<DateTime>(createdAt),
}; };
} }
MatchTableData copyWith({ MatchTableData copyWith({
Value<String?> winnerId = const Value.absent(),
String? id, String? id,
String? name, String? name,
Value<String?> winnerId = const Value.absent(),
DateTime? createdAt, DateTime? createdAt,
}) => MatchTableData( }) => MatchTableData(
winnerId: winnerId.present ? winnerId.value : this.winnerId,
id: id ?? this.id, id: id ?? this.id,
name: name ?? this.name, name: name ?? this.name,
winnerId: winnerId.present ? winnerId.value : this.winnerId,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
); );
MatchTableData copyWithCompanion(MatchTableCompanion data) { MatchTableData copyWithCompanion(MatchTableCompanion data) {
return MatchTableData( return MatchTableData(
winnerId: data.winnerId.present ? data.winnerId.value : this.winnerId,
id: data.id.present ? data.id.value : this.id, id: data.id.present ? data.id.value : this.id,
name: data.name.present ? data.name.value : this.name, name: data.name.present ? data.name.value : this.name,
winnerId: data.winnerId.present ? data.winnerId.value : this.winnerId,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
); );
} }
@@ -722,75 +722,75 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
@override @override
String toString() { String toString() {
return (StringBuffer('MatchTableData(') return (StringBuffer('MatchTableData(')
..write('winnerId: $winnerId, ')
..write('id: $id, ') ..write('id: $id, ')
..write('name: $name, ') ..write('name: $name, ')
..write('winnerId: $winnerId, ')
..write('createdAt: $createdAt') ..write('createdAt: $createdAt')
..write(')')) ..write(')'))
.toString(); .toString();
} }
@override @override
int get hashCode => Object.hash(id, name, winnerId, createdAt); int get hashCode => Object.hash(winnerId, id, name, createdAt);
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
(other is MatchTableData && (other is MatchTableData &&
other.winnerId == this.winnerId &&
other.id == this.id && other.id == this.id &&
other.name == this.name && other.name == this.name &&
other.winnerId == this.winnerId &&
other.createdAt == this.createdAt); other.createdAt == this.createdAt);
} }
class MatchTableCompanion extends UpdateCompanion<MatchTableData> { class MatchTableCompanion extends UpdateCompanion<MatchTableData> {
final Value<String?> winnerId;
final Value<String> id; final Value<String> id;
final Value<String> name; final Value<String> name;
final Value<String?> winnerId;
final Value<DateTime> createdAt; final Value<DateTime> createdAt;
final Value<int> rowid; final Value<int> rowid;
const MatchTableCompanion({ const MatchTableCompanion({
this.winnerId = const Value.absent(),
this.id = const Value.absent(), this.id = const Value.absent(),
this.name = const Value.absent(), this.name = const Value.absent(),
this.winnerId = const Value.absent(),
this.createdAt = const Value.absent(), this.createdAt = const Value.absent(),
this.rowid = const Value.absent(), this.rowid = const Value.absent(),
}); });
MatchTableCompanion.insert({ MatchTableCompanion.insert({
this.winnerId = const Value.absent(),
required String id, required String id,
required String name, required String name,
this.winnerId = const Value.absent(),
required DateTime createdAt, required DateTime createdAt,
this.rowid = const Value.absent(), this.rowid = const Value.absent(),
}) : id = Value(id), }) : id = Value(id),
name = Value(name), name = Value(name),
createdAt = Value(createdAt); createdAt = Value(createdAt);
static Insertable<MatchTableData> custom({ static Insertable<MatchTableData> custom({
Expression<String>? winnerId,
Expression<String>? id, Expression<String>? id,
Expression<String>? name, Expression<String>? name,
Expression<String>? winnerId,
Expression<DateTime>? createdAt, Expression<DateTime>? createdAt,
Expression<int>? rowid, Expression<int>? rowid,
}) { }) {
return RawValuesInsertable({ return RawValuesInsertable({
if (winnerId != null) 'winner_id': winnerId,
if (id != null) 'id': id, if (id != null) 'id': id,
if (name != null) 'name': name, if (name != null) 'name': name,
if (winnerId != null) 'winner_id': winnerId,
if (createdAt != null) 'created_at': createdAt, if (createdAt != null) 'created_at': createdAt,
if (rowid != null) 'rowid': rowid, if (rowid != null) 'rowid': rowid,
}); });
} }
MatchTableCompanion copyWith({ MatchTableCompanion copyWith({
Value<String?>? winnerId,
Value<String>? id, Value<String>? id,
Value<String>? name, Value<String>? name,
Value<String?>? winnerId,
Value<DateTime>? createdAt, Value<DateTime>? createdAt,
Value<int>? rowid, Value<int>? rowid,
}) { }) {
return MatchTableCompanion( return MatchTableCompanion(
winnerId: winnerId ?? this.winnerId,
id: id ?? this.id, id: id ?? this.id,
name: name ?? this.name, name: name ?? this.name,
winnerId: winnerId ?? this.winnerId,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
rowid: rowid ?? this.rowid, rowid: rowid ?? this.rowid,
); );
@@ -799,15 +799,15 @@ class MatchTableCompanion extends UpdateCompanion<MatchTableData> {
@override @override
Map<String, Expression> toColumns(bool nullToAbsent) { Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{}; final map = <String, Expression>{};
if (winnerId.present) {
map['winner_id'] = Variable<String>(winnerId.value);
}
if (id.present) { if (id.present) {
map['id'] = Variable<String>(id.value); map['id'] = Variable<String>(id.value);
} }
if (name.present) { if (name.present) {
map['name'] = Variable<String>(name.value); map['name'] = Variable<String>(name.value);
} }
if (winnerId.present) {
map['winner_id'] = Variable<String>(winnerId.value);
}
if (createdAt.present) { if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value); map['created_at'] = Variable<DateTime>(createdAt.value);
} }
@@ -820,9 +820,9 @@ class MatchTableCompanion extends UpdateCompanion<MatchTableData> {
@override @override
String toString() { String toString() {
return (StringBuffer('MatchTableCompanion(') return (StringBuffer('MatchTableCompanion(')
..write('winnerId: $winnerId, ')
..write('id: $id, ') ..write('id: $id, ')
..write('name: $name, ') ..write('name: $name, ')
..write('winnerId: $winnerId, ')
..write('createdAt: $createdAt, ') ..write('createdAt: $createdAt, ')
..write('rowid: $rowid') ..write('rowid: $rowid')
..write(')')) ..write(')'))
@@ -2339,17 +2339,17 @@ typedef $$GroupTableTableProcessedTableManager =
>; >;
typedef $$MatchTableTableCreateCompanionBuilder = typedef $$MatchTableTableCreateCompanionBuilder =
MatchTableCompanion Function({ MatchTableCompanion Function({
Value<String?> winnerId,
required String id, required String id,
required String name, required String name,
Value<String?> winnerId,
required DateTime createdAt, required DateTime createdAt,
Value<int> rowid, Value<int> rowid,
}); });
typedef $$MatchTableTableUpdateCompanionBuilder = typedef $$MatchTableTableUpdateCompanionBuilder =
MatchTableCompanion Function({ MatchTableCompanion Function({
Value<String?> winnerId,
Value<String> id, Value<String> id,
Value<String> name, Value<String> name,
Value<String?> winnerId,
Value<DateTime> createdAt, Value<DateTime> createdAt,
Value<int> rowid, Value<int> rowid,
}); });
@@ -2414,6 +2414,11 @@ class $$MatchTableTableFilterComposer
super.$addJoinBuilderToRootComposer, super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer, super.$removeJoinBuilderFromRootComposer,
}); });
ColumnFilters<String> get winnerId => $composableBuilder(
column: $table.winnerId,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<String> get id => $composableBuilder( ColumnFilters<String> get id => $composableBuilder(
column: $table.id, column: $table.id,
builder: (column) => ColumnFilters(column), builder: (column) => ColumnFilters(column),
@@ -2424,11 +2429,6 @@ class $$MatchTableTableFilterComposer
builder: (column) => ColumnFilters(column), builder: (column) => ColumnFilters(column),
); );
ColumnFilters<String> get winnerId => $composableBuilder(
column: $table.winnerId,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<DateTime> get createdAt => $composableBuilder( ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, column: $table.createdAt,
builder: (column) => ColumnFilters(column), builder: (column) => ColumnFilters(column),
@@ -2494,6 +2494,11 @@ class $$MatchTableTableOrderingComposer
super.$addJoinBuilderToRootComposer, super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer, super.$removeJoinBuilderFromRootComposer,
}); });
ColumnOrderings<String> get winnerId => $composableBuilder(
column: $table.winnerId,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get id => $composableBuilder( ColumnOrderings<String> get id => $composableBuilder(
column: $table.id, column: $table.id,
builder: (column) => ColumnOrderings(column), builder: (column) => ColumnOrderings(column),
@@ -2504,11 +2509,6 @@ class $$MatchTableTableOrderingComposer
builder: (column) => ColumnOrderings(column), builder: (column) => ColumnOrderings(column),
); );
ColumnOrderings<String> get winnerId => $composableBuilder(
column: $table.winnerId,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<DateTime> get createdAt => $composableBuilder( ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, column: $table.createdAt,
builder: (column) => ColumnOrderings(column), builder: (column) => ColumnOrderings(column),
@@ -2524,15 +2524,15 @@ class $$MatchTableTableAnnotationComposer
super.$addJoinBuilderToRootComposer, super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer, super.$removeJoinBuilderFromRootComposer,
}); });
GeneratedColumn<String> get winnerId =>
$composableBuilder(column: $table.winnerId, builder: (column) => column);
GeneratedColumn<String> get id => GeneratedColumn<String> get id =>
$composableBuilder(column: $table.id, builder: (column) => column); $composableBuilder(column: $table.id, builder: (column) => column);
GeneratedColumn<String> get name => GeneratedColumn<String> get name =>
$composableBuilder(column: $table.name, builder: (column) => column); $composableBuilder(column: $table.name, builder: (column) => column);
GeneratedColumn<String> get winnerId =>
$composableBuilder(column: $table.winnerId, builder: (column) => column);
GeneratedColumn<DateTime> get createdAt => GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column); $composableBuilder(column: $table.createdAt, builder: (column) => column);
@@ -2618,29 +2618,29 @@ class $$MatchTableTableTableManager
$$MatchTableTableAnnotationComposer($db: db, $table: table), $$MatchTableTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback: updateCompanionCallback:
({ ({
Value<String?> winnerId = const Value.absent(),
Value<String> id = const Value.absent(), Value<String> id = const Value.absent(),
Value<String> name = const Value.absent(), Value<String> name = const Value.absent(),
Value<String?> winnerId = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(), Value<DateTime> createdAt = const Value.absent(),
Value<int> rowid = const Value.absent(), Value<int> rowid = const Value.absent(),
}) => MatchTableCompanion( }) => MatchTableCompanion(
winnerId: winnerId,
id: id, id: id,
name: name, name: name,
winnerId: winnerId,
createdAt: createdAt, createdAt: createdAt,
rowid: rowid, rowid: rowid,
), ),
createCompanionCallback: createCompanionCallback:
({ ({
Value<String?> winnerId = const Value.absent(),
required String id, required String id,
required String name, required String name,
Value<String?> winnerId = const Value.absent(),
required DateTime createdAt, required DateTime createdAt,
Value<int> rowid = const Value.absent(), Value<int> rowid = const Value.absent(),
}) => MatchTableCompanion.insert( }) => MatchTableCompanion.insert(
winnerId: winnerId,
id: id, id: id,
name: name, name: name,
winnerId: winnerId,
createdAt: createdAt, createdAt: createdAt,
rowid: rowid, rowid: rowid,
), ),

View File

@@ -1,6 +1,6 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/tables/group_table.dart'; import 'package:tallee/data/db/tables/group_table.dart';
import 'package:game_tracker/data/db/tables/match_table.dart'; import 'package:tallee/data/db/tables/match_table.dart';
class GroupMatchTable extends Table { class GroupMatchTable extends Table {
TextColumn get groupId => TextColumn get groupId =>

View File

@@ -1,6 +1,6 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/tables/group_table.dart'; import 'package:tallee/data/db/tables/group_table.dart';
import 'package:game_tracker/data/db/tables/player_table.dart'; import 'package:tallee/data/db/tables/player_table.dart';
class PlayerGroupTable extends Table { class PlayerGroupTable extends Table {
TextColumn get playerId => TextColumn get playerId =>

View File

@@ -1,6 +1,6 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/tables/match_table.dart'; import 'package:tallee/data/db/tables/match_table.dart';
import 'package:game_tracker/data/db/tables/player_table.dart'; import 'package:tallee/data/db/tables/player_table.dart';
class PlayerMatchTable extends Table { class PlayerMatchTable extends Table {
TextColumn get playerId => TextColumn get playerId =>

View File

@@ -1,5 +1,5 @@
import 'package:clock/clock.dart'; import 'package:clock/clock.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:tallee/data/dto/player.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class Group { class Group {

View File

@@ -1,6 +1,6 @@
import 'package:clock/clock.dart'; import 'package:clock/clock.dart';
import 'package:game_tracker/data/dto/group.dart'; import 'package:tallee/data/dto/group.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:tallee/data/dto/player.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class Match { class Match {
@@ -9,7 +9,7 @@ class Match {
final String name; final String name;
final List<Player>? players; final List<Player>? players;
final Group? group; final Group? group;
final Player? winner; Player? winner;
Match({ Match({
String? id, String? id,

97
lib/l10n/arb/app_de.arb Normal file
View File

@@ -0,0 +1,97 @@
{
"@@locale": "de",
"all_players": "Alle Spieler:innen",
"all_players_selected": "Alle Spieler:innen ausgewählt",
"amount_of_matches": "Anzahl der Spiele",
"app_name": "Tallee",
"best_player": "Beste:r Spieler:in",
"cancel": "Abbrechen",
"choose_game": "Spielvorlage wählen",
"choose_group": "Gruppe wählen",
"choose_ruleset": "Regelwerk wählen",
"could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden",
"create_group": "Gruppe erstellen",
"create_match": "Spiel erstellen",
"create_new_group": "Neue Gruppe erstellen",
"create_new_match": "Neues Spiel erstellen",
"created_on": "Erstellt am",
"data": "Daten",
"data_successfully_deleted": "Daten erfolgreich gelöscht",
"data_successfully_exported": "Daten erfolgreich exportiert",
"data_successfully_imported": "Daten erfolgreich importiert",
"days_ago": "vor {count} Tagen",
"delete": "Löschen",
"delete_all_data": "Alle Daten löschen",
"delete_group": "Gruppe löschen",
"edit_group": "Gruppe bearbeiten",
"error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen",
"error_reading_file": "Fehler beim Lesen der Datei",
"export_canceled": "Export abgebrochen",
"export_data": "Daten exportieren",
"format_exception": "Formatfehler (siehe Konsole)",
"game": "Spielvorlage",
"game_name": "Spielvorlagenname",
"group": "Gruppe",
"group_name": "Gruppenname",
"group_profile": "Gruppenprofil",
"groups": "Gruppen",
"home": "Startseite",
"import_canceled": "Import abgebrochen",
"import_data": "Daten importieren",
"info": "Info",
"invalid_schema": "Ungültiges Schema",
"least_points": "Niedrigste Punkte",
"legal": "Rechtliches",
"legal_notice": "Impressum",
"licenses": "Lizenzen",
"match_in_progress": "Spiel läuft...",
"match_name": "Spieltitel",
"matches": "Spiele",
"members": "Mitglieder",
"most_points": "Höchste Punkte",
"no_data_available": "Keine Daten verfügbar",
"no_groups_created_yet": "Noch keine Gruppen erstellt",
"no_licenses_found": "Keine Lizenzen gefunden",
"no_license_text_available": "Kein Lizenztext verfügbar",
"no_matches_created_yet": "Noch keine Spiele erstellt",
"no_players_created_yet": "Noch keine Spieler:in erstellt",
"no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden",
"no_players_selected": "Keine Spieler:innen ausgewählt",
"no_recent_matches_available": "Keine letzten Spiele verfügbar",
"no_second_match_available": "Kein zweites Spiel verfügbar",
"no_statistics_available": "Keine Statistiken verfügbar",
"none": "Kein",
"none_group": "Keine",
"not_available": "Nicht verfügbar",
"played_matches": "Gespielte Spiele",
"player_name": "Spieler:innenname",
"players": "Spieler:innen",
"players_count": "{count} Spieler",
"privacy_policy": "Datenschutzerklärung",
"quick_create": "Schnellzugriff",
"recent_matches": "Letzte Spiele",
"ruleset": "Regelwerk",
"ruleset_least_points": "Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.",
"ruleset_most_points": "Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.",
"ruleset_single_loser": "Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.",
"ruleset_single_winner": "Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.",
"search_for_groups": "Nach Gruppen suchen",
"search_for_players": "Nach Spieler:innen suchen",
"select_winner": "Gewinner:in wählen:",
"selected_players": "Ausgewählte Spieler:innen",
"settings": "Einstellungen",
"single_loser": "Ein:e Verlierer:in",
"single_winner": "Ein:e Gewinner:in",
"statistics": "Statistiken",
"stats": "Statistiken",
"successfully_added_player": "Spieler:in {playerName} erfolgreich hinzugefügt",
"there_is_no_group_matching_your_search": "Es gibt keine Gruppe, die deiner Suche entspricht",
"this_cannot_be_undone": "Dies kann nicht rückgängig gemacht werden.",
"today_at": "Heute um",
"undo": "Rückgängig",
"unknown_exception": "Unbekannter Fehler (siehe Konsole)",
"winner": "Gewinner:in",
"winrate": "Siegquote",
"wins": "Siege",
"yesterday_at": "Gestern um"
}

395
lib/l10n/arb/app_en.arb Normal file
View File

@@ -0,0 +1,395 @@
{
"@@locale": "en",
"@all_players": {
"description": "Label for all players list"
},
"@all_players_selected": {
"description": "Message when all players are added to selection"
},
"@amount_of_matches": {
"description": "Label for amount of matches statistic"
},
"@app_name": {
"description": "The name of the App"
},
"@best_player": {
"description": "Label for best player statistic"
},
"@cancel": {
"description": "Cancel button text"
},
"@choose_game": {
"description": "Label for choosing a game"
},
"@choose_group": {
"description": "Label for choosing a group"
},
"@choose_ruleset": {
"description": "Label for choosing a ruleset"
},
"@could_not_add_player": {
"description": "Error message when adding a player fails"
},
"@create_group": {
"description": "Button text to create a group"
},
"@create_match": {
"description": "Button text to create a match"
},
"@create_new_group": {
"description": "Button text to create a new group"
},
"@create_new_match": {
"description": "Button text to create a new match"
},
"@created_on": {
"description": "Label for creation date"
},
"@data": {
"description": "Data label"
},
"@data_successfully_deleted": {
"description": "Success message after deleting data"
},
"@data_successfully_exported": {
"description": "Success message after exporting data"
},
"@data_successfully_imported": {
"description": "Success message after importing data"
},
"@days_ago": {
"description": "Date format for days ago",
"placeholders": {
"count": {
"type": "int"
}
}
},
"@delete": {
"description": "Delete button text"
},
"@delete_all_data": {
"description": "Confirmation dialog for deleting all data"
},
"@delete_group": {
"description": "Button text to delete a group"
},
"@edit_group": {
"description": "Button text to edit a group"
},
"@error_creating_group": {
"description": "Error message when group creation fails"
},
"@error_reading_file": {
"description": "Error message when file cannot be read"
},
"@export_canceled": {
"description": "Message when export is canceled"
},
"@export_data": {
"description": "Export data menu item"
},
"@format_exception": {
"description": "Error message for format exceptions"
},
"@game": {
"description": "Game label"
},
"@game_name": {
"description": "Placeholder for game name search"
},
"@group": {
"description": "Group label"
},
"@group_name": {
"description": "Placeholder for group name input"
},
"@group_profile": {
"description": "Title for group profile view"
},
"@groups": {
"description": "Label for groups"
},
"@home": {
"description": "Home tab label"
},
"@import_canceled": {
"description": "Message when import is canceled"
},
"@import_data": {
"description": "Import data menu item"
},
"@info": {
"description": "Info label"
},
"@invalid_schema": {
"description": "Error message for invalid schema"
},
"@least_points": {
"description": "Title for least points ruleset"
},
"@legal": {
"description": "Legal section header"
},
"@legal_notice": {
"description": "Legal notice menu item"
},
"@licenses": {
"description": "Licenses menu item"
},
"@match_in_progress": {
"description": "Message when match is in progress"
},
"@match_name": {
"description": "Placeholder for match name input"
},
"@matches": {
"description": "Label for matches"
},
"@members": {
"description": "Label for group members"
},
"@most_points": {
"description": "Title for most points ruleset"
},
"@no_data_available": {
"description": "Message when no data in the statistic tiles is given"
},
"@no_groups_created_yet": {
"description": "Message when no groups exist"
},
"@no_licenses_found": {
"description": "Message when no licenses are found"
},
"@no_license_text_available": {
"description": "Message when no license text is available"
},
"@no_matches_created_yet": {
"description": "Message when no matches exist"
},
"@no_players_created_yet": {
"description": "Message when no players exist"
},
"@no_players_found_with_that_name": {
"description": "Message when search returns no results"
},
"@no_players_selected": {
"description": "Message when no players are selected"
},
"@no_recent_matches_available": {
"description": "Message when no recent matches exist"
},
"@no_second_match_available": {
"description": "Message when no second match exists"
},
"@no_statistics_available": {
"description": "Message when no statistics are available, because no matches were played yet"
},
"@none": {
"description": "None option label"
},
"@none_group": {
"description": "None group option label"
},
"@not_available": {
"description": "Abbreviation for not available"
},
"@played_matches": {
"description": "Label for played matches statistic"
},
"@player_name": {
"description": "Placeholder for player name input"
},
"@players": {
"description": "Players label"
},
"@players_count": {
"description": "Shows the number of players",
"placeholders": {
"count": {
"type": "int"
}
}
},
"@privacy_policy": {
"description": "Privacy policy menu item"
},
"@quick_create": {
"description": "Title for quick create section"
},
"@recent_matches": {
"description": "Title for recent matches section"
},
"@ruleset": {
"description": "Ruleset label"
},
"@ruleset_least_points": {
"description": "Description for least points ruleset"
},
"@ruleset_most_points": {
"description": "Description for most points ruleset"
},
"@ruleset_single_loser": {
"description": "Description for single loser ruleset"
},
"@ruleset_single_winner": {
"description": "Description for single winner ruleset"
},
"@search_for_groups": {
"description": "Hint text for group search input field"
},
"@search_for_players": {
"description": "Hint text for player search input field"
},
"@select_winner": {
"description": "Label to select the winner"
},
"@selected_players": {
"description": "Shows the number of selected players"
},
"@settings": {
"description": "Label for the App Settings"
},
"@single_loser": {
"description": "Title for single loser ruleset"
},
"@single_winner": {
"description": "Title for single winner ruleset"
},
"@statistics": {
"description": "Statistics tab label"
},
"@stats": {
"description": "Stats tab label (short)"
},
"@successfully_added_player": {
"description": "Success message when adding a player",
"placeholders": {
"playerName": {
"type": "String",
"example": "John"
}
}
},
"@there_is_no_group_matching_your_search": {
"description": "Message when search returns no groups"
},
"@this_cannot_be_undone": {
"description": "Warning message for irreversible actions"
},
"@today_at": {
"description": "Date format for today"
},
"@undo": {
"description": "Undo button text"
},
"@unknown_exception": {
"description": "Error message for unknown exceptions"
},
"@winner": {
"description": "Winner label"
},
"@winrate": {
"description": "Label for winrate statistic"
},
"@wins": {
"description": "Label for wins statistic"
},
"@yesterday_at": {
"description": "Date format for yesterday"
},
"all_players": "All players",
"all_players_selected": "All players selected",
"amount_of_matches": "Amount of Matches",
"app_name": "Tallee",
"best_player": "Best Player",
"cancel": "Cancel",
"choose_game": "Choose Game",
"choose_group": "Choose Group",
"choose_ruleset": "Choose Ruleset",
"could_not_add_player": "Could not add player",
"create_group": "Create Group",
"create_match": "Create match",
"create_new_group": "Create new group",
"created_on": "Created on",
"create_new_match": "Create new match",
"data": "Data",
"data_successfully_deleted": "Data successfully deleted",
"data_successfully_exported": "Data successfully exported",
"data_successfully_imported": "Data successfully imported",
"days_ago": "{count} days ago",
"delete": "Delete",
"delete_all_data": "Delete all data",
"delete_group": "Delete Group",
"edit_group": "Edit Group",
"error_creating_group": "Error while creating group, please try again",
"error_reading_file": "Error reading file",
"export_canceled": "Export canceled",
"export_data": "Export data",
"format_exception": "Format Exception (see console)",
"game": "Game",
"game_name": "Game Name",
"group": "Group",
"group_name": "Group name",
"group_profile": "Group Profile",
"groups": "Groups",
"home": "Home",
"import_canceled": "Import canceled",
"import_data": "Import data",
"info": "Info",
"invalid_schema": "Invalid Schema",
"least_points": "Least Points",
"legal": "Legal",
"legal_notice": "Legal Notice",
"licenses": "Licenses",
"match_in_progress": "Match in progress...",
"match_name": "Match name",
"matches": "Matches",
"members": "Members",
"most_points": "Most Points",
"no_data_available": "No data available",
"no_groups_created_yet": "No groups created yet",
"no_licenses_found": "No licenses found",
"no_license_text_available": "No license text available",
"no_matches_created_yet": "No matches created yet",
"no_players_created_yet": "No players created yet",
"no_players_found_with_that_name": "No players found with that name",
"no_players_selected": "No players selected",
"no_recent_matches_available": "No recent matches available",
"no_second_match_available": "No second match available",
"no_statistics_available": "No statistics available",
"none": "None",
"none_group": "None",
"not_available": "Not available",
"played_matches": "Played Matches",
"player_name": "Player name",
"players": "Players",
"players_count": "{count} Players",
"privacy_policy": "Privacy Policy",
"quick_create": "Quick Create",
"recent_matches": "Recent Matches",
"ruleset": "Ruleset",
"ruleset_least_points": "Inverse scoring: the player with the fewest points wins.",
"ruleset_most_points": "Traditional ruleset: the player with the most points wins.",
"ruleset_single_loser": "Exactly one loser is determined; last place receives the penalty or consequence.",
"ruleset_single_winner": "Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.",
"search_for_groups": "Search for groups",
"search_for_players": "Search for players",
"select_winner": "Select Winner:",
"selected_players": "Selected players",
"settings": "Settings",
"single_loser": "Single Loser",
"single_winner": "Single Winner",
"statistics": "Statistics",
"stats": "Stats",
"successfully_added_player": "Successfully added player {playerName}",
"there_is_no_group_matching_your_search": "There is no group matching your search",
"this_cannot_be_undone": "This can't be undone.",
"today_at": "Today at",
"undo": "Undo",
"unknown_exception": "Unknown Exception (see console)",
"winner": "Winner",
"winrate": "Winrate",
"wins": "Wins",
"yesterday_at": "Yesterday at"
}

View File

@@ -0,0 +1,698 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart' as intl;
import 'app_localizations_de.dart';
import 'app_localizations_en.dart';
// ignore_for_file: type=lint
/// Callers can lookup localized strings with an instance of AppLocalizations
/// returned by `AppLocalizations.of(context)`.
///
/// Applications need to include `AppLocalizations.delegate()` in their app's
/// `localizationDelegates` list, and the locales they support in the app's
/// `supportedLocales` list. For example:
///
/// ```dart
/// import 'generated/app_localizations.dart';
///
/// return MaterialApp(
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
/// supportedLocales: AppLocalizations.supportedLocales,
/// home: MyApplicationHome(),
/// );
/// ```
///
/// ## Update pubspec.yaml
///
/// Please make sure to update your pubspec.yaml to include the following
/// packages:
///
/// ```yaml
/// dependencies:
/// # Internationalization support.
/// flutter_localizations:
/// sdk: flutter
/// intl: any # Use the pinned version from flutter_localizations
///
/// # Rest of dependencies
/// ```
///
/// ## iOS Applications
///
/// iOS applications define key application metadata, including supported
/// locales, in an Info.plist file that is built into the application bundle.
/// To configure the locales supported by your app, youll need to edit this
/// file.
///
/// First, open your projects ios/Runner.xcworkspace Xcode workspace file.
/// Then, in the Project Navigator, open the Info.plist file under the Runner
/// projects Runner folder.
///
/// Next, select the Information Property List item, select Add Item from the
/// Editor menu, then select Localizations from the pop-up menu.
///
/// Select and expand the newly-created Localizations item then, for each
/// locale your application supports, add a new item and select the locale
/// you wish to add from the pop-up menu in the Value field. This list should
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
/// property.
abstract class AppLocalizations {
AppLocalizations(String locale)
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
final String localeName;
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
}
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
/// A list of this localizations delegate along with the default localizations
/// delegates.
///
/// Returns a list of localizations delegates containing this delegate along with
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
/// and GlobalWidgetsLocalizations.delegate.
///
/// Additional delegates can be added by appending to this list in
/// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required.
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
<LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];
/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[
Locale('de'),
Locale('en'),
];
/// Label for all players list
///
/// In en, this message translates to:
/// **'All players'**
String get all_players;
/// Message when all players are added to selection
///
/// In en, this message translates to:
/// **'All players selected'**
String get all_players_selected;
/// Label for amount of matches statistic
///
/// In en, this message translates to:
/// **'Amount of Matches'**
String get amount_of_matches;
/// The name of the App
///
/// In en, this message translates to:
/// **'Tallee'**
String get app_name;
/// Label for best player statistic
///
/// In en, this message translates to:
/// **'Best Player'**
String get best_player;
/// Cancel button text
///
/// In en, this message translates to:
/// **'Cancel'**
String get cancel;
/// Label for choosing a game
///
/// In en, this message translates to:
/// **'Choose Game'**
String get choose_game;
/// Label for choosing a group
///
/// In en, this message translates to:
/// **'Choose Group'**
String get choose_group;
/// Label for choosing a ruleset
///
/// In en, this message translates to:
/// **'Choose Ruleset'**
String get choose_ruleset;
/// Error message when adding a player fails
///
/// In en, this message translates to:
/// **'Could not add player'**
String could_not_add_player(Object playerName);
/// Button text to create a group
///
/// In en, this message translates to:
/// **'Create Group'**
String get create_group;
/// Button text to create a match
///
/// In en, this message translates to:
/// **'Create match'**
String get create_match;
/// Button text to create a new group
///
/// In en, this message translates to:
/// **'Create new group'**
String get create_new_group;
/// Label for creation date
///
/// In en, this message translates to:
/// **'Created on'**
String get created_on;
/// Button text to create a new match
///
/// In en, this message translates to:
/// **'Create new match'**
String get create_new_match;
/// Data label
///
/// In en, this message translates to:
/// **'Data'**
String get data;
/// Success message after deleting data
///
/// In en, this message translates to:
/// **'Data successfully deleted'**
String get data_successfully_deleted;
/// Success message after exporting data
///
/// In en, this message translates to:
/// **'Data successfully exported'**
String get data_successfully_exported;
/// Success message after importing data
///
/// In en, this message translates to:
/// **'Data successfully imported'**
String get data_successfully_imported;
/// Date format for days ago
///
/// In en, this message translates to:
/// **'{count} days ago'**
String days_ago(int count);
/// Delete button text
///
/// In en, this message translates to:
/// **'Delete'**
String get delete;
/// Confirmation dialog for deleting all data
///
/// In en, this message translates to:
/// **'Delete all data'**
String get delete_all_data;
/// Button text to delete a group
///
/// In en, this message translates to:
/// **'Delete Group'**
String get delete_group;
/// Button text to edit a group
///
/// In en, this message translates to:
/// **'Edit Group'**
String get edit_group;
/// Error message when group creation fails
///
/// In en, this message translates to:
/// **'Error while creating group, please try again'**
String get error_creating_group;
/// Error message when file cannot be read
///
/// In en, this message translates to:
/// **'Error reading file'**
String get error_reading_file;
/// Message when export is canceled
///
/// In en, this message translates to:
/// **'Export canceled'**
String get export_canceled;
/// Export data menu item
///
/// In en, this message translates to:
/// **'Export data'**
String get export_data;
/// Error message for format exceptions
///
/// In en, this message translates to:
/// **'Format Exception (see console)'**
String get format_exception;
/// Game label
///
/// In en, this message translates to:
/// **'Game'**
String get game;
/// Placeholder for game name search
///
/// In en, this message translates to:
/// **'Game Name'**
String get game_name;
/// Group label
///
/// In en, this message translates to:
/// **'Group'**
String get group;
/// Placeholder for group name input
///
/// In en, this message translates to:
/// **'Group name'**
String get group_name;
/// Title for group profile view
///
/// In en, this message translates to:
/// **'Group Profile'**
String get group_profile;
/// Label for groups
///
/// In en, this message translates to:
/// **'Groups'**
String get groups;
/// Home tab label
///
/// In en, this message translates to:
/// **'Home'**
String get home;
/// Message when import is canceled
///
/// In en, this message translates to:
/// **'Import canceled'**
String get import_canceled;
/// Import data menu item
///
/// In en, this message translates to:
/// **'Import data'**
String get import_data;
/// Info label
///
/// In en, this message translates to:
/// **'Info'**
String get info;
/// Error message for invalid schema
///
/// In en, this message translates to:
/// **'Invalid Schema'**
String get invalid_schema;
/// Title for least points ruleset
///
/// In en, this message translates to:
/// **'Least Points'**
String get least_points;
/// Legal section header
///
/// In en, this message translates to:
/// **'Legal'**
String get legal;
/// Legal notice menu item
///
/// In en, this message translates to:
/// **'Legal Notice'**
String get legal_notice;
/// Licenses menu item
///
/// In en, this message translates to:
/// **'Licenses'**
String get licenses;
/// Message when match is in progress
///
/// In en, this message translates to:
/// **'Match in progress...'**
String get match_in_progress;
/// Placeholder for match name input
///
/// In en, this message translates to:
/// **'Match name'**
String get match_name;
/// Label for matches
///
/// In en, this message translates to:
/// **'Matches'**
String get matches;
/// Label for group members
///
/// In en, this message translates to:
/// **'Members'**
String get members;
/// Title for most points ruleset
///
/// In en, this message translates to:
/// **'Most Points'**
String get most_points;
/// Message when no data in the statistic tiles is given
///
/// In en, this message translates to:
/// **'No data available'**
String get no_data_available;
/// Message when no groups exist
///
/// In en, this message translates to:
/// **'No groups created yet'**
String get no_groups_created_yet;
/// Message when no licenses are found
///
/// In en, this message translates to:
/// **'No licenses found'**
String get no_licenses_found;
/// Message when no license text is available
///
/// In en, this message translates to:
/// **'No license text available'**
String get no_license_text_available;
/// Message when no matches exist
///
/// In en, this message translates to:
/// **'No matches created yet'**
String get no_matches_created_yet;
/// Message when no players exist
///
/// In en, this message translates to:
/// **'No players created yet'**
String get no_players_created_yet;
/// Message when search returns no results
///
/// In en, this message translates to:
/// **'No players found with that name'**
String get no_players_found_with_that_name;
/// Message when no players are selected
///
/// In en, this message translates to:
/// **'No players selected'**
String get no_players_selected;
/// Message when no recent matches exist
///
/// In en, this message translates to:
/// **'No recent matches available'**
String get no_recent_matches_available;
/// Message when no second match exists
///
/// In en, this message translates to:
/// **'No second match available'**
String get no_second_match_available;
/// Message when no statistics are available, because no matches were played yet
///
/// In en, this message translates to:
/// **'No statistics available'**
String get no_statistics_available;
/// None option label
///
/// In en, this message translates to:
/// **'None'**
String get none;
/// None group option label
///
/// In en, this message translates to:
/// **'None'**
String get none_group;
/// Abbreviation for not available
///
/// In en, this message translates to:
/// **'Not available'**
String get not_available;
/// Label for played matches statistic
///
/// In en, this message translates to:
/// **'Played Matches'**
String get played_matches;
/// Placeholder for player name input
///
/// In en, this message translates to:
/// **'Player name'**
String get player_name;
/// Players label
///
/// In en, this message translates to:
/// **'Players'**
String get players;
/// Shows the number of players
///
/// In en, this message translates to:
/// **'{count} Players'**
String players_count(int count);
/// Privacy policy menu item
///
/// In en, this message translates to:
/// **'Privacy Policy'**
String get privacy_policy;
/// Title for quick create section
///
/// In en, this message translates to:
/// **'Quick Create'**
String get quick_create;
/// Title for recent matches section
///
/// In en, this message translates to:
/// **'Recent Matches'**
String get recent_matches;
/// Ruleset label
///
/// In en, this message translates to:
/// **'Ruleset'**
String get ruleset;
/// Description for least points ruleset
///
/// In en, this message translates to:
/// **'Inverse scoring: the player with the fewest points wins.'**
String get ruleset_least_points;
/// Description for most points ruleset
///
/// In en, this message translates to:
/// **'Traditional ruleset: the player with the most points wins.'**
String get ruleset_most_points;
/// Description for single loser ruleset
///
/// In en, this message translates to:
/// **'Exactly one loser is determined; last place receives the penalty or consequence.'**
String get ruleset_single_loser;
/// Description for single winner ruleset
///
/// In en, this message translates to:
/// **'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.'**
String get ruleset_single_winner;
/// Hint text for group search input field
///
/// In en, this message translates to:
/// **'Search for groups'**
String get search_for_groups;
/// Hint text for player search input field
///
/// In en, this message translates to:
/// **'Search for players'**
String get search_for_players;
/// Label to select the winner
///
/// In en, this message translates to:
/// **'Select Winner:'**
String get select_winner;
/// Shows the number of selected players
///
/// In en, this message translates to:
/// **'Selected players'**
String get selected_players;
/// Label for the App Settings
///
/// In en, this message translates to:
/// **'Settings'**
String get settings;
/// Title for single loser ruleset
///
/// In en, this message translates to:
/// **'Single Loser'**
String get single_loser;
/// Title for single winner ruleset
///
/// In en, this message translates to:
/// **'Single Winner'**
String get single_winner;
/// Statistics tab label
///
/// In en, this message translates to:
/// **'Statistics'**
String get statistics;
/// Stats tab label (short)
///
/// In en, this message translates to:
/// **'Stats'**
String get stats;
/// Success message when adding a player
///
/// In en, this message translates to:
/// **'Successfully added player {playerName}'**
String successfully_added_player(String playerName);
/// Message when search returns no groups
///
/// In en, this message translates to:
/// **'There is no group matching your search'**
String get there_is_no_group_matching_your_search;
/// Warning message for irreversible actions
///
/// In en, this message translates to:
/// **'This can\'t be undone.'**
String get this_cannot_be_undone;
/// Date format for today
///
/// In en, this message translates to:
/// **'Today at'**
String get today_at;
/// Undo button text
///
/// In en, this message translates to:
/// **'Undo'**
String get undo;
/// Error message for unknown exceptions
///
/// In en, this message translates to:
/// **'Unknown Exception (see console)'**
String get unknown_exception;
/// Winner label
///
/// In en, this message translates to:
/// **'Winner'**
String get winner;
/// Label for winrate statistic
///
/// In en, this message translates to:
/// **'Winrate'**
String get winrate;
/// Label for wins statistic
///
/// In en, this message translates to:
/// **'Wins'**
String get wins;
/// Date format for yesterday
///
/// In en, this message translates to:
/// **'Yesterday at'**
String get yesterday_at;
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
Future<AppLocalizations> load(Locale locale) {
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
}
@override
bool isSupported(Locale locale) =>
<String>['de', 'en'].contains(locale.languageCode);
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}
AppLocalizations lookupAppLocalizations(Locale locale) {
// Lookup logic when only language code is specified.
switch (locale.languageCode) {
case 'de':
return AppLocalizationsDe();
case 'en':
return AppLocalizationsEn();
}
throw FlutterError(
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
'an issue with the localizations generation tool. Please file an issue '
'on GitHub with a reproducible sample app and the gen-l10n configuration '
'that was used.',
);
}

View File

@@ -0,0 +1,308 @@
// ignore: unused_import
import 'package:intl/intl.dart' as intl;
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for German (`de`).
class AppLocalizationsDe extends AppLocalizations {
AppLocalizationsDe([String locale = 'de']) : super(locale);
@override
String get all_players => 'Alle Spieler:innen';
@override
String get all_players_selected => 'Alle Spieler:innen ausgewählt';
@override
String get amount_of_matches => 'Anzahl der Spiele';
@override
String get app_name => 'Tallee';
@override
String get best_player => 'Beste:r Spieler:in';
@override
String get cancel => 'Abbrechen';
@override
String get choose_game => 'Spielvorlage wählen';
@override
String get choose_group => 'Gruppe wählen';
@override
String get choose_ruleset => 'Regelwerk wählen';
@override
String could_not_add_player(Object playerName) {
return 'Spieler:in $playerName konnte nicht hinzugefügt werden';
}
@override
String get create_group => 'Gruppe erstellen';
@override
String get create_match => 'Spiel erstellen';
@override
String get create_new_group => 'Neue Gruppe erstellen';
@override
String get created_on => 'Erstellt am';
@override
String get create_new_match => 'Neues Spiel erstellen';
@override
String get data => 'Daten';
@override
String get data_successfully_deleted => 'Daten erfolgreich gelöscht';
@override
String get data_successfully_exported => 'Daten erfolgreich exportiert';
@override
String get data_successfully_imported => 'Daten erfolgreich importiert';
@override
String days_ago(int count) {
return 'vor $count Tagen';
}
@override
String get delete => 'Löschen';
@override
String get delete_all_data => 'Alle Daten löschen';
@override
String get delete_group => 'Gruppe löschen';
@override
String get edit_group => 'Gruppe bearbeiten';
@override
String get error_creating_group =>
'Fehler beim Erstellen der Gruppe, bitte erneut versuchen';
@override
String get error_reading_file => 'Fehler beim Lesen der Datei';
@override
String get export_canceled => 'Export abgebrochen';
@override
String get export_data => 'Daten exportieren';
@override
String get format_exception => 'Formatfehler (siehe Konsole)';
@override
String get game => 'Spielvorlage';
@override
String get game_name => 'Spielvorlagenname';
@override
String get group => 'Gruppe';
@override
String get group_name => 'Gruppenname';
@override
String get group_profile => 'Gruppenprofil';
@override
String get groups => 'Gruppen';
@override
String get home => 'Startseite';
@override
String get import_canceled => 'Import abgebrochen';
@override
String get import_data => 'Daten importieren';
@override
String get info => 'Info';
@override
String get invalid_schema => 'Ungültiges Schema';
@override
String get least_points => 'Niedrigste Punkte';
@override
String get legal => 'Rechtliches';
@override
String get legal_notice => 'Impressum';
@override
String get licenses => 'Lizenzen';
@override
String get match_in_progress => 'Spiel läuft...';
@override
String get match_name => 'Spieltitel';
@override
String get matches => 'Spiele';
@override
String get members => 'Mitglieder';
@override
String get most_points => 'Höchste Punkte';
@override
String get no_data_available => 'Keine Daten verfügbar';
@override
String get no_groups_created_yet => 'Noch keine Gruppen erstellt';
@override
String get no_licenses_found => 'Keine Lizenzen gefunden';
@override
String get no_license_text_available => 'Kein Lizenztext verfügbar';
@override
String get no_matches_created_yet => 'Noch keine Spiele erstellt';
@override
String get no_players_created_yet => 'Noch keine Spieler:in erstellt';
@override
String get no_players_found_with_that_name =>
'Keine Spieler:in mit diesem Namen gefunden';
@override
String get no_players_selected => 'Keine Spieler:innen ausgewählt';
@override
String get no_recent_matches_available => 'Keine letzten Spiele verfügbar';
@override
String get no_second_match_available => 'Kein zweites Spiel verfügbar';
@override
String get no_statistics_available => 'Keine Statistiken verfügbar';
@override
String get none => 'Kein';
@override
String get none_group => 'Keine';
@override
String get not_available => 'Nicht verfügbar';
@override
String get played_matches => 'Gespielte Spiele';
@override
String get player_name => 'Spieler:innenname';
@override
String get players => 'Spieler:innen';
@override
String players_count(int count) {
return '$count Spieler';
}
@override
String get privacy_policy => 'Datenschutzerklärung';
@override
String get quick_create => 'Schnellzugriff';
@override
String get recent_matches => 'Letzte Spiele';
@override
String get ruleset => 'Regelwerk';
@override
String get ruleset_least_points =>
'Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.';
@override
String get ruleset_most_points =>
'Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.';
@override
String get ruleset_single_loser =>
'Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.';
@override
String get ruleset_single_winner =>
'Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.';
@override
String get search_for_groups => 'Nach Gruppen suchen';
@override
String get search_for_players => 'Nach Spieler:innen suchen';
@override
String get select_winner => 'Gewinner:in wählen:';
@override
String get selected_players => 'Ausgewählte Spieler:innen';
@override
String get settings => 'Einstellungen';
@override
String get single_loser => 'Ein:e Verlierer:in';
@override
String get single_winner => 'Ein:e Gewinner:in';
@override
String get statistics => 'Statistiken';
@override
String get stats => 'Statistiken';
@override
String successfully_added_player(String playerName) {
return 'Spieler:in $playerName erfolgreich hinzugefügt';
}
@override
String get there_is_no_group_matching_your_search =>
'Es gibt keine Gruppe, die deiner Suche entspricht';
@override
String get this_cannot_be_undone =>
'Dies kann nicht rückgängig gemacht werden.';
@override
String get today_at => 'Heute um';
@override
String get undo => 'Rückgängig';
@override
String get unknown_exception => 'Unbekannter Fehler (siehe Konsole)';
@override
String get winner => 'Gewinner:in';
@override
String get winrate => 'Siegquote';
@override
String get wins => 'Siege';
@override
String get yesterday_at => 'Gestern um';
}

View File

@@ -0,0 +1,307 @@
// ignore: unused_import
import 'package:intl/intl.dart' as intl;
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for English (`en`).
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);
@override
String get all_players => 'All players';
@override
String get all_players_selected => 'All players selected';
@override
String get amount_of_matches => 'Amount of Matches';
@override
String get app_name => 'Tallee';
@override
String get best_player => 'Best Player';
@override
String get cancel => 'Cancel';
@override
String get choose_game => 'Choose Game';
@override
String get choose_group => 'Choose Group';
@override
String get choose_ruleset => 'Choose Ruleset';
@override
String could_not_add_player(Object playerName) {
return 'Could not add player';
}
@override
String get create_group => 'Create Group';
@override
String get create_match => 'Create match';
@override
String get create_new_group => 'Create new group';
@override
String get created_on => 'Created on';
@override
String get create_new_match => 'Create new match';
@override
String get data => 'Data';
@override
String get data_successfully_deleted => 'Data successfully deleted';
@override
String get data_successfully_exported => 'Data successfully exported';
@override
String get data_successfully_imported => 'Data successfully imported';
@override
String days_ago(int count) {
return '$count days ago';
}
@override
String get delete => 'Delete';
@override
String get delete_all_data => 'Delete all data';
@override
String get delete_group => 'Delete Group';
@override
String get edit_group => 'Edit Group';
@override
String get error_creating_group =>
'Error while creating group, please try again';
@override
String get error_reading_file => 'Error reading file';
@override
String get export_canceled => 'Export canceled';
@override
String get export_data => 'Export data';
@override
String get format_exception => 'Format Exception (see console)';
@override
String get game => 'Game';
@override
String get game_name => 'Game Name';
@override
String get group => 'Group';
@override
String get group_name => 'Group name';
@override
String get group_profile => 'Group Profile';
@override
String get groups => 'Groups';
@override
String get home => 'Home';
@override
String get import_canceled => 'Import canceled';
@override
String get import_data => 'Import data';
@override
String get info => 'Info';
@override
String get invalid_schema => 'Invalid Schema';
@override
String get least_points => 'Least Points';
@override
String get legal => 'Legal';
@override
String get legal_notice => 'Legal Notice';
@override
String get licenses => 'Licenses';
@override
String get match_in_progress => 'Match in progress...';
@override
String get match_name => 'Match name';
@override
String get matches => 'Matches';
@override
String get members => 'Members';
@override
String get most_points => 'Most Points';
@override
String get no_data_available => 'No data available';
@override
String get no_groups_created_yet => 'No groups created yet';
@override
String get no_licenses_found => 'No licenses found';
@override
String get no_license_text_available => 'No license text available';
@override
String get no_matches_created_yet => 'No matches created yet';
@override
String get no_players_created_yet => 'No players created yet';
@override
String get no_players_found_with_that_name =>
'No players found with that name';
@override
String get no_players_selected => 'No players selected';
@override
String get no_recent_matches_available => 'No recent matches available';
@override
String get no_second_match_available => 'No second match available';
@override
String get no_statistics_available => 'No statistics available';
@override
String get none => 'None';
@override
String get none_group => 'None';
@override
String get not_available => 'Not available';
@override
String get played_matches => 'Played Matches';
@override
String get player_name => 'Player name';
@override
String get players => 'Players';
@override
String players_count(int count) {
return '$count Players';
}
@override
String get privacy_policy => 'Privacy Policy';
@override
String get quick_create => 'Quick Create';
@override
String get recent_matches => 'Recent Matches';
@override
String get ruleset => 'Ruleset';
@override
String get ruleset_least_points =>
'Inverse scoring: the player with the fewest points wins.';
@override
String get ruleset_most_points =>
'Traditional ruleset: the player with the most points wins.';
@override
String get ruleset_single_loser =>
'Exactly one loser is determined; last place receives the penalty or consequence.';
@override
String get ruleset_single_winner =>
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.';
@override
String get search_for_groups => 'Search for groups';
@override
String get search_for_players => 'Search for players';
@override
String get select_winner => 'Select Winner:';
@override
String get selected_players => 'Selected players';
@override
String get settings => 'Settings';
@override
String get single_loser => 'Single Loser';
@override
String get single_winner => 'Single Winner';
@override
String get statistics => 'Statistics';
@override
String get stats => 'Stats';
@override
String successfully_added_player(String playerName) {
return 'Successfully added player $playerName';
}
@override
String get there_is_no_group_matching_your_search =>
'There is no group matching your search';
@override
String get this_cannot_be_undone => 'This can\'t be undone.';
@override
String get today_at => 'Today at';
@override
String get undo => 'Undo';
@override
String get unknown_exception => 'Unknown Exception (see console)';
@override
String get winner => 'Winner';
@override
String get winrate => 'Winrate';
@override
String get wins => 'Wins';
@override
String get yesterday_at => 'Yesterday at';
}

View File

@@ -1,8 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/presentation/views/main_menu/custom_navigation_bar.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/custom_navigation_bar.dart';
void main() { void main() {
runApp( runApp(
@@ -20,22 +21,36 @@ class GameTracker extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
localeResolutionCallback: (locale, supportedLocales) {
for (final supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale?.languageCode) {
return supportedLocale;
}
}
return supportedLocales.firstWhere(
(locale) => locale.languageCode == 'en',
);
},
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
title: 'Game Tracker', onGenerateTitle: (context) => AppLocalizations.of(context).app_name,
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.dark, // forces dark mode themeMode: ThemeMode.dark, // forces dark mode
theme: ThemeData( theme: ThemeData(
primaryColor: CustomTheme.primaryColor, primaryColor: CustomTheme.primaryColor,
scaffoldBackgroundColor: CustomTheme.backgroundColor, scaffoldBackgroundColor: CustomTheme.backgroundColor,
appBarTheme: CustomTheme.appBarTheme, appBarTheme: CustomTheme.appBarTheme,
colorScheme: ColorScheme.fromSeed( colorScheme: ColorScheme.fromSeed(
seedColor: CustomTheme.primaryColor, seedColor: CustomTheme.primaryColor,
brightness: Brightness.dark, brightness: Brightness.dark,
).copyWith(surface: CustomTheme.backgroundColor), ).copyWith(surface: CustomTheme.backgroundColor),
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.android: PredictiveBackPageTransitionsBuilder(),
},
),
), ),
home: const CustomNavigationBar(), home: const CustomNavigationBar(),
); );
} }

View File

@@ -1,13 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:tallee/core/adaptive_page_route.dart';
import 'package:game_tracker/presentation/views/main_menu/group_view/groups_view.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:game_tracker/presentation/views/main_menu/home_view.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/views/main_menu/match_view/match_view.dart'; import 'package:tallee/presentation/views/main_menu/group_view/groups_view.dart';
import 'package:game_tracker/presentation/views/main_menu/settings_view.dart'; import 'package:tallee/presentation/views/main_menu/home_view.dart';
import 'package:game_tracker/presentation/views/main_menu/statistics_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart';
import 'package:game_tracker/presentation/widgets/navbar_item.dart'; import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart';
import 'package:tallee/presentation/views/main_menu/statistics_view.dart';
import 'package:tallee/presentation/widgets/navbar_item.dart';
class CustomNavigationBar extends StatefulWidget { class CustomNavigationBar extends StatefulWidget {
/// A custom navigation bar widget that provides tabbed navigation
/// between different views: Home, Matches, Groups, and Statistics.
const CustomNavigationBar({super.key}); const CustomNavigationBar({super.key});
@override @override
@@ -16,16 +20,15 @@ class CustomNavigationBar extends StatefulWidget {
class _CustomNavigationBarState extends State<CustomNavigationBar> class _CustomNavigationBarState extends State<CustomNavigationBar>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
/// Currently selected tab index
int currentIndex = 0; int currentIndex = 0;
/// Key count to force rebuild of tab views
int tabKeyCount = 0; int tabKeyCount = 0;
@override
void initState() {
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
// Pretty ugly but works // Pretty ugly but works
final List<Widget> tabs = [ final List<Widget> tabs = [
KeyedSubtree(key: ValueKey('home_$tabKeyCount'), child: const HomeView()), KeyedSubtree(key: ValueKey('home_$tabKeyCount'), child: const HomeView()),
@@ -46,7 +49,7 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
appBar: AppBar( appBar: AppBar(
centerTitle: true, centerTitle: true,
title: Text( title: Text(
_currentTabTitle(), _currentTabTitle(context),
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
), ),
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
@@ -56,7 +59,7 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
onPressed: () async { onPressed: () async {
await Navigator.push( await Navigator.push(
context, context,
MaterialPageRoute(builder: (_) => const SettingsView()), adaptivePageRoute(builder: (_) => const SettingsView()),
); );
setState(() { setState(() {
tabKeyCount++; tabKeyCount++;
@@ -70,74 +73,85 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
body: tabs[currentIndex], body: tabs[currentIndex],
extendBody: true, extendBody: true,
bottomNavigationBar: SafeArea( bottomNavigationBar: Container(
minimum: const EdgeInsets.only(bottom: 30), height: 115,
child: Container( decoration: BoxDecoration(
margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 10), color: CustomTheme.navBarBackgroundColor,
decoration: BoxDecoration( border: Border.all(
borderRadius: BorderRadius.circular(24), strokeAlign: BorderSide.strokeAlignOutside,
color: CustomTheme.primaryColor, color: CustomTheme.boxBorderColor,
width: 2,
), ),
child: ClipRRect( borderRadius: const BorderRadius.only(
borderRadius: BorderRadius.circular(24), topLeft: Radius.circular(30),
child: SizedBox( topRight: Radius.circular(30),
height: 60, ),
child: Row( boxShadow: [
mainAxisAlignment: MainAxisAlignment.spaceAround, BoxShadow(
children: <Widget>[ color: Colors.black.withValues(alpha: 0.1),
NavbarItem( blurRadius: 20,
index: 0, offset: const Offset(0, -5),
isSelected: currentIndex == 0,
icon: Icons.home_rounded,
label: 'Home',
onTabTapped: onTabTapped,
),
NavbarItem(
index: 1,
isSelected: currentIndex == 1,
icon: Icons.gamepad_rounded,
label: 'Matches',
onTabTapped: onTabTapped,
),
NavbarItem(
index: 2,
isSelected: currentIndex == 2,
icon: Icons.group_rounded,
label: 'Groups',
onTabTapped: onTabTapped,
),
NavbarItem(
index: 3,
isSelected: currentIndex == 3,
icon: Icons.bar_chart_rounded,
label: 'Stats',
onTabTapped: onTabTapped,
),
],
),
), ),
],
),
child: SafeArea(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
NavbarItem(
index: 0,
isSelected: currentIndex == 0,
icon: Icons.home_rounded,
label: loc.home,
onTabTapped: onTabTapped,
),
NavbarItem(
index: 1,
isSelected: currentIndex == 1,
icon: Icons.gamepad_rounded,
label: loc.matches,
onTabTapped: onTabTapped,
),
NavbarItem(
index: 2,
isSelected: currentIndex == 2,
icon: Icons.group_rounded,
label: loc.groups,
onTabTapped: onTabTapped,
),
NavbarItem(
index: 3,
isSelected: currentIndex == 3,
icon: Icons.bar_chart_rounded,
label: loc.statistics,
onTabTapped: onTabTapped,
),
],
), ),
), ),
), ),
); );
} }
/// Handles tab tap events. Updates the current [index] state.
void onTabTapped(int index) { void onTabTapped(int index) {
setState(() { setState(() {
currentIndex = index; currentIndex = index;
}); });
} }
String _currentTabTitle() { /// Returns the title of the current tab based on [currentIndex].
String _currentTabTitle(context) {
final loc = AppLocalizations.of(context);
switch (currentIndex) { switch (currentIndex) {
case 0: case 0:
return 'Home'; return loc.home;
case 1: case 1:
return 'Matches'; return loc.matches;
case 2: case 2:
return 'Groups'; return loc.groups;
case 3: case 3:
return 'Statistics'; return loc.statistics;
default: default:
return ''; return '';
} }

View File

@@ -1,15 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/core/enums.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
import 'package:game_tracker/presentation/widgets/player_selection.dart';
import 'package:game_tracker/presentation/widgets/text_input/text_input_field.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/constants.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/data/dto/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
import 'package:tallee/presentation/widgets/player_selection.dart';
import 'package:tallee/presentation/widgets/text_input/text_input_field.dart';
class CreateGroupView extends StatefulWidget { class CreateGroupView extends StatefulWidget {
/// A view that allows the user to create a new group
const CreateGroupView({super.key}); const CreateGroupView({super.key});
@override @override
@@ -17,8 +20,12 @@ class CreateGroupView extends StatefulWidget {
} }
class _CreateGroupViewState extends State<CreateGroupView> { class _CreateGroupViewState extends State<CreateGroupView> {
final _groupNameController = TextEditingController();
late final AppDatabase db; late final AppDatabase db;
/// Controller for the group name input field
final _groupNameController = TextEditingController();
/// List of currently selected players
List<Player> selectedPlayers = []; List<Player> selectedPlayers = [];
@override @override
@@ -38,76 +45,71 @@ class _CreateGroupViewState extends State<CreateGroupView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( final loc = AppLocalizations.of(context);
backgroundColor: CustomTheme.backgroundColor, return ScaffoldMessenger(
appBar: AppBar( child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0, appBar: AppBar(title: Text(loc.create_new_group)),
title: const Text( body: SafeArea(
'Create new group', child: Column(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), mainAxisAlignment: MainAxisAlignment.start,
), children: [
centerTitle: true, Container(
), margin: CustomTheme.standardMargin,
body: SafeArea( child: TextInputField(
child: Column( controller: _groupNameController,
mainAxisAlignment: MainAxisAlignment.start, hintText: loc.group_name,
children: [ maxLength: Constants.MAX_GROUP_NAME_LENGTH,
Container( ),
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
child: TextInputField(
controller: _groupNameController,
hintText: 'Group name',
onChanged: (value) {
setState(() {});
},
), ),
), Expanded(
Expanded( child: PlayerSelection(
child: PlayerSelection( onChanged: (value) {
onChanged: (value) { setState(() {
setState(() { selectedPlayers = [...value];
selectedPlayers = [...value]; });
}); },
}, ),
), ),
), CustomWidthButton(
CustomWidthButton( text: loc.create_group,
text: 'Create group', sizeRelativeToWidth: 0.95,
sizeRelativeToWidth: 0.95, buttonType: ButtonType.primary,
buttonType: ButtonType.primary, onPressed:
onPressed: (_groupNameController.text.isEmpty ||
(_groupNameController.text.isEmpty || (selectedPlayers.length < 2))
(selectedPlayers.length < 2)) ? null
? null : () async {
: () async { bool success = await db.groupDao.addGroup(
bool success = await db.groupDao.addGroup( group: Group(
group: Group( name: _groupNameController.text.trim(),
name: _groupNameController.text.trim(), members: selectedPlayers,
members: selectedPlayers,
),
);
if (!context.mounted) return;
if (success) {
Navigator.pop(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: CustomTheme.boxColor,
content: const Center(
child: Text(
'Error while creating group, please try again',
style: TextStyle(color: Colors.white),
),
),
), ),
); );
} if (!context.mounted) return;
setState(() {}); if (success) {
}, Navigator.pop(context);
), } else {
const SizedBox(height: 20), ScaffoldMessenger.of(context).showSnackBar(
], SnackBar(
backgroundColor: CustomTheme.boxColor,
content: Center(
child: Text(
AppLocalizations.of(
context,
).error_creating_group,
style: const TextStyle(color: Colors.white),
),
),
),
);
}
},
),
const SizedBox(height: 20),
],
),
), ),
), ),
); );

View File

@@ -0,0 +1,273 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/data/dto/match.dart';
import 'package:tallee/data/dto/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/app_skeleton.dart';
import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart';
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart';
import 'package:tallee/presentation/widgets/custom_alert_dialog.dart';
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
class GroupProfileView extends StatefulWidget {
/// A view that displays the profile of a group
/// - [group]: The group to display
const GroupProfileView({
super.key,
required this.group,
required this.callback,
});
/// The group to display
final Group group;
final VoidCallback callback;
@override
State<GroupProfileView> createState() => _GroupProfileViewState();
}
class _GroupProfileViewState extends State<GroupProfileView> {
late final AppDatabase db;
bool isLoading = true;
/// Total matches played in this group
int totalMatches = 0;
String bestPlayer = '';
@override
void initState() {
super.initState();
db = Provider.of<AppDatabase>(context, listen: false);
_loadStatistics();
}
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold(
backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar(
title: Text(loc.group_profile),
actions: [
IconButton(
icon: const Icon(Icons.delete),
onPressed: () async {
showDialog<bool>(
context: context,
builder: (context) => CustomAlertDialog(
title: '${loc.delete_group}?',
content: loc.this_cannot_be_undone,
actions: [
AnimatedDialogButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(
loc.cancel,
style: const TextStyle(color: CustomTheme.textColor),
),
),
AnimatedDialogButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(
loc.delete,
style: const TextStyle(
color: CustomTheme.secondaryColor,
),
),
),
],
),
).then((confirmed) async {
if (confirmed! && context.mounted) {
await db.groupDao.deleteGroup(groupId: widget.group.id);
if (!context.mounted) return;
Navigator.pop(context);
widget.callback.call();
}
});
},
),
],
),
body: SafeArea(
child: Stack(
alignment: Alignment.center,
children: [
ListView(
padding: const EdgeInsets.only(
left: 12,
right: 12,
top: 20,
bottom: 100,
),
children: [
const Center(
child: ColoredIconContainer(
icon: Icons.group,
containerSize: 55,
iconSize: 38,
),
),
const SizedBox(height: 10),
Text(
widget.group.name,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: CustomTheme.textColor,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 5),
Text(
'${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(widget.group.createdAt)}',
style: const TextStyle(
fontSize: 12,
color: CustomTheme.textColor,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
InfoTile(
title: loc.members,
icon: Icons.people,
horizontalAlignment: CrossAxisAlignment.start,
content: Wrap(
alignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 12,
runSpacing: 8,
children: widget.group.members.map((member) {
return TextIconTile(
text: member.name,
iconEnabled: false,
);
}).toList(),
),
),
const SizedBox(height: 15),
InfoTile(
title: loc.statistics,
icon: Icons.bar_chart,
content: AppSkeleton(
enabled: isLoading,
child: Column(
children: [
_buildStatRow(
loc.members,
widget.group.members.length.toString(),
),
_buildStatRow(
loc.played_matches,
totalMatches.toString(),
),
_buildStatRow(loc.best_player, bestPlayer),
],
),
),
),
],
),
Positioned(
bottom: MediaQuery.paddingOf(context).bottom,
child: MainMenuButton(
text: loc.edit_group,
icon: Icons.edit,
onPressed: () {
// TODO: Uncomment when GroupDetailView is implemented
/*
await Navigator.push(
context,
adaptivePageRoute(
builder: (context) {
return const GroupDetailView();
},
),
);*/
print('Edit Group pressed');
},
),
),
],
),
),
);
}
/// Builds a single statistic row with a label and value
/// - [label]: The label of the statistic
/// - [value]: The value of the statistic
Widget _buildStatRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
label,
style: const TextStyle(
fontSize: 16,
color: CustomTheme.textColor,
),
),
],
),
Text(
value,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
);
}
/// Loads statistics for this group
Future<void> _loadStatistics() async {
final matches = await db.matchDao.getAllMatches();
final groupMatches = matches
.where((match) => match.group?.id == widget.group.id)
.toList();
setState(() {
totalMatches = groupMatches.length;
bestPlayer = _getBestPlayer(groupMatches);
isLoading = false;
});
}
/// Determines the best player in the group based on match wins
String _getBestPlayer(List<Match> matches) {
final bestPlayerCounts = <Player, int>{};
// Count wins for each player
for (var match in matches) {
if (match.winner != null) {
bestPlayerCounts.update(
match.winner!,
(value) => value + 1,
ifAbsent: () => 1,
);
}
}
// Sort players by win count
final sortedPlayers = bestPlayerCounts.entries.toList()
..sort((a, b) => b.value.compareTo(a.value));
// Get the best player
bestPlayer = sortedPlayers.isNotEmpty ? sortedPlayers.first.key.name : '-';
return bestPlayer;
}
}

View File

@@ -1,17 +1,21 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/constants.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/presentation/views/main_menu/group_view/create_group_view.dart';
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart';
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/constants.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/data/dto/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/group_view/create_group_view.dart';
import 'package:tallee/presentation/views/main_menu/group_view/group_profile_view.dart';
import 'package:tallee/presentation/widgets/app_skeleton.dart';
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
import 'package:tallee/presentation/widgets/tiles/group_tile.dart';
import 'package:tallee/presentation/widgets/top_centered_message.dart';
class GroupsView extends StatefulWidget { class GroupsView extends StatefulWidget {
/// A view that displays a list of groups
const GroupsView({super.key}); const GroupsView({super.key});
@override @override
@@ -20,7 +24,11 @@ class GroupsView extends StatefulWidget {
class _GroupsViewState extends State<GroupsView> { class _GroupsViewState extends State<GroupsView> {
late final AppDatabase db; late final AppDatabase db;
/// Loaded groups from the database
late List<Group> loadedGroups; late List<Group> loadedGroups;
/// Loading state
bool isLoading = true; bool isLoading = true;
List<Group> groups = List.filled( List<Group> groups = List.filled(
@@ -34,12 +42,14 @@ class _GroupsViewState extends State<GroupsView> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
db = Provider.of<AppDatabase>(context, listen: false); db = Provider.of<AppDatabase>(context, listen: false);
loadGroups(); loadGroups();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold( return Scaffold(
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
body: Stack( body: Stack(
@@ -49,11 +59,11 @@ class _GroupsViewState extends State<GroupsView> {
enabled: isLoading, enabled: isLoading,
child: Visibility( child: Visibility(
visible: groups.isNotEmpty, visible: groups.isNotEmpty,
replacement: const Center( replacement: Center(
child: TopCenteredMessage( child: TopCenteredMessage(
icon: Icons.info, icon: Icons.info,
title: 'Info', title: loc.info,
message: 'No groups created yet', message: loc.no_groups_created_yet,
), ),
), ),
child: ListView.builder( child: ListView.builder(
@@ -65,20 +75,35 @@ class _GroupsViewState extends State<GroupsView> {
height: MediaQuery.paddingOf(context).bottom - 20, height: MediaQuery.paddingOf(context).bottom - 20,
); );
} }
return GroupTile(group: groups[index]); return GroupTile(
group: groups[index],
onTap: () async {
await Navigator.push(
context,
adaptivePageRoute(
builder: (context) {
return GroupProfileView(
group: groups[index],
callback: loadGroups,
);
},
),
);
},
);
}, },
), ),
), ),
), ),
Positioned( Positioned(
bottom: MediaQuery.paddingOf(context).bottom, bottom: MediaQuery.paddingOf(context).bottom + 20,
child: CustomWidthButton( child: MainMenuButton(
text: 'Create Group', text: loc.create_group,
sizeRelativeToWidth: 0.90, icon: Icons.group_add,
onPressed: () async { onPressed: () async {
await Navigator.push( await Navigator.push(
context, context,
MaterialPageRoute( adaptivePageRoute(
builder: (context) { builder: (context) {
return const CreateGroupView(); return const CreateGroupView();
}, },
@@ -96,9 +121,12 @@ class _GroupsViewState extends State<GroupsView> {
} }
void loadGroups() { void loadGroups() {
setState(() {
isLoading = true;
});
Future.wait([ Future.wait([
db.groupDao.getAllGroups(), db.groupDao.getAllGroups(),
Future.delayed(minimumSkeletonDuration), Future.delayed(Constants.MINIMUM_SKELETON_DURATION),
]).then((results) { ]).then((results) {
loadedGroups = results[0] as List<Group>; loadedGroups = results[0] as List<Group>;
setState(() { setState(() {

View File

@@ -1,17 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/constants.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
import 'package:game_tracker/presentation/widgets/buttons/quick_create_button.dart';
import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart';
import 'package:game_tracker/presentation/widgets/tiles/match_summary_tile.dart';
import 'package:game_tracker/presentation/widgets/tiles/quick_info_tile.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/constants.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/data/dto/match.dart';
import 'package:tallee/data/dto/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart';
import 'package:tallee/presentation/widgets/app_skeleton.dart';
import 'package:tallee/presentation/widgets/buttons/quick_create_button.dart';
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_tile.dart';
import 'package:tallee/presentation/widgets/tiles/quick_info_tile.dart';
class HomeView extends StatefulWidget { class HomeView extends StatefulWidget {
/// The main home view of the application, displaying quick info,
/// recent matches, and quick create options.
const HomeView({super.key}); const HomeView({super.key});
@override @override
@@ -20,9 +25,17 @@ class HomeView extends StatefulWidget {
class _HomeViewState extends State<HomeView> { class _HomeViewState extends State<HomeView> {
bool isLoading = true; bool isLoading = true;
/// Amount of matches in the database
int matchCount = 0; int matchCount = 0;
/// Amount of groups in the database
int groupCount = 0; int groupCount = 0;
/// Loaded recent matches from the database
List<Match> loadedRecentMatches = []; List<Match> loadedRecentMatches = [];
/// Recent matches to display, initially filled with skeleton matches
List<Match> recentMatches = List.filled( List<Match> recentMatches = List.filled(
2, 2,
Match( Match(
@@ -34,43 +47,18 @@ class _HomeViewState extends State<HomeView> {
Player(name: 'Skeleton Player 2'), Player(name: 'Skeleton Player 2'),
], ],
), ),
winner: Player(name: 'Skeleton Player 1'),
), ),
); );
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final db = Provider.of<AppDatabase>(context, listen: false); loadHomeViewData();
Future.wait([
db.matchDao.getMatchCount(),
db.groupDao.getGroupCount(),
db.matchDao.getAllMatches(),
Future.delayed(minimumSkeletonDuration),
]).then((results) {
matchCount = results[0] as int;
groupCount = results[1] as int;
loadedRecentMatches = results[2] as List<Match>;
recentMatches =
(loadedRecentMatches
..sort((a, b) => b.createdAt.compareTo(a.createdAt)))
.take(2)
.toList();
if (loadedRecentMatches.length < 2) {
recentMatches.add(
Match(name: 'Dummy Match', winner: null, group: null, players: null),
);
}
if (mounted) {
setState(() {
isLoading = false;
});
}
});
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return LayoutBuilder( return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) { builder: (BuildContext context, BoxConstraints constraints) {
return AppSkeleton( return AppSkeleton(
@@ -86,7 +74,7 @@ class _HomeViewState extends State<HomeView> {
QuickInfoTile( QuickInfoTile(
width: constraints.maxWidth * 0.45, width: constraints.maxWidth * 0.45,
height: constraints.maxHeight * 0.15, height: constraints.maxHeight * 0.15,
title: 'Matches', title: loc.matches,
icon: Icons.groups_rounded, icon: Icons.groups_rounded,
value: matchCount, value: matchCount,
), ),
@@ -94,7 +82,7 @@ class _HomeViewState extends State<HomeView> {
QuickInfoTile( QuickInfoTile(
width: constraints.maxWidth * 0.45, width: constraints.maxWidth * 0.45,
height: constraints.maxHeight * 0.15, height: constraints.maxHeight * 0.15,
title: 'Groups', title: loc.groups,
icon: Icons.groups_rounded, icon: Icons.groups_rounded,
value: groupCount, value: groupCount,
), ),
@@ -104,104 +92,93 @@ class _HomeViewState extends State<HomeView> {
padding: const EdgeInsets.symmetric(vertical: 16.0), padding: const EdgeInsets.symmetric(vertical: 16.0),
child: InfoTile( child: InfoTile(
width: constraints.maxWidth * 0.95, width: constraints.maxWidth * 0.95,
title: 'Recent Matches', title: loc.recent_matches,
icon: Icons.timer, icon: Icons.history_rounded,
content: Padding( content: Column(
padding: const EdgeInsets.symmetric(horizontal: 40.0), children: [
child: Visibility( if (recentMatches.isNotEmpty)
visible: !isLoading && loadedRecentMatches.isNotEmpty, for (Match match in recentMatches)
replacement: const Center( Padding(
heightFactor: 12, padding: const EdgeInsets.symmetric(
child: Text('No recent matches available'), vertical: 6.0,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MatchSummaryTile(
matchTitle: recentMatches[0].name,
game: 'Winner',
ruleset: 'Ruleset',
players: _getPlayerText(recentMatches[0]),
winner: recentMatches[0].winner == null
? 'Match in progress...'
: recentMatches[0].winner!.name,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Divider(),
),
if (loadedRecentMatches.length > 1) ...[
MatchSummaryTile(
matchTitle: recentMatches[1].name,
game: 'Winner',
ruleset: 'Ruleset',
players: _getPlayerText(recentMatches[1]),
winner: recentMatches[1].winner == null
? 'Match in progress...'
: recentMatches[1].winner!.name,
), ),
const SizedBox(height: 8), child: MatchTile(
] else ...[ compact: true,
const Center( width: constraints.maxWidth * 0.9,
heightFactor: 5.35, match: match,
child: Text('No second match available'), onTap: () async {
await Navigator.of(context).push(
adaptivePageRoute(
fullscreenDialog: true,
builder: (context) =>
MatchResultView(match: match),
),
);
await updatedWinnerinRecentMatches(match.id);
},
), ),
], )
], else
), Center(
), heightFactor: 5,
child: Text(loc.no_recent_matches_available),
),
],
), ),
), ),
), ),
InfoTile( Padding(
width: constraints.maxWidth * 0.95, padding: EdgeInsets.zero,
title: 'Quick Create', child: InfoTile(
icon: Icons.add_box_rounded, width: constraints.maxWidth * 0.95,
content: Column( title: loc.quick_create,
children: [ icon: Icons.add_box_rounded,
Row( content: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
children: [ Row(
QuickCreateButton( mainAxisAlignment: MainAxisAlignment.spaceEvenly,
text: 'Category 1', children: [
onPressed: () {}, QuickCreateButton(
), text: 'Category 1',
QuickCreateButton( onPressed: () {},
text: 'Category 2', ),
onPressed: () {}, QuickCreateButton(
), text: 'Category 2',
], onPressed: () {},
), ),
Row( ],
mainAxisAlignment: MainAxisAlignment.spaceEvenly, ),
children: [ Row(
QuickCreateButton( mainAxisAlignment: MainAxisAlignment.spaceEvenly,
text: 'Category 3', children: [
onPressed: () {}, QuickCreateButton(
), text: 'Category 3',
QuickCreateButton( onPressed: () {},
text: 'Category 4', ),
onPressed: () {}, QuickCreateButton(
), text: 'Category 4',
], onPressed: () {},
), ),
Row( ],
mainAxisAlignment: MainAxisAlignment.spaceEvenly, ),
children: [ Row(
QuickCreateButton( mainAxisAlignment: MainAxisAlignment.spaceEvenly,
text: 'Category 5', children: [
onPressed: () {}, QuickCreateButton(
), text: 'Category 5',
QuickCreateButton( onPressed: () {},
text: 'Category 6', ),
onPressed: () {}, QuickCreateButton(
), text: 'Category 6',
], onPressed: () {},
), ),
], ],
),
],
),
), ),
), ),
SizedBox(height: MediaQuery.paddingOf(context).bottom),
], ],
), ),
), ),
@@ -210,14 +187,41 @@ class _HomeViewState extends State<HomeView> {
); );
} }
String _getPlayerText(Match game) { /// Loads the data for the HomeView from the database.
if (game.group == null) { /// This includes the match count, group count, and recent matches.
final playerCount = game.players?.length ?? 0; Future<void> loadHomeViewData() async {
return '$playerCount Players'; final db = Provider.of<AppDatabase>(context, listen: false);
Future.wait([
db.matchDao.getMatchCount(),
db.groupDao.getGroupCount(),
db.matchDao.getAllMatches(),
Future.delayed(Constants.MINIMUM_SKELETON_DURATION),
]).then((results) {
matchCount = results[0] as int;
groupCount = results[1] as int;
loadedRecentMatches = results[2] as List<Match>;
recentMatches =
(loadedRecentMatches
..sort((a, b) => b.createdAt.compareTo(a.createdAt)))
.take(2)
.toList();
if (mounted) {
setState(() {
isLoading = false;
});
}
});
}
/// Updates the winner information for a specific match in the recent matches list.
Future<void> updatedWinnerinRecentMatches(String matchId) async {
final db = Provider.of<AppDatabase>(context, listen: false);
final winner = await db.matchDao.getWinner(matchId: matchId);
final matchIndex = recentMatches.indexWhere((match) => match.id == matchId);
if (matchIndex != -1) {
setState(() {
recentMatches[matchIndex].winner = winner;
});
} }
if (game.players == null || game.players!.isEmpty) {
return game.group!.name;
}
return '${game.group!.name} + ${game.players!.length}';
} }
} }

View File

@@ -1,27 +1,37 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:game_tracker/core/enums.dart'; import 'package:tallee/core/enums.dart';
import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart'; import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:tallee/presentation/widgets/tiles/title_description_list_tile.dart';
class ChooseGameView extends StatefulWidget { class ChooseGameView extends StatefulWidget {
final List<(String, String, Ruleset)> games; /// A view that allows the user to choose a game from a list of available games
final int initialGameIndex; /// - [games]: A list of tuples containing the game name, description and ruleset
/// - [initialGameIndex]: The index of the initially selected game
const ChooseGameView({ const ChooseGameView({
super.key, super.key,
required this.games, required this.games,
required this.initialGameIndex, required this.initialGameIndex,
}); });
/// A list of tuples containing the game name, description and ruleset
final List<(String, String, Ruleset)> games;
/// The index of the initially selected game
final int initialGameIndex;
@override @override
State<ChooseGameView> createState() => _ChooseGameViewState(); State<ChooseGameView> createState() => _ChooseGameViewState();
} }
class _ChooseGameViewState extends State<ChooseGameView> { class _ChooseGameViewState extends State<ChooseGameView> {
late int selectedGameIndex; /// Controller for the search bar
final TextEditingController searchBarController = TextEditingController(); final TextEditingController searchBarController = TextEditingController();
/// Currently selected game index
late int selectedGameIndex;
@override @override
void initState() { void initState() {
selectedGameIndex = widget.initialGameIndex; selectedGameIndex = widget.initialGameIndex;
@@ -30,22 +40,18 @@ class _ChooseGameViewState extends State<ChooseGameView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold( return Scaffold(
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
resizeToAvoidBottomInset: false,
appBar: AppBar( appBar: AppBar(
backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0,
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.arrow_back_ios), icon: const Icon(Icons.arrow_back_ios),
onPressed: () { onPressed: () {
Navigator.of(context).pop(selectedGameIndex); Navigator.of(context).pop(selectedGameIndex);
}, },
), ),
title: const Text( title: Text(loc.choose_game),
'Choose Game',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
centerTitle: true,
), ),
body: PopScope( body: PopScope(
// This fixes that the Android Back Gesture didn't return the // This fixes that the Android Back Gesture didn't return the
@@ -63,7 +69,7 @@ class _ChooseGameViewState extends State<ChooseGameView> {
padding: const EdgeInsets.symmetric(horizontal: 10), padding: const EdgeInsets.symmetric(horizontal: 10),
child: CustomSearchBar( child: CustomSearchBar(
controller: searchBarController, controller: searchBarController,
hintText: 'Game Name', hintText: loc.game_name,
), ),
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
@@ -74,7 +80,10 @@ class _ChooseGameViewState extends State<ChooseGameView> {
return TitleDescriptionListTile( return TitleDescriptionListTile(
title: widget.games[index].$1, title: widget.games[index].$1,
description: widget.games[index].$2, description: widget.games[index].$2,
badgeText: translateRulesetToString(widget.games[index].$3), badgeText: translateRulesetToString(
widget.games[index].$3,
context,
),
isHighlighted: selectedGameIndex == index, isHighlighted: selectedGameIndex == index,
onPressed: () async { onPressed: () async {
setState(() { setState(() {

View File

@@ -1,20 +1,27 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:game_tracker/data/dto/group.dart'; import 'package:tallee/data/dto/group.dart';
import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart'; import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:tallee/presentation/widgets/tiles/group_tile.dart';
import 'package:tallee/presentation/widgets/top_centered_message.dart';
class ChooseGroupView extends StatefulWidget { class ChooseGroupView extends StatefulWidget {
final List<Group> groups; /// A view that allows the user to choose a group from a list of groups.
final String initialGroupId; /// - [groups]: A list of available groups to choose from
/// - [initialGroupId]: The ID of the initially selected group
const ChooseGroupView({ const ChooseGroupView({
super.key, super.key,
required this.groups, required this.groups,
required this.initialGroupId, required this.initialGroupId,
}); });
/// A list of available groups to choose from
final List<Group> groups;
/// The ID of the initially selected group
final String initialGroupId;
@override @override
State<ChooseGroupView> createState() => _ChooseGroupViewState(); State<ChooseGroupView> createState() => _ChooseGroupViewState();
} }
@@ -22,7 +29,6 @@ class ChooseGroupView extends StatefulWidget {
class _ChooseGroupViewState extends State<ChooseGroupView> { class _ChooseGroupViewState extends State<ChooseGroupView> {
late String selectedGroupId; late String selectedGroupId;
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
final String hintText = 'Group Name';
late final List<Group> filteredGroups; late final List<Group> filteredGroups;
@override @override
@@ -34,11 +40,11 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold( return Scaffold(
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
resizeToAvoidBottomInset: false,
appBar: AppBar( appBar: AppBar(
backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0,
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.arrow_back_ios), icon: const Icon(Icons.arrow_back_ios),
onPressed: () { onPressed: () {
@@ -51,11 +57,7 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
); );
}, },
), ),
title: const Text( title: Text(loc.choose_group),
'Choose Group',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
centerTitle: true,
), ),
body: PopScope( body: PopScope(
// This fixes that the Android Back Gesture didn't return the // This fixes that the Android Back Gesture didn't return the
@@ -79,7 +81,7 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
padding: const EdgeInsets.symmetric(horizontal: 10), padding: const EdgeInsets.symmetric(horizontal: 10),
child: CustomSearchBar( child: CustomSearchBar(
controller: controller, controller: controller,
hintText: hintText, hintText: loc.search_for_groups,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
filterGroups(value); filterGroups(value);
@@ -92,15 +94,17 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
visible: filteredGroups.isNotEmpty, visible: filteredGroups.isNotEmpty,
replacement: Visibility( replacement: Visibility(
visible: widget.groups.isNotEmpty, visible: widget.groups.isNotEmpty,
replacement: const TopCenteredMessage( replacement: TopCenteredMessage(
icon: Icons.info, icon: Icons.info,
title: 'Info', title: loc.info,
message: 'You have no groups created yet', message: loc.no_groups_created_yet,
), ),
child: const TopCenteredMessage( child: TopCenteredMessage(
icon: Icons.info, icon: Icons.info,
title: 'Info', title: loc.info,
message: 'There is no group matching your search', message: AppLocalizations.of(
context,
).there_is_no_group_matching_your_search,
), ),
), ),
child: ListView.builder( child: ListView.builder(
@@ -133,8 +137,7 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
); );
} }
/// Filters the groups based on the search query. /// Filters the groups based on the search [query].
/// TODO: Maybe implement also targetting player names?
void filterGroups(String query) { void filterGroups(String query) {
setState(() { setState(() {
if (query.isEmpty) { if (query.isEmpty) {
@@ -144,7 +147,12 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
filteredGroups.clear(); filteredGroups.clear();
filteredGroups.addAll( filteredGroups.addAll(
widget.groups.where( widget.groups.where(
(group) => group.name.toLowerCase().contains(query.toLowerCase()), (group) =>
group.name.toLowerCase().contains(query.toLowerCase()) ||
group.members.any(
(player) =>
player.name.toLowerCase().contains(query.toLowerCase()),
),
), ),
); );
} }

View File

@@ -1,93 +0,0 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/core/enums.dart';
import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart';
class ChooseRulesetView extends StatefulWidget {
final List<(Ruleset, String)> rulesets;
final int initialRulesetIndex;
const ChooseRulesetView({
super.key,
required this.rulesets,
required this.initialRulesetIndex,
});
@override
State<ChooseRulesetView> createState() => _ChooseRulesetViewState();
}
class _ChooseRulesetViewState extends State<ChooseRulesetView> {
late int selectedRulesetIndex;
@override
void initState() {
selectedRulesetIndex = widget.initialRulesetIndex;
super.initState();
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
initialIndex: 0,
child: Scaffold(
backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar(
backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.of(context).pop(
selectedRulesetIndex == -1
? null
: widget.rulesets[selectedRulesetIndex].$1,
);
},
),
title: const Text(
'Choose Ruleset',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
centerTitle: true,
),
body: PopScope(
// This fixes that the Android Back Gesture didn't return the
// selectedRulesetIndex and therefore the selected Ruleset wasn't saved
canPop: false,
onPopInvokedWithResult: (bool didPop, Object? result) {
if (didPop) {
return;
}
Navigator.of(context).pop(
selectedRulesetIndex == -1
? null
: widget.rulesets[selectedRulesetIndex].$1,
);
},
child: ListView.builder(
padding: const EdgeInsets.only(bottom: 85),
itemCount: widget.rulesets.length,
itemBuilder: (BuildContext context, int index) {
return TitleDescriptionListTile(
onPressed: () async {
setState(() {
if (selectedRulesetIndex == index) {
selectedRulesetIndex = -1;
} else {
selectedRulesetIndex = index;
}
});
},
title: translateRulesetToString(widget.rulesets[index].$1),
description: widget.rulesets[index].$2,
isHighlighted: selectedRulesetIndex == index,
);
},
),
),
),
);
}
}

View File

@@ -1,38 +1,42 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/core/enums.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/choose_game_view.dart';
import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/choose_group_view.dart';
import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/choose_ruleset_view.dart';
import 'package:game_tracker/presentation/views/main_menu/match_view/match_result_view.dart';
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
import 'package:game_tracker/presentation/widgets/player_selection.dart';
import 'package:game_tracker/presentation/widgets/text_input/text_input_field.dart';
import 'package:game_tracker/presentation/widgets/tiles/choose_tile.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/constants.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/data/dto/match.dart';
import 'package:tallee/data/dto/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/match_view/create_match/choose_game_view.dart';
import 'package:tallee/presentation/views/main_menu/match_view/create_match/choose_group_view.dart';
import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart';
import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
import 'package:tallee/presentation/widgets/player_selection.dart';
import 'package:tallee/presentation/widgets/text_input/text_input_field.dart';
import 'package:tallee/presentation/widgets/tiles/choose_tile.dart';
class CreateMatchView extends StatefulWidget { class CreateMatchView extends StatefulWidget {
final VoidCallback? onWinnerChanged; /// A view that allows creating a new match
/// [onWinnerChanged]: Optional callback invoked when the winner is changed
const CreateMatchView({super.key, this.onWinnerChanged}); const CreateMatchView({super.key, this.onWinnerChanged});
/// Optional callback invoked when the winner is changed
final VoidCallback? onWinnerChanged;
@override @override
State<CreateMatchView> createState() => _CreateMatchViewState(); State<CreateMatchView> createState() => _CreateMatchViewState();
} }
class _CreateMatchViewState extends State<CreateMatchView> { class _CreateMatchViewState extends State<CreateMatchView> {
/// Reference to the app database
late final AppDatabase db; late final AppDatabase db;
/// Controller for the match name input field /// Controller for the match name input field
final TextEditingController _matchNameController = TextEditingController(); final TextEditingController _matchNameController = TextEditingController();
/// Hint text for the match name input field /// Hint text for the match name input field
String hintText = 'Match Name'; String? hintText;
/// List of all groups from the database /// List of all groups from the database
List<Group> groupsList = []; List<Group> groupsList = [];
@@ -53,13 +57,6 @@ class _CreateMatchViewState extends State<CreateMatchView> {
/// the [ChooseGroupView] /// the [ChooseGroupView]
String selectedGroupId = ''; String selectedGroupId = '';
/// The currently selected ruleset
Ruleset? selectedRuleset;
/// The index of the currently selected ruleset in [rulesets] to mark it in
/// the [ChooseRulesetView]
int selectedRulesetIndex = -1;
/// The index of the currently selected game in [games] to mark it in /// The index of the currently selected game in [games] to mark it in
/// the [ChooseGameView] /// the [ChooseGameView]
int selectedGameIndex = -1; int selectedGameIndex = -1;
@@ -67,34 +64,6 @@ class _CreateMatchViewState extends State<CreateMatchView> {
/// The currently selected players /// The currently selected players
List<Player>? selectedPlayers; List<Player>? selectedPlayers;
/// List of available rulesets with their descriptions
/// as tuples of (Ruleset, String)
/// TODO: Replace when rulesets are implemented
List<(Ruleset, String)> rulesets = [
(
Ruleset.singleWinner,
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.',
),
(
Ruleset.singleLoser,
'Exactly one loser is determined; last place receives the penalty or consequence.',
),
(
Ruleset.mostPoints,
'Traditional ruleset: the player with the most points wins.',
),
(
Ruleset.leastPoints,
'Inverse scoring: the player with the fewest points wins.',
),
];
// TODO: Replace when games are implemented
List<(String, String, Ruleset)> games = [
('Example Game 1', 'This is a discription', Ruleset.leastPoints),
('Example Game 2', '', Ruleset.singleWinner),
];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -116,159 +85,151 @@ class _CreateMatchViewState extends State<CreateMatchView> {
}); });
} }
@override
void dispose() {
_matchNameController.dispose();
super.dispose();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
final loc = AppLocalizations.of(context);
hintText ??= loc.match_name;
}
List<(String, String, Ruleset)> games = [
('Example Game 1', 'This is a description', Ruleset.leastPoints),
('Example Game 2', '', Ruleset.singleWinner),
];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( final loc = AppLocalizations.of(context);
backgroundColor: CustomTheme.backgroundColor, return ScaffoldMessenger(
appBar: AppBar( child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0, appBar: AppBar(title: Text(loc.create_new_match)),
title: const Text( body: SafeArea(
'Create new match', child: Column(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), mainAxisAlignment: MainAxisAlignment.start,
), children: [
centerTitle: true, Container(
), margin: CustomTheme.tileMargin,
body: SafeArea( child: TextInputField(
child: Column( controller: _matchNameController,
mainAxisAlignment: MainAxisAlignment.start, hintText: hintText ?? '',
children: [ maxLength: Constants.MAX_MATCH_NAME_LENGTH,
Container( ),
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 5),
child: TextInputField(
controller: _matchNameController,
hintText: hintText,
), ),
), ChooseTile(
ChooseTile( title: loc.game,
title: 'Game', trailingText: selectedGameIndex == -1
trailingText: selectedGameIndex == -1 ? loc.none
? 'None' : games[selectedGameIndex].$1,
: games[selectedGameIndex].$1, onPressed: () async {
onPressed: () async { selectedGameIndex = await Navigator.of(context).push(
selectedGameIndex = await Navigator.of(context).push( adaptivePageRoute(
MaterialPageRoute( builder: (context) => ChooseGameView(
builder: (context) => ChooseGameView( games: games,
games: games, initialGameIndex: selectedGameIndex,
initialGameIndex: selectedGameIndex, ),
), ),
), );
);
setState(() {
if (selectedGameIndex != -1) {
hintText = games[selectedGameIndex].$1;
selectedRuleset = games[selectedGameIndex].$3;
selectedRulesetIndex = rulesets.indexWhere(
(r) => r.$1 == selectedRuleset,
);
} else {
hintText = 'Match Name';
selectedRuleset = null;
}
});
},
),
ChooseTile(
title: 'Ruleset',
trailingText: selectedRuleset == null
? 'None'
: translateRulesetToString(selectedRuleset!),
onPressed: () async {
selectedRuleset = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChooseRulesetView(
rulesets: rulesets,
initialRulesetIndex: selectedRulesetIndex,
),
),
);
selectedRulesetIndex = rulesets.indexWhere(
(r) => r.$1 == selectedRuleset,
);
selectedGameIndex = -1;
setState(() {});
},
),
ChooseTile(
title: 'Group',
trailingText: selectedGroup == null
? 'None'
: selectedGroup!.name,
onPressed: () async {
selectedGroup = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChooseGroupView(
groups: groupsList,
initialGroupId: selectedGroupId,
),
),
);
selectedGroupId = selectedGroup?.id ?? '';
if (selectedGroup != null) {
filteredPlayerList = playerList
.where(
(p) => !selectedGroup!.members.any((m) => m.id == p.id),
)
.toList();
} else {
filteredPlayerList = List.from(playerList);
}
setState(() {});
},
),
Expanded(
child: PlayerSelection(
key: ValueKey(selectedGroup?.id ?? 'no_group'),
initialSelectedPlayers: selectedPlayers ?? [],
availablePlayers: filteredPlayerList,
onChanged: (value) {
setState(() { setState(() {
selectedPlayers = value; if (selectedGameIndex != -1) {
hintText = games[selectedGameIndex].$1;
} else {
hintText = loc.match_name;
}
}); });
}, },
), ),
), ChooseTile(
CustomWidthButton( title: loc.group,
text: 'Create match', trailingText: selectedGroup == null
sizeRelativeToWidth: 0.95, ? loc.none_group
buttonType: ButtonType.primary, : selectedGroup!.name,
onPressed: _enableCreateGameButton() onPressed: () async {
? () async { selectedGroup = await Navigator.of(context).push(
Match match = Match( adaptivePageRoute(
name: _matchNameController.text.isEmpty builder: (context) => ChooseGroupView(
? hintText groups: groupsList,
: _matchNameController.text.trim(), initialGroupId: selectedGroupId,
createdAt: DateTime.now(), ),
group: selectedGroup, ),
players: selectedPlayers, );
); selectedGroupId = selectedGroup?.id ?? '';
await db.matchDao.addMatch(match: match); if (selectedGroup != null) {
if (context.mounted) { filteredPlayerList = playerList
Navigator.pushReplacement( .where(
context, (p) =>
CupertinoPageRoute( !selectedGroup!.members.any((m) => m.id == p.id),
fullscreenDialog: true, )
builder: (context) => MatchResultView( .toList();
match: match, } else {
onWinnerChanged: widget.onWinnerChanged, filteredPlayerList = List.from(playerList);
), }
), setState(() {});
},
),
Expanded(
child: PlayerSelection(
key: ValueKey(selectedGroup?.id ?? 'no_group'),
initialSelectedPlayers: selectedPlayers ?? [],
availablePlayers: filteredPlayerList,
onChanged: (value) {
setState(() {
selectedPlayers = value;
});
},
),
),
CustomWidthButton(
text: loc.create_match,
sizeRelativeToWidth: 0.95,
buttonType: ButtonType.primary,
onPressed: _enableCreateGameButton()
? () async {
Match match = Match(
name: _matchNameController.text.isEmpty
? (hintText ?? '')
: _matchNameController.text.trim(),
createdAt: DateTime.now(),
group: selectedGroup,
players: selectedPlayers,
); );
await db.matchDao.addMatch(match: match);
if (context.mounted) {
Navigator.pushReplacement(
context,
adaptivePageRoute(
fullscreenDialog: true,
builder: (context) => MatchResultView(
match: match,
onWinnerChanged: widget.onWinnerChanged,
),
),
);
}
} }
} : null,
: null, ),
), ],
], ),
), ),
), ),
); );
} }
/// Determines whether the "Create Game" button should be enabled based on /// Determines whether the "Create Match" button should be enabled.
/// the current state of the input fields. ///
/// Returns `true` if:
/// - A ruleset is selected AND
/// - Either a group is selected OR at least 2 players are selected
bool _enableCreateGameButton() { bool _enableCreateGameButton() {
return selectedGroup != null || return (selectedGroup != null ||
(selectedPlayers != null && selectedPlayers!.length > 1) && (selectedPlayers != null && selectedPlayers!.length > 1));
selectedRuleset != null;
} }
} }

View File

@@ -1,24 +1,35 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/presentation/widgets/tiles/custom_radio_list_tile.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/dto/match.dart';
import 'package:tallee/data/dto/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/tiles/custom_radio_list_tile.dart';
class MatchResultView extends StatefulWidget { class MatchResultView extends StatefulWidget {
/// A view that allows selecting and saving the winner of a match
/// [match]: The match for which the winner is to be selected
/// [onWinnerChanged]: Optional callback invoked when the winner is changed
const MatchResultView({super.key, required this.match, this.onWinnerChanged});
/// The match for which the winner is to be selected
final Match match; final Match match;
/// Optional callback invoked when the winner is changed
final VoidCallback? onWinnerChanged; final VoidCallback? onWinnerChanged;
const MatchResultView({super.key, required this.match, this.onWinnerChanged});
@override @override
State<MatchResultView> createState() => _MatchResultViewState(); State<MatchResultView> createState() => _MatchResultViewState();
} }
class _MatchResultViewState extends State<MatchResultView> { class _MatchResultViewState extends State<MatchResultView> {
late final List<Player> allPlayers;
late final AppDatabase db; late final AppDatabase db;
/// List of all players who participated in the match
late final List<Player> allPlayers;
/// Currently selected winner player
Player? _selectedPlayer; Player? _selectedPlayer;
@override @override
@@ -35,6 +46,7 @@ class _MatchResultViewState extends State<MatchResultView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold( return Scaffold(
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar( appBar: AppBar(
@@ -45,17 +57,7 @@ class _MatchResultViewState extends State<MatchResultView> {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),
backgroundColor: CustomTheme.backgroundColor, title: Text(widget.match.name),
scrolledUnderElevation: 0,
title: Text(
widget.match.name,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
overflow: TextOverflow.ellipsis,
),
),
centerTitle: true,
), ),
body: SafeArea( body: SafeArea(
child: Column( child: Column(
@@ -72,16 +74,16 @@ class _MatchResultViewState extends State<MatchResultView> {
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: CustomTheme.boxColor, color: CustomTheme.boxColor,
border: Border.all(color: CustomTheme.boxBorder), border: Border.all(color: CustomTheme.boxBorderColor),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'Select Winner:', loc.select_winner,
style: TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -130,6 +132,8 @@ class _MatchResultViewState extends State<MatchResultView> {
); );
} }
/// Handles saving or removing the winner in the database
/// based on the current selection.
Future<void> _handleWinnerSaving() async { Future<void> _handleWinnerSaving() async {
if (_selectedPlayer == null) { if (_selectedPlayer == null) {
await db.matchDao.removeWinner(matchId: widget.match.id); await db.matchDao.removeWinner(matchId: widget.match.id);
@@ -142,6 +146,10 @@ class _MatchResultViewState extends State<MatchResultView> {
widget.onWinnerChanged?.call(); widget.onWinnerChanged?.call();
} }
/// Retrieves all players associated with the given [match].
/// This includes players directly assigned to the match
/// as well as members of the group (if any).
/// The returned list is sorted alphabetically by player name.
List<Player> getAllPlayers(Match match) { List<Player> getAllPlayers(Match match) {
List<Player> players = []; List<Player> players = [];

View File

@@ -1,22 +1,25 @@
import 'dart:core' hide Match; import 'dart:core' hide Match;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/constants.dart'; import 'package:fluttericon/rpg_awesome_icons.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/create_match_view.dart';
import 'package:game_tracker/presentation/views/main_menu/match_view/match_result_view.dart';
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
import 'package:game_tracker/presentation/widgets/tiles/match_tile.dart';
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/constants.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/data/dto/match.dart';
import 'package:tallee/data/dto/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_match_view.dart';
import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart';
import 'package:tallee/presentation/widgets/app_skeleton.dart';
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
import 'package:tallee/presentation/widgets/tiles/match_tile.dart';
import 'package:tallee/presentation/widgets/top_centered_message.dart';
class MatchView extends StatefulWidget { class MatchView extends StatefulWidget {
/// A view that displays a list of matches
const MatchView({super.key}); const MatchView({super.key});
@override @override
@@ -27,6 +30,8 @@ class _MatchViewState extends State<MatchView> {
late final AppDatabase db; late final AppDatabase db;
bool isLoading = true; bool isLoading = true;
/// Loaded matches from the database,
/// initially filled with skeleton matches
List<Match> matches = List.filled( List<Match> matches = List.filled(
4, 4,
Match( Match(
@@ -49,6 +54,7 @@ class _MatchViewState extends State<MatchView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold( return Scaffold(
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
body: Stack( body: Stack(
@@ -58,11 +64,11 @@ class _MatchViewState extends State<MatchView> {
enabled: isLoading, enabled: isLoading,
child: Visibility( child: Visibility(
visible: matches.isNotEmpty, visible: matches.isNotEmpty,
replacement: const Center( replacement: Center(
child: TopCenteredMessage( child: TopCenteredMessage(
icon: Icons.report, icon: Icons.report,
title: 'Info', title: loc.info,
message: 'No games created yet', message: loc.no_matches_created_yet,
), ),
), ),
child: ListView.builder( child: ListView.builder(
@@ -74,34 +80,40 @@ class _MatchViewState extends State<MatchView> {
height: MediaQuery.paddingOf(context).bottom - 20, height: MediaQuery.paddingOf(context).bottom - 20,
); );
} }
return MatchTile( return Center(
onTap: () async { child: Padding(
Navigator.push( padding: const EdgeInsets.only(bottom: 12.0),
context, child: MatchTile(
CupertinoPageRoute( width: MediaQuery.sizeOf(context).width * 0.95,
fullscreenDialog: true, onTap: () async {
builder: (context) => MatchResultView( Navigator.push(
match: matches[index], context,
onWinnerChanged: loadGames, adaptivePageRoute(
), fullscreenDialog: true,
), builder: (context) => MatchResultView(
); match: matches[index],
}, onWinnerChanged: loadGames,
match: matches[index], ),
),
);
},
match: matches[index],
),
),
); );
}, },
), ),
), ),
), ),
Positioned( Positioned(
bottom: MediaQuery.paddingOf(context).bottom, bottom: MediaQuery.paddingOf(context).bottom + 20,
child: CustomWidthButton( child: MainMenuButton(
text: 'Create Match', text: 'Spiel erstellen',
sizeRelativeToWidth: 0.90, icon: RpgAwesome.clovers_card,
onPressed: () async { onPressed: () async {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( adaptivePageRoute(
builder: (context) => builder: (context) =>
CreateMatchView(onWinnerChanged: loadGames), CreateMatchView(onWinnerChanged: loadGames),
), ),
@@ -114,10 +126,11 @@ class _MatchViewState extends State<MatchView> {
); );
} }
/// Loads the games from the database and sorts them by creation date.
void loadGames() { void loadGames() {
Future.wait([ Future.wait([
db.matchDao.getAllMatches(), db.matchDao.getAllMatches(),
Future.delayed(minimumSkeletonDuration), Future.delayed(Constants.MINIMUM_SKELETON_DURATION),
]).then((results) { ]).then((results) {
if (mounted) { if (mounted) {
setState(() { setState(() {

View File

@@ -1,191 +0,0 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/core/enums.dart';
import 'package:game_tracker/presentation/widgets/tiles/settings_list_tile.dart';
import 'package:game_tracker/services/data_transfer_service.dart';
class SettingsView extends StatefulWidget {
const SettingsView({super.key});
@override
State<SettingsView> createState() => _SettingsViewState();
}
class _SettingsViewState extends State<SettingsView> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(backgroundColor: CustomTheme.backgroundColor),
backgroundColor: CustomTheme.backgroundColor,
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) =>
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.fromLTRB(24, 0, 24, 10),
child: Text(
textAlign: TextAlign.start,
'Menu',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
child: Text(
textAlign: TextAlign.start,
'Settings',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
),
SettingsListTile(
title: 'Export data',
icon: Icons.upload_outlined,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () async {
final String json =
await DataTransferService.getAppDataAsJson(context);
final result = await DataTransferService.exportData(
json,
'game_tracker-data',
);
if (!context.mounted) return;
showExportSnackBar(context: context, result: result);
},
),
SettingsListTile(
title: 'Import data',
icon: Icons.download_outlined,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () async {
final result = await DataTransferService.importData(
context,
);
if (!context.mounted) return;
showImportSnackBar(context: context, result: result);
},
),
SettingsListTile(
title: 'Delete all data',
icon: Icons.download_outlined,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () {
showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Delete all data?'),
content: const Text('This can\'t be undone'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Abbrechen'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Löschen'),
),
],
),
).then((confirmed) {
if (confirmed == true && context.mounted) {
DataTransferService.deleteAllData(context);
showSnackbar(
context: context,
message: 'Daten erfolgreich gelöscht',
);
}
});
},
),
],
),
),
),
);
}
/// Displays a snackbar based on the import result.
///
/// [context] The BuildContext to show the snackbar in.
/// [result] The result of the import operation.
void showImportSnackBar({
required BuildContext context,
required ImportResult result,
}) {
switch (result) {
case ImportResult.success:
showSnackbar(context: context, message: 'Data successfully imported');
case ImportResult.invalidSchema:
showSnackbar(context: context, message: 'Invalid Schema');
case ImportResult.fileReadError:
showSnackbar(context: context, message: 'Error reading file');
case ImportResult.canceled:
showSnackbar(context: context, message: 'Import canceled');
case ImportResult.formatException:
showSnackbar(
context: context,
message: 'Format Exception (see console)',
);
case ImportResult.unknownException:
showSnackbar(
context: context,
message: 'Unknown Exception (see console)',
);
}
}
/// Displays a snackbar based on the export result.
///
/// [context] The BuildContext to show the snackbar in.
/// [result] The result of the export operation.
void showExportSnackBar({
required BuildContext context,
required ExportResult result,
}) {
switch (result) {
case ExportResult.success:
showSnackbar(context: context, message: 'Data successfully exported');
case ExportResult.canceled:
showSnackbar(context: context, message: 'Export canceled');
case ExportResult.unknownException:
showSnackbar(
context: context,
message: 'Unknown Exception (see console)',
);
}
}
/// Displays a snackbar with the given message and optional action.
///
/// [context] The BuildContext to show the snackbar in.
/// [message] The message to display in the snackbar.
/// [duration] The duration for which the snackbar is displayed.
/// [action] An optional callback function to execute when the action button is pressed.
void showSnackbar({
required BuildContext context,
required String message,
Duration duration = const Duration(seconds: 3),
VoidCallback? action,
}) {
final messenger = ScaffoldMessenger.of(context);
messenger.hideCurrentSnackBar();
messenger.showSnackBar(
SnackBar(
content: Text(message, style: const TextStyle(color: Colors.white)),
backgroundColor: CustomTheme.onBoxColor,
duration: duration,
action: action != null
? SnackBarAction(label: 'Rückgängig', onPressed: action)
: null,
),
);
}
}

View File

@@ -0,0 +1,133 @@
import 'package:flutter/material.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart';
import 'package:url_launcher/url_launcher.dart';
class LicenseDetailView extends StatelessWidget {
/// A detailed view displaying information about a software package license.
/// - [package]: The package data to be displayed.
const LicenseDetailView({super.key, required this.package});
/// The package data to be displayed.
final Package package;
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold(
appBar: AppBar(title: const Text('Lizenzdetails')),
body: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const ColoredIconContainer(
icon: Icons.description,
margin: EdgeInsetsGeometry.only(right: 15),
containerSize: 60,
iconSize: 30,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
package.name,
textAlign: TextAlign.left,
style: const TextStyle(
height: 0,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
if (package.version != null) ...[
Text(
'Version ${package.version}',
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade300,
),
),
],
],
),
],
),
if (package.authors.isNotEmpty) ...[
const SizedBox(height: 8),
SelectableText(
package.authors.join(', '),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade500,
),
),
],
if (package.homepage != null &&
package.homepage!.isNotEmpty) ...[
const SizedBox(height: 8),
GestureDetector(
onTap: () async {
final uri = Uri.parse(package.homepage!);
await launchUrl(uri, mode: LaunchMode.platformDefault);
},
child: SizedBox(
width: 300,
child: Text(
package.homepage!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 12,
color: CustomTheme.secondaryColor,
decoration: TextDecoration.underline,
decorationColor: CustomTheme.secondaryColor,
),
),
),
),
],
],
),
),
const SizedBox(height: 20),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
decoration: CustomTheme.standardBoxDecoration,
child: (package.license != null && package.license!.isNotEmpty)
? SelectableText(
package.license!,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade300,
height: 1.5,
fontFamily: 'monospace',
),
)
: Center(
heightFactor: 25,
child: Text(
loc.no_license_text_available,
style: TextStyle(color: Colors.grey.shade500),
),
),
),
],
),
),
);
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More