1 Commits

Author SHA1 Message Date
f0379e97c7 Merge pull request 'MVP' (#141) from development into main
Reviewed-on: #141
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
2026-01-09 12:55:50 +00:00
3 changed files with 279 additions and 285 deletions

View File

@@ -44,68 +44,66 @@ class _CreateGroupViewState extends State<CreateGroupView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
return ScaffoldMessenger( return Scaffold(
child: Scaffold( backgroundColor: CustomTheme.backgroundColor,
backgroundColor: CustomTheme.backgroundColor, appBar: AppBar(title: Text(loc.create_new_group)),
appBar: AppBar(title: Text(loc.create_new_group)), body: SafeArea(
body: SafeArea( child: Column(
child: Column( mainAxisAlignment: MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, children: [
children: [ Container(
Container( margin: CustomTheme.standardMargin,
margin: CustomTheme.standardMargin, child: TextInputField(
child: TextInputField( controller: _groupNameController,
controller: _groupNameController, hintText: loc.group_name,
hintText: loc.group_name,
),
), ),
Expanded( ),
child: PlayerSelection( Expanded(
onChanged: (value) { child: PlayerSelection(
setState(() { onChanged: (value) {
selectedPlayers = [...value]; setState(() {
}); selectedPlayers = [...value];
}, });
), },
), ),
CustomWidthButton( ),
text: loc.create_group, CustomWidthButton(
sizeRelativeToWidth: 0.95, text: loc.create_group,
buttonType: ButtonType.primary, sizeRelativeToWidth: 0.95,
onPressed: buttonType: ButtonType.primary,
(_groupNameController.text.isEmpty || onPressed:
(selectedPlayers.length < 2)) (_groupNameController.text.isEmpty ||
? null (selectedPlayers.length < 2))
: () async { ? null
bool success = await db.groupDao.addGroup( : () async {
group: Group( bool success = await db.groupDao.addGroup(
name: _groupNameController.text.trim(), group: Group(
members: selectedPlayers, name: _groupNameController.text.trim(),
), members: selectedPlayers,
); ),
if (!context.mounted) return; );
if (success) { if (!context.mounted) return;
Navigator.pop(context); if (success) {
} else { Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar( } else {
SnackBar( ScaffoldMessenger.of(context).showSnackBar(
backgroundColor: CustomTheme.boxColor, SnackBar(
content: Center( backgroundColor: CustomTheme.boxColor,
child: Text( content: Center(
AppLocalizations.of( child: Text(
context, AppLocalizations.of(
).error_creating_group, context,
style: const TextStyle(color: Colors.white), ).error_creating_group,
), style: const TextStyle(color: Colors.white),
), ),
), ),
); ),
} );
}, }
), },
const SizedBox(height: 20), ),
], const SizedBox(height: 20),
), ],
), ),
), ),
); );

View File

@@ -119,142 +119,140 @@ class _CreateMatchViewState extends State<CreateMatchView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
return ScaffoldMessenger( return Scaffold(
child: Scaffold( backgroundColor: CustomTheme.backgroundColor,
backgroundColor: CustomTheme.backgroundColor, appBar: AppBar(title: Text(loc.create_new_match)),
appBar: AppBar(title: Text(loc.create_new_match)), body: SafeArea(
body: SafeArea( child: Column(
child: Column( mainAxisAlignment: MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, children: [
children: [ Container(
Container( margin: CustomTheme.tileMargin,
margin: CustomTheme.tileMargin, child: TextInputField(
child: TextInputField( controller: _matchNameController,
controller: _matchNameController, hintText: hintText ?? '',
hintText: hintText ?? '',
),
), ),
ChooseTile( ),
title: loc.game, ChooseTile(
trailingText: selectedGameIndex == -1 title: loc.game,
? loc.none trailingText: selectedGameIndex == -1
: games[selectedGameIndex].$1, ? loc.none
onPressed: () async { : games[selectedGameIndex].$1,
selectedGameIndex = await Navigator.of(context).push( onPressed: () async {
MaterialPageRoute( selectedGameIndex = await Navigator.of(context).push(
builder: (context) => ChooseGameView( MaterialPageRoute(
games: games, builder: (context) => ChooseGameView(
initialGameIndex: selectedGameIndex, games: games,
), initialGameIndex: selectedGameIndex,
), ),
); ),
);
setState(() {
if (selectedGameIndex != -1) {
hintText = games[selectedGameIndex].$1;
selectedRuleset = games[selectedGameIndex].$3;
selectedRulesetIndex = _rulesets.indexWhere(
(r) => r.$1 == selectedRuleset,
);
} else {
hintText = loc.match_name;
selectedRuleset = null;
}
});
},
),
ChooseTile(
title: loc.ruleset,
trailingText: selectedRuleset == null
? loc.none
: translateRulesetToString(selectedRuleset!, context),
onPressed: () async {
selectedRuleset = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChooseRulesetView(
rulesets: _rulesets,
initialRulesetIndex: selectedRulesetIndex,
),
),
);
if (!mounted) return;
selectedRulesetIndex = _rulesets.indexWhere(
(r) => r.$1 == selectedRuleset,
);
selectedGameIndex = -1;
setState(() {});
},
),
ChooseTile(
title: loc.group,
trailingText: selectedGroup == null
? loc.none_group
: selectedGroup!.name,
onPressed: () async {
selectedGroup = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChooseGroupView(
groups: groupsList,
initialGroupId: selectedGroupId,
),
),
);
selectedGroupId = selectedGroup?.id ?? '';
if (selectedGroup != null) {
filteredPlayerList = playerList
.where(
(p) => !selectedGroup!.members.any((m) => m.id == p.id),
)
.toList();
} else {
filteredPlayerList = List.from(playerList);
}
setState(() {});
},
),
Expanded(
child: PlayerSelection(
key: ValueKey(selectedGroup?.id ?? 'no_group'),
initialSelectedPlayers: selectedPlayers ?? [],
availablePlayers: filteredPlayerList,
onChanged: (value) {
setState(() { setState(() {
if (selectedGameIndex != -1) { selectedPlayers = value;
hintText = games[selectedGameIndex].$1;
selectedRuleset = games[selectedGameIndex].$3;
selectedRulesetIndex = _rulesets.indexWhere(
(r) => r.$1 == selectedRuleset,
);
} else {
hintText = loc.match_name;
selectedRuleset = null;
}
}); });
}, },
), ),
ChooseTile( ),
title: loc.ruleset, CustomWidthButton(
trailingText: selectedRuleset == null text: loc.create_match,
? loc.none sizeRelativeToWidth: 0.95,
: translateRulesetToString(selectedRuleset!, context), buttonType: ButtonType.primary,
onPressed: () async { onPressed: _enableCreateGameButton()
selectedRuleset = await Navigator.of(context).push( ? () async {
MaterialPageRoute( Match match = Match(
builder: (context) => ChooseRulesetView( name: _matchNameController.text.isEmpty
rulesets: _rulesets, ? (hintText ?? '')
initialRulesetIndex: selectedRulesetIndex, : _matchNameController.text.trim(),
), createdAt: DateTime.now(),
), group: selectedGroup,
); players: selectedPlayers,
if (!mounted) return; );
selectedRulesetIndex = _rulesets.indexWhere( await db.matchDao.addMatch(match: match);
(r) => r.$1 == selectedRuleset, if (context.mounted) {
); Navigator.pushReplacement(
selectedGameIndex = -1; context,
setState(() {}); CupertinoPageRoute(
}, fullscreenDialog: true,
), builder: (context) => MatchResultView(
ChooseTile( match: match,
title: loc.group, onWinnerChanged: widget.onWinnerChanged,
trailingText: selectedGroup == null
? loc.none_group
: selectedGroup!.name,
onPressed: () async {
selectedGroup = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChooseGroupView(
groups: groupsList,
initialGroupId: selectedGroupId,
),
),
);
selectedGroupId = selectedGroup?.id ?? '';
if (selectedGroup != null) {
filteredPlayerList = playerList
.where(
(p) => !selectedGroup!.members.any((m) => m.id == p.id),
)
.toList();
} else {
filteredPlayerList = List.from(playerList);
}
setState(() {});
},
),
Expanded(
child: PlayerSelection(
key: ValueKey(selectedGroup?.id ?? 'no_group'),
initialSelectedPlayers: selectedPlayers ?? [],
availablePlayers: filteredPlayerList,
onChanged: (value) {
setState(() {
selectedPlayers = value;
});
},
),
),
CustomWidthButton(
text: loc.create_match,
sizeRelativeToWidth: 0.95,
buttonType: ButtonType.primary,
onPressed: _enableCreateGameButton()
? () async {
Match match = Match(
name: _matchNameController.text.isEmpty
? (hintText ?? '')
: _matchNameController.text.trim(),
createdAt: DateTime.now(),
group: selectedGroup,
players: selectedPlayers,
);
await db.matchDao.addMatch(match: match);
if (context.mounted) {
Navigator.pushReplacement(
context,
CupertinoPageRoute(
fullscreenDialog: true,
builder: (context) => MatchResultView(
match: match,
onWinnerChanged: widget.onWinnerChanged,
),
), ),
); ),
} );
} }
: null, }
), : null,
], ),
), ],
), ),
), ),
); );

View File

@@ -21,107 +21,105 @@ class _SettingsViewState extends State<SettingsView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
return ScaffoldMessenger( return Scaffold(
child: Scaffold( appBar: AppBar(backgroundColor: CustomTheme.backgroundColor),
appBar: AppBar(backgroundColor: CustomTheme.backgroundColor), backgroundColor: CustomTheme.backgroundColor,
backgroundColor: CustomTheme.backgroundColor, body: LayoutBuilder(
body: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) =>
builder: (BuildContext context, BoxConstraints constraints) => SingleChildScrollView(
SingleChildScrollView( child: Column(
child: Column( mainAxisAlignment: MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ Padding(
Padding( padding: const EdgeInsets.fromLTRB(24, 0, 24, 10),
padding: const EdgeInsets.fromLTRB(24, 0, 24, 10), child: Text(
child: Text( textAlign: TextAlign.start,
textAlign: TextAlign.start, loc.menu,
loc.menu, style: const TextStyle(
style: const TextStyle( fontSize: 28,
fontSize: 28, fontWeight: FontWeight.bold,
fontWeight: FontWeight.bold, ),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 10,
),
child: Text(
textAlign: TextAlign.start,
loc.settings,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
),
SettingsListTile(
title: loc.export_data,
icon: Icons.upload_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () async {
final String json =
await DataTransferService.getAppDataAsJson(context);
final result = await DataTransferService.exportData(
json,
'game_tracker-data',
);
if (!context.mounted) return;
showExportSnackBar(context: context, result: result);
},
),
SettingsListTile(
title: loc.import_data,
icon: Icons.download_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () async {
final result = await DataTransferService.importData(
context,
);
if (!context.mounted) return;
showImportSnackBar(context: context, result: result);
},
),
SettingsListTile(
title: loc.delete_all_data,
icon: Icons.delete_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () {
showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text(loc.delete_all_data),
content: Text(loc.this_cannot_be_undone),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(loc.cancel),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(loc.delete),
),
],
), ),
), ).then((confirmed) {
), if (confirmed == true && context.mounted) {
Padding( DataTransferService.deleteAllData(context);
padding: const EdgeInsets.symmetric( showSnackbar(
horizontal: 24, context: context,
vertical: 10, message: AppLocalizations.of(
), context,
child: Text( ).data_successfully_deleted,
textAlign: TextAlign.start, );
loc.settings, }
style: const TextStyle( });
fontSize: 22, },
fontWeight: FontWeight.bold, ),
), ],
),
),
SettingsListTile(
title: loc.export_data,
icon: Icons.upload_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () async {
final String json =
await DataTransferService.getAppDataAsJson(context);
final result = await DataTransferService.exportData(
json,
'game_tracker-data',
);
if (!context.mounted) return;
showExportSnackBar(context: context, result: result);
},
),
SettingsListTile(
title: loc.import_data,
icon: Icons.download_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () async {
final result = await DataTransferService.importData(
context,
);
if (!context.mounted) return;
showImportSnackBar(context: context, result: result);
},
),
SettingsListTile(
title: loc.delete_all_data,
icon: Icons.delete_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () {
showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text(loc.delete_all_data),
content: Text(loc.this_cannot_be_undone),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(loc.cancel),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(loc.delete),
),
],
),
).then((confirmed) {
if (confirmed == true && context.mounted) {
DataTransferService.deleteAllData(context);
showSnackbar(
context: context,
message: AppLocalizations.of(
context,
).data_successfully_deleted,
);
}
});
},
),
],
),
), ),
), ),
), ),
); );
} }