manager-app/lib/Components/multi_string_input_html_modal.dart

282 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:manager_app/l10n/app_localizations.dart';
import 'package:manager_app/Components/message_notification.dart';
import 'package:manager_app/Components/translation_input_and_resource_container.dart';
import 'package:manager_app/Components/translation_input_container.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_api_new/api.dart';
import 'package:collection/collection.dart';
/// Le modal de traduction — l'interface la plus utilisée de l'app.
///
/// `isHTML` décide du corps : Quill quand le champ accepte du HTML, champ
/// simple sinon. C'est la fusion avec l'ancien `multi_input_modal.dart`, qui
/// faisait le même travail dans une deuxième implémentation.
void showMultiStringInputHTML(String label, String modalLabel, bool isTitle, List<TranslationDTO> values, List<TranslationDTO> newValues, Function onGetResult, int maxLines, List<ResourceType>? resourceTypes, BuildContext context, bool isMandatory, {bool isHTML = true}) {
showDialog(
useRootNavigator: false,
context: context,
builder: (BuildContext context) {
final l = AppLocalizations.of(context)!;
final translated = newValues
.where((t) => (t.value ?? '').trim().isNotEmpty)
.length;
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(kRadiusShell),
side: const BorderSide(color: kLine),
),
clipBehavior: Clip.antiAlias,
insetPadding: const EdgeInsets.symmetric(horizontal: 60, vertical: kSpace7),
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 780,
maxHeight: MediaQuery.of(context).size.height - 48),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(kSpace6, kSpace5, kSpace4, kSpace5),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: kTitleCard),
// Plusieurs appelants passent le même texte aux deux
// paramètres (les messages de score du quiz) : on ne
// l'écrit pas deux fois.
if (modalLabel.trim() != label.trim())
Text(modalLabel, style: kSubtitleCard),
],
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: kSpace3, vertical: kSpace1),
decoration: BoxDecoration(
color: kSurface3,
borderRadius: BorderRadius.circular(kRadiusPill),
),
child: Text(
l.translatedLanguagesCount(translated, newValues.length),
style: const TextStyle(fontSize: 11, color: kInk2)),
),
IconButton(
icon: const Icon(Icons.close, size: 20, color: kInk3),
tooltip: l.cancel,
onPressed: () {
onGetResult(values);
Navigator.of(context).pop();
},
),
],
),
),
const Divider(height: 1, thickness: 1, color: kLineSoft),
Flexible(
child: Padding(
padding: const EdgeInsets.fromLTRB(kSpace6, kSpace5, kSpace6, kSpace5),
child: TranslationInputContainer(
isTitle: isTitle,
values: values,
newValues: newValues,
onGetResult: onGetResult,
maxLines: maxLines,
resourceTypes: resourceTypes,
isHTML: isHTML,
),
),
),
Container(
padding: const EdgeInsets.all(kSpace4),
decoration: const BoxDecoration(
color: kSurface2,
border: Border(top: BorderSide(color: kLineSoft)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
onGetResult(values);
Navigator.of(context).pop();
},
child: Text(l.cancel,
style: const TextStyle(fontSize: 13, color: kInk2)),
),
const SizedBox(width: kSpace2),
FilledButton(
onPressed: () {
Function deepEq = const DeepCollectionEquality().equals;
if (!deepEq(values, newValues)) {
if (isMandatory && newValues.any((label) => label.value == null || label.value!.trim() == "")) {
showNotification(kPrimaryColor, kWhite, l.translationIncomplete, context, null);
} else {
onGetResult(newValues);
Navigator.of(context).pop();
}
} else {
Navigator.of(context).pop();
}
},
style: FilledButton.styleFrom(
backgroundColor: kPrimaryColor,
padding: const EdgeInsets.symmetric(
horizontal: kSpace5, vertical: kSpace3),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(kRadiusPill)),
),
child: Text(l.validate,
style: const TextStyle(
fontSize: 13, fontWeight: FontWeight.w600)),
),
],
),
),
],
),
),
);
},
);
}
/// Le même modal, pour les traductions qui portent aussi une ressource
/// (message de score d'un quiz, fichier PDF, message de jeu). Même coquille,
/// corps différent.
void showMultiStringInputAndResourceHTML(String label, String modalLabel, bool isTitle, List<TranslationAndResourceDTO> values, List<TranslationAndResourceDTO> newValues, Function onGetResult, int maxLines, List<ResourceType>? resourceTypes, BuildContext context) {
showDialog(
useRootNavigator: false,
context: context,
builder: (BuildContext context) {
final l = AppLocalizations.of(context)!;
final translated =
newValues.where((t) => (t.value ?? '').trim().isNotEmpty).length;
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(kRadiusShell),
side: const BorderSide(color: kLine),
),
clipBehavior: Clip.antiAlias,
insetPadding: const EdgeInsets.symmetric(horizontal: 60, vertical: kSpace7),
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 780,
maxHeight: MediaQuery.of(context).size.height - 48),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(kSpace6, kSpace5, kSpace4, kSpace5),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: kTitleCard),
// Plusieurs appelants passent le même texte aux deux
// paramètres (les messages de score du quiz) : on ne
// l'écrit pas deux fois.
if (modalLabel.trim() != label.trim())
Text(modalLabel, style: kSubtitleCard),
],
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: kSpace3, vertical: kSpace1),
decoration: BoxDecoration(
color: kSurface3,
borderRadius: BorderRadius.circular(kRadiusPill),
),
child: Text(
l.translatedLanguagesCount(translated, newValues.length),
style: const TextStyle(fontSize: 11, color: kInk2)),
),
IconButton(
icon: const Icon(Icons.close, size: 20, color: kInk3),
tooltip: l.cancel,
onPressed: () {
onGetResult(values);
Navigator.of(context).pop();
},
),
],
),
),
const Divider(height: 1, thickness: 1, color: kLineSoft),
Flexible(
child: Padding(
padding: const EdgeInsets.fromLTRB(kSpace6, kSpace5, kSpace6, kSpace5),
child: TranslationInputAndResourceContainer(
isTitle: isTitle,
values: values,
newValues: newValues,
onGetResult: onGetResult,
maxLines: maxLines,
resourceTypes: resourceTypes,
),
),
),
Container(
padding: const EdgeInsets.all(kSpace4),
decoration: const BoxDecoration(
color: kSurface2,
border: Border(top: BorderSide(color: kLineSoft)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
onGetResult(values);
Navigator.of(context).pop();
},
child: Text(l.cancel,
style: const TextStyle(fontSize: 13, color: kInk2)),
),
const SizedBox(width: kSpace2),
FilledButton(
onPressed: () {
Function deepEq = const DeepCollectionEquality().equals;
if (!deepEq(values, newValues)) {
if (newValues.any((label) =>
label.value == null || label.value!.trim() == "")) {
showNotification(kPrimaryColor, kWhite,
l.translationIncomplete, context, null);
} else {
onGetResult(newValues);
Navigator.of(context).pop();
}
} else {
Navigator.of(context).pop();
}
},
style: FilledButton.styleFrom(
backgroundColor: kPrimaryColor,
padding: const EdgeInsets.symmetric(
horizontal: kSpace5, vertical: kSpace3),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(kRadiusPill)),
),
child: Text(l.validate,
style: const TextStyle(
fontSize: 13, fontWeight: FontWeight.w600)),
),
],
),
),
],
),
),
);
},
);
}