refactor: dialog boxes
This commit is contained in:
@@ -12,6 +12,7 @@ import '../../chat/providers/chat_providers.dart' as chat;
|
|||||||
// import '../../files/views/files_page.dart';
|
// import '../../files/views/files_page.dart';
|
||||||
import '../../profile/views/profile_page.dart';
|
import '../../profile/views/profile_page.dart';
|
||||||
import '../../../shared/utils/ui_utils.dart';
|
import '../../../shared/utils/ui_utils.dart';
|
||||||
|
import '../../../shared/widgets/themed_dialogs.dart';
|
||||||
import '../../../core/auth/auth_state_manager.dart';
|
import '../../../core/auth/auth_state_manager.dart';
|
||||||
import 'package:conduit/l10n/app_localizations.dart';
|
import 'package:conduit/l10n/app_localizations.dart';
|
||||||
import '../../../core/models/user.dart' as models;
|
import '../../../core/models/user.dart' as models;
|
||||||
@@ -580,43 +581,12 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _promptCreateFolder() async {
|
Future<void> _promptCreateFolder() async {
|
||||||
final theme = context.conduitTheme;
|
final name = await ThemedDialogs.promptTextInput(
|
||||||
final controller = TextEditingController();
|
context,
|
||||||
final name = await showDialog<String>(
|
title: AppLocalizations.of(context)!.newFolder,
|
||||||
context: context,
|
hintText: AppLocalizations.of(context)!.folderName,
|
||||||
builder: (ctx) => AlertDialog(
|
confirmText: AppLocalizations.of(context)!.create,
|
||||||
backgroundColor: theme.surfaceBackground,
|
cancelText: AppLocalizations.of(context)!.cancel,
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(context)!.newFolder,
|
|
||||||
style: TextStyle(color: theme.textPrimary),
|
|
||||||
),
|
|
||||||
content: TextField(
|
|
||||||
controller: controller,
|
|
||||||
autofocus: true,
|
|
||||||
style: TextStyle(color: theme.inputText),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: AppLocalizations.of(context)!.folderName,
|
|
||||||
hintStyle: TextStyle(color: theme.inputPlaceholder),
|
|
||||||
enabledBorder: UnderlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: theme.inputBorder),
|
|
||||||
),
|
|
||||||
focusedBorder: UnderlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: theme.buttonPrimary),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onSubmitted: (v) => Navigator.pop(ctx, controller.text.trim()),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(ctx),
|
|
||||||
child: Text(AppLocalizations.of(context)!.cancel),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(ctx, controller.text.trim()),
|
|
||||||
child: Text(AppLocalizations.of(context)!.create),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (name == null) return;
|
if (name == null) return;
|
||||||
@@ -822,50 +792,13 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
|||||||
String folderId,
|
String folderId,
|
||||||
String currentName,
|
String currentName,
|
||||||
) async {
|
) async {
|
||||||
final theme = context.conduitTheme;
|
final newName = await ThemedDialogs.promptTextInput(
|
||||||
final controller = TextEditingController(text: currentName);
|
context,
|
||||||
|
title: AppLocalizations.of(context)!.rename,
|
||||||
final newName = await showDialog<String>(
|
hintText: AppLocalizations.of(context)!.folderName,
|
||||||
context: context,
|
initialValue: currentName,
|
||||||
builder: (dialogContext) {
|
confirmText: AppLocalizations.of(context)!.save,
|
||||||
return AlertDialog(
|
cancelText: AppLocalizations.of(context)!.cancel,
|
||||||
backgroundColor: theme.surfaceBackground,
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(context)!.rename,
|
|
||||||
style: TextStyle(color: theme.textPrimary),
|
|
||||||
),
|
|
||||||
content: TextField(
|
|
||||||
controller: controller,
|
|
||||||
autofocus: true,
|
|
||||||
style: TextStyle(color: theme.inputText),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: AppLocalizations.of(context)!.folderName,
|
|
||||||
hintStyle: TextStyle(color: theme.inputPlaceholder),
|
|
||||||
enabledBorder: UnderlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: theme.inputBorder),
|
|
||||||
),
|
|
||||||
focusedBorder: UnderlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: theme.buttonPrimary),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textInputAction: TextInputAction.done,
|
|
||||||
onSubmitted: (value) => Navigator.pop(dialogContext, value.trim()),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(dialogContext),
|
|
||||||
child: Text(AppLocalizations.of(context)!.cancel),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
HapticFeedback.lightImpact();
|
|
||||||
Navigator.pop(dialogContext, controller.text.trim());
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(context)!.save),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (newName == null) return;
|
if (newName == null) return;
|
||||||
@@ -892,10 +825,10 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
|||||||
String folderId,
|
String folderId,
|
||||||
String folderName,
|
String folderName,
|
||||||
) async {
|
) async {
|
||||||
final confirmed = await UiUtils.showConfirmationDialog(
|
final confirmed = await ThemedDialogs.confirm(
|
||||||
context,
|
context,
|
||||||
title: 'Delete Folder',
|
title: AppLocalizations.of(context)!.deleteFolderTitle,
|
||||||
message: 'This folder and its assignment references will be removed.',
|
message: AppLocalizations.of(context)!.deleteFolderMessage,
|
||||||
confirmText: AppLocalizations.of(context)!.delete,
|
confirmText: AppLocalizations.of(context)!.delete,
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
);
|
);
|
||||||
@@ -912,7 +845,7 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
UiUtils.showMessage(
|
UiUtils.showMessage(
|
||||||
this.context,
|
this.context,
|
||||||
'Failed to delete folder',
|
AppLocalizations.of(context)!.failedToDeleteFolder,
|
||||||
isError: true,
|
isError: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1470,50 +1403,13 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
|||||||
String conversationId,
|
String conversationId,
|
||||||
String currentTitle,
|
String currentTitle,
|
||||||
) async {
|
) async {
|
||||||
final theme = context.conduitTheme;
|
final newName = await ThemedDialogs.promptTextInput(
|
||||||
final controller = TextEditingController(text: currentTitle);
|
context,
|
||||||
|
title: AppLocalizations.of(context)!.renameChat,
|
||||||
final newName = await showDialog<String>(
|
hintText: AppLocalizations.of(context)!.enterChatName,
|
||||||
context: context,
|
initialValue: currentTitle,
|
||||||
builder: (dialogContext) {
|
confirmText: AppLocalizations.of(context)!.save,
|
||||||
return AlertDialog(
|
cancelText: AppLocalizations.of(context)!.cancel,
|
||||||
backgroundColor: theme.surfaceBackground,
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(context)!.renameChat,
|
|
||||||
style: TextStyle(color: theme.textPrimary),
|
|
||||||
),
|
|
||||||
content: TextField(
|
|
||||||
controller: controller,
|
|
||||||
autofocus: true,
|
|
||||||
style: TextStyle(color: theme.inputText),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: AppLocalizations.of(context)!.enterChatName,
|
|
||||||
hintStyle: TextStyle(color: theme.inputPlaceholder),
|
|
||||||
enabledBorder: UnderlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: theme.inputBorder),
|
|
||||||
),
|
|
||||||
focusedBorder: UnderlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: theme.buttonPrimary),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textInputAction: TextInputAction.done,
|
|
||||||
onSubmitted: (value) => Navigator.pop(dialogContext, value.trim()),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(dialogContext),
|
|
||||||
child: Text(AppLocalizations.of(context)!.cancel),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
HapticFeedback.lightImpact();
|
|
||||||
Navigator.pop(dialogContext, controller.text.trim());
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(context)!.save),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (newName == null) return;
|
if (newName == null) return;
|
||||||
@@ -1546,7 +1442,7 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
String conversationId,
|
String conversationId,
|
||||||
) async {
|
) async {
|
||||||
final confirmed = await UiUtils.showConfirmationDialog(
|
final confirmed = await ThemedDialogs.confirm(
|
||||||
context,
|
context,
|
||||||
title: AppLocalizations.of(context)!.deleteChatTitle,
|
title: AppLocalizations.of(context)!.deleteChatTitle,
|
||||||
message: AppLocalizations.of(context)!.deleteChatMessage,
|
message: AppLocalizations.of(context)!.deleteChatMessage,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import '../../../core/widgets/error_boundary.dart';
|
|||||||
import '../../../shared/widgets/improved_loading_states.dart';
|
import '../../../shared/widgets/improved_loading_states.dart';
|
||||||
|
|
||||||
import '../../../shared/utils/ui_utils.dart';
|
import '../../../shared/utils/ui_utils.dart';
|
||||||
|
import '../../../shared/widgets/themed_dialogs.dart';
|
||||||
import '../../../shared/widgets/sheet_handle.dart';
|
import '../../../shared/widgets/sheet_handle.dart';
|
||||||
import '../../../shared/widgets/conduit_components.dart';
|
import '../../../shared/widgets/conduit_components.dart';
|
||||||
import '../../../core/providers/app_providers.dart';
|
import '../../../core/providers/app_providers.dart';
|
||||||
@@ -574,7 +575,7 @@ class ProfilePage extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _signOut(BuildContext context, WidgetRef ref) async {
|
void _signOut(BuildContext context, WidgetRef ref) async {
|
||||||
final confirm = await UiUtils.showConfirmationDialog(
|
final confirm = await ThemedDialogs.confirm(
|
||||||
context,
|
context,
|
||||||
title: AppLocalizations.of(context)!.signOut,
|
title: AppLocalizations.of(context)!.signOut,
|
||||||
message: AppLocalizations.of(context)!.endYourSession,
|
message: AppLocalizations.of(context)!.endYourSession,
|
||||||
|
|||||||
@@ -199,6 +199,9 @@
|
|||||||
},
|
},
|
||||||
"deleteChatTitle": "Chat löschen",
|
"deleteChatTitle": "Chat löschen",
|
||||||
"deleteChatMessage": "Dieser Chat wird dauerhaft gelöscht.",
|
"deleteChatMessage": "Dieser Chat wird dauerhaft gelöscht.",
|
||||||
|
"deleteFolderTitle": "Ordner löschen",
|
||||||
|
"deleteFolderMessage": "Dieser Ordner und seine Zuordnungen werden entfernt.",
|
||||||
|
"failedToDeleteFolder": "Ordner konnte nicht gelöscht werden",
|
||||||
"aboutApp": "Über die App",
|
"aboutApp": "Über die App",
|
||||||
"aboutAppSubtitle": "Conduit Informationen und Links",
|
"aboutAppSubtitle": "Conduit Informationen und Links",
|
||||||
"typeBelowToBegin": "Unten tippen, um zu beginnen",
|
"typeBelowToBegin": "Unten tippen, um zu beginnen",
|
||||||
|
|||||||
@@ -405,6 +405,12 @@
|
|||||||
"@deleteChatTitle": {"description": "Dialog title asking to confirm deletion of a chat."},
|
"@deleteChatTitle": {"description": "Dialog title asking to confirm deletion of a chat."},
|
||||||
"deleteChatMessage": "This chat will be permanently deleted.",
|
"deleteChatMessage": "This chat will be permanently deleted.",
|
||||||
"@deleteChatMessage": {"description": "Warning that deleting a chat cannot be undone."},
|
"@deleteChatMessage": {"description": "Warning that deleting a chat cannot be undone."},
|
||||||
|
"deleteFolderTitle": "Delete Folder",
|
||||||
|
"@deleteFolderTitle": {"description": "Dialog title asking to confirm deletion of a folder."},
|
||||||
|
"deleteFolderMessage": "This folder and its assignment references will be removed.",
|
||||||
|
"@deleteFolderMessage": {"description": "Warning that deleting a folder will remove it and its associations."},
|
||||||
|
"failedToDeleteFolder": "Failed to delete folder",
|
||||||
|
"@failedToDeleteFolder": {"description": "Error notice when deleting a folder fails."},
|
||||||
"aboutApp": "About App",
|
"aboutApp": "About App",
|
||||||
"@aboutApp": {"description": "Settings tile title to view app information."},
|
"@aboutApp": {"description": "Settings tile title to view app information."},
|
||||||
"aboutAppSubtitle": "Conduit information and links",
|
"aboutAppSubtitle": "Conduit information and links",
|
||||||
|
|||||||
@@ -199,7 +199,10 @@
|
|||||||
},
|
},
|
||||||
"deleteChatTitle": "Supprimer le chat",
|
"deleteChatTitle": "Supprimer le chat",
|
||||||
"deleteChatMessage": "Ce chat sera supprimé définitivement.",
|
"deleteChatMessage": "Ce chat sera supprimé définitivement.",
|
||||||
"aboutApp": "À propos de l'application",
|
"deleteFolderTitle": "Supprimer le dossier",
|
||||||
|
"deleteFolderMessage": "Ce dossier et ses associations seront supprimés.",
|
||||||
|
"failedToDeleteFolder": "Échec de la suppression du dossier",
|
||||||
|
"aboutApp": "À propos de l’application",
|
||||||
"aboutAppSubtitle": "Informations et liens Conduit",
|
"aboutAppSubtitle": "Informations et liens Conduit",
|
||||||
"typeBelowToBegin": "Saisissez ci‑dessous pour commencer",
|
"typeBelowToBegin": "Saisissez ci‑dessous pour commencer",
|
||||||
"web": "Web",
|
"web": "Web",
|
||||||
|
|||||||
@@ -199,7 +199,10 @@
|
|||||||
},
|
},
|
||||||
"deleteChatTitle": "Elimina chat",
|
"deleteChatTitle": "Elimina chat",
|
||||||
"deleteChatMessage": "Questa chat verrà eliminata definitivamente.",
|
"deleteChatMessage": "Questa chat verrà eliminata definitivamente.",
|
||||||
"aboutApp": "Informazioni sull'app",
|
"deleteFolderTitle": "Elimina cartella",
|
||||||
|
"deleteFolderMessage": "Questa cartella e le sue associazioni verranno rimosse.",
|
||||||
|
"failedToDeleteFolder": "Impossibile eliminare la cartella",
|
||||||
|
"aboutApp": "Informazioni sull’app",
|
||||||
"aboutAppSubtitle": "Informazioni e link di Conduit",
|
"aboutAppSubtitle": "Informazioni e link di Conduit",
|
||||||
"typeBelowToBegin": "Scrivi qui sotto per iniziare",
|
"typeBelowToBegin": "Scrivi qui sotto per iniziare",
|
||||||
"web": "Web",
|
"web": "Web",
|
||||||
|
|||||||
@@ -1182,6 +1182,24 @@ abstract class AppLocalizations {
|
|||||||
/// **'This chat will be permanently deleted.'**
|
/// **'This chat will be permanently deleted.'**
|
||||||
String get deleteChatMessage;
|
String get deleteChatMessage;
|
||||||
|
|
||||||
|
/// Dialog title asking to confirm deletion of a folder.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Delete Folder'**
|
||||||
|
String get deleteFolderTitle;
|
||||||
|
|
||||||
|
/// Warning that deleting a folder will remove it and its associations.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'This folder and its assignment references will be removed.'**
|
||||||
|
String get deleteFolderMessage;
|
||||||
|
|
||||||
|
/// Error notice when deleting a folder fails.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Failed to delete folder'**
|
||||||
|
String get failedToDeleteFolder;
|
||||||
|
|
||||||
/// Settings tile title to view app information.
|
/// Settings tile title to view app information.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -600,6 +600,16 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get deleteChatMessage => 'Dieser Chat wird dauerhaft gelöscht.';
|
String get deleteChatMessage => 'Dieser Chat wird dauerhaft gelöscht.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteFolderTitle => 'Ordner löschen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteFolderMessage =>
|
||||||
|
'Dieser Ordner und seine Zuordnungen werden entfernt.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get failedToDeleteFolder => 'Ordner konnte nicht gelöscht werden';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get aboutApp => 'Über die App';
|
String get aboutApp => 'Über die App';
|
||||||
|
|
||||||
|
|||||||
@@ -595,6 +595,16 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get deleteChatMessage => 'This chat will be permanently deleted.';
|
String get deleteChatMessage => 'This chat will be permanently deleted.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteFolderTitle => 'Delete Folder';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteFolderMessage =>
|
||||||
|
'This folder and its assignment references will be removed.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get failedToDeleteFolder => 'Failed to delete folder';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get aboutApp => 'About App';
|
String get aboutApp => 'About App';
|
||||||
|
|
||||||
|
|||||||
@@ -605,6 +605,17 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get deleteChatMessage => 'Ce chat sera supprimé définitivement.';
|
String get deleteChatMessage => 'Ce chat sera supprimé définitivement.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteFolderTitle => 'Supprimer le dossier';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteFolderMessage =>
|
||||||
|
'Ce dossier et ses associations seront supprimés.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get failedToDeleteFolder =>
|
||||||
|
'Échec de la suppression du dossier';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get aboutApp => 'À propos de l\'application';
|
String get aboutApp => 'À propos de l\'application';
|
||||||
|
|
||||||
|
|||||||
@@ -598,6 +598,17 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
String get deleteChatMessage =>
|
String get deleteChatMessage =>
|
||||||
'Questa chat verrà eliminata definitivamente.';
|
'Questa chat verrà eliminata definitivamente.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteFolderTitle => 'Elimina cartella';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteFolderMessage =>
|
||||||
|
'Questa cartella e le sue associazioni verranno rimosse.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get failedToDeleteFolder =>
|
||||||
|
'Impossibile eliminare la cartella';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get aboutApp => 'Informazioni sull\'app';
|
String get aboutApp => 'Informazioni sull\'app';
|
||||||
|
|
||||||
|
|||||||
@@ -115,54 +115,7 @@ class UiUtils {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows a Conduit-styled confirmation dialog
|
// Confirmation dialog moved to shared ThemedDialogs.confirm for cohesion
|
||||||
static Future<bool> showConfirmationDialog(
|
|
||||||
BuildContext context, {
|
|
||||||
required String title,
|
|
||||||
required String message,
|
|
||||||
String confirmText = 'Confirm',
|
|
||||||
String cancelText = 'Cancel',
|
|
||||||
bool isDestructive = false,
|
|
||||||
}) async {
|
|
||||||
return await showDialog<bool>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
backgroundColor: context.conduitTheme.surfaceBackground,
|
|
||||||
title: Text(
|
|
||||||
title,
|
|
||||||
style: TextStyle(color: context.conduitTheme.textPrimary),
|
|
||||||
),
|
|
||||||
content: Text(
|
|
||||||
message,
|
|
||||||
style: TextStyle(color: context.conduitTheme.textSecondary),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context, false),
|
|
||||||
child: Text(
|
|
||||||
cancelText.isNotEmpty
|
|
||||||
? cancelText
|
|
||||||
: AppLocalizations.of(context)!.cancel,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context, true),
|
|
||||||
style: isDestructive
|
|
||||||
? TextButton.styleFrom(
|
|
||||||
foregroundColor: context.conduitTheme.error,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
child: Text(
|
|
||||||
confirmText.isNotEmpty
|
|
||||||
? confirmText
|
|
||||||
: AppLocalizations.of(context)!.confirm,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
) ??
|
|
||||||
false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Formats dates in a conversational way following Conduit patterns
|
/// Formats dates in a conversational way following Conduit patterns
|
||||||
static String formatDate(DateTime date) {
|
static String formatDate(DateTime date) {
|
||||||
|
|||||||
@@ -96,4 +96,108 @@ class ThemedDialogs {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cohesive text input dialog used for rename/create flows
|
||||||
|
static Future<String?> promptTextInput(
|
||||||
|
BuildContext context, {
|
||||||
|
required String title,
|
||||||
|
required String hintText,
|
||||||
|
String? initialValue,
|
||||||
|
String confirmText = 'Save',
|
||||||
|
String cancelText = 'Cancel',
|
||||||
|
bool barrierDismissible = true,
|
||||||
|
TextInputType? keyboardType,
|
||||||
|
TextCapitalization textCapitalization = TextCapitalization.sentences,
|
||||||
|
int? maxLength,
|
||||||
|
}) async {
|
||||||
|
final theme = context.conduitTheme;
|
||||||
|
final controller = TextEditingController(text: initialValue ?? '');
|
||||||
|
|
||||||
|
String? result = await showDialog<String>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: barrierDismissible,
|
||||||
|
builder: (ctx) {
|
||||||
|
return buildBase(
|
||||||
|
context: ctx,
|
||||||
|
title: title,
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: controller,
|
||||||
|
autofocus: true,
|
||||||
|
keyboardType: keyboardType,
|
||||||
|
textCapitalization: textCapitalization,
|
||||||
|
maxLength: maxLength,
|
||||||
|
style: TextStyle(color: theme.inputText),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: hintText,
|
||||||
|
hintStyle: TextStyle(color: theme.inputPlaceholder),
|
||||||
|
filled: true,
|
||||||
|
fillColor: theme.inputBackground,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||||
|
borderSide: BorderSide(color: theme.inputBorder, width: 1),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||||
|
borderSide:
|
||||||
|
BorderSide(color: theme.buttonPrimary, width: 1),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: Spacing.md,
|
||||||
|
vertical: Spacing.md,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onSubmitted: (v) {
|
||||||
|
final trimmed = v.trim();
|
||||||
|
final unchanged = (initialValue != null &&
|
||||||
|
trimmed == initialValue.trim());
|
||||||
|
if (trimmed.isEmpty || unchanged) return;
|
||||||
|
Navigator.of(ctx).pop(trimmed);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(ctx).pop(),
|
||||||
|
child: Text(
|
||||||
|
cancelText,
|
||||||
|
style: TextStyle(color: theme.textSecondary),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ValueListenableBuilder<TextEditingValue>(
|
||||||
|
valueListenable: controller,
|
||||||
|
builder: (context, value, _) {
|
||||||
|
final trimmed = value.text.trim();
|
||||||
|
final unchanged =
|
||||||
|
(initialValue != null && trimmed == initialValue.trim());
|
||||||
|
final enabled = trimmed.isNotEmpty && !unchanged;
|
||||||
|
return TextButton(
|
||||||
|
onPressed: enabled
|
||||||
|
? () => Navigator.of(ctx).pop(trimmed)
|
||||||
|
: null,
|
||||||
|
child: Text(
|
||||||
|
confirmText,
|
||||||
|
style: TextStyle(
|
||||||
|
color: enabled
|
||||||
|
? theme.buttonPrimary
|
||||||
|
: theme.textSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user