333 lines
15 KiB
Dart
333 lines
15 KiB
Dart
|
|
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!,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
);
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
);
|
|||
|
|
},
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|