271 Commits

Author SHA1 Message Date
e71943f6e2 Implemented Radio Theme
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 35s
Pull Request Pipeline / lint (pull_request) Successful in 46s
2026-02-24 18:01:10 +01:00
f07103a516 Fixed theme issue 2026-02-24 17:49:24 +01:00
b84a893706 Typo
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 36s
Pull Request Pipeline / lint (pull_request) Successful in 42s
2026-02-23 21:35:26 +01:00
527ffd194f Fixed PR problems
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 37s
Pull Request Pipeline / lint (pull_request) Successful in 44s
2026-02-23 21:34:29 +01:00
9e9491c98e Merge remote-tracking branch 'refs/remotes/origin/development' into feature/120-bearbeiten-und-loeschen-von-matches
# Conflicts:
#	lib/presentation/views/main_menu/custom_navigation_bar.dart
#	lib/presentation/views/main_menu/group_view/create_group_view.dart
#	lib/presentation/views/main_menu/group_view/group_detail_view.dart
#	lib/presentation/views/main_menu/group_view/group_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
#	pubspec.yaml
2026-02-23 21:23:28 +01:00
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
9a5929382b Changed button icon
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-18 13:08:33 +01:00
cca09cc27e Renamed MatchProfileView to MatchDetailView 2026-01-18 13:01:24 +01:00
f00aa15518 Merge remote-tracking branch 'origin/feature/118-bearbeiten-und-löschen-von-gruppen' into feature/120-bearbeiten-und-loeschen-von-matches
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m15s
# Conflicts:
#	lib/l10n/generated/app_localizations.dart
2026-01-18 12:59:56 +01:00
810f635987 merge fix
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m2s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-18 12:25:47 +01:00
49a6259d8a Merge remote-tracking branch 'origin/development' into feature/118-bearbeiten-und-löschen-von-gruppen
# Conflicts:
#	lib/presentation/views/main_menu/group_view/create_group_view.dart
#	pubspec.yaml
2026-01-18 12:24:07 +01: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
e4c3bc1c5e merge & made group_detail_view.dart & group_create_view.dart work together when editing
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-18 11:41:50 +01:00
14d30f55a7 Merge remote-tracking branch 'origin/development' into feature/118-bearbeiten-und-löschen-von-gruppen
# Conflicts:
#	lib/l10n/arb/app_de.arb
#	lib/l10n/arb/app_en.arb
#	lib/l10n/generated/app_localizations.dart
#	lib/l10n/generated/app_localizations_de.dart
#	lib/l10n/generated/app_localizations_en.dart
#	lib/presentation/views/main_menu/group_view/groups_view.dart
#	lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart
#	lib/presentation/widgets/tiles/group_tile.dart
#	pubspec.yaml
2026-01-18 11:15:04 +01:00
f1df067824 delete group view & update build nr
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m14s
2026-01-18 10:51:53 +01:00
765610b184 Enhanced editing
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m18s
2026-01-18 01:02:35 +01:00
48d99a0386 Added match editing 2026-01-18 00:46:50 +01:00
a56e738064 Implemented winner update 2026-01-18 00:42:18 +01:00
1450e9b958 Updated attributes 2026-01-18 00:36:21 +01:00
eb114f2853 Added empty winner tile 2026-01-18 00:36:15 +01:00
7faf80de03 Updated extra player count 2026-01-18 00:14:45 +01:00
8fe01c332e Added comment 2026-01-18 00:10:31 +01:00
374c9295ef Implemented MatchProfileView 2026-01-18 00:08:39 +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
8b7300eac3 Merge remote-tracking branch 'origin/development' into feature/118-bearbeiten-und-löschen-von-gruppen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m7s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
# Conflicts:
#	pubspec.yaml
2026-01-17 16:05:08 +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
919a38afe5 fix merge conflicts
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-17 10:21:55 +01:00
def31acfb1 Merge remote-tracking branch 'origin/development' into feature/118-bearbeiten-und-löschen-von-gruppen
# Conflicts:
#	lib/presentation/views/main_menu/group_view/create_group_view.dart
#	lib/presentation/views/main_menu/settings_view/settings_view.dart
#	lib/presentation/widgets/tiles/group_tile.dart
2026-01-17 10:16:41 +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
016c1ceb6e add context to mounted check
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:38:53 +01:00
1b297d15b0 fix snackbar showing also showing on other screens (#155)
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m10s
Pull Request Pipeline / lint (pull_request) Successful in 2m12s
2026-01-13 21:35:10 +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
ed642e3d4f merge dev into #118
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 1m59s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
2026-01-13 21:07:23 +01:00
7cc3873a31 Merge remote-tracking branch 'origin/development' into feature/118-bearbeiten-und-löschen-von-gruppen
# Conflicts:
#	lib/presentation/views/main_menu/settings_view.dart
2026-01-13 21:02:04 +01:00
b1e9bb3aeb update localization comments for clarity in group and match creation 2026-01-13 21:01:31 +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
caf60d046b fix merge mistake
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 3m9s
Pull Request Pipeline / test (pull_request) Successful in 2m30s
2026-01-10 22:20:14 +01:00
38663c6b67 Merge remote-tracking branch 'origin/development' into feature/118-bearbeiten-und-löschen-von-gruppen
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m33s
Pull Request Pipeline / lint (pull_request) Successful in 2m44s
# Conflicts:
#	lib/l10n/arb/app_de.arb
#	lib/l10n/arb/app_en.arb
#	lib/l10n/generated/app_localizations_de.dart
#	lib/presentation/views/main_menu/settings_view.dart
2026-01-10 22:19:19 +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
ee84c60ba6 remove questionmark
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m2s
Pull Request Pipeline / lint (pull_request) Successful in 2m5s
2026-01-10 22:13:26 +01:00
b6dd0541ae rename CreateGroupView to GroupDetailView for clarity and consistency
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m4s
2026-01-10 22:11:28 +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
525acec1d3 Merge branch 'development' into feature/118-bearbeiten-und-löschen-von-gruppen
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Failing after 2m3s
2026-01-10 20:48:25 +00: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
45a419cae7 implement group edit view
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Failing after 2m3s
2026-01-10 20:14:37 +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
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
137 changed files with 13087 additions and 2885 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
uses: actions/checkout@v4
# Required for Flutter action
- name: Install jq
run: |
apt-get update
apt-get install -y jq
- name: Install Flutter (wget)
run: |
wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.38.2-stable.tar.xz
tar xf flutter_linux_3.38.2-stable.tar.xz
# Set Git safe directory for Flutter path
git config --global --add safe.directory "$(pwd)/flutter"
# Set Flutter path
echo "$(pwd)/flutter/bin" >> $GITHUB_PATH
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.38.6
- 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
run: flutter analyze lib test
@@ -36,22 +36,22 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
# Required for Flutter action
- name: Install jq
run: |
apt-get update
apt-get install -y jq
- name: Install Flutter (wget)
run: |
wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.38.2-stable.tar.xz
tar xf flutter_linux_3.38.2-stable.tar.xz
# Set Git safe directory for Flutter path
git config --global --add safe.directory "$(pwd)/flutter"
# Set Flutter path
echo "$(pwd)/flutter/bin" >> $GITHUB_PATH
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.38.6
- 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
run: flutter test

View File

@@ -7,44 +7,194 @@ on:
- "main"
jobs:
format:
build:
runs-on: ubuntu-latest
if: false # Needs bot user
steps:
- name: Checkout code
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: |
apt-get update
apt-get install -y jq
- name: Install Flutter (wget)
run: |
wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.38.2-stable.tar.xz
tar xf flutter_linux_3.38.2-stable.tar.xz
# Set Git safe directory for Flutter path
git config --global --add safe.directory "$(pwd)/flutter"
# Set Flutter path
echo "$(pwd)/flutter/bin" >> $GITHUB_PATH
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.38.6
- name: Get & upgrade dependencies
- name: Get dependencies
run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64
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: |
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 test
# Needs credentials, push access and the right files need to be staged
- name: Commit Changes
run: |
git config --global user.name "Gitea Actions"
git config --global user.email "actions@gitea.com"
git status
git add lib/
git status
git commit -m "Actions: Auto-formatting [skip ci]"
git push
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 "Auto-format code [skip ci]"
git push origin HEAD:${{ gitea.ref_name }}
else
echo "No changes to commit"
fi
- name: Verify format
run: flutter analyze lib test

27
.gitignore vendored
View File

@@ -150,25 +150,9 @@ app.*.symbols
.gclient_previous_custom_vars
.gclient_previous_sync_commits
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# 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
@@ -176,17 +160,8 @@ migrate_working_dir/
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
@@ -196,3 +171,5 @@ app.*.map.json
/android/app/profile
/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
unnecessary_const: true
lines_longer_than_80_chars: false
constant_identifier_names: false

View File

@@ -6,7 +6,7 @@ plugins {
}
android {
namespace = "com.example.game_tracker"
namespace = "de.liquid.tallee"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
@@ -21,7 +21,7 @@ android {
defaultConfig {
// 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.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion

View File

@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="game_tracker"
android:label="Tallee"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
@@ -42,5 +43,14 @@
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</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>
</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

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"?>
<resources>
<color name="app_icon_background">#E6F1E4</color>
<color name="launch_background">#0B0B0B</color>
<color name="launch_background">#ef681f</color>
</resources>

View File

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

View File

@@ -18,7 +18,7 @@ pluginManagement {
plugins {
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
}

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 */,
E754D1191B3E54E52B6DCC49 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
@@ -478,7 +477,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.gameTracker;
PRODUCT_BUNDLE_IDENTIFIER = de.liquid.tallee;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -661,7 +660,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.gameTracker;
PRODUCT_BUNDLE_IDENTIFIER = de.liquid.tallee;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -684,7 +683,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.gameTracker;
PRODUCT_BUNDLE_IDENTIFIER = de.liquid.tallee;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
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" : [
{
"filename" : "icon_x1024.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 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"}]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

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

View File

@@ -1,17 +1,8 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
"filename" : "icon-transparent.png",
"idiom" : "universal"
}
],
"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"?>
<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"/>
<dependencies>
<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="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -20,12 +20,19 @@
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<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">
<rect key="frame" x="46" y="334" width="301" height="184"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
</subviews>
<color key="backgroundColor" name="LauncherBackgroundColor"/>
<color key="backgroundColor" name="LauncherColor"/>
</view>
</viewController>
<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"/>
<resources>
<image name="LauncherIcon" width="1000" height="1000"/>
<namedColor name="LauncherBackgroundColor">
<color red="0.90196078431372551" green="0.94509803921568625" blue="0.89411764705882346" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<namedColor name="LauncherColor">
<color red="0.93699997663497925" green="0.40799999237060547" blue="0.12200000137090683" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</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">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Game Tracker</string>
<string>Tallee</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@@ -13,7 +15,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>game_tracker</string>
<string>tallee</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
@@ -22,8 +24,15 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https</string>
<string>http</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
@@ -39,9 +48,5 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View File

@@ -3,3 +3,4 @@ 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 @@
/// Application-wide constants
class Constants {
Constants._(); // Private constructor to prevent instantiation
/// Minimum duration of all app skeletons
Duration minimumSkeletonDuration = const Duration(milliseconds: 250);
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';
/// Theme class that defines colors, border radius, padding, and decorations
class CustomTheme {
static Color primaryColor = const Color(0xFF7505E4);
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);
CustomTheme._(); // Private constructor to prevent instantiation
// ==================== 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(
color: boxColor,
border: Border.all(color: boxBorder),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: boxBorderColor),
borderRadius: standardBorderRadiusAll,
);
static BoxDecoration highlightedBoxDecoration = BoxDecoration(
color: boxColor,
border: Border.all(color: primaryColor),
borderRadius: BorderRadius.circular(12),
borderRadius: standardBorderRadiusAll,
boxShadow: [BoxShadow(color: primaryColor.withAlpha(120), blurRadius: 12)],
);
static AppBarTheme appBarTheme = AppBarTheme(
// ==================== App Bar Theme ====================
static const AppBarTheme appBarTheme = AppBarTheme(
backgroundColor: backgroundColor,
foregroundColor: Colors.white,
foregroundColor: textColor,
elevation: 0,
titleTextStyle: const TextStyle(
color: Colors.white,
scrolledUnderElevation: 0,
centerTitle: true,
titleTextStyle: TextStyle(
color: textColor,
fontSize: 20,
fontWeight: FontWeight.bold,
overflow: TextOverflow.ellipsis,
),
iconTheme: const IconThemeData(color: Colors.white),
iconTheme: IconThemeData(color: textColor),
);
}

View File

@@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
/// 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 }
/// Result types for import operations in the [SettingsView]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,18 +1,18 @@
import 'package:drift/drift.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: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';

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
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';
class Group {

View File

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

View File

@@ -1,24 +1,36 @@
{
"@@locale": "de",
"all_players": "Alle Spieler:innen:",
"all_players": "Alle Spieler:innen",
"all_players_selected": "Alle Spieler:innen ausgewählt",
"amount_of_matches": "Anzahl der Matches",
"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": "Match erstellen",
"create_match": "Spiel erstellen",
"create_new_group": "Neue Gruppe erstellen",
"create_new_match": "Neues Match 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_all_data": "Alle Daten löschen",
"delete_group": "Diese Gruppe löschen",
"edit_group": "Gruppe bearbeiten",
"delete_group": "Gruppe löschen",
"delete_match": "Spiel löschen",
"edit_group": "Gruppe bearbeiten",
"enter_results": "Ergebnisse eintragen",
"error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen",
"error_deleting_group": "Fehler beim Löschen der Gruppe, bitte erneut versuchen",
"error_editing_group": "Fehler beim Bearbeiten der Gruppe, bitte erneut versuchen",
"error_reading_file": "Fehler beim Lesen der Datei",
"export_canceled": "Export abgebrochen",
"export_data": "Daten exportieren",
@@ -27,6 +39,7 @@
"game_name": "Spielvorlagenname",
"group": "Gruppe",
"group_name": "Gruppenname",
"group_profile": "Gruppenprofil",
"groups": "Gruppen",
"home": "Startseite",
"import_canceled": "Import abgebrochen",
@@ -34,37 +47,49 @@
"info": "Info",
"invalid_schema": "Ungültiges Schema",
"least_points": "Niedrigste Punkte",
"match_in_progress": "Match läuft...",
"match_name": "Matchname",
"matches": "Matches",
"menu": "Menü",
"legal": "Rechtliches",
"legal_notice": "Impressum",
"licenses": "Lizenzen",
"match_in_progress": "Spiel läuft...",
"match_name": "Spieltitel",
"match_profile": "Spielprofil",
"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_matches_created_yet": "Noch keine Matches 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:in ausgewählt",
"no_recent_matches_available": "Keine letzten Matches verfügbar",
"no_second_match_available": "Kein zweites Match verfügbar",
"no_players_selected": "Keine Spieler:innen ausgewählt",
"no_recent_matches_available": "Keine letzten Spiele verfügbar",
"no_results_entered_yet": "Noch keine Ergebnisse eingetragen",
"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:in",
"players": "Spieler:innen",
"players_count": "{count} Spieler",
"privacy_policy": "Datenschutzerklärung",
"quick_create": "Schnellzugriff",
"recent_matches": "Letzte Matches",
"recent_matches": "Letzte Spiele",
"result": "Ergebnis",
"results": "Ergebnisse",
"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.",
"save_changes": "Änderungen speichern",
"search_for_groups": "Nach Gruppen suchen",
"search_for_players": "Nach Spieler:innen suchen",
"select_winner": "Gewinner:in wählen:",
"selected_players": "Ausgewählte Spieler:in: {count}",
"selected_players": "Ausgewählte Spieler:innen",
"settings": "Einstellungen",
"single_loser": "Ein:e Verlierer:in",
"single_winner": "Ein:e Gewinner:in",
@@ -72,12 +97,12 @@
"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 {time}",
"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: {winnerName}",
"winner": "Gewinner:in",
"winrate": "Siegquote",
"wins": "Siege",
"yesterday_at": "Gestern um {time}"
"yesterday_at": "Gestern um"
}

View File

@@ -9,6 +9,12 @@
"@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"
},
@@ -22,13 +28,7 @@
"description": "Label for choosing a ruleset"
},
"@could_not_add_player": {
"description": "Error message when adding a player fails",
"placeholders": {
"playerName": {
"type": "String",
"example": "John"
}
}
"description": "Error message when adding a player fails"
},
"@create_group": {
"description": "Button text to create a group"
@@ -37,10 +37,16 @@
"description": "Button text to create a match"
},
"@create_new_group": {
"description": "Button text to create a new group"
"description": "Appbar text to create a new group"
},
"@create_new_match": {
"description": "Button text to create a new match"
"description": "Appbar 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"
@@ -65,9 +71,27 @@
"@delete_all_data": {
"description": "Confirmation dialog for deleting all data"
},
"@delete_group": {
"description": "Confirmation dialog for deleting a group"
},
"@delete_match": {
"description": "Button text to delete a match"
},
"@edit_group": {
"description": "Button & Appbar label for editing a group"
},
"@enter_results": {
"description": "Button text to enter match results"
},
"@error_creating_group": {
"description": "Error message when group creation fails"
},
"@error_deleting_group": {
"description": "Error message when group deletion fails"
},
"@error_editing_group": {
"description": "Error message when group editing fails"
},
"@error_reading_file": {
"description": "Error message when file cannot be read"
},
@@ -86,15 +110,15 @@
"@game_name": {
"description": "Placeholder for game name search"
},
"@game_tracker": {
"description": "App Name"
},
"@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"
},
@@ -116,17 +140,29 @@
"@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"
},
"@match_profile": {
"description": "Title for match profile view"
},
"@matches": {
"description": "Label for matches"
},
"@menu": {
"description": "Menu label"
"@members": {
"description": "Label for group members"
},
"@most_points": {
"description": "Title for most points ruleset"
@@ -137,6 +173,12 @@
"@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"
},
@@ -152,6 +194,9 @@
"@no_recent_matches_available": {
"description": "Message when no recent matches exist"
},
"@no_results_entered_yet": {
"description": "Message when no results have been entered yet"
},
"@no_second_match_available": {
"description": "Message when no second match exists"
},
@@ -167,6 +212,9 @@
"@not_available": {
"description": "Abbreviation for not available"
},
"@played_matches": {
"description": "Label for played matches statistic"
},
"@player_name": {
"description": "Placeholder for player name input"
},
@@ -181,12 +229,18 @@
}
}
},
"@privacy_policy": {
"description": "Privacy policy menu item"
},
"@quick_create": {
"description": "Title for quick create section"
},
"@recent_matches": {
"description": "Title for recent matches section"
},
"@results": {
"description": "Label for match results"
},
"@ruleset": {
"description": "Ruleset label"
},
@@ -202,6 +256,9 @@
"@ruleset_single_winner": {
"description": "Description for single winner ruleset"
},
"@save_changes": {
"description": "Save changes button text"
},
"@search_for_groups": {
"description": "Hint text for group search input field"
},
@@ -212,16 +269,10 @@
"description": "Label to select the winner"
},
"@selected_players": {
"description": "Shows the number of selected players",
"placeholders": {
"count": {
"type": "int",
"format": "compact"
}
}
"description": "Shows the number of selected players"
},
"@settings": {
"description": "Settings label"
"description": "Label for the App Settings"
},
"@single_loser": {
"description": "Title for single loser ruleset"
@@ -251,13 +302,7 @@
"description": "Warning message for irreversible actions"
},
"@today_at": {
"description": "Date format for today",
"placeholders": {
"time": {
"type": "String",
"example": "14:30"
}
}
"description": "Date format for today"
},
"@undo": {
"description": "Undo button text"
@@ -275,42 +320,46 @@
"description": "Label for wins statistic"
},
"@yesterday_at": {
"description": "Date format for yesterday",
"placeholders": {
"time": {
"type": "String",
"example": "14:30"
}
}
"description": "Date format for yesterday"
},
"all_players": "All players:",
"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 {playerName}",
"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_all_data": "Delete all data",
"delete_group": "Delete Group",
"delete_match": "Delete Match",
"edit_group": "Edit Group",
"enter_results": "Enter Results",
"error_creating_group": "Error while creating group, please try again",
"error_deleting_group": "Error while deleting group, please try again",
"error_editing_group": "Error while editing 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",
"game_tracker": "Game Tracker",
"group": "Group",
"group_name": "Group name",
"group_profile": "Group Profile",
"groups": "Groups",
"home": "Home",
"import_canceled": "Import canceled",
@@ -318,37 +367,48 @@
"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",
"match_profile": "Match Profile",
"matches": "Matches",
"menu": "Menu",
"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_results_entered_yet": "No results entered yet",
"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",
"results": "Results",
"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.",
"save_changes": "Save Changes",
"search_for_groups": "Search for groups",
"search_for_players": "Search for players",
"select_winner": "Select Winner:",
"selected_players": "Selected players: {count}",
"selected_players": "Selected players",
"settings": "Settings",
"single_loser": "Single Loser",
"single_winner": "Single Winner",
@@ -356,12 +416,12 @@
"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 {time}",
"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 {time}"
"yesterday_at": "Yesterday at"
}

View File

@@ -98,269 +98,17 @@ abstract class AppLocalizations {
Locale('en'),
];
/// Label for choosing a group
///
/// In en, this message translates to:
/// **'Choose Group'**
String get choose_group;
/// Button text to create a new match
///
/// In en, this message translates to:
/// **'Create new match'**
String get create_new_match;
/// Label for choosing a ruleset
///
/// In en, this message translates to:
/// **'Choose Ruleset'**
String get choose_ruleset;
/// Label for choosing a game
///
/// In en, this message translates to:
/// **'Choose Game'**
String get choose_game;
/// Label to select the winner
///
/// In en, this message translates to:
/// **'Select Winner:'**
String get select_winner;
/// App Name
///
/// In en, this message translates to:
/// **'Game Tracker'**
String get game_tracker;
/// 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;
/// Confirmation dialog for deleting all data
///
/// In en, this message translates to:
/// **'Delete all data?'**
String get delete_all_data;
/// Cancel button text
///
/// In en, this message translates to:
/// **'Cancel'**
String get cancel;
/// Delete button text
///
/// In en, this message translates to:
/// **'Delete'**
String get delete;
/// Button text to create a new group
///
/// In en, this message translates to:
/// **'Create new group'**
String get create_new_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;
/// Shows the number of selected players
///
/// In en, this message translates to:
/// **'Selected players: {count}'**
String selected_players(int count);
/// Message when no players are selected
///
/// In en, this message translates to:
/// **'No players selected'**
String get no_players_selected;
/// Label for all players list
///
/// In en, this message translates to:
/// **'All players:'**
/// **'All players'**
String get all_players;
/// Success message when adding a player
/// Message when all players are added to selection
///
/// In en, this message translates to:
/// **'Successfully added player {playerName}'**
String successfully_added_player(String playerName);
/// Error message when adding a player fails
///
/// In en, this message translates to:
/// **'Could not add player {playerName}'**
String could_not_add_player(String playerName);
/// Shows the winner's name
///
/// In en, this message translates to:
/// **'Winner: {winnerName}'**
String winner(String winnerName);
/// Players label
///
/// In en, this message translates to:
/// **'Players'**
String get players;
/// 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;
/// Message when no data in the statistic tiles is given
///
/// In en, this message translates to:
/// **'No data available'**
String get no_data_available;
/// Label for matches
///
/// In en, this message translates to:
/// **'Matches'**
String get matches;
/// Label for groups
///
/// In en, this message translates to:
/// **'Groups'**
String get groups;
/// Title for recent matches section
///
/// In en, this message translates to:
/// **'Recent Matches'**
String get recent_matches;
/// Title for quick create section
///
/// In en, this message translates to:
/// **'Quick Create'**
String get quick_create;
/// Message when match is in progress
///
/// In en, this message translates to:
/// **'Match in progress...'**
String get match_in_progress;
/// Menu label
///
/// In en, this message translates to:
/// **'Menu'**
String get menu;
/// Settings label
///
/// In en, this message translates to:
/// **'Settings'**
String get settings;
/// Export data menu item
///
/// In en, this message translates to:
/// **'Export data'**
String get export_data;
/// Import data menu item
///
/// In en, this message translates to:
/// **'Import data'**
String get import_data;
/// Warning message for irreversible actions
///
/// In en, this message translates to:
/// **'This can\'t be undone'**
String get this_cannot_be_undone;
/// Success message after deleting data
///
/// In en, this message translates to:
/// **'Data successfully deleted'**
String get data_successfully_deleted;
/// Success message after importing data
///
/// In en, this message translates to:
/// **'Data successfully imported'**
String get data_successfully_imported;
/// Error message for invalid schema
///
/// In en, this message translates to:
/// **'Invalid Schema'**
String get invalid_schema;
/// Error message when file cannot be read
///
/// In en, this message translates to:
/// **'Error reading file'**
String get error_reading_file;
/// Message when import is canceled
///
/// In en, this message translates to:
/// **'Import canceled'**
String get import_canceled;
/// Error message for format exceptions
///
/// In en, this message translates to:
/// **'Format Exception (see console)'**
String get format_exception;
/// Error message for unknown exceptions
///
/// In en, this message translates to:
/// **'Unknown Exception (see console)'**
String get unknown_exception;
/// Success message after exporting data
///
/// In en, this message translates to:
/// **'Data successfully exported'**
String get data_successfully_exported;
/// Message when export is canceled
///
/// In en, this message translates to:
/// **'Export canceled'**
String get export_canceled;
/// Undo button text
///
/// In en, this message translates to:
/// **'Undo'**
String get undo;
/// Label for wins statistic
///
/// In en, this message translates to:
/// **'Wins'**
String get wins;
/// Label for winrate statistic
///
/// In en, this message translates to:
/// **'Winrate'**
String get winrate;
/// **'All players selected'**
String get all_players_selected;
/// Label for amount of matches statistic
///
@@ -368,23 +116,47 @@ abstract class AppLocalizations {
/// **'Amount of Matches'**
String get amount_of_matches;
/// Info label
/// The name of the App
///
/// In en, this message translates to:
/// **'Info'**
String get info;
/// **'Tallee'**
String get app_name;
/// Message when no groups exist
/// Label for best player statistic
///
/// In en, this message translates to:
/// **'No groups created yet'**
String get no_groups_created_yet;
/// **'Best Player'**
String get best_player;
/// Message when no players exist
/// Cancel button text
///
/// In en, this message translates to:
/// **'No players created yet'**
String get no_players_created_yet;
/// **'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
///
@@ -392,29 +164,137 @@ abstract class AppLocalizations {
/// **'Create Group'**
String get create_group;
/// Placeholder for group name input
/// Button text to create a match
///
/// In en, this message translates to:
/// **'Group name'**
String get group_name;
/// **'Create match'**
String get create_match;
/// Placeholder for player name input
/// Appbar text to create a new group
///
/// In en, this message translates to:
/// **'Player name'**
String get player_name;
/// **'Create new group'**
String get create_new_group;
/// Message when no matches exist
/// Label for creation date
///
/// In en, this message translates to:
/// **'No matches created yet'**
String get no_matches_created_yet;
/// **'Created on'**
String get created_on;
/// Placeholder for match name input
/// Appbar text to create a new match
///
/// In en, this message translates to:
/// **'Match name'**
String get match_name;
/// **'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;
/// Confirmation dialog for deleting a group
///
/// In en, this message translates to:
/// **'Delete Group'**
String get delete_group;
/// Button text to delete a match
///
/// In en, this message translates to:
/// **'Delete Match'**
String get delete_match;
/// Button & Appbar label for editing a group
///
/// In en, this message translates to:
/// **'Edit Group'**
String get edit_group;
/// Button text to enter match results
///
/// In en, this message translates to:
/// **'Enter Results'**
String get enter_results;
/// 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 group deletion fails
///
/// In en, this message translates to:
/// **'Error while deleting group, please try again'**
String get error_deleting_group;
/// Error message when group editing fails
///
/// In en, this message translates to:
/// **'Error while editing group, please try again'**
String get error_editing_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
///
@@ -422,11 +302,11 @@ abstract class AppLocalizations {
/// **'Game'**
String get game;
/// Ruleset label
/// Placeholder for game name search
///
/// In en, this message translates to:
/// **'Ruleset'**
String get ruleset;
/// **'Game Name'**
String get game_name;
/// Group label
///
@@ -434,6 +314,186 @@ abstract class AppLocalizations {
/// **'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;
/// Title for match profile view
///
/// In en, this message translates to:
/// **'Match Profile'**
String get match_profile;
/// 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 results have been entered yet
///
/// In en, this message translates to:
/// **'No results entered yet'**
String get no_results_entered_yet;
/// 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:
@@ -446,47 +506,137 @@ abstract class AppLocalizations {
/// **'None'**
String get none_group;
/// Button text to create a match
/// Abbreviation for not available
///
/// In en, this message translates to:
/// **'Create match'**
String get create_match;
/// **'Not available'**
String get not_available;
/// Message when search returns no results
/// Label for played matches statistic
///
/// In en, this message translates to:
/// **'No players found with that name'**
String get no_players_found_with_that_name;
/// **'Played Matches'**
String get played_matches;
/// Message when all players are added to selection
/// Placeholder for player name input
///
/// In en, this message translates to:
/// **'All players selected'**
String get all_players_selected;
/// **'Player name'**
String get player_name;
/// Date format for today
/// Players label
///
/// In en, this message translates to:
/// **'Today at {time}'**
String today_at(String time);
/// **'Players'**
String get players;
/// Date format for yesterday
/// Shows the number of players
///
/// In en, this message translates to:
/// **'Yesterday at {time}'**
String yesterday_at(String time);
/// **'{count} Players'**
String players_count(int count);
/// Date format for days ago
/// Privacy policy menu item
///
/// In en, this message translates to:
/// **'{count} days ago'**
String days_ago(int count);
/// **'Privacy Policy'**
String get privacy_policy;
/// Home tab label
/// Title for quick create section
///
/// In en, this message translates to:
/// **'Home'**
String get home;
/// **'Quick Create'**
String get quick_create;
/// Title for recent matches section
///
/// In en, this message translates to:
/// **'Recent Matches'**
String get recent_matches;
/// Label for match results
///
/// In en, this message translates to:
/// **'Results'**
String get results;
/// 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;
/// Save changes button text
///
/// In en, this message translates to:
/// **'Save Changes'**
String get save_changes;
/// 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
///
@@ -500,11 +650,11 @@ abstract class AppLocalizations {
/// **'Stats'**
String get stats;
/// Shows the number of players
/// Success message when adding a player
///
/// In en, this message translates to:
/// **'{count} Players'**
String players_count(int count);
/// **'Successfully added player {playerName}'**
String successfully_added_player(String playerName);
/// Message when search returns no groups
///
@@ -512,77 +662,53 @@ abstract class AppLocalizations {
/// **'There is no group matching your search'**
String get there_is_no_group_matching_your_search;
/// Placeholder for game name search
/// Warning message for irreversible actions
///
/// In en, this message translates to:
/// **'Game Name'**
String get game_name;
/// **'This can\'t be undone.'**
String get this_cannot_be_undone;
/// Description for single winner ruleset
/// Date format for today
///
/// In en, this message translates to:
/// **'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.'**
String get ruleset_single_winner;
/// **'Today at'**
String get today_at;
/// Description for single loser ruleset
/// Undo button text
///
/// In en, this message translates to:
/// **'Exactly one loser is determined; last place receives the penalty or consequence.'**
String get ruleset_single_loser;
/// **'Undo'**
String get undo;
/// Description for most points ruleset
/// Error message for unknown exceptions
///
/// In en, this message translates to:
/// **'Traditional ruleset: the player with the most points wins.'**
String get ruleset_most_points;
/// **'Unknown Exception (see console)'**
String get unknown_exception;
/// Description for least points ruleset
/// Winner label
///
/// In en, this message translates to:
/// **'Inverse scoring: the player with the fewest points wins.'**
String get ruleset_least_points;
/// **'Winner'**
String get winner;
/// Title for single winner ruleset
/// Label for winrate statistic
///
/// In en, this message translates to:
/// **'Single Winner'**
String get single_winner;
/// **'Winrate'**
String get winrate;
/// Title for single loser ruleset
/// Label for wins statistic
///
/// In en, this message translates to:
/// **'Single Loser'**
String get single_loser;
/// **'Wins'**
String get wins;
/// Title for most points ruleset
/// Date format for yesterday
///
/// In en, this message translates to:
/// **'Most Points'**
String get most_points;
/// Title for least points ruleset
///
/// In en, this message translates to:
/// **'Least Points'**
String get least_points;
/// Hint text for player search input field
///
/// In en, this message translates to:
/// **'Search for players'**
String get search_for_players;
/// Hint text for group search input field
///
/// In en, this message translates to:
/// **'Search for groups'**
String get search_for_groups;
/// Abbreviation for not available
///
/// In en, this message translates to:
/// **'Not available'**
String get not_available;
/// **'Yesterday at'**
String get yesterday_at;
}
class _AppLocalizationsDelegate

View File

@@ -9,187 +9,210 @@ class AppLocalizationsDe extends AppLocalizations {
AppLocalizationsDe([String locale = 'de']) : super(locale);
@override
String get choose_group => 'Gruppe wählen';
String get all_players => 'Alle Spieler:innen';
@override
String get create_new_match => 'Neues Match erstellen';
String get all_players_selected => 'Alle Spieler:innen ausgewählt';
@override
String get choose_ruleset => 'Regelwerk wählen';
String get amount_of_matches => 'Anzahl der Spiele';
@override
String get choose_game => 'Spielvorlage wählen';
String get app_name => 'Tallee';
@override
String get select_winner => 'Gewinner:in wählen:';
@override
String get game_tracker => 'Game Tracker';
@override
String get no_recent_matches_available => 'Keine letzten Matches verfügbar';
@override
String get no_second_match_available => 'Kein zweites Match verfügbar';
@override
String get delete_all_data => 'Alle Daten löschen?';
String get best_player => 'Beste:r Spieler:in';
@override
String get cancel => 'Abbrechen';
@override
String get delete => 'Löschen';
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 delete_match => 'Spiel löschen';
@override
String get edit_group => 'Gruppe bearbeiten';
@override
String get enter_results => 'Ergebnisse eintragen';
@override
String get error_creating_group =>
'Fehler beim Erstellen der Gruppe, bitte erneut versuchen';
@override
String selected_players(int count) {
final intl.NumberFormat countNumberFormat = intl.NumberFormat.compact(
locale: localeName,
);
final String countString = countNumberFormat.format(count);
return 'Ausgewählte Spieler:in: $countString';
}
String get error_deleting_group =>
'Fehler beim Löschen der Gruppe, bitte erneut versuchen';
@override
String get no_players_selected => 'Keine Spieler:in ausgewählt';
@override
String get all_players => 'Alle Spieler:innen:';
@override
String successfully_added_player(String playerName) {
return 'Spieler:in $playerName erfolgreich hinzugefügt';
}
@override
String could_not_add_player(String playerName) {
return 'Spieler:in $playerName konnte nicht hinzugefügt werden';
}
@override
String winner(String winnerName) {
return 'Gewinner:in: $winnerName';
}
@override
String get players => 'Spieler:in';
@override
String get no_statistics_available => 'Keine Statistiken verfügbar';
@override
String get no_data_available => 'Keine Daten verfügbar';
@override
String get matches => 'Matches';
@override
String get groups => 'Gruppen';
@override
String get recent_matches => 'Letzte Matches';
@override
String get quick_create => 'Schnellzugriff';
@override
String get match_in_progress => 'Match läuft...';
@override
String get menu => 'Menü';
@override
String get settings => 'Einstellungen';
@override
String get export_data => 'Daten exportieren';
@override
String get import_data => 'Daten importieren';
@override
String get this_cannot_be_undone =>
'Dies kann nicht rückgängig gemacht werden';
@override
String get data_successfully_deleted => 'Daten erfolgreich gelöscht';
@override
String get data_successfully_imported => 'Daten erfolgreich importiert';
@override
String get invalid_schema => 'Ungültiges Schema';
String get error_editing_group =>
'Fehler beim Bearbeiten der Gruppe, bitte erneut versuchen';
@override
String get error_reading_file => 'Fehler beim Lesen der Datei';
@override
String get import_canceled => 'Import abgebrochen';
String get export_canceled => 'Export abgebrochen';
@override
String get export_data => 'Daten exportieren';
@override
String get format_exception => 'Formatfehler (siehe Konsole)';
@override
String get unknown_exception => 'Unbekannter Fehler (siehe Konsole)';
String get game => 'Spielvorlage';
@override
String get data_successfully_exported => 'Daten erfolgreich exportiert';
String get game_name => 'Spielvorlagenname';
@override
String get export_canceled => 'Export abgebrochen';
@override
String get undo => 'Rückgängig';
@override
String get wins => 'Siege';
@override
String get winrate => 'Siegquote';
@override
String get amount_of_matches => 'Anzahl der Matches';
@override
String get info => 'Info';
@override
String get no_groups_created_yet => 'Noch keine Gruppen erstellt';
@override
String get no_players_created_yet => 'Noch keine Spieler:in erstellt';
@override
String get create_group => 'Gruppe erstellen';
String get group => 'Gruppe';
@override
String get group_name => 'Gruppenname';
@override
String get player_name => 'Spieler:innenname';
String get group_profile => 'Gruppenprofil';
@override
String get no_matches_created_yet => 'Noch keine Matches erstellt';
String get groups => 'Gruppen';
@override
String get match_name => 'Matchname';
String get home => 'Startseite';
@override
String get game => 'Spielvorlage';
String get import_canceled => 'Import abgebrochen';
@override
String get ruleset => 'Regelwerk';
String get import_data => 'Daten importieren';
@override
String get group => 'Gruppe';
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 match_profile => 'Spielprofil';
@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_results_entered_yet => 'Noch keine Ergebnisse eingetragen';
@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';
@@ -198,32 +221,76 @@ class AppLocalizationsDe extends AppLocalizations {
String get none_group => 'Keine';
@override
String get create_match => 'Match erstellen';
String get not_available => 'Nicht verfügbar';
@override
String get no_players_found_with_that_name =>
'Keine Spieler:in mit diesem Namen gefunden';
String get played_matches => 'Gespielte Spiele';
@override
String get all_players_selected => 'Alle Spieler:innen ausgewählt';
String get player_name => 'Spieler:innenname';
@override
String today_at(String time) {
return 'Heute um $time';
String get players => 'Spieler:innen';
@override
String players_count(int count) {
return '$count Spieler';
}
@override
String yesterday_at(String time) {
return 'Gestern um $time';
}
String get privacy_policy => 'Datenschutzerklärung';
@override
String days_ago(int count) {
return 'vor $count Tagen';
}
String get quick_create => 'Schnellzugriff';
@override
String get home => 'Startseite';
String get recent_matches => 'Letzte Spiele';
@override
String get results => 'Ergebnisse';
@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 save_changes => 'Änderungen speichern';
@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';
@@ -232,8 +299,8 @@ class AppLocalizationsDe extends AppLocalizations {
String get stats => 'Statistiken';
@override
String players_count(int count) {
return '$count Spieler';
String successfully_added_player(String playerName) {
return 'Spieler:in $playerName erfolgreich hinzugefügt';
}
@override
@@ -241,42 +308,27 @@ class AppLocalizationsDe extends AppLocalizations {
'Es gibt keine Gruppe, die deiner Suche entspricht';
@override
String get game_name => 'Spielvorlagenname';
String get this_cannot_be_undone =>
'Dies kann nicht rückgängig gemacht werden.';
@override
String get ruleset_single_winner =>
'Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.';
String get today_at => 'Heute um';
@override
String get ruleset_single_loser =>
'Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.';
String get undo => 'Rückgängig';
@override
String get ruleset_most_points =>
'Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.';
String get unknown_exception => 'Unbekannter Fehler (siehe Konsole)';
@override
String get ruleset_least_points =>
'Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.';
String get winner => 'Gewinner:in';
@override
String get single_winner => 'Ein:e Gewinner:in';
String get winrate => 'Siegquote';
@override
String get single_loser => 'Ein:e Verlierer:in';
String get wins => 'Siege';
@override
String get most_points => 'Höchste Punkte';
@override
String get least_points => 'Niedrigste Punkte';
@override
String get search_for_players => 'Nach Spieler:innen suchen';
@override
String get search_for_groups => 'Nach Gruppen suchen';
@override
String get not_available => 'Nicht verfügbar';
String get yesterday_at => 'Gestern um';
}

View File

@@ -9,186 +9,210 @@ class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);
@override
String get choose_group => 'Choose Group';
String get all_players => 'All players';
@override
String get create_new_match => 'Create new match';
String get all_players_selected => 'All players selected';
@override
String get choose_ruleset => 'Choose Ruleset';
String get amount_of_matches => 'Amount of Matches';
@override
String get choose_game => 'Choose Game';
String get app_name => 'Tallee';
@override
String get select_winner => 'Select Winner:';
@override
String get game_tracker => 'Game Tracker';
@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 delete_all_data => 'Delete all data?';
String get best_player => 'Best Player';
@override
String get cancel => 'Cancel';
@override
String get delete => 'Delete';
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 delete_match => 'Delete Match';
@override
String get edit_group => 'Edit Group';
@override
String get enter_results => 'Enter Results';
@override
String get error_creating_group =>
'Error while creating group, please try again';
@override
String selected_players(int count) {
final intl.NumberFormat countNumberFormat = intl.NumberFormat.compact(
locale: localeName,
);
final String countString = countNumberFormat.format(count);
return 'Selected players: $countString';
}
String get error_deleting_group =>
'Error while deleting group, please try again';
@override
String get no_players_selected => 'No players selected';
@override
String get all_players => 'All players:';
@override
String successfully_added_player(String playerName) {
return 'Successfully added player $playerName';
}
@override
String could_not_add_player(String playerName) {
return 'Could not add player $playerName';
}
@override
String winner(String winnerName) {
return 'Winner: $winnerName';
}
@override
String get players => 'Players';
@override
String get no_statistics_available => 'No statistics available';
@override
String get no_data_available => 'No data available';
@override
String get matches => 'Matches';
@override
String get groups => 'Groups';
@override
String get recent_matches => 'Recent Matches';
@override
String get quick_create => 'Quick Create';
@override
String get match_in_progress => 'Match in progress...';
@override
String get menu => 'Menu';
@override
String get settings => 'Settings';
@override
String get export_data => 'Export data';
@override
String get import_data => 'Import data';
@override
String get this_cannot_be_undone => 'This can\'t be undone';
@override
String get data_successfully_deleted => 'Data successfully deleted';
@override
String get data_successfully_imported => 'Data successfully imported';
@override
String get invalid_schema => 'Invalid Schema';
String get error_editing_group =>
'Error while editing group, please try again';
@override
String get error_reading_file => 'Error reading file';
@override
String get import_canceled => 'Import canceled';
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 unknown_exception => 'Unknown Exception (see console)';
String get game => 'Game';
@override
String get data_successfully_exported => 'Data successfully exported';
String get game_name => 'Game Name';
@override
String get export_canceled => 'Export canceled';
@override
String get undo => 'Undo';
@override
String get wins => 'Wins';
@override
String get winrate => 'Winrate';
@override
String get amount_of_matches => 'Amount of Matches';
@override
String get info => 'Info';
@override
String get no_groups_created_yet => 'No groups created yet';
@override
String get no_players_created_yet => 'No players created yet';
@override
String get create_group => 'Create Group';
String get group => 'Group';
@override
String get group_name => 'Group name';
@override
String get player_name => 'Player name';
String get group_profile => 'Group Profile';
@override
String get no_matches_created_yet => 'No matches created yet';
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 game => 'Game';
String get match_profile => 'Match Profile';
@override
String get ruleset => 'Ruleset';
String get matches => 'Matches';
@override
String get group => 'Group';
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_results_entered_yet => 'No results entered yet';
@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';
@@ -197,32 +221,76 @@ class AppLocalizationsEn extends AppLocalizations {
String get none_group => 'None';
@override
String get create_match => 'Create match';
String get not_available => 'Not available';
@override
String get no_players_found_with_that_name =>
'No players found with that name';
String get played_matches => 'Played Matches';
@override
String get all_players_selected => 'All players selected';
String get player_name => 'Player name';
@override
String today_at(String time) {
return 'Today at $time';
String get players => 'Players';
@override
String players_count(int count) {
return '$count Players';
}
@override
String yesterday_at(String time) {
return 'Yesterday at $time';
}
String get privacy_policy => 'Privacy Policy';
@override
String days_ago(int count) {
return '$count days ago';
}
String get quick_create => 'Quick Create';
@override
String get home => 'Home';
String get recent_matches => 'Recent Matches';
@override
String get results => 'Results';
@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 save_changes => 'Save Changes';
@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';
@@ -231,8 +299,8 @@ class AppLocalizationsEn extends AppLocalizations {
String get stats => 'Stats';
@override
String players_count(int count) {
return '$count Players';
String successfully_added_player(String playerName) {
return 'Successfully added player $playerName';
}
@override
@@ -240,42 +308,26 @@ class AppLocalizationsEn extends AppLocalizations {
'There is no group matching your search';
@override
String get game_name => 'Game Name';
String get this_cannot_be_undone => 'This can\'t be undone.';
@override
String get ruleset_single_winner =>
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.';
String get today_at => 'Today at';
@override
String get ruleset_single_loser =>
'Exactly one loser is determined; last place receives the penalty or consequence.';
String get undo => 'Undo';
@override
String get ruleset_most_points =>
'Traditional ruleset: the player with the most points wins.';
String get unknown_exception => 'Unknown Exception (see console)';
@override
String get ruleset_least_points =>
'Inverse scoring: the player with the fewest points wins.';
String get winner => 'Winner';
@override
String get single_winner => 'Single Winner';
String get winrate => 'Winrate';
@override
String get single_loser => 'Single Loser';
String get wins => 'Wins';
@override
String get most_points => 'Most Points';
@override
String get least_points => 'Least Points';
@override
String get search_for_players => 'Search for players';
@override
String get search_for_groups => 'Search for groups';
@override
String get not_available => 'Not available';
String get yesterday_at => 'Yesterday at';
}

View File

@@ -1,9 +1,9 @@
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/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/views/main_menu/custom_navigation_bar.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() {
runApp(
@@ -34,21 +34,35 @@ class GameTracker extends StatelessWidget {
);
},
debugShowCheckedModeBanner: false,
onGenerateTitle: (context) => AppLocalizations.of(context).game_tracker,
darkTheme: ThemeData.dark(),
onGenerateTitle: (context) => AppLocalizations.of(context).app_name,
themeMode: ThemeMode.dark, // forces dark mode
theme: ThemeData(
primaryColor: CustomTheme.primaryColor,
scaffoldBackgroundColor: CustomTheme.backgroundColor,
appBarTheme: CustomTheme.appBarTheme,
radioTheme: RadioThemeData(
fillColor: WidgetStateProperty.resolveWith<Color>((states) {
if (states.contains(WidgetState.selected)) {
return CustomTheme.primaryColor;
}
return CustomTheme.textColor;
}),
),
colorScheme: ColorScheme.fromSeed(
seedColor: CustomTheme.primaryColor,
brightness: Brightness.dark,
).copyWith(surface: CustomTheme.backgroundColor),
primary: CustomTheme.primaryColor,
onPrimary: CustomTheme.textColor,
surface: CustomTheme.backgroundColor,
onSurface: CustomTheme.textColor,
),
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.android: PredictiveBackPageTransitionsBuilder(),
},
),
),
home: const CustomNavigationBar(),
);
}

View File

@@ -1,14 +1,17 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/views/main_menu/group_view/groups_view.dart';
import 'package:game_tracker/presentation/views/main_menu/home_view.dart';
import 'package:game_tracker/presentation/views/main_menu/match_view/match_view.dart';
import 'package:game_tracker/presentation/views/main_menu/settings_view.dart';
import 'package:game_tracker/presentation/views/main_menu/statistics_view.dart';
import 'package:game_tracker/presentation/widgets/navbar_item.dart';
import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/group_view/group_view.dart';
import 'package:tallee/presentation/views/main_menu/home_view.dart';
import 'package:tallee/presentation/views/main_menu/match_view/match_view.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 {
/// A custom navigation bar widget that provides tabbed navigation
/// between different views: Home, Matches, Groups, and Statistics.
const CustomNavigationBar({super.key});
@override
@@ -17,7 +20,10 @@ class CustomNavigationBar extends StatefulWidget {
class _CustomNavigationBarState extends State<CustomNavigationBar>
with SingleTickerProviderStateMixin {
/// Currently selected tab index
int currentIndex = 0;
/// Key count to force rebuild of tab views
int tabKeyCount = 0;
@override
@@ -32,7 +38,7 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
),
KeyedSubtree(
key: ValueKey('groups_$tabKeyCount'),
child: const GroupsView(),
child: const GroupView(),
),
KeyedSubtree(
key: ValueKey('stats_$tabKeyCount'),
@@ -53,7 +59,7 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(builder: (_) => const SettingsView()),
adaptivePageRoute(builder: (_) => const SettingsView()),
);
setState(() {
tabKeyCount++;
@@ -67,18 +73,28 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
backgroundColor: CustomTheme.backgroundColor,
body: tabs[currentIndex],
extendBody: true,
bottomNavigationBar: SafeArea(
minimum: const EdgeInsets.only(bottom: 30),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 10),
bottomNavigationBar: Container(
height: 115,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
color: CustomTheme.primaryColor,
color: CustomTheme.navBarBackgroundColor,
border: Border.all(
strokeAlign: BorderSide.strokeAlignOutside,
color: CustomTheme.boxBorderColor,
width: 2,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: SizedBox(
height: 60,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 20,
offset: const Offset(0, -5),
),
],
),
child: SafeArea(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
@@ -114,17 +130,17 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
),
),
),
),
),
);
}
/// Handles tab tap events. Updates the current [index] state.
void onTabTapped(int index) {
setState(() {
currentIndex = index;
});
}
/// Returns the title of the current tab based on [currentIndex].
String _currentTabTitle(context) {
final loc = AppLocalizations.of(context);
switch (currentIndex) {

View File

@@ -1,33 +1,51 @@
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/l10n/generated/app_localizations.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: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 {
const CreateGroupView({super.key});
const CreateGroupView({super.key, this.groupToEdit});
/// The group to edit, if any
final Group? groupToEdit;
@override
State<CreateGroupView> createState() => _CreateGroupViewState();
}
class _CreateGroupViewState extends State<CreateGroupView> {
final _groupNameController = TextEditingController();
late final AppDatabase db;
/// GlobalKey for ScaffoldMessenger to show snackbars
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
/// Controller for the group name input field
final _groupNameController = TextEditingController();
/// List of currently selected players
List<Player> selectedPlayers = [];
/// List of initially selected players (when editing a group)
List<Player> initialSelectedPlayers = [];
@override
void initState() {
super.initState();
db = Provider.of<AppDatabase>(context, listen: false);
if (widget.groupToEdit != null) {
_groupNameController.text = widget.groupToEdit!.name;
setState(() {
initialSelectedPlayers = widget.groupToEdit!.members;
selectedPlayers = widget.groupToEdit!.members;
});
}
_groupNameController.addListener(() {
setState(() {});
});
@@ -42,33 +60,73 @@ class _CreateGroupViewState extends State<CreateGroupView> {
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold(
return ScaffoldMessenger(
key: _scaffoldMessengerKey,
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar(
backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0,
title: Text(
loc.create_new_group,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
widget.groupToEdit == null ? loc.create_new_group : loc.edit_group,
),
centerTitle: true,
actions: widget.groupToEdit == null
? []
: [
IconButton(
icon: const Icon(Icons.delete),
onPressed: () async {
if (widget.groupToEdit != null) {
showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text(loc.delete_group),
content: Text(loc.this_cannot_be_undone),
actions: [
TextButton(
onPressed: () =>
Navigator.of(context).pop(false),
child: Text(loc.cancel),
),
TextButton(
onPressed: () =>
Navigator.of(context).pop(true),
child: Text(loc.delete),
),
],
),
).then((confirmed) async {
if (confirmed == true && context.mounted) {
bool success = await db.groupDao.deleteGroup(
groupId: widget.groupToEdit!.id,
);
if (!context.mounted) return;
if (success) {
Navigator.pop(context);
} else {
if (!mounted) return;
showSnackbar(message: loc.error_deleting_group);
}
}
});
}
},
),
],
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
margin: CustomTheme.standardMargin,
child: TextInputField(
controller: _groupNameController,
hintText: loc.group_name,
onChanged: (value) {
setState(() {});
},
),
),
Expanded(
child: PlayerSelection(
initialSelectedPlayers: initialSelectedPlayers,
onChanged: (value) {
setState(() {
selectedPlayers = [...value];
@@ -77,7 +135,9 @@ class _CreateGroupViewState extends State<CreateGroupView> {
),
),
CustomWidthButton(
text: loc.create_group,
text: widget.groupToEdit == null
? loc.create_group
: loc.edit_group,
sizeRelativeToWidth: 0.95,
buttonType: ButtonType.primary,
onPressed:
@@ -85,37 +145,62 @@ class _CreateGroupViewState extends State<CreateGroupView> {
(selectedPlayers.length < 2))
? null
: () async {
bool success = await db.groupDao.addGroup(
late Group? updatedGroup;
late bool success;
if (widget.groupToEdit == null) {
success = await db.groupDao.addGroup(
group: Group(
name: _groupNameController.text.trim(),
members: selectedPlayers,
),
);
} else {
updatedGroup = Group(
id: widget.groupToEdit!.id,
name: _groupNameController.text.trim(),
members: selectedPlayers,
);
//TODO: Implement group editing in database
/*
success = await db.groupDao.updateGroup(
group: updatedGroup,
);
*/
success = true;
}
if (!context.mounted) return;
if (success) {
Navigator.pop(context);
Navigator.pop(context, updatedGroup);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: CustomTheme.boxColor,
content: Center(
child: Text(
AppLocalizations.of(
context,
).error_creating_group,
style: const TextStyle(color: Colors.white),
),
),
),
showSnackbar(
message: widget.groupToEdit == null
? loc.error_creating_group
: loc.error_editing_group,
);
}
setState(() {});
},
),
const SizedBox(height: 20),
],
),
),
),
);
}
/// Displays a snackbar with the given message and optional action.
///
/// [message] The message to display in the snackbar.
void showSnackbar({required String message}) {
final messenger = _scaffoldMessengerKey.currentState;
if (messenger != null) {
messenger.hideCurrentSnackBar();
messenger.showSnackBar(
SnackBar(
content: Text(message, style: const TextStyle(color: Colors.white)),
backgroundColor: CustomTheme.boxColor,
),
);
}
}
}

View File

@@ -0,0 +1,281 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.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/group_view/create_group_view.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 GroupDetailView extends StatefulWidget {
/// A view that displays the profile of a group
/// - [group]: The group to display
const GroupDetailView({
super.key,
required this.group,
required this.callback,
});
/// The group to display
final Group group;
final VoidCallback callback;
@override
State<GroupDetailView> createState() => _GroupDetailViewState();
}
class _GroupDetailViewState extends State<GroupDetailView> {
late final AppDatabase db;
bool isLoading = true;
late Group _group;
/// Total matches played in this group
int totalMatches = 0;
/// The best player in this group
String bestPlayer = '';
@override
void initState() {
super.initState();
_group = widget.group;
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: _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(
_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(_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: _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,
_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: () async {
final updatedGroup = await Navigator.push<Group?>(
context,
adaptivePageRoute(
builder: (context) {
return CreateGroupView(groupToEdit: _group);
},
),
);
if (updatedGroup != null && mounted) {
setState(() {
_group = updatedGroup;
});
_loadStatistics();
widget.callback();
}
},
),
),
],
),
),
);
}
/// 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 == _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,27 +1,34 @@
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/l10n/generated/app_localizations.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: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_detail_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 {
const GroupsView({super.key});
class GroupView extends StatefulWidget {
/// A view that displays a list of groups
const GroupView({super.key});
@override
State<GroupsView> createState() => _GroupsViewState();
State<GroupView> createState() => _GroupViewState();
}
class _GroupsViewState extends State<GroupsView> {
class _GroupViewState extends State<GroupView> {
late final AppDatabase db;
/// Loaded groups from the database
late List<Group> loadedGroups;
/// Loading state
bool isLoading = true;
List<Group> groups = List.filled(
@@ -68,20 +75,35 @@ class _GroupsViewState extends State<GroupsView> {
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 GroupDetailView(
group: groups[index],
callback: loadGroups,
);
},
),
);
},
);
},
),
),
),
Positioned(
bottom: MediaQuery.paddingOf(context).bottom,
child: CustomWidthButton(
bottom: MediaQuery.paddingOf(context).bottom + 20,
child: MainMenuButton(
text: loc.create_group,
sizeRelativeToWidth: 0.90,
icon: Icons.group_add,
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(
adaptivePageRoute(
builder: (context) {
return const CreateGroupView();
},
@@ -99,9 +121,12 @@ class _GroupsViewState extends State<GroupsView> {
}
void loadGroups() {
setState(() {
isLoading = true;
});
Future.wait([
db.groupDao.getAllGroups(),
Future.delayed(minimumSkeletonDuration),
Future.delayed(Constants.MINIMUM_SKELETON_DURATION),
]).then((results) {
loadedGroups = results[0] as List<Group>;
setState(() {

View File

@@ -1,18 +1,22 @@
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/l10n/generated/app_localizations.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: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 {
/// The main home view of the application, displaying quick info,
/// recent matches, and quick create options.
const HomeView({super.key});
@override
@@ -21,9 +25,17 @@ class HomeView extends StatefulWidget {
class _HomeViewState extends State<HomeView> {
bool isLoading = true;
/// Amount of matches in the database
int matchCount = 0;
/// Amount of groups in the database
int groupCount = 0;
/// Loaded recent matches from the database
List<Match> loadedRecentMatches = [];
/// Recent matches to display, initially filled with skeleton matches
List<Match> recentMatches = List.filled(
2,
Match(
@@ -35,39 +47,13 @@ class _HomeViewState extends State<HomeView> {
Player(name: 'Skeleton Player 2'),
],
),
winner: Player(name: 'Skeleton Player 1'),
),
);
@override
void initState() {
super.initState();
final db = Provider.of<AppDatabase>(context, listen: false);
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;
});
}
});
loadHomeViewData();
}
@override
@@ -107,74 +93,43 @@ class _HomeViewState extends State<HomeView> {
child: InfoTile(
width: constraints.maxWidth * 0.95,
title: loc.recent_matches,
icon: Icons.timer,
content: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: Visibility(
visible: !isLoading && loadedRecentMatches.isNotEmpty,
replacement: Center(
heightFactor: 12,
child: Text(
AppLocalizations.of(
context,
).no_recent_matches_available,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
icon: Icons.history_rounded,
content: Column(
children: [
MatchSummaryTile(
matchTitle: recentMatches[0].name,
game: 'Winner',
ruleset: 'Ruleset',
players: _getPlayerText(
recentMatches[0],
context,
if (recentMatches.isNotEmpty)
for (Match match in recentMatches)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 6.0,
),
winner: recentMatches[0].winner == null
? AppLocalizations.of(
context,
).match_in_progress
: recentMatches[0].winner!.name,
child: MatchTile(
compact: true,
width: constraints.maxWidth * 0.9,
match: match,
onTap: () async {
await Navigator.of(context).push(
adaptivePageRoute(
fullscreenDialog: true,
builder: (context) =>
MatchResultView(match: match),
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Divider(),
);
await updatedWinnerinRecentMatches(match.id);
},
),
if (loadedRecentMatches.length > 1) ...[
MatchSummaryTile(
matchTitle: recentMatches[1].name,
game: 'Winner',
ruleset: 'Ruleset',
players: _getPlayerText(
recentMatches[1],
context,
),
winner: recentMatches[1].winner == null
? AppLocalizations.of(
context,
).match_in_progress
: recentMatches[1].winner!.name,
),
const SizedBox(height: 8),
] else ...[
)
else
Center(
heightFactor: 5.35,
child: Text(
AppLocalizations.of(
context,
).no_second_match_available,
),
heightFactor: 5,
child: Text(loc.no_recent_matches_available),
),
],
],
),
),
),
),
),
InfoTile(
Padding(
padding: EdgeInsets.zero,
child: InfoTile(
width: constraints.maxWidth * 0.95,
title: loc.quick_create,
icon: Icons.add_box_rounded,
@@ -222,6 +177,8 @@ class _HomeViewState extends State<HomeView> {
],
),
),
),
SizedBox(height: MediaQuery.paddingOf(context).bottom),
],
),
),
@@ -230,15 +187,41 @@ class _HomeViewState extends State<HomeView> {
);
}
String _getPlayerText(Match game, context) {
final loc = AppLocalizations.of(context);
if (game.group == null) {
final playerCount = game.players?.length ?? 0;
return loc.players_count(playerCount);
/// Loads the data for the HomeView from the database.
/// This includes the match count, group count, and recent matches.
Future<void> loadHomeViewData() async {
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;
});
}
if (game.players == null || game.players!.isEmpty) {
return game.group!.name;
});
}
/// 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;
});
}
return '${game.group!.name} + ${game.players!.length}';
}
}

View File

@@ -1,29 +1,37 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/core/enums.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart';
import 'package:tallee/l10n/generated/app_localizations.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 {
final List<(String, String, Ruleset)> games;
final int initialGameIndex;
/// A view that allows the user to choose a game from a list of available games
/// - [games]: A list of tuples containing the game name, description and ruleset
/// - [initialGameIndex]: The index of the initially selected game
const ChooseGameView({
super.key,
required this.games,
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
State<ChooseGameView> createState() => _ChooseGameViewState();
}
class _ChooseGameViewState extends State<ChooseGameView> {
late int selectedGameIndex;
/// Controller for the search bar
final TextEditingController searchBarController = TextEditingController();
/// Currently selected game index
late int selectedGameIndex;
@override
void initState() {
selectedGameIndex = widget.initialGameIndex;
@@ -35,20 +43,15 @@ class _ChooseGameViewState extends State<ChooseGameView> {
final loc = AppLocalizations.of(context);
return Scaffold(
backgroundColor: CustomTheme.backgroundColor,
resizeToAvoidBottomInset: false,
appBar: AppBar(
backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.of(context).pop(selectedGameIndex);
},
),
title: Text(
loc.choose_game,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
centerTitle: true,
title: Text(loc.choose_game),
),
body: PopScope(
// This fixes that the Android Back Gesture didn't return the

View File

@@ -1,21 +1,27 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart';
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:tallee/presentation/widgets/tiles/group_tile.dart';
import 'package:tallee/presentation/widgets/top_centered_message.dart';
class ChooseGroupView extends StatefulWidget {
final List<Group> groups;
final String initialGroupId;
/// A view that allows the user to choose a group from a list of groups.
/// - [groups]: A list of available groups to choose from
/// - [initialGroupId]: The ID of the initially selected group
const ChooseGroupView({
super.key,
required this.groups,
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
State<ChooseGroupView> createState() => _ChooseGroupViewState();
}
@@ -37,9 +43,8 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
final loc = AppLocalizations.of(context);
return Scaffold(
backgroundColor: CustomTheme.backgroundColor,
resizeToAvoidBottomInset: false,
appBar: AppBar(
backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
@@ -52,11 +57,7 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
);
},
),
title: Text(
loc.choose_group,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
centerTitle: true,
title: Text(loc.choose_group),
),
body: PopScope(
// This fixes that the Android Back Gesture didn't return the
@@ -136,8 +137,7 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
);
}
/// Filters the groups based on the search query.
/// TODO: Maybe implement also targetting player names?
/// Filters the groups based on the search [query].
void filterGroups(String query) {
setState(() {
if (query.isEmpty) {
@@ -147,7 +147,12 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
filteredGroups.clear();
filteredGroups.addAll(
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,98 +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/l10n/generated/app_localizations.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) {
final loc = AppLocalizations.of(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: Text(
loc.choose_ruleset,
style: const 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,
context,
),
description: widget.rulesets[index].$2,
isHighlighted: selectedRulesetIndex == index,
);
},
),
),
),
);
}
}

View File

@@ -1,39 +1,45 @@
import 'package:flutter/cupertino.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/l10n/generated/app_localizations.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: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 {
/// A view that allows creating a new match
/// [onWinnerChanged]: Optional callback invoked when the winner is changed
const CreateMatchView({super.key, this.onWinnerChanged, this.match});
/// Optional callback invoked when the winner is changed
final VoidCallback? onWinnerChanged;
const CreateMatchView({super.key, this.onWinnerChanged});
/// An optional match to prefill the fields
final Match? match;
@override
State<CreateMatchView> createState() => _CreateMatchViewState();
}
class _CreateMatchViewState extends State<CreateMatchView> {
/// Reference to the app database
late final AppDatabase db;
/// Controller for the match name input field
final TextEditingController _matchNameController = TextEditingController();
/// Hint text for the match name input field
String hintText = 'Match Name';
String? hintText;
/// List of all groups from the database
List<Group> groupsList = [];
@@ -54,13 +60,6 @@ class _CreateMatchViewState extends State<CreateMatchView> {
/// the [ChooseGroupView]
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 [ChooseGameView]
int selectedGameIndex = -1;
@@ -68,6 +67,9 @@ class _CreateMatchViewState extends State<CreateMatchView> {
/// The currently selected players
List<Player>? selectedPlayers;
/// GlobalKey for ScaffoldMessenger to show snackbars
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
@override
void initState() {
super.initState();
@@ -87,19 +89,35 @@ class _CreateMatchViewState extends State<CreateMatchView> {
filteredPlayerList = List.from(playerList);
});
});
// If a match is provided, prefill the fields
if (widget.match != null) {
final match = widget.match!;
_matchNameController.text = match.name;
selectedGroup = match.group;
selectedGroupId = match.group?.id ?? '';
selectedPlayers = match.players ?? [];
if (selectedGroup != null) {
filteredPlayerList = playerList
.where((p) => !selectedGroup!.members.any((m) => m.id == p.id))
.toList();
}
}
}
List<(Ruleset, String)> _getRulesets(BuildContext context) {
@override
void dispose() {
_matchNameController.dispose();
super.dispose();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
final loc = AppLocalizations.of(context);
return [
(Ruleset.singleWinner, loc.ruleset_single_winner),
(Ruleset.singleLoser, loc.ruleset_single_loser),
(Ruleset.mostPoints, loc.ruleset_most_points),
(Ruleset.leastPoints, loc.ruleset_least_points),
];
hintText ??= loc.match_name;
}
// TODO: Replace when games are implemented
List<(String, String, Ruleset)> games = [
('Example Game 1', 'This is a description', Ruleset.leastPoints),
('Example Game 2', '', Ruleset.singleWinner),
@@ -108,26 +126,26 @@ class _CreateMatchViewState extends State<CreateMatchView> {
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold(
final buttonText = widget.match != null
? loc.save_changes
: loc.create_match;
return ScaffoldMessenger(
key: _scaffoldMessengerKey,
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar(
backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0,
title: Text(
loc.create_new_match,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
centerTitle: true,
),
appBar: AppBar(title: Text(loc.create_new_match)),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 5),
margin: CustomTheme.tileMargin,
child: TextInputField(
controller: _matchNameController,
hintText: hintText,
hintText: hintText ?? '',
maxLength: Constants.MAX_MATCH_NAME_LENGTH,
),
),
ChooseTile(
@@ -137,7 +155,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
: games[selectedGameIndex].$1,
onPressed: () async {
selectedGameIndex = await Navigator.of(context).push(
MaterialPageRoute(
adaptivePageRoute(
builder: (context) => ChooseGameView(
games: games,
initialGameIndex: selectedGameIndex,
@@ -147,40 +165,12 @@ class _CreateMatchViewState extends State<CreateMatchView> {
setState(() {
if (selectedGameIndex != -1) {
hintText = games[selectedGameIndex].$1;
selectedRuleset = games[selectedGameIndex].$3;
selectedRulesetIndex = _getRulesets(
context,
).indexWhere((r) => r.$1 == selectedRuleset);
} else {
hintText = 'Match Name';
selectedRuleset = null;
hintText = loc.match_name;
}
});
},
),
ChooseTile(
title: loc.ruleset,
trailingText: selectedRuleset == null
? loc.none
: translateRulesetToString(selectedRuleset!, context),
onPressed: () async {
final rulesets = _getRulesets(context);
selectedRuleset = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChooseRulesetView(
rulesets: rulesets,
initialRulesetIndex: selectedRulesetIndex,
),
),
);
if (!mounted) return;
selectedRulesetIndex = rulesets.indexWhere(
(r) => r.$1 == selectedRuleset,
);
selectedGameIndex = -1;
setState(() {});
},
),
ChooseTile(
title: loc.group,
trailingText: selectedGroup == null
@@ -188,7 +178,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
: selectedGroup!.name,
onPressed: () async {
selectedGroup = await Navigator.of(context).push(
MaterialPageRoute(
adaptivePageRoute(
builder: (context) => ChooseGroupView(
groups: groupsList,
initialGroupId: selectedGroupId,
@@ -199,7 +189,8 @@ class _CreateMatchViewState extends State<CreateMatchView> {
if (selectedGroup != null) {
filteredPlayerList = playerList
.where(
(p) => !selectedGroup!.members.any((m) => m.id == p.id),
(p) =>
!selectedGroup!.members.any((m) => m.id == p.id),
)
.toList();
} else {
@@ -221,14 +212,40 @@ class _CreateMatchViewState extends State<CreateMatchView> {
),
),
CustomWidthButton(
text: loc.create_match,
text: buttonText,
sizeRelativeToWidth: 0.95,
buttonType: ButtonType.primary,
onPressed: _enableCreateGameButton()
? () async {
? () {
buttonNavigation(context);
}
: null,
),
],
),
),
),
);
}
/// Determines whether the "Create Match" button should be enabled.
///
/// Returns `true` if:
/// - A ruleset is selected AND
/// - Either a group is selected OR at least 2 players are selected
bool _enableCreateGameButton() {
return (selectedGroup != null ||
(selectedPlayers != null && selectedPlayers!.length > 1));
}
void buttonNavigation(BuildContext context) async {
if (widget.match != null) {
// TODO: Implement updating match logic here
Navigator.pop(context);
} else {
Match match = Match(
name: _matchNameController.text.isEmpty
? hintText
? (hintText ?? '')
: _matchNameController.text.trim(),
createdAt: DateTime.now(),
group: selectedGroup,
@@ -238,7 +255,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
if (context.mounted) {
Navigator.pushReplacement(
context,
CupertinoPageRoute(
adaptivePageRoute(
fullscreenDialog: true,
builder: (context) => MatchResultView(
match: match,
@@ -248,19 +265,5 @@ class _CreateMatchViewState extends State<CreateMatchView> {
);
}
}
: null,
),
],
),
),
);
}
/// Determines whether the "Create Game" button should be enabled based on
/// the current state of the input fields.
bool _enableCreateGameButton() {
return selectedGroup != null ||
(selectedPlayers != null && selectedPlayers!.length > 1) &&
selectedRuleset != null;
}
}

View File

@@ -0,0 +1,285 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.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/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/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 MatchDetailView extends StatefulWidget {
/// A view that displays the profile of a match
/// - [match]: The match to display
/// - [callback]: Callback to refresh the match list
const MatchDetailView({
super.key,
required this.match,
required this.callback,
});
/// The match to display
final Match match;
/// Callback to refresh the match list
final VoidCallback callback;
@override
State<MatchDetailView> createState() => _MatchDetailViewState();
}
class _MatchDetailViewState extends State<MatchDetailView> {
late final AppDatabase db;
late Player? currentWinner;
/// All players who participated in the match
late final List<Player> allPlayers;
@override
void initState() {
super.initState();
db = Provider.of<AppDatabase>(context, listen: false);
allPlayers = _getAllPlayers();
currentWinner = widget.match.winner;
}
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold(
backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar(
title: Text(loc.match_profile),
actions: [
IconButton(
icon: const Icon(Icons.delete),
onPressed: () async {
showDialog<bool>(
context: context,
builder: (context) => CustomAlertDialog(
title: '${loc.delete_match}?',
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.matchDao.deleteMatch(matchId: widget.match.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.sports_esports,
containerSize: 55,
iconSize: 38,
),
),
const SizedBox(height: 10),
Text(
widget.match.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.match.createdAt)}',
style: const TextStyle(
fontSize: 12,
color: CustomTheme.textColor,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
if (widget.match.group != null) ...[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.group),
const SizedBox(width: 8),
Text(
// TODO: Update after DB changes
'${widget.match.group!.name} ${widget.match.players != null ? '+ ${widget.match.players!.length}' : ''}',
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 20),
],
InfoTile(
title: loc.players,
icon: Icons.people,
horizontalAlignment: CrossAxisAlignment.start,
content: Wrap(
alignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 12,
runSpacing: 8,
children: allPlayers.map((player) {
return TextIconTile(
text: player.name,
iconEnabled: false,
);
}).toList(),
),
),
const SizedBox(height: 15),
InfoTile(
title: loc.results,
icon: Icons.emoji_events,
content: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 8,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
/// TODO: Implement different ruleset results display
if (currentWinner != null) ...[
Text(
loc.winner,
style: const TextStyle(
fontSize: 16,
color: CustomTheme.textColor,
),
),
Text(
currentWinner!.name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: widget.match.winner != null
? CustomTheme.primaryColor
: CustomTheme.textColor,
),
),
] else ...[
Text(
loc.no_results_entered_yet,
style: const TextStyle(
fontSize: 14,
color: CustomTheme.textColor,
),
),
],
],
),
),
),
],
),
Positioned(
bottom: MediaQuery.paddingOf(context).bottom,
child: Row(
children: [
MainMenuButton(
icon: Icons.edit,
onPressed: () => Navigator.push(
context,
adaptivePageRoute(
fullscreenDialog: true,
builder: (context) =>
CreateMatchView(match: widget.match),
),
),
),
const SizedBox(width: 15),
MainMenuButton(
text: loc.enter_results,
icon: Icons.emoji_events,
onPressed: () async {
currentWinner = await Navigator.push(
context,
adaptivePageRoute(
fullscreenDialog: true,
builder: (context) => MatchResultView(
match: widget.match,
onWinnerChanged: () {
widget.callback.call();
setState(() {});
},
),
),
);
},
),
],
),
),
],
),
),
);
}
/// Gets all players who participated in the match (from group and individual players)
List<Player> _getAllPlayers() {
final List<Player> players = [];
// Add group members if group exists
if (widget.match.group != null) {
players.addAll(widget.match.group!.members);
}
// Add individual players
if (widget.match.players != null) {
for (var player in widget.match.players!) {
// Avoid duplicates
if (!players.any((p) => p.id == player.id)) {
players.add(player);
}
}
}
return players;
}
}

View File

@@ -1,25 +1,35 @@
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/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/widgets/tiles/custom_radio_list_tile.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 {
/// 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;
/// Optional callback invoked when the winner is changed
final VoidCallback? onWinnerChanged;
const MatchResultView({super.key, required this.match, this.onWinnerChanged});
@override
State<MatchResultView> createState() => _MatchResultViewState();
}
class _MatchResultViewState extends State<MatchResultView> {
late final List<Player> allPlayers;
late final AppDatabase db;
/// List of all players who participated in the match
late final List<Player> allPlayers;
/// Currently selected winner player
Player? _selectedPlayer;
@override
@@ -44,20 +54,10 @@ class _MatchResultViewState extends State<MatchResultView> {
icon: const Icon(Icons.close),
onPressed: () {
widget.onWinnerChanged?.call();
Navigator.of(context).pop();
Navigator.of(context).pop(_selectedPlayer);
},
),
backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0,
title: Text(
widget.match.name,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
overflow: TextOverflow.ellipsis,
),
),
centerTitle: true,
title: Text(widget.match.name),
),
body: SafeArea(
child: Column(
@@ -74,7 +74,7 @@ class _MatchResultViewState extends State<MatchResultView> {
),
decoration: BoxDecoration(
color: CustomTheme.boxColor,
border: Border.all(color: CustomTheme.boxBorder),
border: Border.all(color: CustomTheme.boxBorderColor),
borderRadius: BorderRadius.circular(12),
),
child: Column(
@@ -132,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 {
if (_selectedPlayer == null) {
await db.matchDao.removeWinner(matchId: widget.match.id);
@@ -144,6 +146,10 @@ class _MatchResultViewState extends State<MatchResultView> {
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> players = [];

View File

@@ -1,23 +1,25 @@
import 'dart:core' hide Match;
import 'package:flutter/cupertino.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/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/l10n/generated/app_localizations.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:fluttericon/rpg_awesome_icons.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_detail_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 {
/// A view that displays a list of matches
const MatchView({super.key});
@override
@@ -28,6 +30,8 @@ class _MatchViewState extends State<MatchView> {
late final AppDatabase db;
bool isLoading = true;
/// Loaded matches from the database,
/// initially filled with skeleton matches
List<Match> matches = List.filled(
4,
Match(
@@ -44,7 +48,6 @@ class _MatchViewState extends State<MatchView> {
@override
void initState() {
super.initState();
db = Provider.of<AppDatabase>(context, listen: false);
loadGames();
}
@@ -77,34 +80,39 @@ class _MatchViewState extends State<MatchView> {
height: MediaQuery.paddingOf(context).bottom - 20,
);
}
return MatchTile(
return Center(
child: Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: MatchTile(
width: MediaQuery.sizeOf(context).width * 0.95,
onTap: () async {
Navigator.push(
context,
CupertinoPageRoute(
fullscreenDialog: true,
builder: (context) => MatchResultView(
adaptivePageRoute(
builder: (context) => MatchDetailView(
match: matches[index],
onWinnerChanged: loadGames,
callback: loadGames,
),
),
);
},
match: matches[index],
),
),
);
},
),
),
),
Positioned(
bottom: MediaQuery.paddingOf(context).bottom,
child: CustomWidthButton(
text: loc.create_match,
sizeRelativeToWidth: 0.90,
bottom: MediaQuery.paddingOf(context).bottom + 20,
child: MainMenuButton(
text: 'Spiel erstellen',
icon: RpgAwesome.clovers_card,
onPressed: () async {
Navigator.push(
context,
MaterialPageRoute(
adaptivePageRoute(
builder: (context) =>
CreateMatchView(onWinnerChanged: loadGames),
),
@@ -117,10 +125,12 @@ class _MatchViewState extends State<MatchView> {
);
}
/// Loads the games from the database and sorts them by creation date.
void loadGames() {
isLoading = true;
Future.wait([
db.matchDao.getAllMatches(),
Future.delayed(minimumSkeletonDuration),
Future.delayed(Constants.MINIMUM_SKELETON_DURATION),
]).then((results) {
if (mounted) {
setState(() {

View File

@@ -1,197 +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/l10n/generated/app_localizations.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
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(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: [
Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 10),
child: Text(
textAlign: TextAlign.start,
loc.menu,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 10,
),
child: Text(
textAlign: TextAlign.start,
loc.settings,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
),
SettingsListTile(
title: loc.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: loc.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: loc.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: Text(loc.delete_all_data),
content: Text(loc.this_cannot_be_undone),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(loc.cancel),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(loc.delete),
),
],
),
).then((confirmed) {
if (confirmed == true && context.mounted) {
DataTransferService.deleteAllData(context);
showSnackbar(
context: context,
message: AppLocalizations.of(
context,
).data_successfully_deleted,
);
}
});
},
),
],
),
),
),
);
}
/// 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,
}) {
final loc = AppLocalizations.of(context);
switch (result) {
case ImportResult.success:
showSnackbar(context: context, message: loc.data_successfully_imported);
case ImportResult.invalidSchema:
showSnackbar(context: context, message: loc.invalid_schema);
case ImportResult.fileReadError:
showSnackbar(context: context, message: loc.error_reading_file);
case ImportResult.canceled:
showSnackbar(context: context, message: loc.import_canceled);
case ImportResult.formatException:
showSnackbar(context: context, message: loc.format_exception);
case ImportResult.unknownException:
showSnackbar(context: context, message: loc.unknown_exception);
}
}
/// 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,
}) {
final loc = AppLocalizations.of(context);
switch (result) {
case ExportResult.success:
showSnackbar(context: context, message: loc.data_successfully_exported);
case ExportResult.canceled:
showSnackbar(context: context, message: loc.export_canceled);
case ExportResult.unknownException:
showSnackbar(context: context, message: loc.unknown_exception);
}
}
/// 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 loc = AppLocalizations.of(context);
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: loc.undo, 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),
),
),
),
],
),
),
);
}
}

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