diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 50125c3..da306e4 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -2780,6 +2780,10 @@ "invalidAsciiCharacters": "Seuls les caractères ASCII sont autorisés", "channelCreatedSuccessfully": "Canal créé avec succès", "channelCreationFailed": "Échec de la création du canal : {error}", + "deleteChannel": "Supprimer le Canal", + "deleteChannelConfirmation": "Êtes-vous sûr de vouloir supprimer le canal \"{channelName}\" ? Cette action ne peut pas être annulée.", + "channelDeletedSuccessfully": "Canal supprimé avec succès", + "channelDeletionFailed": "Échec de la suppression du canal : {error}", "allChannelSlotsInUse": "Tous les emplacements de canaux sont utilisés (maximum 39 canaux personnalisés)", "createChannel": "Créer un Canal", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 2fa29b8..ba6b934 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -2780,6 +2780,10 @@ "invalidAsciiCharacters": "Sono consentiti solo caratteri ASCII", "channelCreatedSuccessfully": "Canale creato con successo", "channelCreationFailed": "Creazione del canale fallita: {error}", + "deleteChannel": "Elimina Canale", + "deleteChannelConfirmation": "Sei sicuro di voler eliminare il canale \"{channelName}\"? Questa azione non può essere annullata.", + "channelDeletedSuccessfully": "Canale eliminato con successo", + "channelDeletionFailed": "Eliminazione del canale fallita: {error}", "allChannelSlotsInUse": "Tutti gli slot dei canali sono in uso (massimo 39 canali personalizzati)", "createChannel": "Crea Canale", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 2527c3c..3a05c73 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -3777,6 +3777,30 @@ abstract class AppLocalizations { /// **'Failed to create channel: {error}'** String channelCreationFailed(String error); + /// Delete channel button/menu item + /// + /// In en, this message translates to: + /// **'Delete Channel'** + String get deleteChannel; + + /// Confirmation dialog when deleting a channel + /// + /// In en, this message translates to: + /// **'Are you sure you want to delete channel \"{channelName}\"? This action cannot be undone.'** + String deleteChannelConfirmation(String channelName); + + /// Success message after deleting channel + /// + /// In en, this message translates to: + /// **'Channel deleted successfully'** + String get channelDeletedSuccessfully; + + /// Error message when channel deletion fails + /// + /// In en, this message translates to: + /// **'Failed to delete channel: {error}'** + String channelDeletionFailed(String error); + /// Error when no channel slots available /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 4a3a902..70a76b5 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -2109,6 +2109,22 @@ class AppLocalizationsDe extends AppLocalizations { return 'Kanal konnte nicht erstellt werden: $error'; } + @override + String get deleteChannel => 'Kanal löschen'; + + @override + String deleteChannelConfirmation(String channelName) { + return 'Sind Sie sicher, dass Sie den Kanal \"$channelName\" löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.'; + } + + @override + String get channelDeletedSuccessfully => 'Kanal erfolgreich gelöscht'; + + @override + String channelDeletionFailed(String error) { + return 'Kanal konnte nicht gelöscht werden: $error'; + } + @override String get allChannelSlotsInUse => 'Alle Kanalplätze sind belegt (maximal 39 benutzerdefinierte Kanäle)'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index d213296..f6d488a 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -2088,6 +2088,22 @@ class AppLocalizationsEn extends AppLocalizations { return 'Failed to create channel: $error'; } + @override + String get deleteChannel => 'Delete Channel'; + + @override + String deleteChannelConfirmation(String channelName) { + return 'Are you sure you want to delete channel \"$channelName\"? This action cannot be undone.'; + } + + @override + String get channelDeletedSuccessfully => 'Channel deleted successfully'; + + @override + String channelDeletionFailed(String error) { + return 'Failed to delete channel: $error'; + } + @override String get allChannelSlotsInUse => 'All channel slots are in use (maximum 39 custom channels)'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 1cd1d41..4749470 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -2114,6 +2114,22 @@ class AppLocalizationsEs extends AppLocalizations { return 'Error al crear el canal: $error'; } + @override + String get deleteChannel => 'Eliminar Canal'; + + @override + String deleteChannelConfirmation(String channelName) { + return '¿Está seguro de que desea eliminar el canal \"$channelName\"? Esta acción no se puede deshacer.'; + } + + @override + String get channelDeletedSuccessfully => 'Canal eliminado exitosamente'; + + @override + String channelDeletionFailed(String error) { + return 'Error al eliminar el canal: $error'; + } + @override String get allChannelSlotsInUse => 'Todos los espacios de canales están en uso (máximo 39 canales personalizados)'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index d4ea9f2..012e7ff 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -2119,6 +2119,22 @@ class AppLocalizationsFr extends AppLocalizations { return 'Échec de la création du canal : $error'; } + @override + String get deleteChannel => 'Supprimer le Canal'; + + @override + String deleteChannelConfirmation(String channelName) { + return 'Êtes-vous sûr de vouloir supprimer le canal \"$channelName\" ? Cette action ne peut pas être annulée.'; + } + + @override + String get channelDeletedSuccessfully => 'Canal supprimé avec succès'; + + @override + String channelDeletionFailed(String error) { + return 'Échec de la suppression du canal : $error'; + } + @override String get allChannelSlotsInUse => 'Tous les emplacements de canaux sont utilisés (maximum 39 canaux personnalisés)'; diff --git a/lib/l10n/app_localizations_hr.dart b/lib/l10n/app_localizations_hr.dart index 88b4b44..071e291 100644 --- a/lib/l10n/app_localizations_hr.dart +++ b/lib/l10n/app_localizations_hr.dart @@ -2098,6 +2098,22 @@ class AppLocalizationsHr extends AppLocalizations { return 'Neuspješno kreiranje kanala: $error'; } + @override + String get deleteChannel => 'Izbriši kanal'; + + @override + String deleteChannelConfirmation(String channelName) { + return 'Jeste li sigurni da želite izbrisati kanal \"$channelName\"? Ova radnja se ne može poništiti.'; + } + + @override + String get channelDeletedSuccessfully => 'Kanal uspješno izbrisan'; + + @override + String channelDeletionFailed(String error) { + return 'Neuspješno brisanje kanala: $error'; + } + @override String get allChannelSlotsInUse => 'Svi slotovi kanala su zauzeti (maksimalno 39 prilagođenih kanala)'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index ca323b3..75db2fb 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -2109,6 +2109,22 @@ class AppLocalizationsIt extends AppLocalizations { return 'Creazione del canale fallita: $error'; } + @override + String get deleteChannel => 'Elimina Canale'; + + @override + String deleteChannelConfirmation(String channelName) { + return 'Sei sicuro di voler eliminare il canale \"$channelName\"? Questa azione non può essere annullata.'; + } + + @override + String get channelDeletedSuccessfully => 'Canale eliminato con successo'; + + @override + String channelDeletionFailed(String error) { + return 'Eliminazione del canale fallita: $error'; + } + @override String get allChannelSlotsInUse => 'Tutti gli slot dei canali sono in uso (massimo 39 canali personalizzati)'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index acd7386..b5ba728 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -2097,6 +2097,22 @@ class AppLocalizationsSl extends AppLocalizations { return 'Neuspešno ustvarjanje kanala: $error'; } + @override + String get deleteChannel => 'Izbriši kanal'; + + @override + String deleteChannelConfirmation(String channelName) { + return 'Ali ste prepričani, da želite izbrisati kanal \"$channelName\"? Tega dejanja ni mogoče razveljaviti.'; + } + + @override + String get channelDeletedSuccessfully => 'Kanal uspešno izbrisan'; + + @override + String channelDeletionFailed(String error) { + return 'Neuspešno brisanje kanala: $error'; + } + @override String get allChannelSlotsInUse => 'Vsa mesta za kanale so zasedena (maksimalno 39 prilagojenih kanalov)'; diff --git a/lib/screens/messages_tab.dart b/lib/screens/messages_tab.dart index 4c626f4..8ba95d3 100644 --- a/lib/screens/messages_tab.dart +++ b/lib/screens/messages_tab.dart @@ -183,6 +183,9 @@ class _MessagesTabState extends State { final rooms = contactsProvider.contacts .where((c) => c.type == ContactType.room) .toList(); + final channels = contactsProvider.contacts + .where((c) => c.type == ContactType.channel) + .toList(); showModalBottomSheet( context: context, @@ -191,6 +194,7 @@ class _MessagesTabState extends State { builder: (context) => RecipientSelectorSheet( contacts: contacts, rooms: rooms, + channels: channels, currentDestinationType: _destinationType, currentRecipientPublicKey: _selectedRecipient?.publicKeyHex, onSelect: _onRecipientSelected, @@ -200,12 +204,6 @@ class _MessagesTabState extends State { /// Handle recipient selection Future _onRecipientSelected(String type, Contact? recipient) async { - // Get display name before async gap - final recipientName = - type == MessageDestinationPreferences.destinationTypeChannel - ? AppLocalizations.of(context)!.publicChannel - : (recipient?.displayName ?? recipient?.advName ?? 'Unknown'); - setState(() { _destinationType = type; _selectedRecipient = recipient; @@ -238,8 +236,9 @@ class _MessagesTabState extends State { String _getDestinationTooltip() { final l10n = AppLocalizations.of(context)!; if (_destinationType == - MessageDestinationPreferences.destinationTypeChannel) { - return '${l10n.publicChannel} (tap to change)'; + MessageDestinationPreferences.destinationTypeChannel && _selectedRecipient != null) { + final channelName = _selectedRecipient!.getLocalizedDisplayName(context); + return '$channelName (tap to change)'; } else if (_selectedRecipient != null) { final recipientName = _selectedRecipient!.displayName ?? _selectedRecipient!.advName; @@ -266,8 +265,9 @@ class _MessagesTabState extends State { // Check destination type and send accordingly if (_destinationType == MessageDestinationPreferences.destinationTypeChannel) { - // Send to public channel - await _sendToChannel(text, connectionProvider, messagesProvider); + // Send to selected channel (or public channel if none selected) + final channelIdx = _selectedRecipient?.publicKey[1] ?? 0; // Extract channel index from pseudo public key + await _sendToChannel(text, connectionProvider, messagesProvider, channelIdx); } else if (_selectedRecipient != null) { // Send to contact or room await _sendToRecipient( @@ -279,9 +279,9 @@ class _MessagesTabState extends State { } else { // Fallback to public channel if no recipient selected debugPrint( - '⚠️ [MessagesTab] No recipient selected, falling back to channel', + '⚠️ [MessagesTab] No recipient selected, falling back to public channel', ); - await _sendToChannel(text, connectionProvider, messagesProvider); + await _sendToChannel(text, connectionProvider, messagesProvider, 0); } _textController.clear(); @@ -294,11 +294,12 @@ class _MessagesTabState extends State { } } - /// Send message to public channel + /// Send message to channel Future _sendToChannel( String text, ConnectionProvider connectionProvider, MessagesProvider messagesProvider, + int channelIdx, ) async { // Create message ID final messageId = '${DateTime.now().millisecondsSinceEpoch}_channel_sent'; @@ -319,15 +320,15 @@ class _MessagesTabState extends State { text: text, receivedAt: DateTime.now(), deliveryStatus: MessageDeliveryStatus.sending, - channelIdx: 0, + channelIdx: channelIdx, ); // Add to messages list with "sending" status messagesProvider.addSentMessage(sentMessage); - // Send to public channel (channel 0) + // Send to selected channel await connectionProvider.sendChannelMessage( - channelIdx: 0, + channelIdx: channelIdx, text: text, messageId: messageId, ); diff --git a/lib/widgets/messages/recipient_selector_sheet.dart b/lib/widgets/messages/recipient_selector_sheet.dart index 0e5e51c..9059363 100644 --- a/lib/widgets/messages/recipient_selector_sheet.dart +++ b/lib/widgets/messages/recipient_selector_sheet.dart @@ -6,6 +6,7 @@ import '../../l10n/app_localizations.dart'; class RecipientSelectorSheet extends StatefulWidget { final List contacts; final List rooms; + final List channels; final String? currentDestinationType; final String? currentRecipientPublicKey; final Function(String type, Contact? recipient) onSelect; @@ -14,6 +15,7 @@ class RecipientSelectorSheet extends StatefulWidget { super.key, required this.contacts, required this.rooms, + required this.channels, this.currentDestinationType, this.currentRecipientPublicKey, required this.onSelect, @@ -45,7 +47,6 @@ class _RecipientSelectorSheetState extends State { bool _isSelected(String type, Contact? contact) { if (widget.currentDestinationType != type) return false; - if (type == 'channel') return true; if (contact == null) return false; return contact.publicKeyHex == widget.currentRecipientPublicKey; } @@ -55,6 +56,7 @@ class _RecipientSelectorSheetState extends State { final l10n = AppLocalizations.of(context)!; final filteredContacts = _filterContacts(widget.contacts); final filteredRooms = _filterContacts(widget.rooms); + final filteredChannels = _filterContacts(widget.channels); return Container( constraints: BoxConstraints( @@ -136,18 +138,50 @@ class _RecipientSelectorSheetState extends State { child: ListView( shrinkWrap: true, children: [ - // Public Channel option - _buildRecipientTile( - context: context, - icon: Icons.public, - title: l10n.publicChannel, - subtitle: l10n.broadcastToAllNearby, - isSelected: _isSelected('channel', null), - onTap: () { - widget.onSelect('channel', null); - Navigator.pop(context); - }, - ), + // Channels section + if (widget.channels.isNotEmpty) ...[ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + child: Text( + l10n.channels, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + ), + ), + ), + if (filteredChannels.isEmpty) + Padding( + padding: const EdgeInsets.all(16), + child: Text( + l10n.noChannelsFound, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).disabledColor, + fontStyle: FontStyle.italic, + ), + textAlign: TextAlign.center, + ), + ) + else + ...filteredChannels.map((channel) { + return _buildRecipientTile( + context: context, + icon: Icons.public, + title: channel.getLocalizedDisplayName(context), + subtitle: channel.isPublicChannel + ? l10n.broadcastToAllNearby + : '${l10n.channel} ${channel.publicKey[1]}', // Show slot number + isSelected: _isSelected('channel', channel), + onTap: () { + widget.onSelect('channel', channel); + Navigator.pop(context); + }, + ); + }), + ], const Divider(), @@ -242,7 +276,7 @@ class _RecipientSelectorSheetState extends State { ], // Empty state - if (widget.contacts.isEmpty && widget.rooms.isEmpty) ...[ + if (widget.contacts.isEmpty && widget.rooms.isEmpty && widget.channels.isEmpty) ...[ Padding( padding: const EdgeInsets.all(32), child: Column( @@ -254,22 +288,13 @@ class _RecipientSelectorSheetState extends State { ), const SizedBox(height: 16), Text( - l10n.noContactsOrRoomsAvailable, + l10n.noRecipientsAvailable, style: Theme.of(context).textTheme.bodyLarge ?.copyWith( color: Theme.of(context).disabledColor, ), textAlign: TextAlign.center, ), - const SizedBox(height: 8), - Text( - l10n.messagesWillBeSentToPublicChannel, - style: Theme.of(context).textTheme.bodySmall - ?.copyWith( - color: Theme.of(context).disabledColor, - ), - textAlign: TextAlign.center, - ), ], ), ),