mnemo_cards/mnemo_cards_admin/old/add_card.dart

333 lines
15 KiB
Dart
Raw Normal View History

2025-12-02 23:17:15 +00:00
import 'dart:async';
import 'dart:convert';
import 'package:mnemo_cards/domain/router/dialogs.dart';
import 'package:universal_io/io.dart';
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'package:mnemo_cards/admin/admin_api.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../main.dart';
class EditCard {
GameCardDto gameCardDto;
AdminApi adminApi;
bool imageChanged = false;
EditCard(GameCardDto? cardDto, this.adminApi)
: gameCardDto =
cardDto ??
const GameCardDto(
id: -1,
image: null,
mnemo: null,
original: null,
translation: null,
transcription: null,
transcriptionMnemo: null,
imageBack: null,
back: null,
);
void _saveCard() async {
final dto = imageChanged ? gameCardDto : gameCardDto.copyWith.image(null);
final _progress = ValueNotifier<(int, int)>((0, 1));
final _dialogCompleter = Completer();
showInfoDialog(
'',
child: ValueListenableBuilder(
valueListenable: _progress,
builder: (c, v, _) => Text(
(v.$1 == v.$2 || v.$2 < 0)
? 'Waiting for response...'
: 'Uploading ${v.$1}/${v.$2}...',
),
),
completer: _dialogCompleter,
);
final result = await adminApi.addCard(
dto,
progressCallback: (a, b) => _progress.value = (a, b),
);
_dialogCompleter.complete();
if (!result) {
ScaffoldMessenger.of(
scaffoldKey.currentContext!,
).showSnackBar(SnackBar(content: Text('ERROR')));
} else {
ScaffoldMessenger.of(
scaffoldKey.currentContext!,
).showSnackBar(SnackBar(content: Text('Success!!!')));
appRouter.maybePop();
}
}
void _deleteCard() async {
final result = await adminApi.deleteCard(gameCardDto);
if (!result) {
ScaffoldMessenger.of(
scaffoldKey.currentContext!,
).showSnackBar(SnackBar(content: Text('ERROR')));
} else {
ScaffoldMessenger.of(
scaffoldKey.currentContext!,
).showSnackBar(SnackBar(content: Text('Success!!!')));
appRouter.maybePop();
}
}
void addCard(BuildContext context) {
final theme = Theme.of(context).textTheme;
final TextEditingController originalController = TextEditingController(
text: gameCardDto.original,
);
final TextEditingController translationController = TextEditingController(
text: gameCardDto.translation,
);
final TextEditingController transcriptionController = TextEditingController(
text: gameCardDto.transcription,
);
final TextEditingController transcriptionMnemoController =
TextEditingController(text: gameCardDto.transcriptionMnemo);
final TextEditingController mnemoController = TextEditingController(
text: gameCardDto.mnemo,
);
final TextEditingController backController = TextEditingController(
text: gameCardDto.back,
);
showDialog(
context: context,
builder: (c) {
return Center(
child: Scaffold(
body: Material(
child: ListView(
children: [
Header(
'${gameCardDto.id}',
hasPopButton: true,
trail: Row(
children: [
IconButton(
onPressed: _saveCard,
icon: Icon(Icons.save),
),
if (gameCardDto.id >= 0)
GestureDetector(
onLongPress: _deleteCard,
child: Icon(Icons.delete),
),
],
),
),
StatefulBuilder(
builder: (context, setState) {
return Stack(
alignment: Alignment.center,
children: [
Padding(
padding: EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
TextField(
controller: originalController,
onChanged: (v) {
gameCardDto = gameCardDto.copyWith(
original: v,
);
},
decoration: InputDecoration(
hintText: 'Original (cerdo)',
),
maxLines: 1,
style: theme.displayMedium?.copyWith(
fontSize:
theme.displayMedium!.fontSize!,
),
),
TextField(
controller: translationController,
onChanged: (v) {
gameCardDto = gameCardDto.copyWith(
translation: v,
);
},
decoration: InputDecoration(
hintText: 'Translation (свинья)',
),
maxLines: 1,
style: theme.headlineSmall?.copyWith(
color: theme.headlineSmall!.color!
.withOpacity(0.5),
fontSize:
theme.headlineSmall!.fontSize!,
),
),
Row(
children: [
Flexible(
child: TextField(
controller: transcriptionController,
onChanged: (v) {
gameCardDto = gameCardDto
.copyWith(transcription: v);
},
decoration: InputDecoration(
hintText: 'Transcription',
),
maxLines: 1,
style: theme.headlineSmall
?.copyWith(
color: theme
.headlineSmall!
.color!,
fontSize: theme
.headlineSmall!
.fontSize!,
),
),
),
SizedBox(width: 4.0),
Flexible(
child: TextField(
controller:
transcriptionMnemoController,
onChanged: (v) {
gameCardDto = gameCardDto
.copyWith(
transcriptionMnemo: v,
);
},
decoration: InputDecoration(
hintText: 'Transcription mnemo',
),
maxLines: 1,
style: theme.headlineSmall
?.copyWith(
color: theme
.headlineSmall!
.color!,
fontSize: theme
.headlineSmall!
.fontSize!,
),
),
),
],
),
],
),
SizedBox(
width: MediaQuery.of(context).size.width - 20,
height:
MediaQuery.of(context).size.width - 20,
child: GestureDetector(
onTap: () async {
FilePickerResult? result =
await FilePicker.platform.pickFiles();
if (result != null &&
result.files.isNotEmpty) {
imageChanged = true;
final file = result.files.first;
final encoded = base64.normalize(
base64.encode(
File(file.path!).readAsBytesSync(),
),
);
gameCardDto = gameCardDto.copyWith(
image: encoded,
);
}
setState(() {});
},
child: gameCardDto.image == null
? Container(
alignment: Alignment.center,
color: Colors.white,
child: Icon(
Icons.image,
color: Colors.grey,
),
)
: Stack(
children: [
Image(
key: ValueKey(base64),
image: MemoryImage(
base64.decode(
gameCardDto.image!,
),
),
gaplessPlayback: true,
fit: BoxFit.contain,
),
Positioned(
top: 8.0,
right: 8.0,
child: IconButton(
onPressed: () {
gameCardDto = gameCardDto
.copyWith(image: null);
setState(() {});
},
icon: Icon(Icons.delete),
),
),
],
),
),
),
TextField(
controller: mnemoController,
onChanged: (v) {
gameCardDto = gameCardDto.copyWith(
mnemo: v,
);
},
decoration: InputDecoration(
hintText: 'Mnemo ([свинья] с сердечком)',
),
maxLines: 1,
style: theme.headlineSmall?.copyWith(
fontSize: theme.headlineSmall!.fontSize!,
),
),
TextField(
controller: backController,
onChanged: (v) {
gameCardDto = gameCardDto.copyWith(back: v);
},
decoration: InputDecoration(
hintText: 'Задняя сторона',
),
maxLines: 1,
style: theme.headlineSmall?.copyWith(
fontSize: theme.headlineSmall!.fontSize!,
),
),
],
),
),
],
);
},
),
],
),
),
),
);
},
);
}
}