41 Commits

Author SHA1 Message Date
6679a0f942 Updated licenses [skip ci] 2026-05-25 12:56:12 +00:00
bf6c352d54 Updated version number [skip ci] 2026-05-25 12:55:35 +00:00
9b208f4780 Revert "Merge branch 'feature/193-statisticsview-rework' into development"
All checks were successful
Push Pipeline / update_version (push) Successful in 6s
Push Pipeline / generate_licenses (push) Successful in 38s
Push Pipeline / generate_localizations (push) Successful in 29s
Push Pipeline / test (push) Successful in 1m35s
Push Pipeline / sort_arb_files (push) Successful in 31s
Push Pipeline / format (push) Successful in 55s
Push Pipeline / build (push) Successful in 4m58s
This reverts commit 24f49e17b9, reversing
changes made to dba6c218d6.

# Conflicts:
#	pubspec.yaml
2026-05-25 14:55:19 +02:00
5659dc36c2 Updated licenses [skip ci] 2026-05-25 12:52:27 +00:00
712d48b1d7 Updated version number [skip ci] 2026-05-25 12:51:48 +00:00
24f49e17b9 Merge branch 'feature/193-statisticsview-rework' into development
Some checks failed
Push Pipeline / update_version (push) Successful in 6s
Push Pipeline / generate_licenses (push) Successful in 40s
Push Pipeline / generate_localizations (push) Successful in 30s
Push Pipeline / test (push) Successful in 1m34s
Push Pipeline / sort_arb_files (push) Failing after 32s
Push Pipeline / format (push) Has been skipped
Push Pipeline / build (push) Has been skipped
# Conflicts:
#	pubspec.lock
#	pubspec.yaml
2026-05-25 14:51:36 +02:00
dba6c218d6 Updated licenses [skip ci] 2026-05-25 12:12:46 +00:00
82325ea271 Updated version number [skip ci] 2026-05-25 12:12:09 +00:00
f7973a4bc2 fix: build job
All checks were successful
Push Pipeline / update_version (push) Successful in 6s
Push Pipeline / generate_licenses (push) Successful in 37s
Push Pipeline / generate_localizations (push) Successful in 28s
Push Pipeline / test (push) Successful in 1m32s
Push Pipeline / sort_arb_files (push) Successful in 31s
Push Pipeline / format (push) Successful in 54s
Push Pipeline / build (push) Successful in 4m55s
2026-05-25 14:12:01 +02:00
258e668a5e Updated licenses [skip ci] 2026-05-25 12:08:58 +00:00
a951f3c9b2 Updated version number [skip ci] 2026-05-25 12:08:20 +00:00
ad5cd98327 fix: build job
Some checks failed
Push Pipeline / update_version (push) Successful in 5s
Push Pipeline / generate_licenses (push) Successful in 39s
Push Pipeline / generate_localizations (push) Successful in 28s
Push Pipeline / test (push) Successful in 1m32s
Push Pipeline / sort_arb_files (push) Successful in 30s
Push Pipeline / format (push) Successful in 53s
Push Pipeline / build (push) Failing after 14s
2026-05-25 14:08:10 +02:00
250c647fb2 Updated licenses [skip ci] 2026-05-25 12:04:06 +00:00
e7f904296d Updated version number [skip ci] 2026-05-25 12:03:30 +00:00
362ab2a945 Updated test job
Some checks failed
Push Pipeline / update_version (push) Successful in 6s
Push Pipeline / generate_licenses (push) Successful in 38s
Push Pipeline / generate_localizations (push) Successful in 28s
Push Pipeline / test (push) Successful in 1m33s
Push Pipeline / sort_arb_files (push) Successful in 30s
Push Pipeline / format (push) Successful in 54s
Push Pipeline / build (push) Failing after 20s
2026-05-25 14:03:18 +02:00
d4a67f4086 Updated licenses [skip ci] 2026-05-25 12:01:43 +00:00
9fe74c291c Updated version number [skip ci] 2026-05-25 12:01:06 +00:00
84bb8ccccc fix(deps): update dart dependencies (non-major) (#248)
Some checks failed
Push Pipeline / update_version (push) Successful in 6s
Push Pipeline / generate_licenses (push) Successful in 39s
Push Pipeline / test (push) Failing after 47s
Push Pipeline / generate_localizations (push) Successful in 28s
Push Pipeline / sort_arb_files (push) Successful in 33s
Push Pipeline / format (push) Successful in 53s
Push Pipeline / build (push) Has been cancelled
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [build_runner](https://github.com/dart-lang/build) ([source](https://github.com/dart-lang/build/tree/HEAD/build_runner)) | dev_dependencies | minor | `^2.7.0` -> `^2.15.0` |
| [cupertino_icons](https://github.com/flutter/packages) ([source](https://github.com/flutter/packages/tree/HEAD/third_party/packages/cupertino_icons)) | dependencies | patch | `^1.0.6` -> `^1.0.9` |
| [dart](https://dart.dev/) ([source](https://github.com/dart-lang/sdk)) |  | minor | `^3.8.1` -> `^3.12.0` |
| [dart_pubspec_licenses](https://github.com/espresso3389/flutter_oss_licenses/tree/master/packages/dart_pubspec_licenses) ([source](https://github.com/espresso3389/flutter_oss_licenses)) | dev_dependencies | minor | `^3.0.14` -> `^3.2.0` |
| [drift](https://drift.simonbinder.eu/) ([source](https://github.com/simolus3/drift)) | dependencies | minor | `^2.27.0` -> `^2.33.0` |
| [drift_dev](https://drift.simonbinder.eu/) ([source](https://github.com/simolus3/drift)) | dev_dependencies | minor | `^2.27.0` -> `^2.33.0` |
| [drift_flutter](https://drift.simonbinder.eu/) ([source](https://github.com/simolus3/drift)) | dependencies | minor | `^0.2.4` -> `^0.3.0` |
| [file_saver](https://hassanansari.dev) ([source](https://github.com/incrediblezayed/file_saver)) | dependencies | minor | `^0.3.1` -> `^0.4.0` |
| [package_info_plus](https://github.com/fluttercommunity/plus_plugins) ([source](https://github.com/fluttercommunity/plus_plugins/tree/HEAD/packages/package_info_plus/package_info_plus)) | dependencies | patch | `^9.0.0` -> `^9.0.1` |
| [skeletonizer](https://github.com/Milad-Akarie/skeletonizer) | dependencies | patch | `^2.1.0+1` -> `^2.1.3` |
| [uuid](https://github.com/Daegalus/dart-uuid) | dependencies | patch | `^4.5.2` -> `^4.5.3` |

>  **Important**
>
> Release Notes retrieval for this PR were skipped because no github.com credentials were available.
> If you are self-hosted, please see [this instruction](https://github.com/renovatebot/renovate/blob/master/docs/usage/examples/self-hosting.md#githubcom-token-for-release-notes).

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yNjQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjI2NC4xIiwidGFyZ2V0QnJhbmNoIjoiZGV2ZWxvcG1lbnQiLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Felix Kirchner <felix.kirchner.fk@gmail.com>
Reviewed-on: #248
Co-authored-by: Gitea Actions <actions@yannick-weigert.de>
Co-committed-by: Gitea Actions <actions@yannick-weigert.de>
2026-05-25 12:00:57 +00:00
e1d0eb4bd4 Merge remote-tracking branch 'origin/feature/193-statisticsview-rework' into feature/193-statisticsview-rework
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 46s
Pull Request Pipeline / test (pull_request) Successful in 48s
Pull Request Pipeline / localizations (pull_request) Successful in 26s
# Conflicts:
#	lib/l10n/arb/app_de.arb
#	lib/l10n/arb/app_en.arb
#	lib/l10n/generated/app_localizations_de.dart
#	lib/l10n/generated/app_localizations_en.dart
2026-05-25 13:25:09 +02:00
4bd2f972df fix: localizations 2026-05-25 13:24:23 +02:00
730341dc7e fix: localizations
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 47s
Pull Request Pipeline / test (pull_request) Successful in 49s
Pull Request Pipeline / localizations (pull_request) Successful in 28s
2026-05-25 13:17:02 +02:00
fb2f6d3adc feat: dynamic display count shown in tile
Some checks failed
Pull Request Pipeline / lint (pull_request) Successful in 47s
Pull Request Pipeline / test (pull_request) Successful in 49s
Pull Request Pipeline / localizations (pull_request) Failing after 29s
2026-05-25 13:06:37 +02:00
b9710ed851 fix: pixel overflow 2026-05-25 12:55:36 +02:00
efd1097d5a feat: changing display count 2026-05-25 12:51:24 +02:00
bfb40d2eab feat: statistic detail view
Some checks failed
Pull Request Pipeline / lint (pull_request) Failing after 47s
Pull Request Pipeline / test (pull_request) Successful in 48s
Pull Request Pipeline / localizations (pull_request) Failing after 27s
2026-05-25 00:39:01 +02:00
72442b5375 fix: added delete function 2026-05-24 23:34:53 +02:00
bccd47e20e Refactoring 2026-05-24 23:27:14 +02:00
428f967010 feat: displayCount 2026-05-24 23:09:08 +02:00
f65ea09cbe Added spacing 2026-05-24 17:33:41 +02:00
ffd52055fa fixed bar length 2026-05-24 17:28:29 +02:00
398c7a4168 Refactoring 2026-05-24 17:07:09 +02:00
d82206319a Renamed GameColor -> AppColor 2026-05-24 17:04:14 +02:00
5a2cc790dd Renamed GameColor -> AppColor 2026-05-24 17:03:58 +02:00
18a5dcfdd5 Changed colors 2026-05-24 17:03:43 +02:00
2e3b462533 feat: added statistic tile factory
Some checks failed
Pull Request Pipeline / lint (pull_request) Failing after 47s
Pull Request Pipeline / test (pull_request) Successful in 49s
Pull Request Pipeline / localizations (pull_request) Successful in 27s
2026-05-24 15:11:56 +02:00
807ae61df7 feat: basic database functionality
Some checks failed
Pull Request Pipeline / lint (pull_request) Failing after 47s
Pull Request Pipeline / test (pull_request) Successful in 49s
Pull Request Pipeline / localizations (pull_request) Successful in 27s
2026-05-24 13:52:27 +02:00
37031d66c9 Updated attribute order 2026-05-24 12:16:44 +02:00
d389b93cc5 Updated method with join 2026-05-24 12:16:36 +02:00
134f77c5a3 feat: create statistics view
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 48s
Pull Request Pipeline / test (pull_request) Successful in 49s
Pull Request Pipeline / localizations (pull_request) Successful in 27s
2026-05-24 01:26:08 +02:00
57ebea1eb7 Merge branch 'feature/180-Spielerprofile-implementieren' into feature/193-statisticsview-rework 2026-05-24 01:10:45 +02:00
9ad50c9f9c Removed bg color 2026-05-23 21:52:32 +02:00
30 changed files with 483 additions and 1206 deletions

View File

@@ -31,20 +31,16 @@ jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container:
image: ghcr.io/cirruslabs/flutter:stable
steps: steps:
- name: Checkout code - name: Install Node
uses: actions/checkout@v4
# Required for Flutter action
- name: Install jq
run: | run: |
apt-get update apt-get update
apt-get install -y jq apt-get install -y nodejs npm
- name: Set up Flutter - name: Checkout code
uses: subosito/flutter-action@v2 uses: actions/checkout@v4
with:
channel: stable
- name: Get dependencies - name: Get dependencies
run: | run: |

View File

@@ -7,22 +7,19 @@ on:
- "main" - "main"
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container:
image: ghcr.io/cirruslabs/flutter:stable
steps: steps:
- name: Checkout code - name: Install Node
uses: actions/checkout@v4
# Required for Flutter action
- name: Install jq
run: | run: |
apt-get update apt-get update
apt-get install -y jq apt-get install -y nodejs npm
- name: Set up Flutter - name: Checkout code
uses: subosito/flutter-action@v2 uses: actions/checkout@v4
with:
channel: stable
- name: Get dependencies - name: Get dependencies
run: | run: |
@@ -295,6 +292,8 @@ jobs:
- name: Setup Android SDK - name: Setup Android SDK
uses: android-actions/setup-android@v3 uses: android-actions/setup-android@v3
with:
packages: "platform-tools platforms;android-34 build-tools;34.0.0"
# Required for Flutter action # Required for Flutter action
- name: Install jq - name: Install jq

View File

@@ -1,13 +1,5 @@
{ {
"pins" : [ "pins" : [
{
"identity" : "csqlite",
"kind" : "remoteSourceControl",
"location" : "https://github.com/simolus3/CSQLite.git",
"state" : {
"revision" : "1ee46d19a4f451a7aa64ffc64fc99b4748131e62"
}
},
{ {
"identity" : "dkcamera", "identity" : "dkcamera",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",

View File

@@ -185,38 +185,6 @@ class GroupDao extends DatabaseAccessor<AppDatabase> with _$GroupDaoMixin {
return count ?? 0; return count ?? 0;
} }
/// Retrieves all groups a specific player belongs to.
/// Returns an empty list if the player is not part of any group.
Future<List<Group>> getGroupsByPlayer({required String playerId}) async {
final playerGroups = await (select(
playerGroupTable,
)..where((pg) => pg.playerId.equals(playerId))).get();
if (playerGroups.isEmpty) return [];
final groupIds = playerGroups.map((pg) => pg.groupId).toSet().toList();
final rows =
await (select(groupTable)
..where((g) => g.id.isIn(groupIds))
..orderBy([(g) => OrderingTerm.desc(g.createdAt)]))
.get();
return Future.wait(
rows.map((groupData) async {
final members = await db.playerGroupDao.getPlayersOfGroup(
groupId: groupData.id,
);
return Group(
id: groupData.id,
name: groupData.name,
description: groupData.description,
members: members,
createdAt: groupData.createdAt,
);
}),
);
}
/// Checks if a group with the given [groupId] exists in the database. /// Checks if a group with the given [groupId] exists in the database.
/// Returns `true` if the group exists, `false` otherwise. /// Returns `true` if the group exists, `false` otherwise.
Future<bool> groupExists({required String groupId}) async { Future<bool> groupExists({required String groupId}) async {

View File

@@ -352,53 +352,6 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
return count ?? 0; return count ?? 0;
} }
Future<List<Match>> getMatchesByPlayer({required String playerId}) async {
final playerMatches = await (select(
playerMatchTable,
)..where((pm) => pm.playerId.equals(playerId))).get();
if (playerMatches.isEmpty) return [];
final matchIds = playerMatches.map((pm) => pm.matchId).toSet().toList();
final rows =
await (select(matchTable)
..where((m) => m.id.isIn(matchIds))
..orderBy([(m) => OrderingTerm.desc(m.createdAt)]))
.get();
return Future.wait(
rows.map((row) async {
final game = await db.gameDao.getGameById(gameId: row.gameId);
Group? group;
if (row.groupId != null) {
group = await db.groupDao.getGroupById(groupId: row.groupId!);
}
final players = await db.playerMatchDao.getPlayersOfMatch(
matchId: row.id,
);
final scores = await db.scoreEntryDao.getAllMatchScores(
matchId: row.id,
);
final teams = await _getMatchTeams(matchId: row.id);
return Match(
id: row.id,
name: row.name,
game: game,
group: group,
players: players,
teams: teams.isEmpty ? null : teams,
notes: row.notes,
createdAt: row.createdAt,
endedAt: row.endedAt,
scores: scores,
);
}),
);
}
/// Retrieves all matches associated with the given [groupId]. /// Retrieves all matches associated with the given [groupId].
/// Queries the database directly, filtering by [groupId]. /// Queries the database directly, filtering by [groupId].
Future<List<Match>> getMatchesByGroup({required String groupId}) async { Future<List<Match>> getMatchesByGroup({required String groupId}) async {

View File

@@ -17,7 +17,7 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
/// the new one. /// the new one.
Future<bool> addPlayer({required Player player}) async { Future<bool> addPlayer({required Player player}) async {
if (!await playerExists(playerId: player.id)) { if (!await playerExists(playerId: player.id)) {
final int nameCount = await _processNameCount(name: player.name); final int nameCount = await calculateNameCount(name: player.name);
await into(playerTable).insert( await into(playerTable).insert(
PlayerTableCompanion.insert( PlayerTableCompanion.insert(
@@ -64,7 +64,7 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
final playersWithName = entry.value; final playersWithName = entry.value;
// Get the current nameCount // Get the current nameCount
var nameCount = await _processNameCount(name: name); var nameCount = await calculateNameCount(name: name);
// One player with the same name // One player with the same name
if (playersWithName.length == 1) { if (playersWithName.length == 1) {
@@ -159,63 +159,44 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
/* Update */ /* Update */
/// Updates the name of the player with the given [playerId] to [name]. /// Updates the name of the player with the given [playerId] to [name].
///
/// Keeps the `nameCount` values of the affected name groups consistent:
/// - The renamed player gets a fresh `nameCount` for the new name group.
/// - All players in the previous name group whose `nameCount` was greater
/// than the removed one get decremented by 1, so the numbering stays
/// contiguous (1..N) in `createdAt` order.
/// - If only one player remains in the previous name group, their
/// `nameCount` is reset to 0.
Future<bool> updatePlayerName({ Future<bool> updatePlayerName({
required String playerId, required String playerId,
required String name, required String name,
}) async { }) async {
return transaction(() async { // Get previous name and name count for the player before updating
final previousPlayer = await (select( final previousPlayerName =
playerTable, await (select(playerTable)..where((p) => p.id.equals(playerId)))
)..where((p) => p.id.equals(playerId))).getSingleOrNull(); .map((row) => row.name)
if (previousPlayer == null) return false; .getSingleOrNull() ??
'';
final previousNameCount = await getNameCount(name: previousPlayerName);
final previousName = previousPlayer.name; final rowsAffected =
final previousCount = previousPlayer.nameCount; await (update(playerTable)..where((p) => p.id.equals(playerId))).write(
PlayerTableCompanion(name: Value(name)),
);
// Determine the nameCount for the renamed player in the new group. // Update name count for the new name
final newNameCount = await _processNameCount(name: name); final count = await calculateNameCount(name: name);
if (count > 0) {
await (update(playerTable)..where((p) => p.name.equals(name))).write(
PlayerTableCompanion(nameCount: Value(count)),
);
}
final rowsAffected = if (previousNameCount > 0) {
await (update( // Get the player with that name and the hightest nameCount, and update their nameCount to previousNameCount
playerTable, final player = await getPlayerWithHighestNameCount(
)..where((p) => p.id.equals(playerId))).write( name: previousPlayerName,
PlayerTableCompanion( );
name: Value(name), if (player != null) {
nameCount: Value(newNameCount), await updateNameCount(
), playerId: player.id,
); nameCount: previousNameCount,
);
// Consolidate the previous name group.
final remainingCount = await getNameCount(name: previousName);
if (remainingCount == 1) {
// Only one player left
await (update(playerTable)..where((p) => p.name.equals(previousName)))
.write(const PlayerTableCompanion(nameCount: Value(0)));
} else if (remainingCount > 1 && previousCount > 0) {
// Shift every player above the gap down by one to keep numbering in order.
await (update(playerTable)..where(
(p) =>
p.name.equals(previousName) &
p.nameCount.isBiggerThanValue(previousCount),
))
.write(
PlayerTableCompanion.custom(
nameCount: playerTable.nameCount - const Constant(1),
),
);
} }
}
return rowsAffected > 0; return rowsAffected > 0;
});
} }
/// Updates the description of the player with the given [playerId] to /// Updates the description of the player with the given [playerId] to
@@ -245,8 +226,6 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
/* Name count management */ /* Name count management */
/// Retrieves the count of players with the given [name]. /// Retrieves the count of players with the given [name].
/// Returns the highest name count if players with the same name exist,
/// otherwise `null`.
Future<int> getNameCount({required String name}) async { Future<int> getNameCount({required String name}) async {
final query = select(playerTable)..where((p) => p.name.equals(name)); final query = select(playerTable)..where((p) => p.name.equals(name));
final result = await query.get(); final result = await query.get();
@@ -285,39 +264,25 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
return null; return null;
} }
/// Processes the name count for a new player with the given [name].
///- 0 Player: returning 0
///- 1 Player: returning 2, and initializes the nameCount for the existing player to 1
///- Other: returning the existing count + 1
Future<int> _processNameCount({required String name}) async {
final nameCount = await calculateNameCount(name: name);
if (nameCount == 2) {
// If one other player exists with the same name, initialize the nameCount
await initializeNameCount(name: name);
}
return nameCount;
}
@visibleForTesting @visibleForTesting
/// Calculates the name count for a new player with the given [name].
/// - 0 Players: Name count is 0
/// - 1 Player: Name count is 2 (since the existing player will be 1)
/// - Other: Name count is the existing count + 1
Future<int> calculateNameCount({required String name}) async { Future<int> calculateNameCount({required String name}) async {
final count = await getNameCount(name: name); final count = await getNameCount(name: name);
final int nameCount; final int nameCount;
if (count == 0) { if (count == 1) {
// If no other players exist with the same name, the returned nameCount is 0 // If one other player exists with the same name, initialize the nameCount
nameCount = 0; await initializeNameCount(name: name);
} else if (count == 1) { // And for the new player, set nameCount to 2
// If one other player with the name count exists, the returned name count is 2
nameCount = 2; nameCount = 2;
} else { } else if (count > 1) {
// If more than one player exists with the same name, just increment // If more than one player exists with the same name, just increment
// the nameCount for the new player // the nameCount for the new player
nameCount = count + 1; nameCount = count + 1;
} else {
// If no other players exist with the same name, set nameCount to 0
nameCount = 0;
} }
return nameCount; return nameCount;
} }

View File

@@ -19,7 +19,6 @@
"color_red": "Rot", "color_red": "Rot",
"color_teal": "Türkis", "color_teal": "Türkis",
"color_yellow": "Gelb", "color_yellow": "Gelb",
"confirm": "Bestätigen",
"could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden", "could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden",
"create_game": "Spielvorlage erstellen", "create_game": "Spielvorlage erstellen",
"create_group": "Gruppe erstellen", "create_group": "Gruppe erstellen",
@@ -45,14 +44,11 @@
}, },
"delete_group": "Gruppe löschen", "delete_group": "Gruppe löschen",
"delete_match": "Spiel löschen", "delete_match": "Spiel löschen",
"delete_player": "Spieler:in löschen",
"description": "Beschreibung", "description": "Beschreibung",
"drag_to_set_placement": "Ziehen um Platzierung zu setzen", "drag_to_set_placement": "Ziehen um Platzierung zu setzen",
"edit_game": "Spielvorlage bearbeiten", "edit_game": "Spielvorlage bearbeiten",
"edit_group": "Gruppe bearbeiten", "edit_group": "Gruppe bearbeiten",
"edit_match": "Gruppe bearbeiten", "edit_match": "Gruppe bearbeiten",
"edit_name": "Name ändern",
"edit_player": "Spieler bearbeiten",
"enter_points": "Punkte eingeben", "enter_points": "Punkte eingeben",
"enter_results": "Ergebnisse eintragen", "enter_results": "Ergebnisse eintragen",
"error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen", "error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen",
@@ -70,7 +66,6 @@
"group_name": "Gruppenname", "group_name": "Gruppenname",
"group_profile": "Gruppenprofil", "group_profile": "Gruppenprofil",
"groups": "Gruppen", "groups": "Gruppen",
"groups_part_of": "Gruppen Teil von",
"highest_score": "Höchste Punkte", "highest_score": "Höchste Punkte",
"home": "Startseite", "home": "Startseite",
"import_canceled": "Import abgebrochen", "import_canceled": "Import abgebrochen",
@@ -88,9 +83,6 @@
"match_name": "Spieltitel", "match_name": "Spieltitel",
"match_profile": "Spielprofil", "match_profile": "Spielprofil",
"matches": "Spiele", "matches": "Spiele",
"matches_part_of": "Spiele Teil von",
"matches_played": "Spiele gespielt",
"matches_won": "Spiele gewonnen",
"members": "Mitglieder", "members": "Mitglieder",
"most_points": "Höchste Punkte", "most_points": "Höchste Punkte",
"multiple_winners": "Mehrere Gewinner:innen", "multiple_winners": "Mehrere Gewinner:innen",
@@ -100,7 +92,6 @@
"no_license_text_available": "Kein Lizenztext verfügbar", "no_license_text_available": "Kein Lizenztext verfügbar",
"no_licenses_found": "Keine Lizenzen gefunden", "no_licenses_found": "Keine Lizenzen gefunden",
"no_matches_created_yet": "Noch keine Spiele erstellt", "no_matches_created_yet": "Noch keine Spiele erstellt",
"no_matches_played_yet": "Noch kein Spiel gespielt",
"no_players_created_yet": "Noch keine Spieler:in erstellt", "no_players_created_yet": "Noch keine Spieler:in erstellt",
"no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden", "no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden",
"no_players_selected": "Keine Spieler:innen ausgewählt", "no_players_selected": "Keine Spieler:innen ausgewählt",
@@ -111,12 +102,10 @@
"none": "Kein", "none": "Kein",
"none_group": "Keine", "none_group": "Keine",
"not_available": "Nicht verfügbar", "not_available": "Nicht verfügbar",
"not_part_of_any_group": "Noch keiner Gruppe hinzugefügt",
"place": "Platz", "place": "Platz",
"placement": "Platzierung", "placement": "Platzierung",
"played_matches": "Gespielte Spiele", "played_matches": "Gespielte Spiele",
"player_name": "Spieler:innenname", "player_name": "Spieler:innenname",
"player_profile": "Spieler:in-Profil",
"players": "Spieler:innen", "players": "Spieler:innen",
"point": "Punkt", "point": "Punkt",
"points": "Punkte", "points": "Punkte",
@@ -138,7 +127,6 @@
"select_winner": "Gewinner:in wählen", "select_winner": "Gewinner:in wählen",
"select_winners": "Gewinner:innen wählen", "select_winners": "Gewinner:innen wählen",
"selected_players": "Ausgewählte Spieler:innen", "selected_players": "Ausgewählte Spieler:innen",
"set_name": "Name setzen",
"settings": "Einstellungen", "settings": "Einstellungen",
"single_loser": "Ein:e Verlierer:in", "single_loser": "Ein:e Verlierer:in",
"single_winner": "Ein:e Gewinner:in", "single_winner": "Ein:e Gewinner:in",

View File

@@ -19,7 +19,6 @@
"color_red": "Red", "color_red": "Red",
"color_teal": "Teal", "color_teal": "Teal",
"color_yellow": "Yellow", "color_yellow": "Yellow",
"confirm": "Confirm",
"could_not_add_player": "Could not add player", "could_not_add_player": "Could not add player",
"create_game": "Create Game", "create_game": "Create Game",
"create_group": "Create Group", "create_group": "Create Group",
@@ -45,14 +44,11 @@
}, },
"delete_group": "Delete Group", "delete_group": "Delete Group",
"delete_match": "Delete Match", "delete_match": "Delete Match",
"delete_player": "Delete player?",
"description": "Description", "description": "Description",
"drag_to_set_placement": "Drag to set placement", "drag_to_set_placement": "Drag to set placement",
"edit_game": "Edit Game", "edit_game": "Edit Game",
"edit_group": "Edit Group", "edit_group": "Edit Group",
"edit_match": "Edit Match", "edit_match": "Edit Match",
"edit_name": "Edit name",
"edit_player": "Edit player",
"enter_points": "Enter points", "enter_points": "Enter points",
"enter_results": "Enter Results", "enter_results": "Enter Results",
"error_creating_group": "Error while creating group, please try again", "error_creating_group": "Error while creating group, please try again",
@@ -70,7 +66,6 @@
"group_name": "Group name", "group_name": "Group name",
"group_profile": "Group Profile", "group_profile": "Group Profile",
"groups": "Groups", "groups": "Groups",
"groups_part_of": "Groups part of",
"highest_score": "Highest Score", "highest_score": "Highest Score",
"home": "Home", "home": "Home",
"import_canceled": "Import canceled", "import_canceled": "Import canceled",
@@ -88,9 +83,6 @@
"match_name": "Match name", "match_name": "Match name",
"match_profile": "Match Profile", "match_profile": "Match Profile",
"matches": "Matches", "matches": "Matches",
"matches_part_of": "Matches part of",
"matches_played": "Matches played",
"matches_won": "Matches won",
"members": "Members", "members": "Members",
"most_points": "Most Points", "most_points": "Most Points",
"multiple_winners": "Multiple Winners", "multiple_winners": "Multiple Winners",
@@ -100,7 +92,6 @@
"no_license_text_available": "No license text available", "no_license_text_available": "No license text available",
"no_licenses_found": "No licenses found", "no_licenses_found": "No licenses found",
"no_matches_created_yet": "No matches created yet", "no_matches_created_yet": "No matches created yet",
"no_matches_played_yet": "No games played yet",
"no_players_created_yet": "No players created yet", "no_players_created_yet": "No players created yet",
"no_players_found_with_that_name": "No players found with that name", "no_players_found_with_that_name": "No players found with that name",
"no_players_selected": "No players selected", "no_players_selected": "No players selected",
@@ -111,12 +102,10 @@
"none": "None", "none": "None",
"none_group": "None", "none_group": "None",
"not_available": "Not available", "not_available": "Not available",
"not_part_of_any_group": "Not part of any group yet",
"place": "place", "place": "place",
"placement": "Placement", "placement": "Placement",
"played_matches": "Played Matches", "played_matches": "Played Matches",
"player_name": "Player name", "player_name": "Player name",
"player_profile": "Player Profile",
"players": "Players", "players": "Players",
"point": "Point", "point": "Point",
"points": "Points", "points": "Points",
@@ -137,7 +126,6 @@
"select_winner": "Select Winner", "select_winner": "Select Winner",
"select_winners": "Select Winners", "select_winners": "Select Winners",
"selected_players": "Selected players", "selected_players": "Selected players",
"set_name": "Set name",
"settings": "Settings", "settings": "Settings",
"single_loser": "Single Loser", "single_loser": "Single Loser",
"single_winner": "Single Winner", "single_winner": "Single Winner",

View File

@@ -212,12 +212,6 @@ abstract class AppLocalizations {
/// **'Yellow'** /// **'Yellow'**
String get color_yellow; String get color_yellow;
/// No description provided for @confirm.
///
/// In en, this message translates to:
/// **'Confirm'**
String get confirm;
/// No description provided for @could_not_add_player. /// No description provided for @could_not_add_player.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -326,12 +320,6 @@ abstract class AppLocalizations {
/// **'Delete Match'** /// **'Delete Match'**
String get delete_match; String get delete_match;
/// No description provided for @delete_player.
///
/// In en, this message translates to:
/// **'Delete player?'**
String get delete_player;
/// No description provided for @description. /// No description provided for @description.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -362,18 +350,6 @@ abstract class AppLocalizations {
/// **'Edit Match'** /// **'Edit Match'**
String get edit_match; String get edit_match;
/// No description provided for @edit_name.
///
/// In en, this message translates to:
/// **'Edit name'**
String get edit_name;
/// No description provided for @edit_player.
///
/// In en, this message translates to:
/// **'Edit player'**
String get edit_player;
/// No description provided for @enter_points. /// No description provided for @enter_points.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -476,12 +452,6 @@ abstract class AppLocalizations {
/// **'Groups'** /// **'Groups'**
String get groups; String get groups;
/// No description provided for @groups_part_of.
///
/// In en, this message translates to:
/// **'Groups part of'**
String get groups_part_of;
/// No description provided for @highest_score. /// No description provided for @highest_score.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -584,24 +554,6 @@ abstract class AppLocalizations {
/// **'Matches'** /// **'Matches'**
String get matches; String get matches;
/// No description provided for @matches_part_of.
///
/// In en, this message translates to:
/// **'Matches part of'**
String get matches_part_of;
/// No description provided for @matches_played.
///
/// In en, this message translates to:
/// **'Matches played'**
String get matches_played;
/// No description provided for @matches_won.
///
/// In en, this message translates to:
/// **'Matches won'**
String get matches_won;
/// No description provided for @members. /// No description provided for @members.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -656,12 +608,6 @@ abstract class AppLocalizations {
/// **'No matches created yet'** /// **'No matches created yet'**
String get no_matches_created_yet; String get no_matches_created_yet;
/// No description provided for @no_matches_played_yet.
///
/// In en, this message translates to:
/// **'No games played yet'**
String get no_matches_played_yet;
/// No description provided for @no_players_created_yet. /// No description provided for @no_players_created_yet.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -722,12 +668,6 @@ abstract class AppLocalizations {
/// **'Not available'** /// **'Not available'**
String get not_available; String get not_available;
/// No description provided for @not_part_of_any_group.
///
/// In en, this message translates to:
/// **'Not part of any group yet'**
String get not_part_of_any_group;
/// No description provided for @place. /// No description provided for @place.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -752,12 +692,6 @@ abstract class AppLocalizations {
/// **'Player name'** /// **'Player name'**
String get player_name; String get player_name;
/// No description provided for @player_profile.
///
/// In en, this message translates to:
/// **'Player Profile'**
String get player_profile;
/// No description provided for @players. /// No description provided for @players.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -878,12 +812,6 @@ abstract class AppLocalizations {
/// **'Selected players'** /// **'Selected players'**
String get selected_players; String get selected_players;
/// No description provided for @set_name.
///
/// In en, this message translates to:
/// **'Set name'**
String get set_name;
/// No description provided for @settings. /// No description provided for @settings.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -65,9 +65,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get color_yellow => 'Gelb'; String get color_yellow => 'Gelb';
@override
String get confirm => 'Bestätigen';
@override @override
String could_not_add_player(Object playerName) { String could_not_add_player(Object playerName) {
return 'Spieler:in $playerName konnte nicht hinzugefügt werden'; return 'Spieler:in $playerName konnte nicht hinzugefügt werden';
@@ -134,9 +131,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get delete_match => 'Spiel löschen'; String get delete_match => 'Spiel löschen';
@override
String get delete_player => 'Spieler:in löschen';
@override @override
String get description => 'Beschreibung'; String get description => 'Beschreibung';
@@ -152,12 +146,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get edit_match => 'Gruppe bearbeiten'; String get edit_match => 'Gruppe bearbeiten';
@override
String get edit_name => 'Name ändern';
@override
String get edit_player => 'Spieler bearbeiten';
@override @override
String get enter_points => 'Punkte eingeben'; String get enter_points => 'Punkte eingeben';
@@ -213,9 +201,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get groups => 'Gruppen'; String get groups => 'Gruppen';
@override
String get groups_part_of => 'Gruppen Teil von';
@override @override
String get highest_score => 'Höchste Punkte'; String get highest_score => 'Höchste Punkte';
@@ -267,15 +252,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get matches => 'Spiele'; String get matches => 'Spiele';
@override
String get matches_part_of => 'Spiele Teil von';
@override
String get matches_played => 'Spiele gespielt';
@override
String get matches_won => 'Spiele gewonnen';
@override @override
String get members => 'Mitglieder'; String get members => 'Mitglieder';
@@ -303,9 +279,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get no_matches_created_yet => 'Noch keine Spiele erstellt'; String get no_matches_created_yet => 'Noch keine Spiele erstellt';
@override
String get no_matches_played_yet => 'Noch kein Spiel gespielt';
@override @override
String get no_players_created_yet => 'Noch keine Spieler:in erstellt'; String get no_players_created_yet => 'Noch keine Spieler:in erstellt';
@@ -337,9 +310,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get not_available => 'Nicht verfügbar'; String get not_available => 'Nicht verfügbar';
@override
String get not_part_of_any_group => 'Noch keiner Gruppe hinzugefügt';
@override @override
String get place => 'Platz'; String get place => 'Platz';
@@ -352,9 +322,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get player_name => 'Spieler:innenname'; String get player_name => 'Spieler:innenname';
@override
String get player_profile => 'Spieler:in-Profil';
@override @override
String get players => 'Spieler:innen'; String get players => 'Spieler:innen';
@@ -420,9 +387,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get selected_players => 'Ausgewählte Spieler:innen'; String get selected_players => 'Ausgewählte Spieler:innen';
@override
String get set_name => 'Name setzen';
@override @override
String get settings => 'Einstellungen'; String get settings => 'Einstellungen';

View File

@@ -65,9 +65,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get color_yellow => 'Yellow'; String get color_yellow => 'Yellow';
@override
String get confirm => 'Confirm';
@override @override
String could_not_add_player(Object playerName) { String could_not_add_player(Object playerName) {
return 'Could not add player'; return 'Could not add player';
@@ -134,9 +131,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get delete_match => 'Delete Match'; String get delete_match => 'Delete Match';
@override
String get delete_player => 'Delete player?';
@override @override
String get description => 'Description'; String get description => 'Description';
@@ -152,12 +146,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get edit_match => 'Edit Match'; String get edit_match => 'Edit Match';
@override
String get edit_name => 'Edit name';
@override
String get edit_player => 'Edit player';
@override @override
String get enter_points => 'Enter points'; String get enter_points => 'Enter points';
@@ -213,9 +201,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get groups => 'Groups'; String get groups => 'Groups';
@override
String get groups_part_of => 'Groups part of';
@override @override
String get highest_score => 'Highest Score'; String get highest_score => 'Highest Score';
@@ -267,15 +252,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get matches => 'Matches'; String get matches => 'Matches';
@override
String get matches_part_of => 'Matches part of';
@override
String get matches_played => 'Matches played';
@override
String get matches_won => 'Matches won';
@override @override
String get members => 'Members'; String get members => 'Members';
@@ -303,9 +279,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get no_matches_created_yet => 'No matches created yet'; String get no_matches_created_yet => 'No matches created yet';
@override
String get no_matches_played_yet => 'No games played yet';
@override @override
String get no_players_created_yet => 'No players created yet'; String get no_players_created_yet => 'No players created yet';
@@ -337,9 +310,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get not_available => 'Not available'; String get not_available => 'Not available';
@override
String get not_part_of_any_group => 'Not part of any group yet';
@override @override
String get place => 'place'; String get place => 'place';
@@ -352,9 +322,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get player_name => 'Player name'; String get player_name => 'Player name';
@override
String get player_profile => 'Player Profile';
@override @override
String get players => 'Players'; String get players => 'Players';
@@ -420,9 +387,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get selected_players => 'Selected players'; String get selected_players => 'Selected players';
@override
String get set_name => 'Set name';
@override @override
String get settings => 'Settings'; String get settings => 'Settings';

View File

@@ -89,7 +89,6 @@ class _CreateGroupViewState extends State<CreateGroupView> {
Expanded( Expanded(
child: PlayerSelection( child: PlayerSelection(
initialSelectedPlayers: initialSelectedPlayers, initialSelectedPlayers: initialSelectedPlayers,
onPlayerCreated: () => widget.onMembersChanged?.call(),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
selectedPlayers = [...value]; selectedPlayers = [...value];
@@ -135,7 +134,6 @@ class _CreateGroupViewState extends State<CreateGroupView> {
if (!mounted) return; if (!mounted) return;
if (success) { if (success) {
widget.onMembersChanged?.call();
await HapticFeedback.successNotification(); await HapticFeedback.successNotification();
if (mounted) { if (mounted) {
Navigator.pop(context, updatedGroup); Navigator.pop(context, updatedGroup);
@@ -159,6 +157,7 @@ class _CreateGroupViewState extends State<CreateGroupView> {
final success = await db.groupDao.addGroup( final success = await db.groupDao.addGroup(
group: Group(name: groupName, members: selectedPlayers), group: Group(name: groupName, members: selectedPlayers),
); );
return success; return success;
} }

View File

@@ -77,7 +77,6 @@ class _GroupViewState extends State<GroupView> {
); );
} }
return GroupTile( return GroupTile(
onPlayerChanged: loadGroups,
group: groups[index], group: groups[index],
onTap: () async { onTap: () async {
await Navigator.push( await Navigator.push(
@@ -107,10 +106,13 @@ class _GroupViewState extends State<GroupView> {
context, context,
adaptivePageRoute( adaptivePageRoute(
builder: (context) { builder: (context) {
return CreateGroupView(onMembersChanged: loadGroups); return const CreateGroupView();
}, },
), ),
); );
setState(() {
loadGroups();
});
}, },
), ),
), ),

View File

@@ -196,7 +196,6 @@ class _CreateMatchViewState extends State<CreateMatchView> {
child: PlayerSelection( child: PlayerSelection(
key: ValueKey(selectedGroup?.id ?? 'no_group'), key: ValueKey(selectedGroup?.id ?? 'no_group'),
initialSelectedPlayers: selectedPlayers, initialSelectedPlayers: selectedPlayers,
onPlayerCreated: () => widget.onMatchesUpdated?.call(),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
selectedPlayers = value; selectedPlayers = value;

View File

@@ -97,7 +97,6 @@ class _MatchViewState extends State<MatchView> {
child: Padding( child: Padding(
padding: const EdgeInsets.only(bottom: 12.0), padding: const EdgeInsets.only(bottom: 12.0),
child: MatchTile( child: MatchTile(
onPlayerEdited: loadMatches,
width: MediaQuery.sizeOf(context).width * 0.95, width: MediaQuery.sizeOf(context).width * 0.95,
onTap: () async { onTap: () async {
Navigator.push( Navigator.push(

View File

@@ -1,394 +0,0 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/models/game.dart';
import 'package:tallee/data/models/group.dart';
import 'package:tallee/data/models/match.dart';
import 'package:tallee/data/models/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/app_skeleton.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_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/dialog/custom_alert_dialog.dart';
import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart';
import 'package:tallee/presentation/widgets/text_input/text_input_field.dart';
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
class PlayerDetailView extends StatefulWidget {
const PlayerDetailView({
super.key,
required this.player,
required this.callback,
});
/// The player to display
final Player player;
final VoidCallback callback;
@override
State<PlayerDetailView> createState() => _PlayerDetailViewState();
}
class _PlayerDetailViewState extends State<PlayerDetailView> {
late final AppDatabase db;
late Player _player;
late String playerNameCount;
bool isLoading = true;
/// Total matches played by this player
int totalMatches = 0;
/// Total matches won by this player
int matchesWon = 0;
/// Total groups this player belongs to
int totalGroups = 0;
/// Full list of groups this player belongs to
List<Group> playerGroups = List.filled(
4,
Group(name: 'Skeleton group', members: []),
);
/// Full list of matches this player played in
List<Match> playerMatches = List.filled(
4,
Match(
name: 'Skeleton match',
game: Game(name: 'Game name', ruleset: Ruleset.singleWinner),
players: [],
),
);
TextEditingController nameController = TextEditingController();
@override
void initState() {
super.initState();
_player = widget.player;
db = Provider.of<AppDatabase>(context, listen: false);
playerNameCount = getNameCountText(_player);
_loadData();
}
@override
void dispose() {
nameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold(
appBar: AppBar(
title: Text(loc.player_profile),
actions: [
HapticIconButton(
icon: const Icon(Icons.delete),
onPressed: () async {
showDialog<bool>(
context: context,
builder: (context) => CustomAlertDialog(
title: loc.delete_player,
content: Text(loc.this_cannot_be_undone),
actions: [
CustomDialogAction(
onPressed: () => Navigator.of(context).pop(true),
text: loc.delete,
),
CustomDialogAction(
onPressed: () => Navigator.of(context).pop(false),
buttonType: ButtonType.secondary,
text: loc.cancel,
),
],
),
).then((confirmed) async {
if (confirmed! && context.mounted) {
//TODO: implement player deletion in db
if (!context.mounted) return;
Navigator.pop(context);
widget.callback();
}
});
},
),
],
),
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.person,
containerSize: 55,
iconSize: 38,
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
_player.name,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: CustomTheme.textColor,
),
textAlign: TextAlign.center,
),
Text(
playerNameCount,
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: CustomTheme.textColor.withAlpha(120),
),
textAlign: TextAlign.center,
),
],
),
const SizedBox(height: 5),
Text(
'${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(_player.createdAt)}',
style: const TextStyle(
fontSize: 12,
color: CustomTheme.textColor,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
InfoTile(
title: '${loc.matches_part_of} ($totalMatches)',
icon: Icons.sports_esports,
horizontalAlignment: CrossAxisAlignment.start,
content: AppSkeleton(
enabled: isLoading,
fixLayoutBuilder: true,
alignment: Alignment.topLeft,
child: playerMatches.isNotEmpty
? Wrap(
alignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 12,
runSpacing: 8,
children: playerMatches.map((match) {
return TextIconTile(
text: match.name,
iconEnabled: false,
);
}).toList(),
)
: Text(
loc.no_matches_played_yet,
style: const TextStyle(
fontSize: 14,
color: CustomTheme.textColor,
),
),
),
),
const SizedBox(height: 15),
InfoTile(
title: '${loc.groups_part_of} ($totalGroups)',
icon: Icons.people,
horizontalAlignment: CrossAxisAlignment.start,
content: AppSkeleton(
enabled: isLoading,
fixLayoutBuilder: true,
alignment: Alignment.topLeft,
child: playerGroups.isNotEmpty
? Wrap(
alignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 12,
runSpacing: 8,
children: playerGroups.map((group) {
return TextIconTile(
text: group.name,
iconEnabled: false,
);
}).toList(),
)
: Text(
loc.not_part_of_any_group,
style: const TextStyle(
fontSize: 14,
color: CustomTheme.textColor,
),
),
),
),
const SizedBox(height: 15),
InfoTile(
title: loc.statistics,
icon: Icons.bar_chart,
content: AppSkeleton(
enabled: isLoading,
fixLayoutBuilder: true,
child: Column(
children: [
_buildStatRow(
loc.matches_played,
totalMatches.toString(),
),
_buildStatRow(loc.matches_won, matchesWon.toString()),
_buildStatRow(
loc.winrate,
'${totalMatches == 0 ? 0 : ((matchesWon / totalMatches) * 100).round()}%',
),
],
),
),
),
],
),
Positioned(
bottom: MediaQuery.paddingOf(context).bottom,
child: MainMenuButton(
text: loc.edit_player,
icon: Icons.edit,
onPressed: () async {
nameController.text = _player.name;
showDialog<bool>(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setDialogState) {
return CustomAlertDialog(
title: loc.edit_name,
content: TextInputField(
controller: nameController,
hintText: loc.set_name,
onChanged: (_) => setDialogState(() {}),
),
actions: [
CustomDialogAction(
onPressed: isConfirmButtonEnabled()
? () => Navigator.of(context).pop(true)
: null,
text: loc.confirm,
),
CustomDialogAction(
onPressed: () => Navigator.of(context).pop(false),
buttonType: ButtonType.secondary,
text: loc.cancel,
),
],
);
},
),
).then((confirmed) async {
if (confirmed! && context.mounted) {
final newName = nameController.text.trim();
if (newName != _player.name) {
final fetchedPlayerNameCount = await db.playerDao
.getNameCount(name: newName);
await db.playerDao.updatePlayerName(
playerId: _player.id,
name: newName,
);
widget.callback.call();
setState(() {
_player = Player(
name: newName,
createdAt: _player.createdAt,
id: _player.id,
nameCount: _player.nameCount,
description: _player.description,
);
// If there is already a player with the same name,
// the count of that player is 0, so we start counting from 2 to get the correct count for this player. If there are no players with the same name, we just show the name without a count.
playerNameCount = fetchedPlayerNameCount == 0
? ''
: ' #${fetchedPlayerNameCount + 1}';
});
}
}
});
},
),
),
],
),
),
);
}
/// Loads statistics for this player
Future<void> _loadData() async {
isLoading = true;
final fetchedMatches = await db.matchDao.getMatchesByPlayer(
playerId: _player.id,
);
final fetchedGroups = await db.groupDao.getGroupsByPlayer(
playerId: _player.id,
);
if (!mounted) return;
setState(() {
playerMatches = fetchedMatches;
totalMatches = fetchedMatches.length;
matchesWon = fetchedMatches
.where((match) => match.mvp.any((mvp) => mvp.id == _player.id))
.length;
playerGroups = fetchedGroups;
totalGroups = fetchedGroups.length;
isLoading = false;
});
}
/// Builds a single statistic row with a label and value
/// - [label]: The label of the statistic
/// - [value]: The value of the statistic
Widget _buildStatRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
label,
style: const TextStyle(
fontSize: 16,
color: CustomTheme.textColor,
),
),
],
),
Text(
value,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
);
}
bool isConfirmButtonEnabled() {
return nameController.text.trim().isNotEmpty;
}
}

View File

@@ -34,7 +34,6 @@ const allDependencies = <Package>[
_cli_util, _cli_util,
_clock, _clock,
_code_assets, _code_assets,
_code_builder,
_collection, _collection,
_convert, _convert,
_coverage, _coverage,
@@ -154,6 +153,7 @@ const allDependencies = <Package>[
_source_map_stack_trace, _source_map_stack_trace,
_source_maps, _source_maps,
_source_span, _source_span,
_sqlcipher_flutter_libs,
_sqlite3, _sqlite3,
_sqlite3_flutter_libs, _sqlite3_flutter_libs,
_sqlparser, _sqlparser,
@@ -670,17 +670,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// build_runner 2.13.1 /// build_runner 2.15.0
const _build_runner = Package( const _build_runner = Package(
name: 'build_runner', name: 'build_runner',
description: 'A build system for Dart code generation and modular compilation.', description: 'A build system for Dart code generation and modular compilation.',
repository: 'https://github.com/dart-lang/build/tree/master/build_runner', repository: 'https://github.com/dart-lang/build/tree/master/build_runner',
authors: [], authors: [],
version: '2.13.1', version: '2.15.0',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [PackageRef('analyzer'), PackageRef('args'), PackageRef('async'), PackageRef('build'), PackageRef('build_config'), PackageRef('build_daemon'), PackageRef('built_collection'), PackageRef('built_value'), PackageRef('code_builder'), PackageRef('collection'), PackageRef('convert'), PackageRef('crypto'), PackageRef('dart_style'), PackageRef('glob'), PackageRef('graphs'), PackageRef('http_multi_server'), PackageRef('io'), PackageRef('json_annotation'), PackageRef('logging'), PackageRef('meta'), PackageRef('mime'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('pub_semver'), PackageRef('shelf'), PackageRef('shelf_web_socket'), PackageRef('stream_transform'), PackageRef('watcher'), PackageRef('web_socket_channel'), PackageRef('yaml')], dependencies: [PackageRef('analyzer'), PackageRef('args'), PackageRef('async'), PackageRef('build'), PackageRef('build_config'), PackageRef('build_daemon'), PackageRef('built_collection'), PackageRef('built_value'), PackageRef('collection'), PackageRef('convert'), PackageRef('crypto'), PackageRef('dart_style'), PackageRef('glob'), PackageRef('graphs'), PackageRef('http_multi_server'), PackageRef('io'), PackageRef('json_annotation'), PackageRef('logging'), PackageRef('meta'), PackageRef('mime'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('pub_semver'), PackageRef('shelf'), PackageRef('shelf_web_socket'), PackageRef('stream_transform'), PackageRef('watcher'), PackageRef('web_socket_channel'), PackageRef('yaml')],
devDependencies: [PackageRef('stream_channel'), PackageRef('test')], devDependencies: [PackageRef('stream_channel'), PackageRef('test')],
license: '''Copyright 2016, the Dart project authors. license: '''Copyright 2016, the Dart project authors.
@@ -1510,47 +1510,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// code_builder 4.11.1
const _code_builder = Package(
name: 'code_builder',
description: 'A fluent, builder-based library for generating valid Dart code.',
repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/code_builder',
authors: [],
version: '4.11.1',
spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false,
isSdk: false,
dependencies: [PackageRef('built_collection'), PackageRef('built_value'), PackageRef('collection'), PackageRef('matcher'), PackageRef('meta')],
devDependencies: [PackageRef('build'), PackageRef('build_runner'), PackageRef('dart_style'), PackageRef('source_gen'), PackageRef('test')],
license: '''Copyright 2016, 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.''',
);
/// collection 1.19.1 /// collection 1.19.1
const _collection = Package( const _collection = Package(
name: 'collection', name: 'collection',
@@ -2581,14 +2540,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''', SOFTWARE.''',
); );
/// drift 2.31.0 /// drift 2.33.0
const _drift = Package( const _drift = Package(
name: 'drift', name: 'drift',
description: 'Drift is a reactive library to store relational data in Dart and Flutter applications.', description: 'Drift is a reactive library to store relational data in Dart and Flutter applications.',
homepage: 'https://drift.simonbinder.eu/', homepage: 'https://drift.simonbinder.eu/',
repository: 'https://github.com/simolus3/drift', repository: 'https://github.com/simolus3/drift',
authors: [], authors: [],
version: '2.31.0', version: '2.33.0',
spdxIdentifiers: ['MIT'], spdxIdentifiers: ['MIT'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -2617,14 +2576,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''', SOFTWARE.''',
); );
/// drift_dev 2.31.0 /// drift_dev 2.33.0
const _drift_dev = Package( const _drift_dev = Package(
name: 'drift_dev', name: 'drift_dev',
description: 'Dev-dependency for users of drift. Contains the generator and development tools.', description: 'Dev-dependency for users of drift. Contains the generator and development tools.',
homepage: 'https://drift.simonbinder.eu/', homepage: 'https://drift.simonbinder.eu/',
repository: 'https://github.com/simolus3/drift', repository: 'https://github.com/simolus3/drift',
authors: [], authors: [],
version: '2.31.0', version: '2.33.0',
spdxIdentifiers: ['MIT'], spdxIdentifiers: ['MIT'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -2653,18 +2612,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''', SOFTWARE.''',
); );
/// drift_flutter 0.2.8 /// drift_flutter 0.3.0
const _drift_flutter = Package( const _drift_flutter = Package(
name: 'drift_flutter', name: 'drift_flutter',
description: 'Easily set up drift databases across platforms in Flutter apps.', description: 'Easily set up drift databases across platforms in Flutter apps.',
homepage: 'https://drift.simonbinder.eu/', homepage: 'https://drift.simonbinder.eu/',
repository: 'https://github.com/simolus3/drift', repository: 'https://github.com/simolus3/drift',
authors: [], authors: [],
version: '0.2.8', version: '0.3.0',
spdxIdentifiers: ['MIT'], spdxIdentifiers: ['MIT'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [PackageRef('drift'), PackageRef('flutter'), PackageRef('meta'), PackageRef('path'), PackageRef('path_provider'), PackageRef('sqlite3'), PackageRef('sqlite3_flutter_libs')], dependencies: [PackageRef('drift'), PackageRef('flutter'), PackageRef('meta'), PackageRef('path'), PackageRef('path_provider'), PackageRef('sqlite3'), PackageRef('sqlite3_flutter_libs'), PackageRef('sqlcipher_flutter_libs')],
devDependencies: [PackageRef('build_runner'), PackageRef('drift_dev'), PackageRef('lints'), PackageRef('test'), PackageRef('flutter_test'), PackageRef('async')], devDependencies: [PackageRef('build_runner'), PackageRef('drift_dev'), PackageRef('lints'), PackageRef('test'), PackageRef('flutter_test'), PackageRef('async')],
license: '''MIT License license: '''MIT License
@@ -3057,18 +3016,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''', SOFTWARE.''',
); );
/// file_saver 0.3.1 /// file_saver 0.4.0
const _file_saver = Package( const _file_saver = Package(
name: 'file_saver', name: 'file_saver',
description: 'A Flutter plugin for saving files across all platforms (Android, iOS, Web, Windows, macOS, Linux). Save files from bytes, File objects, file paths, or download from URLs with a single method call. Features include MIME type support, Dio integration, and platform-specific save locations. Supports saveAs() dialog for user-selected locations on supported platforms.', description: 'Save files from bytes, paths, streams, and URLs across Android, iOS, Web, Windows, macOS, and Linux.',
homepage: 'https://hassanansari.dev', homepage: 'https://hassanansari.dev',
repository: 'https://github.com/incrediblezayed/file_saver', repository: 'https://github.com/incrediblezayed/file_saver',
authors: [], authors: [],
version: '0.3.1', version: '0.4.0',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [PackageRef('collection'), PackageRef('dio'), PackageRef('flutter'), PackageRef('flutter_web_plugins'), PackageRef('path_provider'), PackageRef('path_provider_linux'), PackageRef('path_provider_windows'), PackageRef('web')], dependencies: [PackageRef('collection'), PackageRef('dio'), PackageRef('flutter'), PackageRef('flutter_web_plugins'), PackageRef('path_provider'), PackageRef('web')],
devDependencies: [PackageRef('flutter_lints'), PackageRef('flutter_test')], devDependencies: [PackageRef('flutter_lints'), PackageRef('flutter_test')],
license: '''BSD 3-Clause License license: '''BSD 3-Clause License
@@ -37683,18 +37642,264 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// sqlite3 2.9.4 /// sqlcipher_flutter_libs 0.7.0+eol
const _sqlcipher_flutter_libs = Package(
name: 'sqlcipher_flutter_libs',
description: 'Not used anymore, update to version 3.x of package:sqlite3 instead',
homepage: 'https://github.com/simolus3/sqlite3.dart/tree/main/legacy/sqlcipher_flutter_libs',
authors: [],
version: '0.7.0+eol',
spdxIdentifiers: ['Pixar', 'MIT', 'BSD-3-Clause-HP'],
isMarkdown: false,
isSdk: false,
dependencies: [],
devDependencies: [],
license: '''sqlcipher_flutter_libs
MIT License
Copyright (c) 2020 Simon Binder
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--------------------------------------------------------------------------------
OpenSSL
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------
SQLCipher
Copyright (c) 2008-2020 Zetetic LLC
All rights reserved.
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 the ZETETIC 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 ZETETIC LLC ''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 ZETETIC LLC 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.''',
);
/// sqlite3 3.3.1
const _sqlite3 = Package( const _sqlite3 = Package(
name: 'sqlite3', name: 'sqlite3',
description: 'Provides lightweight yet convenient bindings to SQLite by using dart:ffi', description: 'Provides lightweight yet convenient bindings to SQLite by using dart:ffi',
homepage: 'https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3', homepage: 'https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3',
authors: [], authors: [],
version: '2.9.4', version: '3.3.1',
spdxIdentifiers: ['MIT'], spdxIdentifiers: ['MIT'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [PackageRef('collection'), PackageRef('ffi'), PackageRef('meta'), PackageRef('path'), PackageRef('web'), PackageRef('typed_data')], dependencies: [PackageRef('collection'), PackageRef('ffi'), PackageRef('meta'), PackageRef('path'), PackageRef('web'), PackageRef('typed_data'), PackageRef('hooks'), PackageRef('code_assets'), PackageRef('native_toolchain_c'), PackageRef('crypto')],
devDependencies: [PackageRef('analyzer'), PackageRef('build_daemon'), PackageRef('build_runner'), PackageRef('dart_style'), PackageRef('http'), PackageRef('lints'), PackageRef('shelf'), PackageRef('shelf_static'), PackageRef('stream_channel'), PackageRef('test'), PackageRef('pub_semver'), PackageRef('convert')], devDependencies: [PackageRef('analyzer'), PackageRef('build_daemon'), PackageRef('build_runner'), PackageRef('dart_style'), PackageRef('http'), PackageRef('lints'), PackageRef('shelf'), PackageRef('shelf_static'), PackageRef('stream_channel'), PackageRef('test'), PackageRef('pub_semver'), PackageRef('convert'), PackageRef('package_config'), PackageRef('logging')],
license: '''MIT License license: '''MIT License
Copyright (c) 2020 Simon Binder Copyright (c) 2020 Simon Binder
@@ -37718,17 +37923,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''', SOFTWARE.''',
); );
/// sqlite3_flutter_libs 0.5.42 /// sqlite3_flutter_libs 0.6.0+eol
const _sqlite3_flutter_libs = Package( const _sqlite3_flutter_libs = Package(
name: 'sqlite3_flutter_libs', name: 'sqlite3_flutter_libs',
description: 'Flutter plugin to include native sqlite3 libraries with your app', description: 'Not used anymore, update to version 3.x of package:sqlite3 instead',
homepage: 'https://github.com/simolus3/sqlite3.dart/tree/v2/sqlite3_flutter_libs', homepage: 'https://github.com/simolus3/sqlite3.dart/tree/main/legacy/sqlite3_flutter_libs',
authors: [], authors: [],
version: '0.5.42', version: '0.6.0+eol',
spdxIdentifiers: ['MIT'], spdxIdentifiers: ['MIT'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [PackageRef('flutter')], dependencies: [],
devDependencies: [], devDependencies: [],
license: '''MIT License license: '''MIT License
@@ -37753,14 +37958,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''', SOFTWARE.''',
); );
/// sqlparser 0.43.1 /// sqlparser 0.44.4
const _sqlparser = Package( const _sqlparser = Package(
name: 'sqlparser', name: 'sqlparser',
description: 'Parses sqlite statements and performs static analysis on them', description: 'Parses sqlite statements and performs static analysis on them',
homepage: 'https://github.com/simolus3/drift/tree/develop/sqlparser', homepage: 'https://github.com/simolus3/drift/tree/develop/sqlparser',
repository: 'https://github.com/simolus3/drift', repository: 'https://github.com/simolus3/drift',
authors: [], authors: [],
version: '0.43.1', version: '0.44.4',
spdxIdentifiers: ['MIT'], spdxIdentifiers: ['MIT'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -39415,12 +39620,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''', SOFTWARE.''',
); );
/// tallee 0.0.33+273 /// tallee 0.0.35+283
const _tallee = Package( const _tallee = Package(
name: 'tallee', name: 'tallee',
description: 'Tracking App for Card Games', description: 'Tracking App for Card Games',
authors: [], authors: [],
version: '0.0.33+273', version: '0.0.35+283',
spdxIdentifiers: ['LGPL-3.0'], spdxIdentifiers: ['LGPL-3.0'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,

View File

@@ -6,13 +6,11 @@ class AppSkeleton extends StatefulWidget {
/// - [child]: The widget tree to apply the skeleton effect to. /// - [child]: The widget tree to apply the skeleton effect to.
/// - [enabled]: A boolean to enable or disable the skeleton effect. /// - [enabled]: A boolean to enable or disable the skeleton effect.
/// - [fixLayoutBuilder]: A boolean to fix the layout builder for AnimatedSwitcher. /// - [fixLayoutBuilder]: A boolean to fix the layout builder for AnimatedSwitcher.
/// - [alignment]: The alignment used for the custom layout builder and optional Align wrapper. Defaults to [Alignment.center].
const AppSkeleton({ const AppSkeleton({
super.key, super.key,
required this.child, required this.child,
this.enabled = true, this.enabled = true,
this.fixLayoutBuilder = false, this.fixLayoutBuilder = false,
this.alignment = Alignment.center,
}); });
/// The widget tree to apply the skeleton effect to. /// The widget tree to apply the skeleton effect to.
@@ -24,9 +22,6 @@ class AppSkeleton extends StatefulWidget {
/// A boolean to fix the layout builder for AnimatedSwitcher. /// A boolean to fix the layout builder for AnimatedSwitcher.
final bool fixLayoutBuilder; final bool fixLayoutBuilder;
/// The alignment used for the custom layout builder and optional Align wrapper
final Alignment alignment;
@override @override
State<AppSkeleton> createState() => _AppSkeletonState(); State<AppSkeleton> createState() => _AppSkeletonState();
} }
@@ -50,14 +45,13 @@ class _AppSkeletonState extends State<AppSkeleton> {
layoutBuilder: !widget.fixLayoutBuilder layoutBuilder: !widget.fixLayoutBuilder
? AnimatedSwitcher.defaultLayoutBuilder ? AnimatedSwitcher.defaultLayoutBuilder
: (Widget? currentChild, List<Widget> previousChildren) { : (Widget? currentChild, List<Widget> previousChildren) {
final children = <Widget>[...previousChildren]; return Stack(
if (currentChild != null) children.add(currentChild); alignment: Alignment.topCenter,
return Stack(alignment: widget.alignment, children: children); children: [...previousChildren, ?currentChild],
);
}, },
), ),
child: widget.fixLayoutBuilder child: widget.child,
? Align(alignment: widget.alignment, child: widget.child)
: widget.child,
); );
} }
} }

View File

@@ -11,7 +11,7 @@ class AnimatedDialogButton extends StatefulWidget {
const AnimatedDialogButton({ const AnimatedDialogButton({
super.key, super.key,
required this.buttonText, required this.buttonText,
this.onPressed, required this.onPressed,
this.buttonConstraints, this.buttonConstraints,
this.buttonType = ButtonType.primary, this.buttonType = ButtonType.primary,
this.isDescructive = false, this.isDescructive = false,
@@ -19,7 +19,7 @@ class AnimatedDialogButton extends StatefulWidget {
final String buttonText; final String buttonText;
final VoidCallback? onPressed; final VoidCallback onPressed;
final BoxConstraints? buttonConstraints; final BoxConstraints? buttonConstraints;
@@ -38,38 +38,28 @@ class _AnimatedDialogButtonState extends State<AnimatedDialogButton> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final textStyling = _getTextStyling(); final textStyling = _getTextStyling();
final buttonDecoration = _getButtonDecoration(); final buttonDecoration = _getButtonDecoration();
bool isDisabled = widget.onPressed == null;
return IgnorePointer( return GestureDetector(
ignoring: isDisabled, onTapDown: (_) => setState(() => _isPressed = true),
child: Opacity( onTapUp: (_) => setState(() => _isPressed = false),
opacity: isDisabled ? 0.5 : 1.0, onTapCancel: () => setState(() => _isPressed = false),
child: GestureDetector( onTap: widget.onPressed,
onTapDown: (_) => setState(() => _isPressed = true), child: AnimatedScale(
onTapUp: (_) => setState(() => _isPressed = false), scale: _isPressed ? 0.95 : 1.0,
onTapCancel: () => setState(() => _isPressed = false), duration: const Duration(milliseconds: 100),
onTap: widget.onPressed, child: AnimatedOpacity(
child: AnimatedScale( opacity: _isPressed ? 0.6 : 1.0,
scale: _isPressed ? 0.95 : 1.0, duration: const Duration(milliseconds: 100),
duration: const Duration(milliseconds: 100), child: Center(
child: AnimatedOpacity( child: Container(
opacity: _isPressed ? 0.6 : 1.0, constraints: widget.buttonConstraints,
duration: const Duration(milliseconds: 100), decoration: buttonDecoration,
child: Center( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Container( margin: const EdgeInsets.symmetric(vertical: 8),
constraints: widget.buttonConstraints, child: Text(
decoration: buttonDecoration, widget.buttonText,
padding: const EdgeInsets.symmetric( style: textStyling,
horizontal: 16, textAlign: TextAlign.center,
vertical: 12,
),
margin: const EdgeInsets.symmetric(vertical: 8),
child: Text(
widget.buttonText,
style: textStyling,
textAlign: TextAlign.center,
),
),
), ),
), ),
), ),

View File

@@ -19,6 +19,7 @@ class CustomAlertDialog extends StatelessWidget {
final String title; final String title;
final Widget content; final Widget content;
final List<CustomDialogAction> actions; final List<CustomDialogAction> actions;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(

View File

@@ -10,7 +10,7 @@ class CustomDialogAction extends StatelessWidget {
/// - [onPressed]: Callback function that is triggered when the button is pressed. /// - [onPressed]: Callback function that is triggered when the button is pressed.
const CustomDialogAction({ const CustomDialogAction({
super.key, super.key,
this.onPressed, required this.onPressed,
required this.text, required this.text,
this.buttonType = ButtonType.primary, this.buttonType = ButtonType.primary,
this.isDestructive = false, this.isDestructive = false,
@@ -20,18 +20,17 @@ class CustomDialogAction extends StatelessWidget {
final ButtonType buttonType; final ButtonType buttonType;
final VoidCallback? onPressed; final VoidCallback onPressed;
final bool isDestructive; final bool isDestructive;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedDialogButton( return AnimatedDialogButton(
onPressed: onPressed != null onPressed: () async {
? () async { await HapticFeedback.selectionClick();
await HapticFeedback.selectionClick(); onPressed.call();
onPressed?.call(); },
}
: null,
buttonText: text, buttonText: text,
buttonType: buttonType, buttonType: buttonType,
isDescructive: isDestructive, isDescructive: isDestructive,

View File

@@ -26,7 +26,6 @@ class PlayerSelection extends StatefulWidget {
this.availablePlayers, this.availablePlayers,
this.initialSelectedPlayers, this.initialSelectedPlayers,
required this.onChanged, required this.onChanged,
this.onPlayerCreated,
}); });
/// An optional list of players to choose from. If null, all players from the database are used. /// An optional list of players to choose from. If null, all players from the database are used.
@@ -38,9 +37,6 @@ class PlayerSelection extends StatefulWidget {
/// A callback function that is invoked whenever the selection changes, /// A callback function that is invoked whenever the selection changes,
final Function(List<Player> value) onChanged; final Function(List<Player> value) onChanged;
/// A callback function that is invoked when a player was created in this widget
final VoidCallback? onPlayerCreated;
@override @override
State<PlayerSelection> createState() => _PlayerSelectionState(); State<PlayerSelection> createState() => _PlayerSelectionState();
} }
@@ -327,7 +323,6 @@ class _PlayerSelectionState extends State<PlayerSelection> {
/// Updates the state after successfully adding a new player. /// Updates the state after successfully adding a new player.
void _handleSuccessfulPlayerCreation(Player player) { void _handleSuccessfulPlayerCreation(Player player) {
widget.onPlayerCreated?.call();
selectedPlayers.insert(0, player); selectedPlayers.insert(0, player);
widget.onChanged([...selectedPlayers]); widget.onChanged([...selectedPlayers]);
allPlayers.add(player); allPlayers.add(player);

View File

@@ -1,10 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/common.dart'; import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/group.dart';
import 'package:tallee/presentation/views/main_menu/player_detail_view.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart'; import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
class GroupTile extends StatefulWidget { class GroupTile extends StatefulWidget {
@@ -17,7 +15,6 @@ class GroupTile extends StatefulWidget {
required this.group, required this.group,
this.isHighlighted = false, this.isHighlighted = false,
this.onTap, this.onTap,
this.onPlayerChanged,
}); });
/// The group data to be displayed. /// The group data to be displayed.
@@ -29,9 +26,6 @@ class GroupTile extends StatefulWidget {
/// Callback function to be executed when the tile is tapped. /// Callback function to be executed when the tile is tapped.
final VoidCallback? onTap; final VoidCallback? onTap;
/// Callback function to be executed when the players in the group are changed.
final VoidCallback? onPlayerChanged;
@override @override
State<GroupTile> createState() => _GroupTileState(); State<GroupTile> createState() => _GroupTileState();
} }
@@ -98,19 +92,6 @@ class _GroupTileState extends State<GroupTile> {
text: member.name, text: member.name,
suffixText: getNameCountText(member), suffixText: getNameCountText(member),
iconEnabled: false, iconEnabled: false,
onTileTap: () {
Navigator.push(
context,
adaptivePageRoute(
builder: (context) => PlayerDetailView(
player: member,
callback: () {
widget.onPlayerChanged?.call();
},
),
),
);
},
), ),
], ],
), ),

View File

@@ -3,13 +3,11 @@ import 'dart:core' hide Match;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/common.dart'; import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart'; import 'package:tallee/core/enums.dart';
import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/match.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/player_detail_view.dart';
import 'package:tallee/presentation/widgets/game_label.dart'; import 'package:tallee/presentation/widgets/game_label.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart'; import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
@@ -26,7 +24,6 @@ class MatchTile extends StatefulWidget {
required this.onTap, required this.onTap,
this.width, this.width,
this.compact = false, this.compact = false,
this.onPlayerEdited,
}); });
/// The match data to be displayed. /// The match data to be displayed.
@@ -35,9 +32,6 @@ class MatchTile extends StatefulWidget {
/// The callback invoked when the tile is tapped. /// The callback invoked when the tile is tapped.
final VoidCallback onTap; final VoidCallback onTap;
/// The callback invoked when the players are edited
final VoidCallback? onPlayerEdited;
/// Optional width for the tile. /// Optional width for the tile.
final double? width; final double? width;
@@ -230,19 +224,6 @@ class _MatchTileState extends State<MatchTile> {
text: player.name, text: player.name,
suffixText: getNameCountText(player), suffixText: getNameCountText(player),
iconEnabled: false, iconEnabled: false,
onTileTap: () {
Navigator.push(
context,
adaptivePageRoute(
builder: (context) => PlayerDetailView(
player: player,
callback: () {
widget.onPlayerEdited?.call();
},
),
),
);
},
); );
}).toList(), }).toList(),
), ),

View File

@@ -12,7 +12,6 @@ class TextIconTile extends StatelessWidget {
this.suffixText = '', this.suffixText = '',
this.iconEnabled = true, this.iconEnabled = true,
this.onIconTap, this.onIconTap,
this.onTileTap,
}); });
/// The text to display in the tile. /// The text to display in the tile.
@@ -26,58 +25,52 @@ class TextIconTile extends StatelessWidget {
/// The callback to be invoked when the icon is tapped. /// The callback to be invoked when the icon is tapped.
final VoidCallback? onIconTap; final VoidCallback? onIconTap;
/// The callback to be invoked when the tile is tapped.
final VoidCallback? onTileTap;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return Container(
onTap: onTileTap, padding: const EdgeInsets.all(5),
child: Container( decoration: BoxDecoration(
padding: const EdgeInsets.all(5), color: CustomTheme.onBoxColor,
decoration: BoxDecoration( borderRadius: BorderRadius.circular(12),
color: CustomTheme.onBoxColor, ),
borderRadius: BorderRadius.circular(12), child: Row(
), mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: Row( mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
mainAxisSize: MainAxisSize.min, if (iconEnabled) const SizedBox(width: 3),
children: [ Flexible(
if (iconEnabled) const SizedBox(width: 3), child: RichText(
Flexible( overflow: TextOverflow.ellipsis,
child: RichText( text: TextSpan(
overflow: TextOverflow.ellipsis, style: DefaultTextStyle.of(context).style,
text: TextSpan( children: [
style: DefaultTextStyle.of(context).style, TextSpan(
children: [ text: text,
TextSpan( style: const TextStyle(
text: text, fontSize: 14,
style: const TextStyle( fontWeight: FontWeight.w500,
fontSize: 14,
fontWeight: FontWeight.w500,
),
), ),
TextSpan( ),
text: suffixText, TextSpan(
style: TextStyle( text: suffixText,
fontSize: 13, style: TextStyle(
fontWeight: FontWeight.w500, fontSize: 13,
color: CustomTheme.textColor.withAlpha(120), fontWeight: FontWeight.w500,
), color: CustomTheme.textColor.withAlpha(120),
), ),
], ),
), ],
), ),
), ),
if (iconEnabled) ...<Widget>[ ),
const SizedBox(width: 3), if (iconEnabled) ...<Widget>[
GestureDetector( const SizedBox(width: 3),
onTap: onIconTap, GestureDetector(
child: const Icon(Icons.close, size: 20), onTap: onIconTap,
), child: const Icon(Icons.close, size: 20),
], ),
], ],
), ],
), ),
); );
} }

View File

@@ -85,10 +85,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e" sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.13.1" version: "2.15.0"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@@ -177,14 +177,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d"
url: "https://pub.dev"
source: hosted
version: "4.11.1"
collection: collection:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -333,26 +325,26 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: drift name: drift
sha256: "970cd188fddb111b26ea6a9b07a62bf5c2432d74147b8122c67044ae3b97e99e" sha256: "8033500116b24398fba0cca0369cc31678cd627c01e41753a61186911cea743e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.31.0" version: "2.33.0"
drift_dev: drift_dev:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: drift_dev name: drift_dev
sha256: "917184b2fb867b70a548a83bf0d36268423b38d39968c06cce4905683da49587" sha256: b3dd5b75e30522a91da8abda9f5bb17230cb038097f6d15fa75d42bb563428aa
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.31.0" version: "2.33.0"
drift_flutter: drift_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
name: drift_flutter name: drift_flutter
sha256: c07120854742a0cae2f7501a0da02493addde550db6641d284983c08762e60a7 sha256: "887fdec622174dc7eaefd0048403e34ee07cc18626ac8a7544cc3b8a4a172166"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.8" version: "0.3.0"
equatable: equatable:
dependency: transitive dependency: transitive
description: description:
@@ -397,10 +389,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: file_saver name: file_saver
sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" sha256: "68c9a085d9bb4546e0a31d1e583a48d7c17a6987d538788ea064f0043b1fc02d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.1" version: "0.4.0"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@@ -1122,30 +1114,38 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.2" version: "1.10.2"
sqlcipher_flutter_libs:
dependency: transitive
description:
name: sqlcipher_flutter_libs
sha256: "38d62d659d2fb8739bf25a42c9a350d1fdd6c29a5a61f13a946778ec75d27929"
url: "https://pub.dev"
source: hosted
version: "0.7.0+eol"
sqlite3: sqlite3:
dependency: transitive dependency: transitive
description: description:
name: sqlite3 name: sqlite3
sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2" sha256: "56da3e13ed7d28a66f930aa2b2b29db6736a233f08283326e96321dd812030f5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.9.4" version: "3.3.1"
sqlite3_flutter_libs: sqlite3_flutter_libs:
dependency: transitive dependency: transitive
description: description:
name: sqlite3_flutter_libs name: sqlite3_flutter_libs
sha256: eeb9e3a45207649076b808f8a5a74d68770d0b7f26ccef6d5f43106eee5375ad sha256: "3ed7553eee7bb368f8950f58ba29f634e06e813c029aff6a0d60862b96de8454"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.42" version: "0.6.0+eol"
sqlparser: sqlparser:
dependency: transitive dependency: transitive
description: description:
name: sqlparser name: sqlparser
sha256: "337e9997f7141ffdd054259128553c348635fa318f7ca492f07a4ab76f850d19" sha256: ecdc06d4a7d79dcbc928d99afd2f7f5b0f98a637c46f89be83d911617f759978
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.43.1" version: "0.44.4"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@@ -1427,5 +1427,5 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.10.3 <4.0.0" dart: ">=3.12.0 <4.0.0"
flutter: ">=3.38.4" flutter: ">=3.41.0"

View File

@@ -1,19 +1,19 @@
name: tallee name: tallee
description: "Tracking App for Card Games" description: "Tracking App for Card Games"
publish_to: 'none' publish_to: 'none'
version: 0.0.33+273 version: 0.0.35+283
environment: environment:
sdk: ^3.8.1 sdk: ^3.12.0
dependencies: dependencies:
clock: ^1.1.2 clock: ^1.1.2
collection: ^1.19.1 collection: ^1.19.1
cupertino_icons: ^1.0.6 cupertino_icons: ^1.0.9
drift: ^2.27.0 drift: ^2.33.0
drift_flutter: ^0.2.4 drift_flutter: ^0.3.0
file_picker: ^11.0.2 file_picker: ^11.0.2
file_saver: ^0.3.1 file_saver: ^0.4.0
flutter: flutter:
sdk: flutter sdk: flutter
flutter_localizations: flutter_localizations:
@@ -24,20 +24,20 @@ dependencies:
font_awesome_flutter: ^11.0.0 font_awesome_flutter: ^11.0.0
intl: any intl: any
json_schema: ^5.2.2 json_schema: ^5.2.2
package_info_plus: ^9.0.0 package_info_plus: ^9.0.1
path_provider: ^2.1.5 path_provider: ^2.1.5
provider: ^6.1.5 provider: ^6.1.5
skeletonizer: ^2.1.0+1 skeletonizer: ^2.1.3
url_launcher: ^6.3.2 url_launcher: ^6.3.2
uuid: ^4.5.2 uuid: ^4.5.3
dev_dependencies: dev_dependencies:
arb_utils: ^0.11.0 arb_utils: ^0.11.0
flutter_test: flutter_test:
sdk: flutter sdk: flutter
build_runner: ^2.7.0 build_runner: ^2.15.0
dart_pubspec_licenses: ^3.0.14 dart_pubspec_licenses: ^3.2.0
drift_dev: ^2.27.0 drift_dev: ^2.33.0
flutter_lints: ^6.0.0 flutter_lints: ^6.0.0
flutter: flutter:

View File

@@ -194,31 +194,6 @@ void main() {
expect(allGroups, isEmpty); expect(allGroups, isEmpty);
}); });
test('getGroupsByPlayer() works correctly', () async {
await database.groupDao.addGroupsAsList(
groups: [testGroup1, testGroup2],
);
final groups = await database.groupDao.getGroupsByPlayer(
playerId: testPlayer2.id,
);
expect(groups, hasLength(2));
expect(groups.any((group) => group.id == testGroup1.id), isTrue);
expect(groups.any((group) => group.id == testGroup2.id), isTrue);
});
test(
'getGroupsByPlayer() returns empty list for non-existent player',
() async {
final groups = await database.groupDao.getGroupsByPlayer(
playerId: 'non-existent-player-id',
);
expect(groups, isEmpty);
},
);
test('addGroupsAsList() with duplicate groups only adds once', () async { test('addGroupsAsList() with duplicate groups only adds once', () async {
await database.groupDao.addGroupsAsList( await database.groupDao.addGroupsAsList(
groups: [testGroup1, testGroup1, testGroup1], groups: [testGroup1, testGroup1, testGroup1],

View File

@@ -260,34 +260,6 @@ void main() {
expect(match.group!.id, testGroup1.id); expect(match.group!.id, testGroup1.id);
}); });
test('getMatchesByPlayer() works correctly', () async {
await database.matchDao.addMatchesAsList(
matches: [testMatch1, testMatch2],
);
final matches = await database.matchDao.getMatchesByPlayer(
playerId: testPlayer1.id,
);
expect(matches, hasLength(1));
expect(matches.first.id, testMatch2.id);
expect(
matches.first.players.any((p) => p.id == testPlayer1.id),
isTrue,
);
});
test(
'getMatchesByPlayer() returns empty list for non-existent player',
() async {
final matches = await database.matchDao.getMatchesByPlayer(
playerId: 'non-existing-player-id',
);
expect(matches, isEmpty);
},
);
test('getMatchCount() works correctly', () async { test('getMatchCount() works correctly', () async {
var count = await database.matchDao.getMatchCount(); var count = await database.matchDao.getMatchCount();
expect(count, 0); expect(count, 0);

View File

@@ -233,95 +233,6 @@ void main() {
expect(allPlayers, isEmpty); expect(allPlayers, isEmpty);
}); });
test('updatePlayerName() sets correct nameCount with 2 player', () async {
await database.playerDao.addPlayer(player: testPlayer1);
await database.playerDao.addPlayer(player: testPlayer2);
final newName = testPlayer1.name;
await database.playerDao.updatePlayerName(
playerId: testPlayer2.id,
name: newName,
);
var player = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(player.nameCount, 1);
player = await database.playerDao.getPlayerById(
playerId: testPlayer2.id,
);
expect(player.nameCount, 2);
await database.playerDao.updatePlayerName(
playerId: testPlayer1.id,
name: 'different name',
);
player = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(player.nameCount, 0);
player = await database.playerDao.getPlayerById(
playerId: testPlayer2.id,
);
expect(player.nameCount, 0);
});
test('updatePlayerName() sets correct nameCount with 3 player', () async {
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer2, testPlayer3],
);
// Changing both names to player 1's name
final newName = testPlayer1.name;
await database.playerDao.updatePlayerName(
playerId: testPlayer2.id,
name: newName,
);
await database.playerDao.updatePlayerName(
playerId: testPlayer3.id,
name: newName,
);
var player = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(player.nameCount, 1);
player = await database.playerDao.getPlayerById(
playerId: testPlayer2.id,
);
expect(player.nameCount, 2);
player = await database.playerDao.getPlayerById(
playerId: testPlayer3.id,
);
expect(player.nameCount, 3);
// Changing the middle players name
await database.playerDao.updatePlayerName(
playerId: testPlayer2.id,
name: 'different name',
);
player = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(player.nameCount, 1);
player = await database.playerDao.getPlayerById(
playerId: testPlayer2.id,
);
expect(player.nameCount, 0);
player = await database.playerDao.getPlayerById(
playerId: testPlayer3.id,
);
expect(player.nameCount, 2);
});
test('updatePlayerDescription() works correctly', () async { test('updatePlayerDescription() works correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1); await database.playerDao.addPlayer(player: testPlayer1);
@@ -461,22 +372,14 @@ void main() {
final player1 = Player(name: testPlayer1.name, description: ''); final player1 = Player(name: testPlayer1.name, description: '');
await database.playerDao.addPlayer(player: player1); await database.playerDao.addPlayer(player: player1);
final player2 = Player(name: testPlayer1.name, description: '');
await database.playerDao.addPlayer(player: player2);
var players = await database.playerDao.getAllPlayers(); var players = await database.playerDao.getAllPlayers();
expect(players.length, 3); expect(players.length, 2);
players.sort((a, b) => a.nameCount.compareTo(b.nameCount)); players.sort((a, b) => a.nameCount.compareTo(b.nameCount));
for (int i = 0; i < players.length - 1; i++) { for (int i = 0; i < players.length - 1; i++) {
expect(players[i].nameCount, i + 1); expect(players[i].nameCount, i + 1);
} }
// ids are correct in the right order
expect(players[0].id, testPlayer1.id);
expect(players[1].id, player1.id);
expect(players[2].id, player2.id);
}, },
); );
@@ -501,62 +404,24 @@ void main() {
for (int i = 0; i < players.length - 1; i++) { for (int i = 0; i < players.length - 1; i++) {
expect(players[i].nameCount, i + 1); expect(players[i].nameCount, i + 1);
} }
// ids are correct in the right order
expect(players[0].id, testPlayer1.id);
expect(players[1].id, player1.id);
expect(players[2].id, player2.id);
expect(players[3].id, player3.id);
}, },
); );
test('getNameCount works correctly', () async { test('getNameCount works correctly', () async {
final player1 = Player(name: testPlayer1.name);
final player2 = Player(name: testPlayer1.name); final player2 = Player(name: testPlayer1.name);
final player3 = Player(name: testPlayer1.name);
await database.playerDao.addPlayer(player: testPlayer1); await database.playerDao.addPlayersAsList(
players: [testPlayer1, player2, player3],
var nameCount = await database.playerDao.getNameCount(
name: testPlayer1.name,
); );
expect(nameCount, 1); final nameCount = await database.playerDao.getNameCount(
await database.playerDao.addPlayersAsList(players: [player1, player2]);
nameCount = await database.playerDao.getNameCount(
name: testPlayer1.name, name: testPlayer1.name,
); );
expect(nameCount, 3); expect(nameCount, 3);
}); });
test('calculateNameCount works correctly', () async {
final player1 = Player(name: testPlayer1.name);
final player2 = Player(name: testPlayer1.name);
// Case 1: No existing players with the name
var nameCount = await database.playerDao.calculateNameCount(
name: testPlayer1.name,
);
expect(nameCount, 0);
// Case 2: One existing player with the name. Should return 2 for
// the new player
await database.playerDao.addPlayer(player: testPlayer1);
nameCount = await database.playerDao.calculateNameCount(
name: testPlayer1.name,
);
expect(nameCount, 2);
// Case 3: Multiple existing players with the name. Should return count + 1
await database.playerDao.addPlayersAsList(players: [player1, player2]);
nameCount = await database.playerDao.calculateNameCount(
name: testPlayer1.name,
);
expect(nameCount, 4);
});
test('updateNameCount works correctly', () async { test('updateNameCount works correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1); await database.playerDao.addPlayer(player: testPlayer1);
@@ -576,24 +441,14 @@ void main() {
final player2 = Player(name: testPlayer1.name, description: ''); final player2 = Player(name: testPlayer1.name, description: '');
final player3 = Player(name: testPlayer1.name, description: ''); final player3 = Player(name: testPlayer1.name, description: '');
await database.playerDao.addPlayer(player: testPlayer1); await database.playerDao.addPlayersAsList(
var player = await database.playerDao.getPlayerWithHighestNameCount( players: [testPlayer1, player2, player3],
name: testPlayer1.name,
); );
expect(player, isNotNull);
expect(player!.nameCount, 0);
await database.playerDao.addPlayer(player: player2); final player = await database.playerDao.getPlayerWithHighestNameCount(
player = await database.playerDao.getPlayerWithHighestNameCount(
name: testPlayer1.name, name: testPlayer1.name,
); );
expect(player, isNotNull);
expect(player!.nameCount, 2);
await database.playerDao.addPlayer(player: player3);
player = await database.playerDao.getPlayerWithHighestNameCount(
name: testPlayer1.name,
);
expect(player, isNotNull); expect(player, isNotNull);
expect(player!.nameCount, 3); expect(player!.nameCount, 3);
}); });
@@ -605,6 +460,32 @@ void main() {
expect(player, isNull); expect(player, isNull);
}); });
test('calculateNameCount works correctly', () async {
// Case 1: No existing players with the name
var count = await database.playerDao.calculateNameCount(
name: testPlayer1.name,
);
expect(count, 0);
// Case 2: One existing player with the name. Should update that
// player's nameCount to 1 and return 2 for the new player
await database.playerDao.addPlayer(player: testPlayer1);
count = await database.playerDao.calculateNameCount(
name: testPlayer1.name,
);
expect(count, 2);
// Case 3: Multiple existing players with the name.
final player2 = Player(name: testPlayer1.name, nameCount: count);
await database.playerDao.addPlayer(player: player2);
count = await database.playerDao.calculateNameCount(
name: testPlayer1.name,
);
expect(count, 3);
});
test('getPlayerWithHighestNameCount with non existing player', () async { test('getPlayerWithHighestNameCount with non existing player', () async {
await database.playerDao.addPlayer(player: testPlayer1); await database.playerDao.addPlayer(player: testPlayer1);
await database.playerDao.initializeNameCount(name: testPlayer1.name); await database.playerDao.initializeNameCount(name: testPlayer1.name);