Compare commits
74 Commits
27508b5e29
...
feature/12
| Author | SHA1 | Date | |
|---|---|---|---|
| e71943f6e2 | |||
| f07103a516 | |||
| b84a893706 | |||
| 527ffd194f | |||
| 9e9491c98e | |||
| 048fb0ef43 | |||
| a4bc03111d | |||
| 00abc4d1c0 | |||
| d4fcc8106f | |||
| 487efb4d61 | |||
| 3f790cfbb1 | |||
| ee1962ef9c | |||
| a4d4703069 | |||
| fabb7bae19 | |||
| d1458443eb | |||
| 9c00b48de5 | |||
| 03ce304a0a | |||
| dde617d429 | |||
| 03a2df4fdf | |||
| 96ef70b209 | |||
| 4ae59ec881 | |||
| c162e245bd | |||
| 481afd7533 | |||
| af42ebbf92 | |||
| 34e442dbe9 | |||
| 8783d0ac44 | |||
| 7f356b0c86 | |||
| e4a2ac6b47 | |||
| 6065b53ce9 | |||
| c644ccfb06 | |||
| 2dd7d0285d | |||
| cf5883b430 | |||
| 7af2b43193 | |||
| fd7dbd1155 | |||
| 61b72cf595 | |||
| 81c6c377d4 | |||
| 92f2b4db95 | |||
| 5178adf71c | |||
| 4a6d639f1c | |||
| e9cf707fca | |||
| 9a5929382b | |||
| cca09cc27e | |||
| f00aa15518 | |||
| 810f635987 | |||
| 49a6259d8a | |||
| e4c3bc1c5e | |||
| 14d30f55a7 | |||
| f1df067824 | |||
| 765610b184 | |||
| 48d99a0386 | |||
| a56e738064 | |||
| 1450e9b958 | |||
| eb114f2853 | |||
| 7faf80de03 | |||
| 8fe01c332e | |||
| 374c9295ef | |||
| 8b7300eac3 | |||
| 919a38afe5 | |||
| def31acfb1 | |||
| 016c1ceb6e | |||
| 1b297d15b0 | |||
| ed642e3d4f | |||
| 7cc3873a31 | |||
| b1e9bb3aeb | |||
| 5da12939cb | |||
| 0d20b5847f | |||
| 973e327232 | |||
| caf60d046b | |||
| 38663c6b67 | |||
| ee84c60ba6 | |||
| b6dd0541ae | |||
| 525acec1d3 | |||
| 45a419cae7 | |||
| 0f79495775 |
@@ -85,13 +85,13 @@ jobs:
|
||||
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]"
|
||||
@@ -103,6 +103,10 @@ jobs:
|
||||
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
|
||||
@@ -131,6 +135,7 @@ jobs:
|
||||
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]"
|
||||
@@ -182,6 +187,8 @@ jobs:
|
||||
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 }}
|
||||
|
||||
13
CONTRIBUTING.md
Normal file
13
CONTRIBUTING.md
Normal 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
165
LICENSE
Normal 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.
|
||||
66
README.md
66
README.md
@@ -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>
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 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).
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
A all-in-one app to track card- and board games, manage players and groups and get statistics about your played games.
|
||||
|
||||
BIN
artefacts/app-logo.png
Normal file
BIN
artefacts/app-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
BIN
artefacts/screenshot-1.png
Normal file
BIN
artefacts/screenshot-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 291 KiB |
BIN
artefacts/screenshot-2.png
Normal file
BIN
artefacts/screenshot-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 279 KiB |
BIN
artefacts/screenshot-3.png
Normal file
BIN
artefacts/screenshot-3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 354 KiB |
BIN
artefacts/screenshot-4.png
Normal file
BIN
artefacts/screenshot-4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 294 KiB |
@@ -13,13 +13,13 @@ class CustomTheme {
|
||||
static const Color secondaryColor = Color(0xFFf2a981);
|
||||
|
||||
/// Background color of the app theme
|
||||
static const backgroundColor = Color(0xFF0B0B0B);
|
||||
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 boxBorder = Color(0xFF272727);
|
||||
static const Color boxBorderColor = Color(0xFF272727);
|
||||
|
||||
/// Color for boxes and containers displayed on boxes
|
||||
static const Color onBoxColor = Color(0xFF181818);
|
||||
@@ -27,6 +27,9 @@ class CustomTheme {
|
||||
/// 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);
|
||||
|
||||
@@ -51,7 +54,7 @@ class CustomTheme {
|
||||
// ==================== Decorations ====================
|
||||
static BoxDecoration standardBoxDecoration = BoxDecoration(
|
||||
color: boxColor,
|
||||
border: Border.all(color: boxBorder),
|
||||
border: Border.all(color: boxBorderColor),
|
||||
borderRadius: standardBorderRadiusAll,
|
||||
);
|
||||
|
||||
|
||||
@@ -22,9 +22,15 @@
|
||||
"days_ago": "vor {count} Tagen",
|
||||
"delete": "Löschen",
|
||||
"delete_all_data": "Alle Daten löschen",
|
||||
"delete_group": "Gruppe 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",
|
||||
@@ -46,6 +52,7 @@
|
||||
"licenses": "Lizenzen",
|
||||
"match_in_progress": "Spiel läuft...",
|
||||
"match_name": "Spieltitel",
|
||||
"match_profile": "Spielprofil",
|
||||
"matches": "Spiele",
|
||||
"members": "Mitglieder",
|
||||
"most_points": "Höchste Punkte",
|
||||
@@ -58,6 +65,7 @@
|
||||
"no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden",
|
||||
"no_players_selected": "Keine Spieler:innen ausgewählt",
|
||||
"no_recent_matches_available": "Keine letzten Spiele verfügbar",
|
||||
"no_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",
|
||||
@@ -70,11 +78,14 @@
|
||||
"privacy_policy": "Datenschutzerklärung",
|
||||
"quick_create": "Schnellzugriff",
|
||||
"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:",
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
"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"
|
||||
@@ -72,14 +72,26 @@
|
||||
"description": "Confirmation dialog for deleting all data"
|
||||
},
|
||||
"@delete_group": {
|
||||
"description": "Button text to delete a group"
|
||||
"description": "Confirmation dialog for deleting a group"
|
||||
},
|
||||
"@delete_match": {
|
||||
"description": "Button text to delete a match"
|
||||
},
|
||||
"@edit_group": {
|
||||
"description": "Button text to edit a 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"
|
||||
},
|
||||
@@ -143,6 +155,9 @@
|
||||
"@match_name": {
|
||||
"description": "Placeholder for match name input"
|
||||
},
|
||||
"@match_profile": {
|
||||
"description": "Title for match profile view"
|
||||
},
|
||||
"@matches": {
|
||||
"description": "Label for matches"
|
||||
},
|
||||
@@ -179,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"
|
||||
},
|
||||
@@ -220,6 +238,9 @@
|
||||
"@recent_matches": {
|
||||
"description": "Title for recent matches section"
|
||||
},
|
||||
"@results": {
|
||||
"description": "Label for match results"
|
||||
},
|
||||
"@ruleset": {
|
||||
"description": "Ruleset label"
|
||||
},
|
||||
@@ -235,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"
|
||||
},
|
||||
@@ -321,8 +345,12 @@
|
||||
"delete": "Delete",
|
||||
"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",
|
||||
@@ -344,6 +372,7 @@
|
||||
"licenses": "Licenses",
|
||||
"match_in_progress": "Match in progress...",
|
||||
"match_name": "Match name",
|
||||
"match_profile": "Match Profile",
|
||||
"matches": "Matches",
|
||||
"members": "Members",
|
||||
"most_points": "Most Points",
|
||||
@@ -356,6 +385,7 @@
|
||||
"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",
|
||||
@@ -368,11 +398,13 @@
|
||||
"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:",
|
||||
|
||||
@@ -170,7 +170,7 @@ abstract class AppLocalizations {
|
||||
/// **'Create match'**
|
||||
String get create_match;
|
||||
|
||||
/// Button text to create a new group
|
||||
/// Appbar text to create a new group
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create new group'**
|
||||
@@ -182,7 +182,7 @@ abstract class AppLocalizations {
|
||||
/// **'Created on'**
|
||||
String get created_on;
|
||||
|
||||
/// Button text to create a new match
|
||||
/// Appbar text to create a new match
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create new match'**
|
||||
@@ -230,24 +230,48 @@ abstract class AppLocalizations {
|
||||
/// **'Delete all data'**
|
||||
String get delete_all_data;
|
||||
|
||||
/// Button text to delete a group
|
||||
/// Confirmation dialog for deleting a group
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete Group'**
|
||||
String get delete_group;
|
||||
|
||||
/// Button text to edit a 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:
|
||||
@@ -374,6 +398,12 @@ abstract class AppLocalizations {
|
||||
/// **'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:
|
||||
@@ -446,6 +476,12 @@ abstract class AppLocalizations {
|
||||
/// **'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:
|
||||
@@ -518,6 +554,12 @@ abstract class AppLocalizations {
|
||||
/// **'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:
|
||||
@@ -548,6 +590,12 @@ abstract class AppLocalizations {
|
||||
/// **'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:
|
||||
|
||||
@@ -81,13 +81,27 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@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 get error_deleting_group =>
|
||||
'Fehler beim Löschen der Gruppe, bitte erneut versuchen';
|
||||
|
||||
@override
|
||||
String get error_editing_group =>
|
||||
'Fehler beim Bearbeiten der Gruppe, bitte erneut versuchen';
|
||||
|
||||
@override
|
||||
String get error_reading_file => 'Fehler beim Lesen der Datei';
|
||||
|
||||
@@ -151,6 +165,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get match_name => 'Spieltitel';
|
||||
|
||||
@override
|
||||
String get match_profile => 'Spielprofil';
|
||||
|
||||
@override
|
||||
String get matches => 'Spiele';
|
||||
|
||||
@@ -188,6 +205,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@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';
|
||||
|
||||
@@ -226,6 +246,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get recent_matches => 'Letzte Spiele';
|
||||
|
||||
@override
|
||||
String get results => 'Ergebnisse';
|
||||
|
||||
@override
|
||||
String get ruleset => 'Regelwerk';
|
||||
|
||||
@@ -245,6 +268,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
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';
|
||||
|
||||
|
||||
@@ -81,13 +81,27 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@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 get error_deleting_group =>
|
||||
'Error while deleting group, please try again';
|
||||
|
||||
@override
|
||||
String get error_editing_group =>
|
||||
'Error while editing group, please try again';
|
||||
|
||||
@override
|
||||
String get error_reading_file => 'Error reading file';
|
||||
|
||||
@@ -151,6 +165,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get match_name => 'Match name';
|
||||
|
||||
@override
|
||||
String get match_profile => 'Match Profile';
|
||||
|
||||
@override
|
||||
String get matches => 'Matches';
|
||||
|
||||
@@ -188,6 +205,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@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';
|
||||
|
||||
@@ -226,6 +246,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get recent_matches => 'Recent Matches';
|
||||
|
||||
@override
|
||||
String get results => 'Results';
|
||||
|
||||
@override
|
||||
String get ruleset => 'Ruleset';
|
||||
|
||||
@@ -245,6 +268,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
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';
|
||||
|
||||
|
||||
@@ -40,10 +40,22 @@ class GameTracker extends StatelessWidget {
|
||||
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(),
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.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/groups_view.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';
|
||||
@@ -40,7 +38,7 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
|
||||
),
|
||||
KeyedSubtree(
|
||||
key: ValueKey('groups_$tabKeyCount'),
|
||||
child: const GroupsView(),
|
||||
child: const GroupView(),
|
||||
),
|
||||
KeyedSubtree(
|
||||
key: ValueKey('stats_$tabKeyCount'),
|
||||
@@ -75,66 +73,28 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
body: tabs[currentIndex],
|
||||
extendBody: true,
|
||||
bottomNavigationBar: SizedBox(
|
||||
height: 70 + MediaQuery.of(context).padding.bottom,
|
||||
child: Stack(
|
||||
children: [
|
||||
// Dynamically generated blur layers for ultra-smooth transition
|
||||
...List.generate(34, (index) {
|
||||
// Use cubic curve for an even more natural, smoother transition
|
||||
final progress = index / 34.0; // 0.0 to 1.0
|
||||
final cubic = progress * progress * progress; // cubic curve
|
||||
final blurStrength =
|
||||
0.5 + (cubic * 50.0); // Very smooth from 0.5 to 50.5
|
||||
|
||||
// Height goes completely from 100% to 0% (all the way down)
|
||||
// With extra density at the bottom for softer transition
|
||||
final heightFactor = index < 25
|
||||
// First 25 layers: 100% to 30%
|
||||
? 1.0 - (progress * 0.7)
|
||||
// Last 10 layers: 30% to 0% (denser)
|
||||
: 0.3 - ((index - 25) / 34.0);
|
||||
|
||||
return Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
height:
|
||||
(70 + MediaQuery.of(context).padding.bottom) *
|
||||
heightFactor.clamp(0.05, 1.0),
|
||||
child: ClipRect(
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: blurStrength,
|
||||
sigmaY: blurStrength,
|
||||
),
|
||||
child: Container(color: Colors.transparent),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
// Gradient overlay
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
bottomNavigationBar: Container(
|
||||
height: 115,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
colors: [
|
||||
CustomTheme.boxColor.withValues(alpha: 1),
|
||||
CustomTheme.boxColor.withValues(alpha: 0.5),
|
||||
CustomTheme.boxColor.withValues(alpha: 0.2),
|
||||
CustomTheme.boxColor.withValues(alpha: 0.0),
|
||||
color: CustomTheme.navBarBackgroundColor,
|
||||
border: Border.all(
|
||||
strokeAlign: BorderSide.strokeAlignOutside,
|
||||
color: CustomTheme.boxBorderColor,
|
||||
width: 2,
|
||||
),
|
||||
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),
|
||||
),
|
||||
],
|
||||
stops: const [0.0, 0.4, 0.8, 1],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Navbar content
|
||||
SafeArea(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: SafeArea(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
@@ -170,9 +130,6 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tallee/core/constants.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
import 'package:tallee/core/enums.dart';
|
||||
import 'package:tallee/data/db/database.dart';
|
||||
@@ -12,8 +11,10 @@ import 'package:tallee/presentation/widgets/player_selection.dart';
|
||||
import 'package:tallee/presentation/widgets/text_input/text_input_field.dart';
|
||||
|
||||
class CreateGroupView extends StatefulWidget {
|
||||
/// A view that allows the user to create a new group
|
||||
const CreateGroupView({super.key});
|
||||
const CreateGroupView({super.key, this.groupToEdit});
|
||||
|
||||
/// The group to edit, if any
|
||||
final Group? groupToEdit;
|
||||
|
||||
@override
|
||||
State<CreateGroupView> createState() => _CreateGroupViewState();
|
||||
@@ -22,16 +23,29 @@ class CreateGroupView extends StatefulWidget {
|
||||
class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
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(() {});
|
||||
});
|
||||
@@ -47,10 +61,58 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return ScaffoldMessenger(
|
||||
key: _scaffoldMessengerKey,
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
appBar: AppBar(title: Text(loc.create_new_group)),
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
widget.groupToEdit == null ? loc.create_new_group : loc.edit_group,
|
||||
),
|
||||
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,
|
||||
@@ -60,11 +122,11 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
child: TextInputField(
|
||||
controller: _groupNameController,
|
||||
hintText: loc.group_name,
|
||||
maxLength: Constants.MAX_GROUP_NAME_LENGTH,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: PlayerSelection(
|
||||
initialSelectedPlayers: initialSelectedPlayers,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
selectedPlayers = [...value];
|
||||
@@ -73,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:
|
||||
@@ -81,28 +145,37 @@ 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,
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -114,4 +187,20 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
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';
|
||||
@@ -15,10 +17,10 @@ import 'package:tallee/presentation/widgets/custom_alert_dialog.dart';
|
||||
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
|
||||
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
|
||||
|
||||
class GroupProfileView extends StatefulWidget {
|
||||
class GroupDetailView extends StatefulWidget {
|
||||
/// A view that displays the profile of a group
|
||||
/// - [group]: The group to display
|
||||
const GroupProfileView({
|
||||
const GroupDetailView({
|
||||
super.key,
|
||||
required this.group,
|
||||
required this.callback,
|
||||
@@ -30,21 +32,24 @@ class GroupProfileView extends StatefulWidget {
|
||||
final VoidCallback callback;
|
||||
|
||||
@override
|
||||
State<GroupProfileView> createState() => _GroupProfileViewState();
|
||||
State<GroupDetailView> createState() => _GroupDetailViewState();
|
||||
}
|
||||
|
||||
class _GroupProfileViewState extends State<GroupProfileView> {
|
||||
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();
|
||||
}
|
||||
@@ -87,7 +92,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
||||
),
|
||||
).then((confirmed) async {
|
||||
if (confirmed! && context.mounted) {
|
||||
await db.groupDao.deleteGroup(groupId: widget.group.id);
|
||||
await db.groupDao.deleteGroup(groupId: _group.id);
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context);
|
||||
widget.callback.call();
|
||||
@@ -118,7 +123,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
widget.group.name,
|
||||
_group.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -128,7 +133,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
'${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(widget.group.createdAt)}',
|
||||
'${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(_group.createdAt)}',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: CustomTheme.textColor,
|
||||
@@ -145,7 +150,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
||||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
spacing: 12,
|
||||
runSpacing: 8,
|
||||
children: widget.group.members.map((member) {
|
||||
children: _group.members.map((member) {
|
||||
return TextIconTile(
|
||||
text: member.name,
|
||||
iconEnabled: false,
|
||||
@@ -163,7 +168,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
||||
children: [
|
||||
_buildStatRow(
|
||||
loc.members,
|
||||
widget.group.members.length.toString(),
|
||||
_group.members.length.toString(),
|
||||
),
|
||||
_buildStatRow(
|
||||
loc.played_matches,
|
||||
@@ -181,19 +186,22 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
||||
child: MainMenuButton(
|
||||
text: loc.edit_group,
|
||||
icon: Icons.edit,
|
||||
onPressed: () {
|
||||
// TODO: Uncomment when GroupDetailView is implemented
|
||||
/*
|
||||
await Navigator.push(
|
||||
onPressed: () async {
|
||||
final updatedGroup = await Navigator.push<Group?>(
|
||||
context,
|
||||
adaptivePageRoute(
|
||||
builder: (context) {
|
||||
|
||||
return const GroupDetailView();
|
||||
return CreateGroupView(groupToEdit: _group);
|
||||
},
|
||||
),
|
||||
);*/
|
||||
print('Edit Group pressed');
|
||||
);
|
||||
if (updatedGroup != null && mounted) {
|
||||
setState(() {
|
||||
_group = updatedGroup;
|
||||
});
|
||||
_loadStatistics();
|
||||
widget.callback();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -236,7 +244,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
||||
Future<void> _loadStatistics() async {
|
||||
final matches = await db.matchDao.getAllMatches();
|
||||
final groupMatches = matches
|
||||
.where((match) => match.group?.id == widget.group.id)
|
||||
.where((match) => match.group?.id == _group.id)
|
||||
.toList();
|
||||
|
||||
setState(() {
|
||||
@@ -8,21 +8,21 @@ import 'package:tallee/data/dto/group.dart';
|
||||
import 'package:tallee/data/dto/player.dart';
|
||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||
import 'package:tallee/presentation/views/main_menu/group_view/create_group_view.dart';
|
||||
import 'package:tallee/presentation/views/main_menu/group_view/group_profile_view.dart';
|
||||
import 'package:tallee/presentation/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 {
|
||||
class GroupView extends StatefulWidget {
|
||||
/// A view that displays a list of groups
|
||||
const GroupsView({super.key});
|
||||
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
|
||||
@@ -82,7 +82,7 @@ class _GroupsViewState extends State<GroupsView> {
|
||||
context,
|
||||
adaptivePageRoute(
|
||||
builder: (context) {
|
||||
return GroupProfileView(
|
||||
return GroupDetailView(
|
||||
group: groups[index],
|
||||
callback: loadGroups,
|
||||
);
|
||||
@@ -20,11 +20,14 @@ 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});
|
||||
const CreateMatchView({super.key, this.onWinnerChanged, this.match});
|
||||
|
||||
/// Optional callback invoked when the winner is changed
|
||||
final VoidCallback? onWinnerChanged;
|
||||
|
||||
/// An optional match to prefill the fields
|
||||
final Match? match;
|
||||
|
||||
@override
|
||||
State<CreateMatchView> createState() => _CreateMatchViewState();
|
||||
}
|
||||
@@ -64,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();
|
||||
@@ -83,6 +89,20 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -106,7 +126,12 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
final buttonText = widget.match != null
|
||||
? loc.save_changes
|
||||
: loc.create_match;
|
||||
|
||||
return ScaffoldMessenger(
|
||||
key: _scaffoldMessengerKey,
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
@@ -187,11 +212,37 @@ 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 ?? '')
|
||||
@@ -214,22 +265,5 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
||||
);
|
||||
}
|
||||
}
|
||||
: 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () {
|
||||
widget.onWinnerChanged?.call();
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pop(_selectedPlayer);
|
||||
},
|
||||
),
|
||||
title: Text(widget.match.name),
|
||||
@@ -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(
|
||||
|
||||
@@ -12,7 +12,7 @@ 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/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';
|
||||
@@ -89,10 +89,9 @@ class _MatchViewState extends State<MatchView> {
|
||||
Navigator.push(
|
||||
context,
|
||||
adaptivePageRoute(
|
||||
fullscreenDialog: true,
|
||||
builder: (context) => MatchResultView(
|
||||
builder: (context) => MatchDetailView(
|
||||
match: matches[index],
|
||||
onWinnerChanged: loadGames,
|
||||
callback: loadGames,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -128,6 +127,7 @@ 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(Constants.MINIMUM_SKELETON_DURATION),
|
||||
|
||||
@@ -31,6 +31,7 @@ const allDependencies = <Package>[
|
||||
_cli_config,
|
||||
_cli_util,
|
||||
_clock,
|
||||
_code_assets,
|
||||
_code_builder,
|
||||
_collection,
|
||||
_convert,
|
||||
@@ -62,6 +63,7 @@ const allDependencies = <Package>[
|
||||
_frontend_server_client,
|
||||
_glob,
|
||||
_graphs,
|
||||
_hooks,
|
||||
_html,
|
||||
_http,
|
||||
_http_multi_server,
|
||||
@@ -81,8 +83,10 @@ const allDependencies = <Package>[
|
||||
_material_color_utilities,
|
||||
_meta,
|
||||
_mime,
|
||||
_native_toolchain_c,
|
||||
_nested,
|
||||
_node_preamble,
|
||||
_objective_c,
|
||||
_package_config,
|
||||
_package_info_plus,
|
||||
_package_info_plus_platform_interface,
|
||||
@@ -1187,6 +1191,47 @@ const _clock = Package(
|
||||
limitations under the License.''',
|
||||
);
|
||||
|
||||
/// code_assets 1.0.0
|
||||
const _code_assets = Package(
|
||||
name: 'code_assets',
|
||||
description: 'This library contains the hook protocol specification for bundling native code with Dart packages.',
|
||||
repository: 'https://github.com/dart-lang/native/tree/main/pkgs/code_assets',
|
||||
authors: [],
|
||||
version: '1.0.0',
|
||||
spdxIdentifiers: ['BSD-3-Clause'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
dependencies: [PackageRef('collection'), PackageRef('hooks')],
|
||||
devDependencies: [PackageRef('json_schema'), PackageRef('test')],
|
||||
license: '''Copyright 2025, the Dart project authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// code_builder 4.11.1
|
||||
const _code_builder = Package(
|
||||
name: 'code_builder',
|
||||
@@ -1351,13 +1396,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// cross_file 0.3.5+1
|
||||
/// cross_file 0.3.5+2
|
||||
const _cross_file = Package(
|
||||
name: 'cross_file',
|
||||
description: 'An abstraction to allow working with files across multiple platforms.',
|
||||
repository: 'https://github.com/flutter/packages/tree/main/packages/cross_file',
|
||||
authors: [],
|
||||
version: '0.3.5+1',
|
||||
version: '0.3.5+2',
|
||||
spdxIdentifiers: ['BSD-3-Clause'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
@@ -1583,13 +1628,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// dbus 0.7.11
|
||||
/// dbus 0.7.12
|
||||
const _dbus = Package(
|
||||
name: 'dbus',
|
||||
description: 'A native Dart implementation of the D-Bus message bus client. This package allows Dart applications to directly access services on the Linux desktop.',
|
||||
homepage: 'https://github.com/canonical/dbus.dart',
|
||||
authors: [],
|
||||
version: '0.7.11',
|
||||
version: '0.7.12',
|
||||
spdxIdentifiers: ['MPL-2.0'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
@@ -1970,7 +2015,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
defined by the Mozilla Public License, v. 2.0.''',
|
||||
);
|
||||
|
||||
/// dio 5.9.0
|
||||
/// dio 5.9.1
|
||||
const _dio = Package(
|
||||
name: 'dio',
|
||||
description: '''A powerful HTTP networking package,
|
||||
@@ -1981,7 +2026,7 @@ Custom adapters, Transformers, etc.
|
||||
homepage: 'https://github.com/cfug/dio',
|
||||
repository: 'https://github.com/cfug/dio/blob/main/dio',
|
||||
authors: [],
|
||||
version: '5.9.0',
|
||||
version: '5.9.1',
|
||||
spdxIdentifiers: ['MIT'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
@@ -2452,14 +2497,14 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// file_picker 10.3.8
|
||||
/// file_picker 10.3.10
|
||||
const _file_picker = Package(
|
||||
name: 'file_picker',
|
||||
description: 'A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support.',
|
||||
homepage: 'https://github.com/miguelpruivo/plugins_flutter_file_picker',
|
||||
repository: 'https://github.com/miguelpruivo/flutter_file_picker',
|
||||
authors: [],
|
||||
version: '10.3.8',
|
||||
version: '10.3.10',
|
||||
spdxIdentifiers: ['MIT'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
@@ -2573,13 +2618,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// flutter 3.38.2
|
||||
/// flutter 3.38.6
|
||||
const _flutter = Package(
|
||||
name: 'flutter',
|
||||
description: 'A framework for writing Flutter applications',
|
||||
homepage: 'https://flutter.dev',
|
||||
authors: [],
|
||||
version: '3.38.2',
|
||||
version: '3.38.6',
|
||||
spdxIdentifiers: ['BSD-3-Clause'],
|
||||
isMarkdown: false,
|
||||
isSdk: true,
|
||||
@@ -2902,6 +2947,47 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// hooks 1.0.1
|
||||
const _hooks = Package(
|
||||
name: 'hooks',
|
||||
description: 'A library that contains a Dart API for the JSON-based protocol for `hook/build.dart` and `hook/link.dart`.',
|
||||
repository: 'https://github.com/dart-lang/native/tree/main/pkgs/hooks',
|
||||
authors: [],
|
||||
version: '1.0.1',
|
||||
spdxIdentifiers: ['BSD-3-Clause'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
dependencies: [PackageRef('collection'), PackageRef('crypto'), PackageRef('logging'), PackageRef('meta'), PackageRef('pub_semver'), PackageRef('yaml')],
|
||||
devDependencies: [PackageRef('args'), PackageRef('code_assets'), PackageRef('glob'), PackageRef('json_schema'), PackageRef('path'), PackageRef('test')],
|
||||
license: '''Copyright 2025, the Dart project authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// html 0.15.6
|
||||
const _html = Package(
|
||||
name: 'html',
|
||||
@@ -3185,13 +3271,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// json_annotation 4.9.0
|
||||
/// json_annotation 4.10.0
|
||||
const _json_annotation = Package(
|
||||
name: 'json_annotation',
|
||||
description: 'Classes and helper functions that support JSON code generation via the `json_serializable` package.',
|
||||
repository: 'https://github.com/google/json_serializable.dart/tree/master/json_annotation',
|
||||
authors: [],
|
||||
version: '4.9.0',
|
||||
version: '4.10.0',
|
||||
spdxIdentifiers: ['BSD-3-Clause'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
@@ -3861,6 +3947,47 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// native_toolchain_c 0.17.4
|
||||
const _native_toolchain_c = Package(
|
||||
name: 'native_toolchain_c',
|
||||
description: 'A library to invoke the native C compiler installed on the host machine.',
|
||||
repository: 'https://github.com/dart-lang/native/tree/main/pkgs/native_toolchain_c',
|
||||
authors: [],
|
||||
version: '0.17.4',
|
||||
spdxIdentifiers: ['BSD-3-Clause'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
dependencies: [PackageRef('code_assets'), PackageRef('glob'), PackageRef('hooks'), PackageRef('logging'), PackageRef('meta'), PackageRef('pub_semver')],
|
||||
devDependencies: [PackageRef('collection'), PackageRef('test')],
|
||||
license: '''Copyright 2023, the Dart project authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// nested 1.0.0
|
||||
const _nested = Package(
|
||||
name: 'nested',
|
||||
@@ -3958,6 +4085,47 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// objective_c 9.3.0
|
||||
const _objective_c = Package(
|
||||
name: 'objective_c',
|
||||
description: 'A library to access Objective C from Flutter that acts as a support library for package:ffigen.',
|
||||
repository: 'https://github.com/dart-lang/native/tree/main/pkgs/objective_c',
|
||||
authors: [],
|
||||
version: '9.3.0',
|
||||
spdxIdentifiers: ['BSD-3-Clause'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
dependencies: [PackageRef('code_assets'), PackageRef('collection'), PackageRef('ffi'), PackageRef('hooks'), PackageRef('logging'), PackageRef('native_toolchain_c'), PackageRef('pub_semver')],
|
||||
devDependencies: [PackageRef('args'), PackageRef('path'), PackageRef('test'), PackageRef('yaml')],
|
||||
license: '''Copyright 2024, the Dart project authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// package_config 2.2.0
|
||||
const _package_config = Package(
|
||||
name: 'package_config',
|
||||
@@ -4243,18 +4411,18 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// path_provider_foundation 2.5.1
|
||||
/// path_provider_foundation 2.6.0
|
||||
const _path_provider_foundation = Package(
|
||||
name: 'path_provider_foundation',
|
||||
description: 'iOS and macOS implementation of the path_provider plugin',
|
||||
repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_foundation',
|
||||
authors: [],
|
||||
version: '2.5.1',
|
||||
version: '2.6.0',
|
||||
spdxIdentifiers: ['BSD-3-Clause'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
dependencies: [PackageRef('flutter'), PackageRef('path_provider_platform_interface')],
|
||||
devDependencies: [PackageRef('path')],
|
||||
dependencies: [PackageRef('ffi'), PackageRef('flutter'), PackageRef('objective_c'), PackageRef('path_provider_platform_interface')],
|
||||
devDependencies: [],
|
||||
license: '''Copyright 2013 The Flutter Authors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -5701,13 +5869,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
|
||||
);
|
||||
|
||||
/// source_span 1.10.1
|
||||
/// source_span 1.10.2
|
||||
const _source_span = Package(
|
||||
name: 'source_span',
|
||||
description: 'Provides a standard representation for source code locations and spans.',
|
||||
repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/source_span',
|
||||
authors: [],
|
||||
version: '1.10.1',
|
||||
version: '1.10.2',
|
||||
spdxIdentifiers: ['BSD-3-Clause'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
@@ -7191,7 +7359,7 @@ const _win32 = Package(
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
dependencies: [PackageRef('ffi')],
|
||||
devDependencies: [PackageRef('args'), PackageRef('path'), PackageRef('test')],
|
||||
devDependencies: [PackageRef('args'), PackageRef('hooks'), PackageRef('path'), PackageRef('test')],
|
||||
license: '''BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2024, Halil Durmus
|
||||
@@ -7331,16 +7499,181 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.''',
|
||||
);
|
||||
|
||||
/// tallee 0.0.12+240
|
||||
/// tallee 0.0.16+250
|
||||
const _tallee = Package(
|
||||
name: 'tallee',
|
||||
description: 'Tracking App for Card Games',
|
||||
authors: [],
|
||||
version: '0.0.12+240',
|
||||
spdxIdentifiers: [],
|
||||
version: '0.0.16+250',
|
||||
spdxIdentifiers: ['LGPL-3.0'],
|
||||
isMarkdown: false,
|
||||
isSdk: false,
|
||||
dependencies: [PackageRef('flutter'), PackageRef('clock'), PackageRef('cupertino_icons'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('file_picker'), PackageRef('file_saver'), PackageRef('fluttericon'), PackageRef('font_awesome_flutter'), PackageRef('intl'), PackageRef('json_schema'), PackageRef('package_info_plus'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('url_launcher'), PackageRef('uuid')],
|
||||
devDependencies: [PackageRef('build_runner'), PackageRef('dart_pubspec_licenses'), PackageRef('drift_dev'), PackageRef('flutter_lints')],
|
||||
license: '''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.''',
|
||||
);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ class _SettingsViewState extends State<SettingsView> {
|
||||
version: 'n.A.',
|
||||
buildNumber: 'n.A.',
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
@@ -2,25 +2,25 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class MainMenuButton extends StatefulWidget {
|
||||
/// A button for the main menu with an optional icon and a press animation.
|
||||
/// - [text]: The text of the button.
|
||||
/// - [icon]: The icon of the button.
|
||||
/// - [onPressed]: The callback to be invoked when the button is pressed.
|
||||
/// - [icon]: The icon of the button.
|
||||
/// - [text]: The text of the button.
|
||||
const MainMenuButton({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.icon,
|
||||
required this.onPressed,
|
||||
required this.icon,
|
||||
this.text,
|
||||
});
|
||||
|
||||
/// The text of the button.
|
||||
final String text;
|
||||
|
||||
/// The icon of the button.
|
||||
final IconData? icon;
|
||||
|
||||
/// The callback to be invoked when the button is pressed.
|
||||
final void Function() onPressed;
|
||||
|
||||
/// The icon of the button.
|
||||
final IconData icon;
|
||||
|
||||
/// The text of the button.
|
||||
final String? text;
|
||||
|
||||
@override
|
||||
State<MainMenuButton> createState() => _MainMenuButtonState();
|
||||
}
|
||||
@@ -71,12 +71,11 @@ class _MainMenuButtonState extends State<MainMenuButton>
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (widget.icon != null) ...[
|
||||
Icon(widget.icon, size: 26, color: Colors.black),
|
||||
if (widget.text != null) ...[
|
||||
const SizedBox(width: 7),
|
||||
],
|
||||
Text(
|
||||
widget.text,
|
||||
widget.text!,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -84,6 +83,7 @@ class _MainMenuButtonState extends State<MainMenuButton>
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -32,7 +32,7 @@ class CustomAlertDialog extends StatelessWidget {
|
||||
actionsAlignment: MainAxisAlignment.spaceAround,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: CustomTheme.standardBorderRadiusAll,
|
||||
side: const BorderSide(color: CustomTheme.boxBorder),
|
||||
side: const BorderSide(color: CustomTheme.boxBorderColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,17 @@ class _NavbarItemState extends State<NavbarItem>
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ScaleTransition(
|
||||
AnimatedContainer(
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.isSelected
|
||||
? CustomTheme.primaryColor.withAlpha(50)
|
||||
: Colors.transparent,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
||||
),
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: ScaleTransition(
|
||||
scale: widget.isSelected
|
||||
? _scaleAnimation
|
||||
: const AlwaysStoppedAnimation(1.0),
|
||||
@@ -99,7 +109,7 @@ class _NavbarItemState extends State<NavbarItem>
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
),
|
||||
Text(
|
||||
widget.label,
|
||||
style: TextStyle(
|
||||
|
||||
@@ -70,6 +70,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
super.initState();
|
||||
db = Provider.of<AppDatabase>(context, listen: false);
|
||||
suggestedPlayers = skeletonData;
|
||||
selectedPlayers = widget.initialSelectedPlayers ?? [];
|
||||
loadPlayerList();
|
||||
}
|
||||
|
||||
@@ -100,7 +101,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
if (value.isEmpty) {
|
||||
// If the search is empty, it shows all unselected players.
|
||||
suggestedPlayers = allPlayers.where((player) {
|
||||
return !selectedPlayers.contains(player);
|
||||
return !selectedPlayers.any((p) => p.id == player.id);
|
||||
}).toList();
|
||||
} else {
|
||||
// If there is input, it filters by name match (case-insensitive) and ensures
|
||||
@@ -109,9 +110,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
final bool nameMatches = player.name.toLowerCase().contains(
|
||||
value.toLowerCase(),
|
||||
);
|
||||
final bool isNotSelected = !selectedPlayers.contains(
|
||||
player,
|
||||
);
|
||||
final bool isNotSelected = !selectedPlayers.any((p) => p.id == player.id);
|
||||
return nameMatches && isNotSelected;
|
||||
}).toList();
|
||||
}
|
||||
@@ -126,6 +125,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: AppSkeleton(
|
||||
enabled: isLoading,
|
||||
child: selectedPlayers.isEmpty
|
||||
? Center(child: Text(loc.no_players_selected))
|
||||
: SingleChildScrollView(
|
||||
@@ -167,6 +168,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
loc.all_players,
|
||||
@@ -245,8 +247,22 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
// Otherwise, use the loaded players from the database.
|
||||
loadedPlayers.sort((a, b) => a.name.compareTo(b.name));
|
||||
allPlayers = [...loadedPlayers];
|
||||
if (widget.initialSelectedPlayers != null) {
|
||||
// Excludes already selected players from the suggested players list.
|
||||
suggestedPlayers = loadedPlayers.where((p) => !widget.initialSelectedPlayers!.any((ip) => ip.id == p.id)).toList();
|
||||
// Ensures that only players available for selection are pre-selected.
|
||||
selectedPlayers = widget.initialSelectedPlayers!
|
||||
.where(
|
||||
(p) => allPlayers.any(
|
||||
(available) => available.id == p.id,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
} else {
|
||||
// If no initial selection, all loaded players are suggested.
|
||||
suggestedPlayers = [...loadedPlayers];
|
||||
}
|
||||
}
|
||||
isLoading = false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -88,7 +88,7 @@ class CustomSearchBar extends StatelessWidget {
|
||||
],
|
||||
backgroundColor: WidgetStateProperty.all(CustomTheme.boxColor),
|
||||
side: WidgetStateProperty.all(
|
||||
const BorderSide(color: CustomTheme.boxBorder),
|
||||
const BorderSide(color: CustomTheme.boxBorderColor),
|
||||
),
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
|
||||
@@ -44,11 +44,11 @@ class TextInputField extends StatelessWidget {
|
||||
counterText: '',
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
borderSide: BorderSide(color: CustomTheme.boxBorder),
|
||||
borderSide: BorderSide(color: CustomTheme.boxBorderColor),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
borderSide: BorderSide(color: CustomTheme.boxBorder),
|
||||
borderSide: BorderSide(color: CustomTheme.boxBorderColor),
|
||||
),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
),
|
||||
|
||||
@@ -31,16 +31,12 @@ class CustomRadioListTile<T> extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
border: Border.all(color: CustomTheme.boxBorderColor),
|
||||
borderRadius: CustomTheme.standardBorderRadiusAll,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Radio<T>(
|
||||
value: value,
|
||||
activeColor: CustomTheme.primaryColor,
|
||||
toggleable: true,
|
||||
),
|
||||
Radio<T>(value: value, toggleable: true),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: tallee
|
||||
description: "Tracking App for Card Games"
|
||||
publish_to: 'none'
|
||||
version: 0.0.14+242
|
||||
version: 0.0.16+250
|
||||
|
||||
environment:
|
||||
sdk: ^3.8.1
|
||||
|
||||
Reference in New Issue
Block a user