synced with last dev, added profiles for cyr2lat replacement dictionaries

This commit is contained in:
HDDen
2026-04-25 01:30:55 +03:00
parent 7a4ac9ae9b
commit f56c28a27d
45 changed files with 1547 additions and 394 deletions
+219 -37
View File
@@ -1262,6 +1262,7 @@ class AppSettingsScreen extends StatelessWidget {
BuildContext context,
AppSettingsService settingsService,
) {
final selectedProfile = settingsService.getSelectedCyr2LatProfile();
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -1273,41 +1274,109 @@ class AppSettingsScreen extends StatelessWidget {
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
ListTile(
leading: const Icon(Icons.translate),
title: Text(context.l10n.channels_cyr2latSettingsSubheading),
subtitle: Text(context.l10n.channels_cyr2latSettingsDscr),
trailing: const Icon(Icons.chevron_right),
onTap: () => _showCyr2LatDialog(context, settingsService),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: DropdownButtonFormField<String>(
initialValue: settingsService.settings.selectedCyr2latProfileId,
decoration: InputDecoration(
labelText: context.l10n.channels_cyr2latSettingsSubheading,
border: const OutlineInputBorder(),
),
items: settingsService.settings.cyr2latProfiles.map((profile) {
return DropdownMenuItem(
value: profile.id,
child: Text(profile.name),
);
}).toList(),
onChanged: (value) {
if (value != null) {
settingsService.setSelectedCyr2LatProfile(value);
}
},
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () =>
_showAddCyr2LatProfileDialog(context, settingsService),
icon: const Icon(Icons.add),
label: Text(context.l10n.common_add),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton.icon(
onPressed: () => _showEditCyr2LatProfileDialog(
context,
settingsService,
selectedProfile,
),
icon: const Icon(Icons.edit),
label: Text(context.l10n.common_edit),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton.icon(
onPressed:
settingsService.settings.cyr2latProfiles.length > 1
? () => _showDeleteCyr2LatProfileDialog(
context,
settingsService,
selectedProfile,
)
: null,
icon: const Icon(Icons.delete),
label: Text(context.l10n.common_delete),
),
),
],
),
),
],
),
);
}
void _showCyr2LatDialog(
void _showAddCyr2LatProfileDialog(
BuildContext context,
AppSettingsService settingsService,
) {
final controller = TextEditingController(
text: const JsonEncoder.withIndent(
' ',
).convert(settingsService.settings.cyr2latCharMap),
final nameController = TextEditingController();
final jsonController = TextEditingController(
text: const JsonEncoder.withIndent(' ').convert(defaultCyr2LatCharMap),
);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(context.l10n.channels_cyr2latSettingsDscr),
title: Text(context.l10n.settings_cyr2latProfileAdd),
content: SingleChildScrollView(
child: TextField(
controller: controller,
maxLines: 20,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: context.l10n.channels_cyr2latSettingsDialogHint,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: InputDecoration(
labelText: context.l10n.settings_cyr2latProfileName,
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: jsonController,
maxLines: 15,
decoration: InputDecoration(
labelText: context.l10n.channels_cyr2latSettingsDialogHint,
border: const OutlineInputBorder(),
hintText: context.l10n.channels_cyr2latSettingsDscr,
),
),
],
),
),
actions: [
@@ -1316,23 +1385,31 @@ class AppSettingsScreen extends StatelessWidget {
child: Text(context.l10n.common_cancel),
),
TextButton(
onPressed: () {
onPressed: () async {
if (nameController.text.isEmpty) {
showDismissibleSnackBar(
context,
content: Text(context.l10n.settings_cyr2latProfileNameEmpty),
);
return;
}
try {
final json =
jsonDecode(controller.text) as Map<String, dynamic>;
jsonDecode(jsonController.text) as Map<String, dynamic>;
final map = json.map(
(key, value) => MapEntry(key, value.toString()),
);
final newSettings = settingsService.settings.copyWith(
cyr2latCharMap: map,
final profile = Cyr2LatProfile(
id: DateTime.now().millisecondsSinceEpoch.toString(),
name: nameController.text,
charMap: map,
);
settingsService.updateSettings(newSettings);
await settingsService.addCyr2LatProfile(profile);
if (!context.mounted) return;
Navigator.pop(context);
showDismissibleSnackBar(
context,
content: Text(
context.l10n.channels_cyr2latSettingsDialogSuccess,
),
content: Text(context.l10n.settings_cyr2latProfileAdded),
);
} catch (e) {
showDismissibleSnackBar(
@@ -1347,21 +1424,126 @@ class AppSettingsScreen extends StatelessWidget {
},
child: Text(context.l10n.common_save),
),
],
),
);
}
void _showEditCyr2LatProfileDialog(
BuildContext context,
AppSettingsService settingsService,
Cyr2LatProfile profile,
) {
final nameController = TextEditingController(text: profile.name);
final jsonController = TextEditingController(
text: const JsonEncoder.withIndent(' ').convert(profile.charMap),
);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(context.l10n.settings_cyr2latProfileEdit),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: InputDecoration(
labelText: context.l10n.settings_cyr2latProfileName,
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: jsonController,
maxLines: 15,
decoration: InputDecoration(
labelText: context.l10n.channels_cyr2latSettingsDialogHint,
border: const OutlineInputBorder(),
hintText: context.l10n.channels_cyr2latSettingsDscr,
),
),
],
),
),
actions: [
TextButton(
onPressed: () {
final newSettings = settingsService.settings.copyWith(
cyr2latCharMap: defaultCyr2LatCharMap,
);
settingsService.updateSettings(newSettings);
onPressed: () => Navigator.pop(context),
child: Text(context.l10n.common_cancel),
),
TextButton(
onPressed: () async {
if (nameController.text.isEmpty) {
showDismissibleSnackBar(
context,
content: Text(context.l10n.settings_cyr2latProfileNameEmpty),
);
return;
}
try {
final json =
jsonDecode(jsonController.text) as Map<String, dynamic>;
final map = json.map(
(key, value) => MapEntry(key, value.toString()),
);
final updatedProfile = profile.copyWith(
name: nameController.text,
charMap: map,
);
await settingsService.updateCyr2LatProfile(updatedProfile);
if (!context.mounted) return;
Navigator.pop(context);
showDismissibleSnackBar(
context,
content: Text(context.l10n.settings_cyr2latProfileUpdated),
);
} catch (e) {
showDismissibleSnackBar(
context,
content: Text(
context.l10n.channels_cyr2latSettingsDialogWrongJSON(
e.toString(),
),
),
);
}
},
child: Text(context.l10n.common_save),
),
],
),
);
}
void _showDeleteCyr2LatProfileDialog(
BuildContext context,
AppSettingsService settingsService,
Cyr2LatProfile profile,
) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(context.l10n.settings_cyr2latProfileDelete),
content: Text(
context.l10n.settings_cyr2latProfileDeleteDscr(profile.name),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(context.l10n.common_cancel),
),
TextButton(
onPressed: () async {
await settingsService.removeCyr2LatProfile(profile.id);
if (!context.mounted) return;
Navigator.pop(context);
showDismissibleSnackBar(
context,
content: Text(
context.l10n.channels_cyr2latSettingsDialogResetted,
),
content: Text(context.l10n.settings_cyr2latProfileDeleted),
);
},
child: Text(context.l10n.channels_cyr2latSettingsDialogReset),
child: Text(context.l10n.common_delete),
),
],
),
+36
View File
@@ -1401,10 +1401,17 @@ class _ChannelsScreenState extends State<ChannelsScreen>
MeshCoreConnector connector,
Channel channel,
) {
final appSettingsService = Provider.of<AppSettingsService>(
context,
listen: false,
);
final nameController = TextEditingController(text: channel.name);
final pskController = TextEditingController(text: channel.pskHex);
bool smazEnabled = connector.isChannelSmazEnabled(channel.index);
bool cyr2latEnabled = connector.isChannelCyr2LatEnabled(channel.index);
String? selectedCyr2LatProfileId = connector.getChannelCyr2LatProfileId(
channel.index,
);
showDialog(
context: context,
@@ -1471,6 +1478,31 @@ class _ChannelsScreenState extends State<ChannelsScreen>
}
}),
),
if (cyr2latEnabled) ...[
Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 8),
child: DropdownButtonFormField<String>(
initialValue: selectedCyr2LatProfileId,
decoration: InputDecoration(
labelText: dialogContext
.l10n
.channels_cyr2latSettingsSubheading,
border: const OutlineInputBorder(),
),
items: appSettingsService.settings.cyr2latProfiles.map((
profile,
) {
return DropdownMenuItem(
value: profile.id,
child: Text(profile.name),
);
}).toList(),
onChanged: (value) => setState(() {
selectedCyr2LatProfileId = value;
}),
),
),
],
],
),
),
@@ -1506,6 +1538,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
channel.index,
cyr2latEnabled,
);
await connector.setChannelCyr2LatProfileId(
channel.index,
selectedCyr2LatProfileId,
);
if (!context.mounted) return;
showDismissibleSnackBar(
context,
+37
View File
@@ -1211,6 +1211,10 @@ class _ChatScreenState extends State<ChatScreen> {
void _showContactSettings(BuildContext context) {
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
final appSettingsService = Provider.of<AppSettingsService>(
context,
listen: false,
);
connector.ensureContactSmazSettingLoaded(widget.contact.publicKeyHex);
connector.ensureContactCyr2LatSettingLoaded(widget.contact.publicKeyHex);
final contact = widget.contact;
@@ -1218,6 +1222,9 @@ class _ChatScreenState extends State<ChatScreen> {
bool cyr2latEnabled = connector.isContactCyr2LatEnabled(
contact.publicKeyHex,
);
String? selectedCyr2LatProfileId = connector.getContactCyr2LatProfileId(
contact.publicKeyHex,
);
bool teleBaseEnabled = contact.teleBaseEnabled;
bool teleLocEnabled = contact.teleLocEnabled;
bool teleEnvEnabled = contact.teleEnvEnabled;
@@ -1283,6 +1290,36 @@ class _ChatScreenState extends State<ChatScreen> {
});
},
),
if (cyr2latEnabled) ...[
Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 8),
child: DropdownButtonFormField<String>(
initialValue: selectedCyr2LatProfileId,
decoration: InputDecoration(
labelText:
context.l10n.channels_cyr2latSettingsSubheading,
border: const OutlineInputBorder(),
),
items: appSettingsService.settings.cyr2latProfiles.map((
profile,
) {
return DropdownMenuItem(
value: profile.id,
child: Text(profile.name),
);
}).toList(),
onChanged: (value) {
connector.setContactCyr2LatProfileId(
contact.publicKeyHex,
value,
);
setDialogState(() {
selectedCyr2LatProfileId = value;
});
},
),
),
],
const Divider(height: 8),
SwitchListTile(
contentPadding: EdgeInsets.zero,