mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-06-14 22:55:12 +10:00
feat: Add community management features with QR code scanning
- Implement Community model for managing community data, including secret handling and PSK derivation. - Create CommunityQrScannerScreen for scanning and joining communities via QR codes. - Develop CommunityStore for persisting community data using SharedPreferences. - Introduce QrCodeDisplay widget for displaying QR codes with customizable options. - Add QrScannerWidget for reusable QR code scanning functionality with validation and controls.
This commit is contained in:
@@ -16,6 +16,9 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<!-- Camera permission for QR code scanning -->
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||
|
||||
<application
|
||||
android:label="meshcore_open"
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 579 KiB |
@@ -53,5 +53,7 @@
|
||||
<string>This app uses Bluetooth to communicate with MeshCore devices.</string>
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
<string>This app uses Bluetooth to communicate with MeshCore devices.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app uses the camera to scan QR codes for joining communities.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1615,6 +1615,10 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
await sendFrame(buildSetChannelFrame(index, '', Uint8List(16)));
|
||||
_channelLastReadMs.remove(index);
|
||||
_unreadStore.saveChannelLastRead(Map<int, int>.from(_channelLastReadMs));
|
||||
// Clear stored messages for this channel
|
||||
await _channelMessageStore.clearChannelMessages(index);
|
||||
// Clear in-memory messages for this channel
|
||||
_channelMessages.remove(index);
|
||||
// Refresh channels after deleting
|
||||
await getChannels();
|
||||
}
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationGPSEnableSubtitle": "Активирайте автоматичното актуализиране на местоположението чрез GPS.",
|
||||
"settings_locationIntervalInvalid": "Интервалът трябва да бъде поне 60 секунди и по-малко от 86400 секунди.",
|
||||
"room_management": "Управление на сървъра за стая",
|
||||
"contacts_manageRoom": "Управление на сървър за стая"
|
||||
"contacts_manageRoom": "Управление на сървър за стая",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"community_title": "Общност",
|
||||
"common_ok": "Добре",
|
||||
"community_createDesc": "Създайте нова общност и я споделете чрез QR код.",
|
||||
"community_create": "Създай общност",
|
||||
"community_joinTitle": "Присъедини се към общността",
|
||||
"community_joinConfirmation": "Искате ли да се присъедините към общността \"{name}\"?",
|
||||
"community_scanQr": "Сканирайте QR кода на общността",
|
||||
"community_scanInstructions": "Насочете камерата към QR код на общността",
|
||||
"community_showQr": "Покажи QR код",
|
||||
"community_publicChannel": "Обществено общност",
|
||||
"community_hashtagChannel": "Хаштаг на общността",
|
||||
"community_name": "Име на общността",
|
||||
"community_enterName": "Въведете име на общността",
|
||||
"community_created": "Общността \"{name}\" е създадена",
|
||||
"community_joined": "Присъединено общност \"{name}\"",
|
||||
"community_qrTitle": "Споделяне в общността",
|
||||
"community_join": "Присъедини се",
|
||||
"community_qrInstructions": "Сканирайте този QR код, за да се присъедините към {name}.",
|
||||
"community_hashtagPrivacyHint": "Хаштаг каналите на общността са достъпни само за членове на общността",
|
||||
"community_invalidQrCode": "Невалиден QR код на общността",
|
||||
"community_alreadyMember": "Вече съм член",
|
||||
"community_alreadyMemberMessage": "Вие вече сте член на \"{name}\".",
|
||||
"community_addPublicChannel": "Добави публичен общностен канал",
|
||||
"community_addPublicChannelHint": "Автоматично добавете публичния канал за тази общност.",
|
||||
"community_noCommunities": "Няма присъединени общности още.",
|
||||
"community_scanOrCreate": "Сканирайте QR код или създайте общност, за да започнете.",
|
||||
"community_manageCommunities": "Управление на общности",
|
||||
"community_delete": "Напусни общността",
|
||||
"community_deleteConfirm": "Напускате \"{name}\"?",
|
||||
"community_deleteChannelsWarning": "Това ще изтрие също {count} канал(а) и техните съобщения.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Остави общността \"{name}\"",
|
||||
"community_addHashtagChannel": "Добави общностен хаштаг",
|
||||
"community_addHashtagChannelDesc": "Добавете хаштаг канал за тази общност",
|
||||
"community_selectCommunity": "Изберете общност",
|
||||
"community_regularHashtag": "Обикновен хаштаг",
|
||||
"community_regularHashtagDesc": "Общ хаштаг (всеки може да се присъедини)",
|
||||
"community_communityHashtag": "Общностен хаштаг",
|
||||
"community_communityHashtagDesc": "Само за членове на общността",
|
||||
"community_forCommunity": "За {name}"
|
||||
}
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalSec": "Intervall für GPS (Sekunden)",
|
||||
"settings_locationIntervalInvalid": "Das Intervall muss mindestens 60 Sekunden und weniger als 86400 Sekunden betragen.",
|
||||
"contacts_manageRoom": "Raum-Server verwalten",
|
||||
"room_management": "Raum-Server-Verwaltung"
|
||||
"room_management": "Raum-Server-Verwaltung",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"common_ok": "OK",
|
||||
"community_create": "Erstelle Community",
|
||||
"community_createDesc": "Erstelle eine neue Community und teile sie über den QR-Code.",
|
||||
"community_join": "Beitreten",
|
||||
"community_joinTitle": "Tritt der Community bei",
|
||||
"community_joinConfirmation": "Möchten Sie sich der Community \"{name}\" anschließen?",
|
||||
"community_scanQr": "Scannen Sie die Community QR-Code",
|
||||
"community_scanInstructions": "Richten Sie die Kamera auf einen Community-QR-Code.",
|
||||
"community_showQr": "Zeige QR-Code",
|
||||
"community_publicChannel": "Community Öffentlich",
|
||||
"community_enterName": "Bitte Community-Name eingeben",
|
||||
"community_title": "Community",
|
||||
"community_created": "Community \"{name}\" wurde erstellt",
|
||||
"community_joined": "Community \"{name}\" beigetreten",
|
||||
"community_qrTitle": "Teile Community",
|
||||
"community_qrInstructions": "Scannen Sie diesen QR-Code, um sich \"{name}\" anzuschließen.",
|
||||
"community_hashtagPrivacyHint": "Community-Hashtag-Kanäle können nur von Mitgliedern der Community betreten werden",
|
||||
"community_hashtagChannel": "Community Hashtag",
|
||||
"community_name": "Community Name",
|
||||
"community_invalidQrCode": "Ungültiger Community-QR-Code",
|
||||
"community_alreadyMember": "Bereits registriert",
|
||||
"community_alreadyMemberMessage": "Sie sind bereits Mitglied von \"{name}\".",
|
||||
"community_addPublicChannel": "Füge einen öffentlichen Community-Kanal hinzu",
|
||||
"community_addPublicChannelHint": "Automatisch den öffentlichen Kanal für diese Community hinzufügen",
|
||||
"community_noCommunities": "Noch keiner Community beigetreten",
|
||||
"community_scanOrCreate": "Scannen Sie einen QR-Code oder eine Community erstellen, um loszulegen.",
|
||||
"community_manageCommunities": "Verwalten von Communities",
|
||||
"community_delete": "Verlasse Community",
|
||||
"community_deleteConfirm": "\"{name}\" verlassen?",
|
||||
"community_deleteChannelsWarning": "Dies löscht auch {count} Kanal/Kanäle und deren Nachrichten.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Community \"{name}\" verlassen",
|
||||
"community_addHashtagChannel": "Füge einen Community-Hashtag hinzu",
|
||||
"community_addHashtagChannelDesc": "Füge einen Hashtag-Kanal für diese Community hinzu",
|
||||
"community_selectCommunity": "Wählen Sie Community",
|
||||
"community_regularHashtag": "Regulärer Hashtag",
|
||||
"community_regularHashtagDesc": "Öffentliches Hashtag (jeder kann teilnehmen)",
|
||||
"community_communityHashtagDesc": "Nur für Mitglieder der Community",
|
||||
"community_forCommunity": "Für {name}",
|
||||
"community_communityHashtag": "Community Hashtag"
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"nav_map": "Map",
|
||||
|
||||
"common_cancel": "Cancel",
|
||||
"common_ok": "OK",
|
||||
"common_connect": "Connect",
|
||||
"common_unknownDevice": "Unknown Device",
|
||||
"common_save": "Save",
|
||||
@@ -1174,6 +1175,118 @@
|
||||
},
|
||||
"channelPath_noHopDetailsAvailable": "No hop details available for this packet.",
|
||||
"channelPath_unknownRepeater": "Unknown Repeater",
|
||||
|
||||
"community_title": "Community",
|
||||
"community_create": "Create Community",
|
||||
"community_createDesc": "Create a new community and share via QR code.",
|
||||
"community_join": "Join",
|
||||
"community_joinTitle": "Join Community",
|
||||
"community_joinConfirmation": "Do you want to join the community \"{name}\"?",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_scanQr": "Scan Community QR",
|
||||
"community_scanInstructions": "Point the camera at a community QR code",
|
||||
"community_showQr": "Show QR Code",
|
||||
"community_publicChannel": "Community Public",
|
||||
"community_hashtagChannel": "Community Hashtag",
|
||||
"community_name": "Community Name",
|
||||
"community_enterName": "Enter community name",
|
||||
"community_created": "Community \"{name}\" created",
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_joined": "Joined community \"{name}\"",
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_qrTitle": "Share Community",
|
||||
"community_qrInstructions": "Scan this QR code to join \"{name}\"",
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_hashtagPrivacyHint": "Community hashtag channels are only joinable by members of the community",
|
||||
"community_invalidQrCode": "Invalid community QR code",
|
||||
"community_alreadyMember": "Already a Member",
|
||||
"community_alreadyMemberMessage": "You are already a member of \"{name}\".",
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_addPublicChannel": "Add Community Public Channel",
|
||||
"community_addPublicChannelHint": "Automatically add the public channel for this community",
|
||||
"community_noCommunities": "No communities joined yet",
|
||||
"community_scanOrCreate": "Scan a QR code or create a community to get started",
|
||||
"community_manageCommunities": "Manage Communities",
|
||||
"community_delete": "Leave Community",
|
||||
"community_deleteConfirm": "Leave \"{name}\"?",
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_deleteChannelsWarning": "This will also delete {count} channel(s) and their messages.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Left community \"{name}\"",
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_regenerateSecret": "Regenerate Secret",
|
||||
"community_regenerateSecretConfirm": "Regenerate the secret key for \"{name}\"? All members will need to scan the new QR code to continue communicating.",
|
||||
"@community_regenerateSecretConfirm": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_regenerate": "Regenerate",
|
||||
"community_secretRegenerated": "Secret regenerated for \"{name}\"",
|
||||
"@community_secretRegenerated": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_updateSecret": "Update Secret",
|
||||
"community_secretUpdated": "Secret updated for \"{name}\"",
|
||||
"@community_secretUpdated": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_scanToUpdateSecret": "Scan the new QR code to update the secret for \"{name}\"",
|
||||
"@community_scanToUpdateSecret": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"community_addHashtagChannel": "Add Community Hashtag",
|
||||
"community_addHashtagChannelDesc": "Add a hashtag channel for this community",
|
||||
"community_selectCommunity": "Select Community",
|
||||
"community_regularHashtag": "Regular Hashtag",
|
||||
"community_regularHashtagDesc": "Public hashtag (anyone can join)",
|
||||
"community_communityHashtag": "Community Hashtag",
|
||||
"community_communityHashtagDesc": "Private to community members",
|
||||
"community_forCommunity": "For {name}",
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {"type": "String"}
|
||||
}
|
||||
},
|
||||
|
||||
"listFilter_tooltip": "Filter and sort",
|
||||
"listFilter_sortBy": "Sort by",
|
||||
"listFilter_latestMessages": "Latest messages",
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalSec": "Intervalo para GPS (Segundos)",
|
||||
"settings_locationIntervalInvalid": "El intervalo debe ser de al menos 60 segundos y menor que 86400 segundos.",
|
||||
"contacts_manageRoom": "Gestionar Servidor de Habitación",
|
||||
"room_management": "Administración del Servidor de Habitación"
|
||||
"room_management": "Administración del Servidor de Habitación",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"community_create": "Crear Comunidad",
|
||||
"community_createDesc": "Crear una nueva comunidad y compartir a través de código QR.",
|
||||
"community_title": "Comunidad",
|
||||
"community_join": "Únete",
|
||||
"community_joinTitle": "Únete a la comunidad",
|
||||
"community_joinConfirmation": "¿Quieres unirte a la comunidad \"{name}\"?",
|
||||
"community_scanQr": "Escanear Código QR de la Comunidad",
|
||||
"community_scanInstructions": "Apunte la cámara a un código QR de la comunidad",
|
||||
"community_showQr": "Mostrar Código QR",
|
||||
"community_publicChannel": "Comunidad Pública",
|
||||
"community_hashtagChannel": "Hashtag de la Comunidad",
|
||||
"community_name": "Nombre de la comunidad",
|
||||
"common_ok": "De acuerdo",
|
||||
"community_enterName": "Introducir nombre de comunidad",
|
||||
"community_created": "Comunidad \"{name}\" creada",
|
||||
"community_joined": "Se unió a la comunidad \"{name}\"",
|
||||
"community_qrTitle": "Compartir Comunidad",
|
||||
"community_qrInstructions": "Escanear este código QR para unirte a {name}",
|
||||
"community_hashtagPrivacyHint": "Los canales de hashtag de la comunidad solo son accesibles para los miembros de la comunidad",
|
||||
"community_invalidQrCode": "Código QR de comunidad no válido",
|
||||
"community_alreadyMember": "Ya eres Miembro",
|
||||
"community_alreadyMemberMessage": "Ya eres miembro de \"{name}\".",
|
||||
"community_addPublicChannel": "Añadir Canal Público de la Comunidad",
|
||||
"community_addPublicChannelHint": "Añade automáticamente el canal público para esta comunidad.",
|
||||
"community_noCommunities": "Aún no se han unido comunidades.",
|
||||
"community_scanOrCreate": "Escanear un código QR o crear una comunidad para comenzar",
|
||||
"community_manageCommunities": "Gestionar Comunidades",
|
||||
"community_delete": "Salir de la Comunidad",
|
||||
"community_deleteConfirm": "¿Salir de \"{name}\"?",
|
||||
"community_deleteChannelsWarning": "Esto también eliminará {count} canal(es) y sus mensajes.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Has salido de la comunidad \"{name}\"",
|
||||
"community_addHashtagChannel": "Añadir Hashtag de la Comunidad",
|
||||
"community_addHashtagChannelDesc": "Añadir un canal con hashtag para esta comunidad",
|
||||
"community_selectCommunity": "Seleccionar Comunidad",
|
||||
"community_regularHashtag": "Etiqueta de Hashtag Regular",
|
||||
"community_regularHashtagDesc": "Hashtag público (cualquiera puede unirse)",
|
||||
"community_communityHashtag": "Hashtag de la Comunidad",
|
||||
"community_communityHashtagDesc": "Exclusivo para miembros de la comunidad",
|
||||
"community_forCommunity": "Para {name}"
|
||||
}
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalSec": "Intervalo pour GPS (Segundos)",
|
||||
"settings_locationIntervalInvalid": "El intervalo debe ser de al menos 60 segundos y menor que 86400 segundos.",
|
||||
"contacts_manageRoom": "Gestionar Servidor de Habitación",
|
||||
"room_management": "Administración del Servidor de Habitación"
|
||||
"room_management": "Administración del Servidor de Habitación",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"common_ok": "OK",
|
||||
"community_title": "Communauté",
|
||||
"community_create": "Créer une Communauté",
|
||||
"community_createDesc": "Créer une nouvelle communauté et la partager via QR code.",
|
||||
"community_join": "Rejoindre",
|
||||
"community_joinTitle": "Rejoindre la communauté",
|
||||
"community_joinConfirmation": "Souhaitez-vous rejoindre la communauté \"{name}\" ?",
|
||||
"community_scanQr": "Scanner la communauté QR",
|
||||
"community_scanInstructions": "Pointez l'appareil photo vers un code QR communautaire.",
|
||||
"community_showQr": "Afficher le QR Code",
|
||||
"community_publicChannel": "Communauté Publique",
|
||||
"community_hashtagChannel": "Hashtag Communauté",
|
||||
"community_name": "Nom de la communauté",
|
||||
"community_enterName": "Entrez le nom de la communauté",
|
||||
"community_created": "Communauté \"{name}\" créée",
|
||||
"community_joined": "Rejoint la communauté \"{name}\"",
|
||||
"community_qrTitle": "Partager Communauté",
|
||||
"community_qrInstructions": "Scanner ce QR code pour rejoindre {name}",
|
||||
"community_hashtagPrivacyHint": "Les canaux hashtag de la communauté ne sont accessibles qu'aux membres de la communauté",
|
||||
"community_invalidQrCode": "Code QR de communauté non valide",
|
||||
"community_alreadyMember": "Déjà membre",
|
||||
"community_alreadyMemberMessage": "Vous êtes déjà membre de \"{name}\".",
|
||||
"community_addPublicChannel": "Ajouter un Canal Public de la Communauté",
|
||||
"community_addPublicChannelHint": "Ajouter automatiquement le canal public pour cette communauté",
|
||||
"community_noCommunities": "Aucun groupe n'a été rejoint pour le moment.",
|
||||
"community_scanOrCreate": "Scanner un code QR ou créer une communauté pour commencer",
|
||||
"community_manageCommunities": "Gérer les Communautés",
|
||||
"community_delete": "Quitter la communauté",
|
||||
"community_deleteConfirm": "Quitter \"{name}\" ?",
|
||||
"community_deleteChannelsWarning": "Cela supprimera également {count} canal/canaux et leurs messages.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Communauté \"{name}\" quittée",
|
||||
"community_addHashtagChannel": "Ajouter un Hashtag Communauté",
|
||||
"community_addHashtagChannelDesc": "Ajouter un canal hachage pour cette communauté",
|
||||
"community_selectCommunity": "Sélectionner Communauté",
|
||||
"community_regularHashtag": "Hashtag régulier",
|
||||
"community_regularHashtagDesc": "Hashtag public (tout le monde peut rejoindre)",
|
||||
"community_communityHashtag": "Hashtag de la communauté",
|
||||
"community_communityHashtagDesc": "Exclusif aux membres de la communauté",
|
||||
"community_forCommunity": "Pour {name}"
|
||||
}
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalSec": "Intervallo GPS (Secondi)",
|
||||
"settings_locationIntervalInvalid": "L'intervallo deve essere di almeno 60 secondi e inferiore a 86400 secondi.",
|
||||
"contacts_manageRoom": "Gestisci Server Camera",
|
||||
"room_management": "Gestione del Server di Camera"
|
||||
"room_management": "Gestione del Server di Camera",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"common_ok": "OK",
|
||||
"community_title": "Comunità",
|
||||
"community_create": "Crea Comunità",
|
||||
"community_createDesc": "Crea una nuova comunità e condividila tramite codice QR.",
|
||||
"community_join": "Unisciti",
|
||||
"community_joinTitle": "Unisciti alla Community",
|
||||
"community_joinConfirmation": "Vuoi unirti alla community \"{name}\"?",
|
||||
"community_scanQr": "Scansiona il QR Code della Community",
|
||||
"community_scanInstructions": "Punta la fotocamera su un codice QR della comunità",
|
||||
"community_showQr": "Mostra il codice QR",
|
||||
"community_publicChannel": "Comunità Pubblica",
|
||||
"community_hashtagChannel": "Hashtag della Comunità",
|
||||
"community_name": "Nome della Comunità",
|
||||
"community_enterName": "Inserisci il nome della comunità",
|
||||
"community_created": "Comunità \"{name}\" creata",
|
||||
"community_joined": "Unito alla comunità \"{name}\"",
|
||||
"community_qrTitle": "Condividi Comunità",
|
||||
"community_qrInstructions": "Scansiona questo codice QR per unirti a {name}",
|
||||
"community_hashtagPrivacyHint": "I canali hashtag della community sono accessibili solo ai membri della community",
|
||||
"community_invalidQrCode": "Codice QR della community non valido",
|
||||
"community_alreadyMember": "Già membro",
|
||||
"community_alreadyMemberMessage": "Sei già un membro di \"{name}\".",
|
||||
"community_addPublicChannel": "Aggiungi Canale Pubblico della Comunità",
|
||||
"community_addPublicChannelHint": "Aggiungi automaticamente il canale pubblico per questa community",
|
||||
"community_noCommunities": "Nessun gruppo aggiunto finora",
|
||||
"community_scanOrCreate": "Scansiona un codice QR o crea una community per iniziare.",
|
||||
"community_manageCommunities": "Gestisci Comunità",
|
||||
"community_delete": "Lascia la Comunità",
|
||||
"community_deleteConfirm": "Uscire da \"{name}\"?",
|
||||
"community_deleteChannelsWarning": "Questo eliminerà anche {count} canale/i e i loro messaggi.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Hai lasciato la comunità \"{name}\"",
|
||||
"community_addHashtagChannel": "Aggiungi Hashtag della Community",
|
||||
"community_addHashtagChannelDesc": "Aggiungi un canale con hashtag per questa community",
|
||||
"community_selectCommunity": "Seleziona Comunità",
|
||||
"community_regularHashtag": "Hashtag regolare",
|
||||
"community_regularHashtagDesc": "Hashtag pubblico (chiunque può unirsi)",
|
||||
"community_communityHashtag": "Hashtag della Comunità",
|
||||
"community_communityHashtagDesc": "Visibile solo ai membri della comunità",
|
||||
"community_forCommunity": "Per {name}"
|
||||
}
|
||||
|
||||
@@ -150,6 +150,12 @@ abstract class AppLocalizations {
|
||||
/// **'Cancel'**
|
||||
String get common_cancel;
|
||||
|
||||
/// No description provided for @common_ok.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'OK'**
|
||||
String get common_ok;
|
||||
|
||||
/// No description provided for @common_connect.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -4306,6 +4312,276 @@ abstract class AppLocalizations {
|
||||
/// **'Unknown Repeater'**
|
||||
String get channelPath_unknownRepeater;
|
||||
|
||||
/// No description provided for @community_title.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Community'**
|
||||
String get community_title;
|
||||
|
||||
/// No description provided for @community_create.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create Community'**
|
||||
String get community_create;
|
||||
|
||||
/// No description provided for @community_createDesc.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create a new community and share via QR code.'**
|
||||
String get community_createDesc;
|
||||
|
||||
/// No description provided for @community_join.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Join'**
|
||||
String get community_join;
|
||||
|
||||
/// No description provided for @community_joinTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Join Community'**
|
||||
String get community_joinTitle;
|
||||
|
||||
/// No description provided for @community_joinConfirmation.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Do you want to join the community \"{name}\"?'**
|
||||
String community_joinConfirmation(String name);
|
||||
|
||||
/// No description provided for @community_scanQr.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Scan Community QR'**
|
||||
String get community_scanQr;
|
||||
|
||||
/// No description provided for @community_scanInstructions.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Point the camera at a community QR code'**
|
||||
String get community_scanInstructions;
|
||||
|
||||
/// No description provided for @community_showQr.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Show QR Code'**
|
||||
String get community_showQr;
|
||||
|
||||
/// No description provided for @community_publicChannel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Community Public'**
|
||||
String get community_publicChannel;
|
||||
|
||||
/// No description provided for @community_hashtagChannel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Community Hashtag'**
|
||||
String get community_hashtagChannel;
|
||||
|
||||
/// No description provided for @community_name.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Community Name'**
|
||||
String get community_name;
|
||||
|
||||
/// No description provided for @community_enterName.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enter community name'**
|
||||
String get community_enterName;
|
||||
|
||||
/// No description provided for @community_created.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Community \"{name}\" created'**
|
||||
String community_created(String name);
|
||||
|
||||
/// No description provided for @community_joined.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Joined community \"{name}\"'**
|
||||
String community_joined(String name);
|
||||
|
||||
/// No description provided for @community_qrTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Share Community'**
|
||||
String get community_qrTitle;
|
||||
|
||||
/// No description provided for @community_qrInstructions.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Scan this QR code to join \"{name}\"'**
|
||||
String community_qrInstructions(String name);
|
||||
|
||||
/// No description provided for @community_hashtagPrivacyHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Community hashtag channels are only joinable by members of the community'**
|
||||
String get community_hashtagPrivacyHint;
|
||||
|
||||
/// No description provided for @community_invalidQrCode.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Invalid community QR code'**
|
||||
String get community_invalidQrCode;
|
||||
|
||||
/// No description provided for @community_alreadyMember.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Already a Member'**
|
||||
String get community_alreadyMember;
|
||||
|
||||
/// No description provided for @community_alreadyMemberMessage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'You are already a member of \"{name}\".'**
|
||||
String community_alreadyMemberMessage(String name);
|
||||
|
||||
/// No description provided for @community_addPublicChannel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add Community Public Channel'**
|
||||
String get community_addPublicChannel;
|
||||
|
||||
/// No description provided for @community_addPublicChannelHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Automatically add the public channel for this community'**
|
||||
String get community_addPublicChannelHint;
|
||||
|
||||
/// No description provided for @community_noCommunities.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No communities joined yet'**
|
||||
String get community_noCommunities;
|
||||
|
||||
/// No description provided for @community_scanOrCreate.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Scan a QR code or create a community to get started'**
|
||||
String get community_scanOrCreate;
|
||||
|
||||
/// No description provided for @community_manageCommunities.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Manage Communities'**
|
||||
String get community_manageCommunities;
|
||||
|
||||
/// No description provided for @community_delete.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Leave Community'**
|
||||
String get community_delete;
|
||||
|
||||
/// No description provided for @community_deleteConfirm.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Leave \"{name}\"?'**
|
||||
String community_deleteConfirm(String name);
|
||||
|
||||
/// No description provided for @community_deleteChannelsWarning.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This will also delete {count} channel(s) and their messages.'**
|
||||
String community_deleteChannelsWarning(int count);
|
||||
|
||||
/// No description provided for @community_deleted.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Left community \"{name}\"'**
|
||||
String community_deleted(String name);
|
||||
|
||||
/// No description provided for @community_regenerateSecret.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Regenerate Secret'**
|
||||
String get community_regenerateSecret;
|
||||
|
||||
/// No description provided for @community_regenerateSecretConfirm.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Regenerate the secret key for \"{name}\"? All members will need to scan the new QR code to continue communicating.'**
|
||||
String community_regenerateSecretConfirm(String name);
|
||||
|
||||
/// No description provided for @community_regenerate.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Regenerate'**
|
||||
String get community_regenerate;
|
||||
|
||||
/// No description provided for @community_secretRegenerated.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Secret regenerated for \"{name}\"'**
|
||||
String community_secretRegenerated(String name);
|
||||
|
||||
/// No description provided for @community_updateSecret.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Update Secret'**
|
||||
String get community_updateSecret;
|
||||
|
||||
/// No description provided for @community_secretUpdated.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Secret updated for \"{name}\"'**
|
||||
String community_secretUpdated(String name);
|
||||
|
||||
/// No description provided for @community_scanToUpdateSecret.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Scan the new QR code to update the secret for \"{name}\"'**
|
||||
String community_scanToUpdateSecret(String name);
|
||||
|
||||
/// No description provided for @community_addHashtagChannel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add Community Hashtag'**
|
||||
String get community_addHashtagChannel;
|
||||
|
||||
/// No description provided for @community_addHashtagChannelDesc.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add a hashtag channel for this community'**
|
||||
String get community_addHashtagChannelDesc;
|
||||
|
||||
/// No description provided for @community_selectCommunity.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select Community'**
|
||||
String get community_selectCommunity;
|
||||
|
||||
/// No description provided for @community_regularHashtag.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Regular Hashtag'**
|
||||
String get community_regularHashtag;
|
||||
|
||||
/// No description provided for @community_regularHashtagDesc.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Public hashtag (anyone can join)'**
|
||||
String get community_regularHashtagDesc;
|
||||
|
||||
/// No description provided for @community_communityHashtag.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Community Hashtag'**
|
||||
String get community_communityHashtag;
|
||||
|
||||
/// No description provided for @community_communityHashtagDesc.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Private to community members'**
|
||||
String get community_communityHashtagDesc;
|
||||
|
||||
/// No description provided for @community_forCommunity.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'For {name}'**
|
||||
String community_forCommunity(String name);
|
||||
|
||||
/// No description provided for @listFilter_tooltip.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsBg extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Отказ';
|
||||
|
||||
@override
|
||||
String get common_ok => 'Добре';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Свържи се';
|
||||
|
||||
@@ -2452,6 +2455,174 @@ class AppLocalizationsBg extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Неизвестен повторител';
|
||||
|
||||
@override
|
||||
String get community_title => 'Общност';
|
||||
|
||||
@override
|
||||
String get community_create => 'Създай общност';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Създайте нова общност и я споделете чрез QR код.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Присъедини се';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Присъедини се към общността';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Искате ли да се присъедините към общността \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Сканирайте QR кода на общността';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Насочете камерата към QR код на общността';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Покажи QR код';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Обществено общност';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Хаштаг на общността';
|
||||
|
||||
@override
|
||||
String get community_name => 'Име на общността';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Въведете име на общността';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Общността \"$name\" е създадена';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Присъединено общност \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Споделяне в общността';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Сканирайте този QR код, за да се присъедините към $name.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Хаштаг каналите на общността са достъпни само за членове на общността';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Невалиден QR код на общността';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Вече съм член';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'Вие вече сте член на \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel => 'Добави публичен общностен канал';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Автоматично добавете публичния канал за тази общност.';
|
||||
|
||||
@override
|
||||
String get community_noCommunities => 'Няма присъединени общности още.';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Сканирайте QR код или създайте общност, за да започнете.';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Управление на общности';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Напусни общността';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return 'Напускате \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'Това ще изтрие също $count канал(а) и техните съобщения.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Остави общността \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Добави общностен хаштаг';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Добавете хаштаг канал за тази общност';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Изберете общност';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Обикновен хаштаг';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'Общ хаштаг (всеки може да се присъедини)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Общностен хаштаг';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc => 'Само за членове на общността';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'За $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Филтрирайте и сортирайте';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Abbrechen';
|
||||
|
||||
@override
|
||||
String get common_ok => 'OK';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Verbinden';
|
||||
|
||||
@@ -2454,6 +2457,177 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Unbekannter Repeater';
|
||||
|
||||
@override
|
||||
String get community_title => 'Community';
|
||||
|
||||
@override
|
||||
String get community_create => 'Erstelle Community';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Erstelle eine neue Community und teile sie über den QR-Code.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Beitreten';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Tritt der Community bei';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Möchten Sie sich der Community \"$name\" anschließen?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Scannen Sie die Community QR-Code';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Richten Sie die Kamera auf einen Community-QR-Code.';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Zeige QR-Code';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Community Öffentlich';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Community Hashtag';
|
||||
|
||||
@override
|
||||
String get community_name => 'Community Name';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Bitte Community-Name eingeben';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Community \"$name\" wurde erstellt';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Community \"$name\" beigetreten';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Teile Community';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Scannen Sie diesen QR-Code, um sich \"$name\" anzuschließen.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Community-Hashtag-Kanäle können nur von Mitgliedern der Community betreten werden';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Ungültiger Community-QR-Code';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Bereits registriert';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'Sie sind bereits Mitglied von \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel =>
|
||||
'Füge einen öffentlichen Community-Kanal hinzu';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Automatisch den öffentlichen Kanal für diese Community hinzufügen';
|
||||
|
||||
@override
|
||||
String get community_noCommunities => 'Noch keiner Community beigetreten';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Scannen Sie einen QR-Code oder eine Community erstellen, um loszulegen.';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Verwalten von Communities';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Verlasse Community';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return '\"$name\" verlassen?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'Dies löscht auch $count Kanal/Kanäle und deren Nachrichten.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Community \"$name\" verlassen';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel =>
|
||||
'Füge einen Community-Hashtag hinzu';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Füge einen Hashtag-Kanal für diese Community hinzu';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Wählen Sie Community';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Regulärer Hashtag';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'Öffentliches Hashtag (jeder kann teilnehmen)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Community Hashtag';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc =>
|
||||
'Nur für Mitglieder der Community';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'Für $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filteren und sortieren';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Cancel';
|
||||
|
||||
@override
|
||||
String get common_ok => 'OK';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Connect';
|
||||
|
||||
@@ -2413,6 +2416,173 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Unknown Repeater';
|
||||
|
||||
@override
|
||||
String get community_title => 'Community';
|
||||
|
||||
@override
|
||||
String get community_create => 'Create Community';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Create a new community and share via QR code.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Join';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Join Community';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Do you want to join the community \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Scan Community QR';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Point the camera at a community QR code';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Show QR Code';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Community Public';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Community Hashtag';
|
||||
|
||||
@override
|
||||
String get community_name => 'Community Name';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Enter community name';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Community \"$name\" created';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Joined community \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Share Community';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Scan this QR code to join \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Community hashtag channels are only joinable by members of the community';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Invalid community QR code';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Already a Member';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'You are already a member of \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel => 'Add Community Public Channel';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Automatically add the public channel for this community';
|
||||
|
||||
@override
|
||||
String get community_noCommunities => 'No communities joined yet';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Scan a QR code or create a community to get started';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Manage Communities';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Leave Community';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return 'Leave \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'This will also delete $count channel(s) and their messages.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Left community \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Add Community Hashtag';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Add a hashtag channel for this community';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Select Community';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Regular Hashtag';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc => 'Public hashtag (anyone can join)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Community Hashtag';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc => 'Private to community members';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'For $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filter and sort';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Cancelar';
|
||||
|
||||
@override
|
||||
String get common_ok => 'De acuerdo';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Conectar';
|
||||
|
||||
@@ -2449,6 +2452,176 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Repetidor Desconocido';
|
||||
|
||||
@override
|
||||
String get community_title => 'Comunidad';
|
||||
|
||||
@override
|
||||
String get community_create => 'Crear Comunidad';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Crear una nueva comunidad y compartir a través de código QR.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Únete';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Únete a la comunidad';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return '¿Quieres unirte a la comunidad \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Escanear Código QR de la Comunidad';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Apunte la cámara a un código QR de la comunidad';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Mostrar Código QR';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Comunidad Pública';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Hashtag de la Comunidad';
|
||||
|
||||
@override
|
||||
String get community_name => 'Nombre de la comunidad';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Introducir nombre de comunidad';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Comunidad \"$name\" creada';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Se unió a la comunidad \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Compartir Comunidad';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Escanear este código QR para unirte a $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Los canales de hashtag de la comunidad solo son accesibles para los miembros de la comunidad';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Código QR de comunidad no válido';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Ya eres Miembro';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'Ya eres miembro de \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel =>
|
||||
'Añadir Canal Público de la Comunidad';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Añade automáticamente el canal público para esta comunidad.';
|
||||
|
||||
@override
|
||||
String get community_noCommunities => 'Aún no se han unido comunidades.';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Escanear un código QR o crear una comunidad para comenzar';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Gestionar Comunidades';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Salir de la Comunidad';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return '¿Salir de \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'Esto también eliminará $count canal(es) y sus mensajes.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Has salido de la comunidad \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Añadir Hashtag de la Comunidad';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Añadir un canal con hashtag para esta comunidad';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Seleccionar Comunidad';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Etiqueta de Hashtag Regular';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'Hashtag público (cualquiera puede unirse)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Hashtag de la Comunidad';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc =>
|
||||
'Exclusivo para miembros de la comunidad';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'Para $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filtrar y ordenar';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Annuler';
|
||||
|
||||
@override
|
||||
String get common_ok => 'OK';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Connecter';
|
||||
|
||||
@@ -2464,6 +2467,177 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Répéteur Inconnu';
|
||||
|
||||
@override
|
||||
String get community_title => 'Communauté';
|
||||
|
||||
@override
|
||||
String get community_create => 'Créer une Communauté';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Créer une nouvelle communauté et la partager via QR code.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Rejoindre';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Rejoindre la communauté';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Souhaitez-vous rejoindre la communauté \"$name\" ?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Scanner la communauté QR';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Pointez l\'appareil photo vers un code QR communautaire.';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Afficher le QR Code';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Communauté Publique';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Hashtag Communauté';
|
||||
|
||||
@override
|
||||
String get community_name => 'Nom de la communauté';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Entrez le nom de la communauté';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Communauté \"$name\" créée';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Rejoint la communauté \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Partager Communauté';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Scanner ce QR code pour rejoindre $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Les canaux hashtag de la communauté ne sont accessibles qu\'aux membres de la communauté';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Code QR de communauté non valide';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Déjà membre';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'Vous êtes déjà membre de \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel =>
|
||||
'Ajouter un Canal Public de la Communauté';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Ajouter automatiquement le canal public pour cette communauté';
|
||||
|
||||
@override
|
||||
String get community_noCommunities =>
|
||||
'Aucun groupe n\'a été rejoint pour le moment.';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Scanner un code QR ou créer une communauté pour commencer';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Gérer les Communautés';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Quitter la communauté';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return 'Quitter \"$name\" ?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'Cela supprimera également $count canal/canaux et leurs messages.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Communauté \"$name\" quittée';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Ajouter un Hashtag Communauté';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Ajouter un canal hachage pour cette communauté';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Sélectionner Communauté';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Hashtag régulier';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'Hashtag public (tout le monde peut rejoindre)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Hashtag de la communauté';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc =>
|
||||
'Exclusif aux membres de la communauté';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'Pour $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filtrer et trier';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsIt extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Annulla';
|
||||
|
||||
@override
|
||||
String get common_ok => 'OK';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Connetti';
|
||||
|
||||
@@ -2449,6 +2452,176 @@ class AppLocalizationsIt extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Ripetitore sconosciuto';
|
||||
|
||||
@override
|
||||
String get community_title => 'Comunità';
|
||||
|
||||
@override
|
||||
String get community_create => 'Crea Comunità';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Crea una nuova comunità e condividila tramite codice QR.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Unisciti';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Unisciti alla Community';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Vuoi unirti alla community \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Scansiona il QR Code della Community';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Punta la fotocamera su un codice QR della comunità';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Mostra il codice QR';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Comunità Pubblica';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Hashtag della Comunità';
|
||||
|
||||
@override
|
||||
String get community_name => 'Nome della Comunità';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Inserisci il nome della comunità';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Comunità \"$name\" creata';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Unito alla comunità \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Condividi Comunità';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Scansiona questo codice QR per unirti a $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'I canali hashtag della community sono accessibili solo ai membri della community';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Codice QR della community non valido';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Già membro';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'Sei già un membro di \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel =>
|
||||
'Aggiungi Canale Pubblico della Comunità';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Aggiungi automaticamente il canale pubblico per questa community';
|
||||
|
||||
@override
|
||||
String get community_noCommunities => 'Nessun gruppo aggiunto finora';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Scansiona un codice QR o crea una community per iniziare.';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Gestisci Comunità';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Lascia la Comunità';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return 'Uscire da \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'Questo eliminerà anche $count canale/i e i loro messaggi.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Hai lasciato la comunità \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Aggiungi Hashtag della Community';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Aggiungi un canale con hashtag per questa community';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Seleziona Comunità';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Hashtag regolare';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'Hashtag pubblico (chiunque può unirsi)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Hashtag della Comunità';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc =>
|
||||
'Visibile solo ai membri della comunità';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'Per $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filtra e ordina';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Annuleren';
|
||||
|
||||
@override
|
||||
String get common_ok => 'OK';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Verbinden';
|
||||
|
||||
@@ -2439,6 +2442,177 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Onbekend Repeater';
|
||||
|
||||
@override
|
||||
String get community_title => 'Gemeenschap';
|
||||
|
||||
@override
|
||||
String get community_create => 'Maak Gemeenschap';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Maak een nieuwe community en deel deze via QR-code.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Sluit aan';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Worden lid van de community';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Wil je je aansluiten bij de community \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Scan Gemeenschap QR';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Richt de camera op een gemeenschappelijke QR-code';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Toon QR-code';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Gemeenschap Openbaar';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Gemeenschappelijk Hashtag';
|
||||
|
||||
@override
|
||||
String get community_name => 'Gemeenschapnaam';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Voer de gemeenschapsnaam in';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Gemeenschap \"$name\" is aangemaakt';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Gevonden in de community \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Deel Gemeenschap';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Scan deze QR-code om je aan te sluiten bij $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Community hashtag-kanalen zijn alleen toegankelijk voor leden van de community';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Ongeldige community QR-code';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Alleen al lid';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'U bent al lid van \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel =>
|
||||
'Voeg een Openbaar Gemeenschapskanaal toe';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Automatisch de publieke kanaal toevoegen voor deze community';
|
||||
|
||||
@override
|
||||
String get community_noCommunities =>
|
||||
'Nog geen gemeenschappen zijn bijgesloten.';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Scan een QR-code of een community aanmaken om te beginnen';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Beheer Gemeenschappen';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Laat Gemeenschap';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return '\"$name\" verlaten?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'Dit verwijdert ook $count kanaal/kanalen en hun berichten.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Community \"$name\" verlaten';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Voeg Community Hashtag toe';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Voeg een hashtag-kanaal toe aan deze community';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Selecteer Gemeenschap';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Gewone Hashtag';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'Open hashtag (iedereen kan deelnemen)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Gemeenschappelijk Hashtag';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc =>
|
||||
'Alleen zichtbaar voor leden van de community';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'Voor $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filteren en sorteren';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsPl extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Anuluj';
|
||||
|
||||
@override
|
||||
String get common_ok => 'OK';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Połącz';
|
||||
|
||||
@@ -2448,6 +2451,176 @@ class AppLocalizationsPl extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Nieznany Powtarzacz';
|
||||
|
||||
@override
|
||||
String get community_title => 'Społeczność';
|
||||
|
||||
@override
|
||||
String get community_create => 'Utwórz Społeczność';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Utwórz nową społeczność i udostępnij za pomocą kodu QR.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Dołącz';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Dołącz do społeczności';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Czy chcesz dołączyć do społeczności \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Skanuj QR kod społeczności';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Skieruj kamerę w kierunku kodu QR społeczności.';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Pokaż kod QR';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Społeczność Publiczna';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Hashtag Społeczności';
|
||||
|
||||
@override
|
||||
String get community_name => 'Nazwa Społeczności';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Wprowadź nazwę społeczności';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Społeczność \"$name\" została utworzona';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Dołączył do społeczności \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Dziel się Społecznością';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Skanuj ten kod QR, aby dołączyć $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Kanały hashtagowe społeczności są dostępne tylko dla członków społeczności';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Nieprawidłowy kod QR społeczności.';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Już jesteś członkiem.';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'Jesteś już członkiem \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel => 'Dodaj Kanał Publiczny Społeczności';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Automatycznie dodaj kanał publiczny dla tej społeczności.';
|
||||
|
||||
@override
|
||||
String get community_noCommunities =>
|
||||
'Nie dołączono jeszcze żadnych społeczności.';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Skanuj kod QR lub utwórz społeczność, aby zacząć.';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Zarządzaj Grupami';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Opuszczenie Społeczności';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return 'Opuścić \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'Spowoduje to również usunięcie $count kanału/kanałów i ich wiadomości.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Opuszczono społeczność \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Dodaj hashtag społeczności';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Dodaj kanał z hashtagiem dla tej społeczności';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Wybierz społeczność';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Hashtag regular';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'Publiczny hashtag (każdy może dołączyć)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Hashtag Społeczności';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc =>
|
||||
'Dostępne tylko dla członków społeczności';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'Dla $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filtruj i sortuj';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Cancelar';
|
||||
|
||||
@override
|
||||
String get common_ok => 'OK';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Conectar';
|
||||
|
||||
@@ -2450,6 +2453,177 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Repetidor Desconhecido';
|
||||
|
||||
@override
|
||||
String get community_title => 'Comunidade';
|
||||
|
||||
@override
|
||||
String get community_create => 'Criar Comunidade';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Crie uma nova comunidade e compartilhe via código QR.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Junte-se';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Junte-se à Comunidade';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Você gostaria de se juntar à comunidade \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Digitalizar a QR Code da Comunidade';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Aponte a câmera para um código QR da comunidade';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Mostrar Código QR';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Comunidade Pública';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Hashtag da Comunidade';
|
||||
|
||||
@override
|
||||
String get community_name => 'Nome da Comunidade';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Insira o nome da comunidade';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Comunidade \"$name\" criada';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Juntou-se à comunidade \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Partilhar Comunidade';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Escanear este código QR para juntar-se a $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Os canais de hashtag da comunidade só podem ser acessados por membros da comunidade';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Código QR da comunidade inválido';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Já é Membro';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'Você já é membro de \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel =>
|
||||
'Adicionar Canal Público da Comunidade';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Adicionar automaticamente o canal público para esta comunidade';
|
||||
|
||||
@override
|
||||
String get community_noCommunities =>
|
||||
'Ainda não foram adicionadas comunidades.';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Escaneie um código QR ou crie uma comunidade para começar.';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Gerenciar Comunidades';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Deixar Comunidade';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return 'Sair de \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'Isso também excluirá $count canal/canais e suas mensagens.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Saiu da comunidade \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Adicionar Hashtag da Comunidade';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Adicionar um canal de hashtag para esta comunidade';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Selecione Comunidade';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Hashtag Regular';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'Hashtag público (qualquer pessoa pode participar)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Hashtag da Comunidade';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc =>
|
||||
'Apenas para membros da comunidade';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'Para $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filtrar e ordenar';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsSk extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Zrušiť';
|
||||
|
||||
@override
|
||||
String get common_ok => 'OK\nDobre';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Pripojiť';
|
||||
|
||||
@@ -2437,6 +2440,175 @@ class AppLocalizationsSk extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Neznáme opakovače';
|
||||
|
||||
@override
|
||||
String get community_title => 'Komunita';
|
||||
|
||||
@override
|
||||
String get community_create => 'Vytvoriť komunitu';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Vytvorte novú komunitu a zdieľajte cez QR kód.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Pripojiť';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Pripojiť sa k spoločenstvu';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Chceš sa pridať do komunity \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Skontrolujte komunitný QR kód';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Zamerte kameru na komunitný QR kód.';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Zobraziť QR kód';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Komunita verejná';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Komunitný Hashtag';
|
||||
|
||||
@override
|
||||
String get community_name => 'Komunita';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Zadajte názov komunity';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Komunita \"$name\" vytvorená';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Pripojená komunita \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Zdieľť komunitu';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Skenejte tento QR kód, aby ste sa pripojili k $name.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Hashtagové kanály komunity sú prístupné len členom komunity';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Neplatná QR kód komunity.';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Už ste členom.';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'Vy ste už členom \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel => 'Pridať verejný komunikačný kanál';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Automaticky prida verejný kanál pre túto komunitu.';
|
||||
|
||||
@override
|
||||
String get community_noCommunities =>
|
||||
'Zatiaľ ste sa nepripojili k žiadnej komunite';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Skene QR kód alebo vytvor komunitu na začiatok.';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Spravovať komunity';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Nechajte komunitu';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return 'Opustiť \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'Tým sa tiež vymaže $count kanál/kanálov a ich správy.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Opustená komunita \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Pridať komunitný hashtag';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Pridajte hashtagový kanál pre túto komunitu.';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Vyberte komunitu';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Zvyčajný hashtag';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'Veľký hashtag (ktočokoľvek sa môže pridať)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Komunitný Hashtag';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc => 'Špecifické pre členov komunity';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'Pre $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filtrovať a triediť';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Prekliči';
|
||||
|
||||
@override
|
||||
String get common_ok => 'V redu';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Poveži se';
|
||||
|
||||
@@ -2442,6 +2445,175 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Nepoznati ponovitelj';
|
||||
|
||||
@override
|
||||
String get community_title => 'Skupnost';
|
||||
|
||||
@override
|
||||
String get community_create => 'Ustvari skupnost';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Ustvari novo skupnost in jo deli preko QR kode.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Pridružiti se';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Pridružite se skupnosti';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Želiš se pridružiti skupnosti \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Skeniraj QR kode skupnosti';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Nasmerite kamero s skupnostnim QR kodom.';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Pokaži QR kodo';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Skupnostna javna';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Skupnostni hashtag';
|
||||
|
||||
@override
|
||||
String get community_name => 'Komunitarne ime';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Vnesite ime skupnosti';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Skupnost \"$name\" je bila ustvarila.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Prilojen k skupnosti \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Delite skupnost';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Skenirajte to QR kodo za vključitev $name.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Hashtag kanali skupnosti so dostopni samo članom skupnosti';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Neveljaven QR koden skupnosti';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Že član';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'Kljub temu ste že član/ka $name.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel => 'Dodaj Objavni Kanal Komunitarja';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Samodejno dodaj javni kanal za to skupnost.';
|
||||
|
||||
@override
|
||||
String get community_noCommunities => 'Še nobena skupnost se ni pridružila.';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Skenirajte QR kodo ali ustvarite skupnost za začetek.';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Upravljajte skupnosti';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Opusti skupnost';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return 'Zapustiti \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'To bo izbrisalo tudi $count kanal/kanalov in njihova sporočila.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Zapustil skupnost \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Dodaj Oznako Obštnine';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Dodajte hashtag kanal za to skupnost.';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Izberi skupnost';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Oznaka s hashtagom';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'javna oznaka (kateri koli lahko sodelujejo)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Skupnostni hashtag';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc =>
|
||||
'Izključeno za uporabnike skupnosti';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'Za $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filtri in vrstiči';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => 'Avbryt';
|
||||
|
||||
@override
|
||||
String get common_ok => 'Okej';
|
||||
|
||||
@override
|
||||
String get common_connect => 'Anslut';
|
||||
|
||||
@@ -2425,6 +2428,175 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => 'Okänt Upprepare';
|
||||
|
||||
@override
|
||||
String get community_title => 'Gemenskap';
|
||||
|
||||
@override
|
||||
String get community_create => 'Skapa Gemenskap';
|
||||
|
||||
@override
|
||||
String get community_createDesc =>
|
||||
'Skapa en ny gemenskap och dela via QR-kod.';
|
||||
|
||||
@override
|
||||
String get community_join => 'Gå med';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => 'Gå med i gemenskapen';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return 'Vill du gå med i communityn \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => 'Skanna Gemenskapens QR';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions =>
|
||||
'Rikta kameran mot en QR-kod i communityn';
|
||||
|
||||
@override
|
||||
String get community_showQr => 'Visa QR-kod';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => 'Föreningens Offentliga';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => 'Community Hashtag';
|
||||
|
||||
@override
|
||||
String get community_name => 'Gemenskapens namn';
|
||||
|
||||
@override
|
||||
String get community_enterName => 'Ange communities namn';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return 'Community \"$name\" har skapats';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return 'Medlem i communityn \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => 'Dela Gemenskap';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return 'Skanna denna QR-kod för att gå med i \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint =>
|
||||
'Community-hashtagkanaler kan endast nås av medlemmar i communityn';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => 'Ogiltig community QR-kod';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => 'Är redan medlem';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return 'Du är redan medlem av \"$name\".';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel =>
|
||||
'Lägg till Gemenskapskanal (Offentlig)';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint =>
|
||||
'Lägg automatiskt till den offentliga kanalen för denna community';
|
||||
|
||||
@override
|
||||
String get community_noCommunities => 'Inga gemenskaper har anslutats ännu';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate =>
|
||||
'Skanna en QR-kod eller skapa en community för att komma igång';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => 'Hantera Gemenskaper';
|
||||
|
||||
@override
|
||||
String get community_delete => 'Lämna Gemenskap';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return 'Lämna \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return 'Detta kommer också att radera $count kanal/kanaler och deras meddelanden.';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return 'Lämnade community \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => 'Lägg till Gemenskapens Hashtag';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc =>
|
||||
'Lägg till en hashtag-kanal för denna community';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => 'Välj Gemenskap';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => 'Vanlig Hash Tag';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc =>
|
||||
'Offentlig hashtag (alla kan gå med)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => 'Community Hashtag';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc => 'Endast för medlemmar';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return 'För $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => 'Filtrera och sortera';
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
@override
|
||||
String get common_cancel => '取消';
|
||||
|
||||
@override
|
||||
String get common_ok => '好的';
|
||||
|
||||
@override
|
||||
String get common_connect => '连接';
|
||||
|
||||
@@ -2315,6 +2318,167 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
@override
|
||||
String get channelPath_unknownRepeater => '未知重复器';
|
||||
|
||||
@override
|
||||
String get community_title => '社区';
|
||||
|
||||
@override
|
||||
String get community_create => '创建社区';
|
||||
|
||||
@override
|
||||
String get community_createDesc => '创建新的社区并可通过二维码分享。';
|
||||
|
||||
@override
|
||||
String get community_join => '加入';
|
||||
|
||||
@override
|
||||
String get community_joinTitle => '加入社区';
|
||||
|
||||
@override
|
||||
String community_joinConfirmation(String name) {
|
||||
return '您想加入社区 \"$name\" 吗?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_scanQr => '扫描社区二维码';
|
||||
|
||||
@override
|
||||
String get community_scanInstructions => '将相机对准社区二维码';
|
||||
|
||||
@override
|
||||
String get community_showQr => '显示二维码';
|
||||
|
||||
@override
|
||||
String get community_publicChannel => '社区公开';
|
||||
|
||||
@override
|
||||
String get community_hashtagChannel => '社区标签';
|
||||
|
||||
@override
|
||||
String get community_name => '社区名称';
|
||||
|
||||
@override
|
||||
String get community_enterName => '请输入社区名称';
|
||||
|
||||
@override
|
||||
String community_created(String name) {
|
||||
return '社区“$name”已创建';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_joined(String name) {
|
||||
return '加入社区 \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_qrTitle => '分享社区';
|
||||
|
||||
@override
|
||||
String community_qrInstructions(String name) {
|
||||
return '扫描此二维码加入$name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_hashtagPrivacyHint => '社区标签频道仅社区成员可加入';
|
||||
|
||||
@override
|
||||
String get community_invalidQrCode => '无效的社区二维码';
|
||||
|
||||
@override
|
||||
String get community_alreadyMember => '已经是会员了';
|
||||
|
||||
@override
|
||||
String community_alreadyMemberMessage(String name) {
|
||||
return '您已经是 \"$name\" 的会员。';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addPublicChannel => '添加社区公共频道';
|
||||
|
||||
@override
|
||||
String get community_addPublicChannelHint => '自动添加该社区的公共频道';
|
||||
|
||||
@override
|
||||
String get community_noCommunities => '尚未加入任何社区';
|
||||
|
||||
@override
|
||||
String get community_scanOrCreate => '扫描二维码或创建社区开始';
|
||||
|
||||
@override
|
||||
String get community_manageCommunities => '管理社群';
|
||||
|
||||
@override
|
||||
String get community_delete => '退出社区';
|
||||
|
||||
@override
|
||||
String community_deleteConfirm(String name) {
|
||||
return '退出 \"$name\"?';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleteChannelsWarning(int count) {
|
||||
return '这也将删除 $count 个频道及其消息。';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_deleted(String name) {
|
||||
return '已退出社区 \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerateSecret => 'Regenerate Secret';
|
||||
|
||||
@override
|
||||
String community_regenerateSecretConfirm(String name) {
|
||||
return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_regenerate => 'Regenerate';
|
||||
|
||||
@override
|
||||
String community_secretRegenerated(String name) {
|
||||
return 'Secret regenerated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_updateSecret => 'Update Secret';
|
||||
|
||||
@override
|
||||
String community_secretUpdated(String name) {
|
||||
return 'Secret updated for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String community_scanToUpdateSecret(String name) {
|
||||
return 'Scan the new QR code to update the secret for \"$name\"';
|
||||
}
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannel => '添加社区标签';
|
||||
|
||||
@override
|
||||
String get community_addHashtagChannelDesc => '添加一个话题频道给此社区';
|
||||
|
||||
@override
|
||||
String get community_selectCommunity => '选择社区';
|
||||
|
||||
@override
|
||||
String get community_regularHashtag => '常规话题标签';
|
||||
|
||||
@override
|
||||
String get community_regularHashtagDesc => '公共话题(任何人都可以加入)';
|
||||
|
||||
@override
|
||||
String get community_communityHashtag => '社区标签';
|
||||
|
||||
@override
|
||||
String get community_communityHashtagDesc => '仅限社区成员使用';
|
||||
|
||||
@override
|
||||
String community_forCommunity(String name) {
|
||||
return '对于 $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get listFilter_tooltip => '筛选和排序';
|
||||
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalSec": "Interval voor GPS (Seconden)",
|
||||
"settings_locationIntervalInvalid": "De intervallen moeten minstens 60 seconden zijn en minder dan 86400 seconden.",
|
||||
"contacts_manageRoom": "Beheer Ruimte Server",
|
||||
"room_management": "Beheer Server Kamer"
|
||||
"room_management": "Beheer Server Kamer",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"community_title": "Gemeenschap",
|
||||
"common_ok": "OK",
|
||||
"community_createDesc": "Maak een nieuwe community en deel deze via QR-code.",
|
||||
"community_create": "Maak Gemeenschap",
|
||||
"community_join": "Sluit aan",
|
||||
"community_joinTitle": "Worden lid van de community",
|
||||
"community_joinConfirmation": "Wil je je aansluiten bij de community \"{name}\"?",
|
||||
"community_scanQr": "Scan Gemeenschap QR",
|
||||
"community_scanInstructions": "Richt de camera op een gemeenschappelijke QR-code",
|
||||
"community_showQr": "Toon QR-code",
|
||||
"community_publicChannel": "Gemeenschap Openbaar",
|
||||
"community_hashtagChannel": "Gemeenschappelijk Hashtag",
|
||||
"community_name": "Gemeenschapnaam",
|
||||
"community_enterName": "Voer de gemeenschapsnaam in",
|
||||
"community_created": "Gemeenschap \"{name}\" is aangemaakt",
|
||||
"community_joined": "Gevonden in de community \"{name}\"",
|
||||
"community_qrTitle": "Deel Gemeenschap",
|
||||
"community_qrInstructions": "Scan deze QR-code om je aan te sluiten bij {name}",
|
||||
"community_hashtagPrivacyHint": "Community hashtag-kanalen zijn alleen toegankelijk voor leden van de community",
|
||||
"community_invalidQrCode": "Ongeldige community QR-code",
|
||||
"community_alreadyMember": "Alleen al lid",
|
||||
"community_alreadyMemberMessage": "U bent al lid van \"{name}\".",
|
||||
"community_addPublicChannel": "Voeg een Openbaar Gemeenschapskanaal toe",
|
||||
"community_addPublicChannelHint": "Automatisch de publieke kanaal toevoegen voor deze community",
|
||||
"community_noCommunities": "Nog geen gemeenschappen zijn bijgesloten.",
|
||||
"community_scanOrCreate": "Scan een QR-code of een community aanmaken om te beginnen",
|
||||
"community_manageCommunities": "Beheer Gemeenschappen",
|
||||
"community_delete": "Laat Gemeenschap",
|
||||
"community_deleteConfirm": "\"{name}\" verlaten?",
|
||||
"community_deleteChannelsWarning": "Dit verwijdert ook {count} kanaal/kanalen en hun berichten.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Community \"{name}\" verlaten",
|
||||
"community_addHashtagChannel": "Voeg Community Hashtag toe",
|
||||
"community_addHashtagChannelDesc": "Voeg een hashtag-kanaal toe aan deze community",
|
||||
"community_selectCommunity": "Selecteer Gemeenschap",
|
||||
"community_regularHashtag": "Gewone Hashtag",
|
||||
"community_regularHashtagDesc": "Open hashtag (iedereen kan deelnemen)",
|
||||
"community_communityHashtag": "Gemeenschappelijk Hashtag",
|
||||
"community_communityHashtagDesc": "Alleen zichtbaar voor leden van de community",
|
||||
"community_forCommunity": "Voor {name}"
|
||||
}
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalSec": "Interwał dla GPS (Sekundy)",
|
||||
"settings_locationIntervalInvalid": "Interwał musi wynosić co najmniej 60 sekund i mniej niż 86400 sekund.",
|
||||
"contacts_manageRoom": "Zarządzaj Serwerem Pokoju",
|
||||
"room_management": "Zarządzanie Serwerem Pokoju"
|
||||
"room_management": "Zarządzanie Serwerem Pokoju",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"community_createDesc": "Utwórz nową społeczność i udostępnij za pomocą kodu QR.",
|
||||
"community_title": "Społeczność",
|
||||
"community_create": "Utwórz Społeczność",
|
||||
"common_ok": "OK",
|
||||
"community_join": "Dołącz",
|
||||
"community_joinTitle": "Dołącz do społeczności",
|
||||
"community_joinConfirmation": "Czy chcesz dołączyć do społeczności \"{name}\"?",
|
||||
"community_scanQr": "Skanuj QR kod społeczności",
|
||||
"community_scanInstructions": "Skieruj kamerę w kierunku kodu QR społeczności.",
|
||||
"community_showQr": "Pokaż kod QR",
|
||||
"community_publicChannel": "Społeczność Publiczna",
|
||||
"community_hashtagChannel": "Hashtag Społeczności",
|
||||
"community_name": "Nazwa Społeczności",
|
||||
"community_enterName": "Wprowadź nazwę społeczności",
|
||||
"community_created": "Społeczność \"{name}\" została utworzona",
|
||||
"community_joined": "Dołączył do społeczności \"{name}\"",
|
||||
"community_qrTitle": "Dziel się Społecznością",
|
||||
"community_qrInstructions": "Skanuj ten kod QR, aby dołączyć {name}",
|
||||
"community_hashtagPrivacyHint": "Kanały hashtagowe społeczności są dostępne tylko dla członków społeczności",
|
||||
"community_invalidQrCode": "Nieprawidłowy kod QR społeczności.",
|
||||
"community_alreadyMember": "Już jesteś członkiem.",
|
||||
"community_alreadyMemberMessage": "Jesteś już członkiem \"{name}\".",
|
||||
"community_addPublicChannel": "Dodaj Kanał Publiczny Społeczności",
|
||||
"community_addPublicChannelHint": "Automatycznie dodaj kanał publiczny dla tej społeczności.",
|
||||
"community_noCommunities": "Nie dołączono jeszcze żadnych społeczności.",
|
||||
"community_scanOrCreate": "Skanuj kod QR lub utwórz społeczność, aby zacząć.",
|
||||
"community_manageCommunities": "Zarządzaj Grupami",
|
||||
"community_delete": "Opuszczenie Społeczności",
|
||||
"community_deleteConfirm": "Opuścić \"{name}\"?",
|
||||
"community_deleteChannelsWarning": "Spowoduje to również usunięcie {count} kanału/kanałów i ich wiadomości.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Opuszczono społeczność \"{name}\"",
|
||||
"community_addHashtagChannel": "Dodaj hashtag społeczności",
|
||||
"community_addHashtagChannelDesc": "Dodaj kanał z hashtagiem dla tej społeczności",
|
||||
"community_selectCommunity": "Wybierz społeczność",
|
||||
"community_regularHashtag": "Hashtag regular",
|
||||
"community_regularHashtagDesc": "Publiczny hashtag (każdy może dołączyć)",
|
||||
"community_communityHashtag": "Hashtag Społeczności",
|
||||
"community_communityHashtagDesc": "Dostępne tylko dla członków społeczności",
|
||||
"community_forCommunity": "Dla {name}"
|
||||
}
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalInvalid": "O intervalo deve ser de pelo menos 60 segundos e inferior a 86400 segundos.",
|
||||
"settings_locationIntervalSec": "Intervalo para GPS (Segundos)",
|
||||
"contacts_manageRoom": "Gerenciar Servidor de Sala",
|
||||
"room_management": "Gerenciamento de Servidor de Sala"
|
||||
"room_management": "Gerenciamento de Servidor de Sala",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"community_title": "Comunidade",
|
||||
"community_createDesc": "Crie uma nova comunidade e compartilhe via código QR.",
|
||||
"common_ok": "OK",
|
||||
"community_create": "Criar Comunidade",
|
||||
"community_join": "Junte-se",
|
||||
"community_joinTitle": "Junte-se à Comunidade",
|
||||
"community_joinConfirmation": "Você gostaria de se juntar à comunidade \"{name}\"?",
|
||||
"community_scanQr": "Digitalizar a QR Code da Comunidade",
|
||||
"community_scanInstructions": "Aponte a câmera para um código QR da comunidade",
|
||||
"community_showQr": "Mostrar Código QR",
|
||||
"community_publicChannel": "Comunidade Pública",
|
||||
"community_hashtagChannel": "Hashtag da Comunidade",
|
||||
"community_name": "Nome da Comunidade",
|
||||
"community_enterName": "Insira o nome da comunidade",
|
||||
"community_created": "Comunidade \"{name}\" criada",
|
||||
"community_joined": "Juntou-se à comunidade \"{name}\"",
|
||||
"community_qrTitle": "Partilhar Comunidade",
|
||||
"community_qrInstructions": "Escanear este código QR para juntar-se a {name}",
|
||||
"community_hashtagPrivacyHint": "Os canais de hashtag da comunidade só podem ser acessados por membros da comunidade",
|
||||
"community_invalidQrCode": "Código QR da comunidade inválido",
|
||||
"community_alreadyMember": "Já é Membro",
|
||||
"community_alreadyMemberMessage": "Você já é membro de \"{name}\".",
|
||||
"community_addPublicChannel": "Adicionar Canal Público da Comunidade",
|
||||
"community_addPublicChannelHint": "Adicionar automaticamente o canal público para esta comunidade",
|
||||
"community_noCommunities": "Ainda não foram adicionadas comunidades.",
|
||||
"community_scanOrCreate": "Escaneie um código QR ou crie uma comunidade para começar.",
|
||||
"community_manageCommunities": "Gerenciar Comunidades",
|
||||
"community_delete": "Deixar Comunidade",
|
||||
"community_deleteConfirm": "Sair de \"{name}\"?",
|
||||
"community_deleteChannelsWarning": "Isso também excluirá {count} canal/canais e suas mensagens.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Saiu da comunidade \"{name}\"",
|
||||
"community_addHashtagChannel": "Adicionar Hashtag da Comunidade",
|
||||
"community_addHashtagChannelDesc": "Adicionar um canal de hashtag para esta comunidade",
|
||||
"community_selectCommunity": "Selecione Comunidade",
|
||||
"community_regularHashtag": "Hashtag Regular",
|
||||
"community_regularHashtagDesc": "Hashtag público (qualquer pessoa pode participar)",
|
||||
"community_communityHashtag": "Hashtag da Comunidade",
|
||||
"community_communityHashtagDesc": "Apenas para membros da comunidade",
|
||||
"community_forCommunity": "Para {name}"
|
||||
}
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalSec": "Interval pre GPS (Sekundy)",
|
||||
"settings_locationIntervalInvalid": "Interval musí byť aspoň 60 sekúnd a menej ako 86400 sekúnd.",
|
||||
"contacts_manageRoom": "Spravovať server miestnosti",
|
||||
"room_management": "Správa servera miestnosti"
|
||||
"room_management": "Správa servera miestnosti",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"community_create": "Vytvoriť komunitu",
|
||||
"community_title": "Komunita",
|
||||
"community_createDesc": "Vytvorte novú komunitu a zdieľajte cez QR kód.",
|
||||
"community_join": "Pripojiť",
|
||||
"community_joinTitle": "Pripojiť sa k spoločenstvu",
|
||||
"community_joinConfirmation": "Chceš sa pridať do komunity \"{name}\"?",
|
||||
"community_scanQr": "Skontrolujte komunitný QR kód",
|
||||
"community_scanInstructions": "Zamerte kameru na komunitný QR kód.",
|
||||
"community_showQr": "Zobraziť QR kód",
|
||||
"common_ok": "OK\nDobre",
|
||||
"community_publicChannel": "Komunita verejná",
|
||||
"community_hashtagChannel": "Komunitný Hashtag",
|
||||
"community_name": "Komunita",
|
||||
"community_enterName": "Zadajte názov komunity",
|
||||
"community_created": "Komunita \"{name}\" vytvorená",
|
||||
"community_joined": "Pripojená komunita \"{name}\"",
|
||||
"community_qrTitle": "Zdieľť komunitu",
|
||||
"community_qrInstructions": "Skenejte tento QR kód, aby ste sa pripojili k {name}.",
|
||||
"community_hashtagPrivacyHint": "Hashtagové kanály komunity sú prístupné len členom komunity",
|
||||
"community_invalidQrCode": "Neplatná QR kód komunity.",
|
||||
"community_alreadyMember": "Už ste členom.",
|
||||
"community_alreadyMemberMessage": "Vy ste už členom \"{name}\".",
|
||||
"community_addPublicChannel": "Pridať verejný komunikačný kanál",
|
||||
"community_addPublicChannelHint": "Automaticky prida verejný kanál pre túto komunitu.",
|
||||
"community_noCommunities": "Zatiaľ ste sa nepripojili k žiadnej komunite",
|
||||
"community_scanOrCreate": "Skene QR kód alebo vytvor komunitu na začiatok.",
|
||||
"community_manageCommunities": "Spravovať komunity",
|
||||
"community_delete": "Nechajte komunitu",
|
||||
"community_deleteConfirm": "Opustiť \"{name}\"?",
|
||||
"community_deleteChannelsWarning": "Tým sa tiež vymaže {count} kanál/kanálov a ich správy.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Opustená komunita \"{name}\"",
|
||||
"community_addHashtagChannel": "Pridať komunitný hashtag",
|
||||
"community_addHashtagChannelDesc": "Pridajte hashtagový kanál pre túto komunitu.",
|
||||
"community_selectCommunity": "Vyberte komunitu",
|
||||
"community_regularHashtag": "Zvyčajný hashtag",
|
||||
"community_regularHashtagDesc": "Veľký hashtag (ktočokoľvek sa môže pridať)",
|
||||
"community_communityHashtag": "Komunitný Hashtag",
|
||||
"community_communityHashtagDesc": "Špecifické pre členov komunity",
|
||||
"community_forCommunity": "Pre {name}"
|
||||
}
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalSec": "Interval za GPS (Sekunde)",
|
||||
"settings_locationIntervalInvalid": "Intervallo mora biti vsaj 60 sekund in manj kot 86400 sekund.",
|
||||
"contacts_manageRoom": "Upravljajte strežnik sobe",
|
||||
"room_management": "Upravljanje stremlišča"
|
||||
"room_management": "Upravljanje stremlišča",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"community_createDesc": "Ustvari novo skupnost in jo deli preko QR kode.",
|
||||
"community_title": "Skupnost",
|
||||
"common_ok": "V redu",
|
||||
"community_create": "Ustvari skupnost",
|
||||
"community_joinTitle": "Pridružite se skupnosti",
|
||||
"community_joinConfirmation": "Želiš se pridružiti skupnosti \"{name}\"?",
|
||||
"community_scanQr": "Skeniraj QR kode skupnosti",
|
||||
"community_scanInstructions": "Nasmerite kamero s skupnostnim QR kodom.",
|
||||
"community_showQr": "Pokaži QR kodo",
|
||||
"community_publicChannel": "Skupnostna javna",
|
||||
"community_hashtagChannel": "Skupnostni hashtag",
|
||||
"community_name": "Komunitarne ime",
|
||||
"community_enterName": "Vnesite ime skupnosti",
|
||||
"community_join": "Pridružiti se",
|
||||
"community_created": "Skupnost \"{name}\" je bila ustvarila.",
|
||||
"community_joined": "Prilojen k skupnosti \"{name}\"",
|
||||
"community_qrTitle": "Delite skupnost",
|
||||
"community_qrInstructions": "Skenirajte to QR kodo za vključitev {name}.",
|
||||
"community_hashtagPrivacyHint": "Hashtag kanali skupnosti so dostopni samo članom skupnosti",
|
||||
"community_invalidQrCode": "Neveljaven QR koden skupnosti",
|
||||
"community_alreadyMember": "Že član",
|
||||
"community_alreadyMemberMessage": "Kljub temu ste že član/ka {name}.",
|
||||
"community_addPublicChannel": "Dodaj Objavni Kanal Komunitarja",
|
||||
"community_addPublicChannelHint": "Samodejno dodaj javni kanal za to skupnost.",
|
||||
"community_noCommunities": "Še nobena skupnost se ni pridružila.",
|
||||
"community_scanOrCreate": "Skenirajte QR kodo ali ustvarite skupnost za začetek.",
|
||||
"community_manageCommunities": "Upravljajte skupnosti",
|
||||
"community_delete": "Opusti skupnost",
|
||||
"community_deleteConfirm": "Zapustiti \"{name}\"?",
|
||||
"community_deleteChannelsWarning": "To bo izbrisalo tudi {count} kanal/kanalov in njihova sporočila.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Zapustil skupnost \"{name}\"",
|
||||
"community_addHashtagChannel": "Dodaj Oznako Obštnine",
|
||||
"community_addHashtagChannelDesc": "Dodajte hashtag kanal za to skupnost.",
|
||||
"community_selectCommunity": "Izberi skupnost",
|
||||
"community_regularHashtag": "Oznaka s hashtagom",
|
||||
"community_regularHashtagDesc": "javna oznaka (kateri koli lahko sodelujejo)",
|
||||
"community_communityHashtag": "Skupnostni hashtag",
|
||||
"community_communityHashtagDesc": "Izključeno za uporabnike skupnosti",
|
||||
"community_forCommunity": "Za {name}"
|
||||
}
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalSec": "Interval för GPS (Sekunder)",
|
||||
"settings_locationIntervalInvalid": "Intervalet måste vara minst 60 sekunder och mindre än 86400 sekunder.",
|
||||
"contacts_manageRoom": "Hantera Rumserver",
|
||||
"room_management": "Rumserverhantering"
|
||||
"room_management": "Rumserverhantering",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"community_create": "Skapa Gemenskap",
|
||||
"community_createDesc": "Skapa en ny gemenskap och dela via QR-kod.",
|
||||
"common_ok": "Okej",
|
||||
"community_title": "Gemenskap",
|
||||
"community_join": "Gå med",
|
||||
"community_joinTitle": "Gå med i gemenskapen",
|
||||
"community_joinConfirmation": "Vill du gå med i communityn \"{name}\"?",
|
||||
"community_scanQr": "Skanna Gemenskapens QR",
|
||||
"community_scanInstructions": "Rikta kameran mot en QR-kod i communityn",
|
||||
"community_showQr": "Visa QR-kod",
|
||||
"community_publicChannel": "Föreningens Offentliga",
|
||||
"community_name": "Gemenskapens namn",
|
||||
"community_enterName": "Ange communities namn",
|
||||
"community_created": "Community \"{name}\" har skapats",
|
||||
"community_joined": "Medlem i communityn \"{name}\"",
|
||||
"community_qrTitle": "Dela Gemenskap",
|
||||
"community_qrInstructions": "Skanna denna QR-kod för att gå med i \"{name}\"",
|
||||
"community_hashtagPrivacyHint": "Community-hashtagkanaler kan endast nås av medlemmar i communityn",
|
||||
"community_hashtagChannel": "Community Hashtag",
|
||||
"community_invalidQrCode": "Ogiltig community QR-kod",
|
||||
"community_alreadyMember": "Är redan medlem",
|
||||
"community_alreadyMemberMessage": "Du är redan medlem av \"{name}\".",
|
||||
"community_addPublicChannel": "Lägg till Gemenskapskanal (Offentlig)",
|
||||
"community_addPublicChannelHint": "Lägg automatiskt till den offentliga kanalen för denna community",
|
||||
"community_noCommunities": "Inga gemenskaper har anslutats ännu",
|
||||
"community_scanOrCreate": "Skanna en QR-kod eller skapa en community för att komma igång",
|
||||
"community_manageCommunities": "Hantera Gemenskaper",
|
||||
"community_delete": "Lämna Gemenskap",
|
||||
"community_deleteConfirm": "Lämna \"{name}\"?",
|
||||
"community_deleteChannelsWarning": "Detta kommer också att radera {count} kanal/kanaler och deras meddelanden.",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "Lämnade community \"{name}\"",
|
||||
"community_addHashtagChannel": "Lägg till Gemenskapens Hashtag",
|
||||
"community_addHashtagChannelDesc": "Lägg till en hashtag-kanal för denna community",
|
||||
"community_selectCommunity": "Välj Gemenskap",
|
||||
"community_regularHashtag": "Vanlig Hash Tag",
|
||||
"community_regularHashtagDesc": "Offentlig hashtag (alla kan gå med)",
|
||||
"community_communityHashtagDesc": "Endast för medlemmar",
|
||||
"community_forCommunity": "För {name}",
|
||||
"community_communityHashtag": "Community Hashtag"
|
||||
}
|
||||
|
||||
+101
-1
@@ -1384,5 +1384,105 @@
|
||||
"settings_locationIntervalSec": "GPS 间隔(秒)",
|
||||
"settings_locationIntervalInvalid": "时间间隔必须至少为60秒,且小于86400秒。",
|
||||
"contacts_manageRoom": "管理房间服务器",
|
||||
"room_management": "房间服务器管理"
|
||||
"room_management": "房间服务器管理",
|
||||
"@community_joinConfirmation": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_created": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_joined": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_qrInstructions": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_alreadyMemberMessage": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleteConfirm": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_deleted": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@community_forCommunity": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"community_create": "创建社区",
|
||||
"community_title": "社区",
|
||||
"community_createDesc": "创建新的社区并可通过二维码分享。",
|
||||
"common_ok": "好的",
|
||||
"community_join": "加入",
|
||||
"community_joinTitle": "加入社区",
|
||||
"community_joinConfirmation": "您想加入社区 \"{name}\" 吗?",
|
||||
"community_scanQr": "扫描社区二维码",
|
||||
"community_scanInstructions": "将相机对准社区二维码",
|
||||
"community_showQr": "显示二维码",
|
||||
"community_publicChannel": "社区公开",
|
||||
"community_hashtagChannel": "社区标签",
|
||||
"community_name": "社区名称",
|
||||
"community_enterName": "请输入社区名称",
|
||||
"community_created": "社区“{name}”已创建",
|
||||
"community_joined": "加入社区 \"{name}\"",
|
||||
"community_qrTitle": "分享社区",
|
||||
"community_qrInstructions": "扫描此二维码加入{name}",
|
||||
"community_hashtagPrivacyHint": "社区标签频道仅社区成员可加入",
|
||||
"community_invalidQrCode": "无效的社区二维码",
|
||||
"community_alreadyMember": "已经是会员了",
|
||||
"community_alreadyMemberMessage": "您已经是 \"{name}\" 的会员。",
|
||||
"community_addPublicChannel": "添加社区公共频道",
|
||||
"community_addPublicChannelHint": "自动添加该社区的公共频道",
|
||||
"community_noCommunities": "尚未加入任何社区",
|
||||
"community_scanOrCreate": "扫描二维码或创建社区开始",
|
||||
"community_manageCommunities": "管理社群",
|
||||
"community_delete": "退出社区",
|
||||
"community_deleteConfirm": "退出 \"{name}\"?",
|
||||
"community_deleteChannelsWarning": "这也将删除 {count} 个频道及其消息。",
|
||||
"@community_deleteChannelsWarning": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"community_deleted": "已退出社区 \"{name}\"",
|
||||
"community_addHashtagChannel": "添加社区标签",
|
||||
"community_addHashtagChannelDesc": "添加一个话题频道给此社区",
|
||||
"community_selectCommunity": "选择社区",
|
||||
"community_regularHashtag": "常规话题标签",
|
||||
"community_regularHashtagDesc": "公共话题(任何人都可以加入)",
|
||||
"community_communityHashtag": "社区标签",
|
||||
"community_communityHashtagDesc": "仅限社区成员使用",
|
||||
"community_forCommunity": "对于 {name}"
|
||||
}
|
||||
|
||||
@@ -73,6 +73,35 @@ class Channel {
|
||||
return Uint8List.fromList(hash.sublist(0, 16));
|
||||
}
|
||||
|
||||
/// Derive PSK for community public channel using HMAC-SHA256.
|
||||
/// PSK = HMAC-SHA256(K, "channel:v1:__public__")[:16]
|
||||
///
|
||||
/// This creates a channel that is "public" only to members who have
|
||||
/// the community secret. Outsiders see only opaque IDs.
|
||||
static Uint8List deriveCommunityPublicPsk(Uint8List secret) {
|
||||
final hmac = crypto.Hmac(crypto.sha256, secret);
|
||||
final digest = hmac.convert(utf8.encode('channel:v1:__public__'));
|
||||
return Uint8List.fromList(digest.bytes.sublist(0, 16));
|
||||
}
|
||||
|
||||
/// Derive PSK for community hashtag channel using HMAC-SHA256.
|
||||
/// PSK = HMAC-SHA256(K, "channel:v1:" + normalized_name)[:16]
|
||||
///
|
||||
/// Community hashtag channels are deterministic for all members
|
||||
/// (same name => same id) but impossible to enumerate/guess without K.
|
||||
static Uint8List deriveCommunityHashtagPsk(Uint8List secret, String hashtag) {
|
||||
final normalized = _normalizeCommunityHashtag(hashtag);
|
||||
final hmac = crypto.Hmac(crypto.sha256, secret);
|
||||
final digest = hmac.convert(utf8.encode('channel:v1:$normalized'));
|
||||
return Uint8List.fromList(digest.bytes.sublist(0, 16));
|
||||
}
|
||||
|
||||
/// Normalize a hashtag name for consistent community PSK derivation.
|
||||
/// Strips leading #, converts to lowercase, trims whitespace.
|
||||
static String _normalizeCommunityHashtag(String hashtag) {
|
||||
return hashtag.replaceFirst(RegExp(r'^#'), '').toLowerCase().trim();
|
||||
}
|
||||
|
||||
static String formatPskHex(Uint8List psk) {
|
||||
return _bytesToHex(psk);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:crypto/crypto.dart' as crypto;
|
||||
|
||||
/// Represents a community with a shared secret for deriving channel PSKs.
|
||||
///
|
||||
/// A Community is a namespace with a shared secret K (32 random bytes),
|
||||
/// distributed via QR code. Members can create Community Public Channels
|
||||
/// and Community Hashtag Channels that are opaque to outsiders.
|
||||
class Community {
|
||||
/// Unique identifier for local storage
|
||||
final String id;
|
||||
|
||||
/// Display name for the community
|
||||
final String name;
|
||||
|
||||
/// The 32-byte shared secret (K)
|
||||
final Uint8List secret;
|
||||
|
||||
/// Timestamp when the community was created/joined
|
||||
final DateTime createdAt;
|
||||
|
||||
/// List of hashtag channel names (without #) that have been added
|
||||
final List<String> hashtagChannels;
|
||||
|
||||
Community({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.secret,
|
||||
required this.createdAt,
|
||||
List<String>? hashtagChannels,
|
||||
}) : hashtagChannels = hashtagChannels ?? [];
|
||||
|
||||
/// Generate a new community with a random 32-byte secret
|
||||
factory Community.create({
|
||||
required String id,
|
||||
required String name,
|
||||
}) {
|
||||
final random = Random.secure();
|
||||
final secret = Uint8List(32);
|
||||
for (int i = 0; i < 32; i++) {
|
||||
secret[i] = random.nextInt(256);
|
||||
}
|
||||
return Community(
|
||||
id: id,
|
||||
name: name,
|
||||
secret: secret,
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse a community from QR code JSON data
|
||||
factory Community.fromQrData(String id, String qrData) {
|
||||
final json = jsonDecode(qrData) as Map<String, dynamic>;
|
||||
if (json['type'] != 'meshcore_community') {
|
||||
throw const FormatException('Invalid QR code type');
|
||||
}
|
||||
if (json['v'] != 1) {
|
||||
throw const FormatException('Unsupported QR code version');
|
||||
}
|
||||
|
||||
final name = json['name'] as String;
|
||||
final secretBase64 = json['k'] as String;
|
||||
final secret = base64Url.decode(secretBase64);
|
||||
|
||||
if (secret.length != 32) {
|
||||
throw const FormatException('Invalid secret length');
|
||||
}
|
||||
|
||||
return Community(
|
||||
id: id,
|
||||
name: name,
|
||||
secret: Uint8List.fromList(secret),
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse a community from storage JSON
|
||||
factory Community.fromJson(Map<String, dynamic> json) {
|
||||
return Community(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
secret: base64Decode(json['secret'] as String),
|
||||
createdAt: DateTime.fromMillisecondsSinceEpoch(json['created_at'] as int),
|
||||
hashtagChannels: (json['hashtag_channels'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/// Convert to JSON for storage
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'secret': base64Encode(secret),
|
||||
'created_at': createdAt.millisecondsSinceEpoch,
|
||||
'hashtag_channels': hashtagChannels,
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate QR code JSON payload for sharing
|
||||
String toQrJson() {
|
||||
return jsonEncode({
|
||||
'v': 1,
|
||||
'type': 'meshcore_community',
|
||||
'name': name,
|
||||
'k': base64Url.encode(secret),
|
||||
});
|
||||
}
|
||||
|
||||
/// Derive the public Community ID from the secret.
|
||||
/// This is safe to display/log since it's one-way derived.
|
||||
/// CID = SHA256("community:v1" || K)
|
||||
String get communityId {
|
||||
final data = utf8.encode('community:v1') + secret;
|
||||
final hash = crypto.sha256.convert(data).bytes;
|
||||
return _bytesToHex(Uint8List.fromList(hash));
|
||||
}
|
||||
|
||||
/// Short version of community ID for display (first 8 chars)
|
||||
String get shortCommunityId => communityId.substring(0, 8);
|
||||
|
||||
/// Derive PSK for community public channel.
|
||||
/// PSK = HMAC-SHA256(K, "channel:v1:__public__")[:16]
|
||||
Uint8List deriveCommunityPublicPsk() {
|
||||
final hmac = crypto.Hmac(crypto.sha256, secret);
|
||||
final digest = hmac.convert(utf8.encode('channel:v1:__public__'));
|
||||
return Uint8List.fromList(digest.bytes.sublist(0, 16));
|
||||
}
|
||||
|
||||
/// Derive PSK for community hashtag channel.
|
||||
/// PSK = HMAC-SHA256(K, "channel:v1:" + normalized_name)[:16]
|
||||
Uint8List deriveCommunityHashtagPsk(String hashtag) {
|
||||
final normalized = _normalizeCommunityHashtag(hashtag);
|
||||
final hmac = crypto.Hmac(crypto.sha256, secret);
|
||||
final digest = hmac.convert(utf8.encode('channel:v1:$normalized'));
|
||||
return Uint8List.fromList(digest.bytes.sublist(0, 16));
|
||||
}
|
||||
|
||||
/// Check if QR data is valid community data
|
||||
static bool isValidQrData(String data) {
|
||||
try {
|
||||
final json = jsonDecode(data) as Map<String, dynamic>;
|
||||
if (json['type'] != 'meshcore_community') return false;
|
||||
if (json['v'] != 1) return false;
|
||||
if (json['name'] == null || (json['name'] as String).isEmpty) {
|
||||
return false;
|
||||
}
|
||||
if (json['k'] == null) return false;
|
||||
final secret = base64Url.decode(json['k'] as String);
|
||||
return secret.length == 32;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Normalize a hashtag name for consistent PSK derivation.
|
||||
/// Strips leading #, converts to lowercase, trims whitespace.
|
||||
static String _normalizeCommunityHashtag(String hashtag) {
|
||||
return hashtag.replaceFirst(RegExp(r'^#'), '').toLowerCase().trim();
|
||||
}
|
||||
|
||||
/// Add a hashtag channel to this community's list
|
||||
Community addHashtagChannel(String hashtag) {
|
||||
final normalized = _normalizeCommunityHashtag(hashtag);
|
||||
if (hashtagChannels.contains(normalized)) {
|
||||
return this;
|
||||
}
|
||||
return Community(
|
||||
id: id,
|
||||
name: name,
|
||||
secret: secret,
|
||||
createdAt: createdAt,
|
||||
hashtagChannels: [...hashtagChannels, normalized],
|
||||
);
|
||||
}
|
||||
|
||||
/// Remove a hashtag channel from this community's list
|
||||
Community removeHashtagChannel(String hashtag) {
|
||||
final normalized = _normalizeCommunityHashtag(hashtag);
|
||||
return Community(
|
||||
id: id,
|
||||
name: name,
|
||||
secret: secret,
|
||||
createdAt: createdAt,
|
||||
hashtagChannels: hashtagChannels.where((h) => h != normalized).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Create a copy of this community with a new secret
|
||||
Community withNewSecret(Uint8List newSecret) {
|
||||
return Community(
|
||||
id: id,
|
||||
name: name,
|
||||
secret: newSecret,
|
||||
createdAt: createdAt,
|
||||
hashtagChannels: hashtagChannels,
|
||||
);
|
||||
}
|
||||
|
||||
/// Create a copy of this community with a regenerated random secret
|
||||
Community withRegeneratedSecret() {
|
||||
final random = Random.secure();
|
||||
final newSecret = Uint8List(32);
|
||||
for (int i = 0; i < 32; i++) {
|
||||
newSecret[i] = random.nextInt(256);
|
||||
}
|
||||
return withNewSecret(newSecret);
|
||||
}
|
||||
|
||||
/// Extract secret from QR data (for updating existing community)
|
||||
static Uint8List? extractSecretFromQrData(String qrData) {
|
||||
try {
|
||||
final json = jsonDecode(qrData) as Map<String, dynamic>;
|
||||
if (json['type'] != 'meshcore_community') return null;
|
||||
if (json['v'] != 1) return null;
|
||||
final secretBase64 = json['k'] as String;
|
||||
final secret = base64Url.decode(secretBase64);
|
||||
if (secret.length != 32) return null;
|
||||
return Uint8List.fromList(secret);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static String _bytesToHex(Uint8List bytes) {
|
||||
return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is Community &&
|
||||
runtimeType == other.runtimeType &&
|
||||
id == other.id;
|
||||
|
||||
@override
|
||||
int get hashCode => id.hashCode;
|
||||
}
|
||||
@@ -4,19 +4,25 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../connector/meshcore_connector.dart';
|
||||
import '../l10n/l10n.dart';
|
||||
import '../models/channel.dart';
|
||||
import '../models/community.dart';
|
||||
import '../storage/community_store.dart';
|
||||
import '../utils/dialog_utils.dart';
|
||||
import '../utils/disconnect_navigation_mixin.dart';
|
||||
import '../utils/route_transitions.dart';
|
||||
import '../widgets/battery_indicator.dart';
|
||||
import '../widgets/list_filter_widget.dart';
|
||||
import '../widgets/empty_state.dart';
|
||||
import '../widgets/qr_code_display.dart';
|
||||
import '../widgets/qr_scanner_widget.dart';
|
||||
import '../widgets/quick_switch_bar.dart';
|
||||
import '../widgets/unread_badge.dart';
|
||||
import 'channel_chat_screen.dart';
|
||||
import 'community_qr_scanner_screen.dart';
|
||||
import 'contacts_screen.dart';
|
||||
import 'map_screen.dart';
|
||||
import 'settings_screen.dart';
|
||||
@@ -43,17 +49,59 @@ class ChannelsScreen extends StatefulWidget {
|
||||
class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
with DisconnectNavigationMixin {
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
final CommunityStore _communityStore = CommunityStore();
|
||||
String _searchQuery = '';
|
||||
Timer? _searchDebounce;
|
||||
ChannelSortOption _sortOption = ChannelSortOption.manual;
|
||||
List<Community> _communities = [];
|
||||
|
||||
// Cache of PSK hex -> Community for quick lookup
|
||||
final Map<String, Community> _pskToCommunity = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.read<MeshCoreConnector>().getChannels();
|
||||
_loadCommunities();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _loadCommunities() async {
|
||||
final communities = await _communityStore.loadCommunities();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_communities = communities;
|
||||
_buildPskCommunityMap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _buildPskCommunityMap() {
|
||||
_pskToCommunity.clear();
|
||||
for (final community in _communities) {
|
||||
// Map the community public channel PSK
|
||||
final publicPsk = community.deriveCommunityPublicPsk();
|
||||
_pskToCommunity[Channel.formatPskHex(publicPsk)] = community;
|
||||
|
||||
// Map all known hashtag channel PSKs
|
||||
for (final hashtag in community.hashtagChannels) {
|
||||
final hashtagPsk = community.deriveCommunityHashtagPsk(hashtag);
|
||||
_pskToCommunity[Channel.formatPskHex(hashtagPsk)] = community;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the community this channel belongs to, or null if not a community channel
|
||||
Community? _getCommunityForChannel(Channel channel) {
|
||||
return _pskToCommunity[channel.pskHex];
|
||||
}
|
||||
|
||||
/// Returns true if this is the community's public channel
|
||||
bool _isCommunityPublicChannel(Channel channel, Community community) {
|
||||
final publicPsk = community.deriveCommunityPublicPsk();
|
||||
return channel.pskHex == Channel.formatPskHex(publicPsk);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -82,6 +130,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
centerTitle: true,
|
||||
automaticallyImplyLeading: false,
|
||||
actions: [
|
||||
if (_communities.isNotEmpty)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.groups),
|
||||
tooltip: context.l10n.community_manageCommunities,
|
||||
onPressed: () => _showManageCommunitiesDialog(context),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.bluetooth_disabled),
|
||||
tooltip: context.l10n.common_disconnect,
|
||||
@@ -268,6 +322,44 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
}
|
||||
) {
|
||||
final unreadCount = connector.getUnreadCountForChannel(channel);
|
||||
final community = _getCommunityForChannel(channel);
|
||||
final isCommunityChannel = community != null;
|
||||
final isCommunityPublic = isCommunityChannel && _isCommunityPublicChannel(channel, community);
|
||||
|
||||
// Determine icon and colors based on channel type
|
||||
IconData icon;
|
||||
Color iconColor;
|
||||
Color bgColor;
|
||||
String subtitle;
|
||||
|
||||
if (isCommunityChannel) {
|
||||
// Community channel styling
|
||||
iconColor = Colors.purple;
|
||||
bgColor = Colors.purple.withValues(alpha: 0.2);
|
||||
if (isCommunityPublic) {
|
||||
icon = Icons.groups;
|
||||
subtitle = '${context.l10n.community_publicChannel} • ${community.name}';
|
||||
} else {
|
||||
icon = Icons.tag;
|
||||
subtitle = '${context.l10n.community_hashtagChannel} • ${community.name}';
|
||||
}
|
||||
} else if (channel.isPublicChannel) {
|
||||
icon = Icons.public;
|
||||
iconColor = Colors.green;
|
||||
bgColor = Colors.green.withValues(alpha: 0.2);
|
||||
subtitle = context.l10n.channels_publicChannel;
|
||||
} else if (channel.name.startsWith('#')) {
|
||||
icon = Icons.tag;
|
||||
iconColor = Colors.blue;
|
||||
bgColor = Colors.blue.withValues(alpha: 0.2);
|
||||
subtitle = context.l10n.channels_hashtagChannel;
|
||||
} else {
|
||||
icon = Icons.lock;
|
||||
iconColor = Colors.blue;
|
||||
bgColor = Colors.blue.withValues(alpha: 0.2);
|
||||
subtitle = context.l10n.channels_privateChannel;
|
||||
}
|
||||
|
||||
return Card(
|
||||
key: ValueKey('channel_${channel.index}'),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
@@ -276,29 +368,44 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
minVerticalPadding: 0,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
visualDensity: const VisualDensity(vertical: -2),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: channel.isPublicChannel
|
||||
? Colors.green.withValues(alpha: 0.2)
|
||||
: Colors.blue.withValues(alpha: 0.2),
|
||||
child: Icon(
|
||||
channel.isPublicChannel
|
||||
? Icons.public
|
||||
: channel.name.startsWith('#')
|
||||
? Icons.tag
|
||||
: Icons.lock,
|
||||
color: channel.isPublicChannel ? Colors.green : Colors.blue,
|
||||
),
|
||||
leading: Stack(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: bgColor,
|
||||
child: Icon(icon, color: iconColor),
|
||||
),
|
||||
if (isCommunityChannel)
|
||||
Positioned(
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
width: 14,
|
||||
height: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.purple,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Theme.of(context).cardColor,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.people,
|
||||
size: 8,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
title: Text(
|
||||
channel.name.isEmpty ? context.l10n.channels_channelIndex(channel.index) : channel.name,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
channel.name.startsWith('#')
|
||||
? context.l10n.channels_hashtagChannel
|
||||
: channel.isPublicChannel
|
||||
? context.l10n.channels_publicChannel
|
||||
: context.l10n.channels_privateChannel,
|
||||
subtitle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -521,6 +628,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
final nameController = TextEditingController();
|
||||
final pskController = TextEditingController();
|
||||
final hashtagController = TextEditingController();
|
||||
bool addPublicChannel = true;
|
||||
bool isRegularHashtag = true;
|
||||
Community? selectedCommunity;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -713,6 +823,55 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
case 3: // Join Hashtag Channel
|
||||
return Column(
|
||||
children: [
|
||||
// Only show type selection if user has communities
|
||||
if (_communities.isNotEmpty) ...[
|
||||
RadioGroup<bool>(
|
||||
groupValue: isRegularHashtag,
|
||||
onChanged: (v) => setDialogState(() {
|
||||
if (v != null) {
|
||||
isRegularHashtag = v;
|
||||
if (isRegularHashtag) {
|
||||
selectedCommunity = null;
|
||||
} else if (selectedCommunity == null && _communities.isNotEmpty) {
|
||||
selectedCommunity = _communities.first;
|
||||
}
|
||||
}
|
||||
}),
|
||||
child: Column(
|
||||
children: [
|
||||
RadioListTile<bool>(
|
||||
value: true,
|
||||
title: Text(dialogContext.l10n.community_regularHashtag),
|
||||
subtitle: Text(dialogContext.l10n.community_regularHashtagDesc),
|
||||
dense: true,
|
||||
),
|
||||
RadioListTile<bool>(
|
||||
value: false,
|
||||
title: Text(dialogContext.l10n.community_communityHashtag),
|
||||
subtitle: Text(dialogContext.l10n.community_communityHashtagDesc),
|
||||
dense: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
// Community dropdown (only if community hashtag selected)
|
||||
if (!isRegularHashtag && _communities.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: DropdownMenu<Community>(
|
||||
initialSelection: selectedCommunity,
|
||||
dropdownMenuEntries: _communities.map((c) => DropdownMenuEntry(
|
||||
value: c,
|
||||
label: c.name,
|
||||
)).toList(),
|
||||
onSelected: (c) => setDialogState(() => selectedCommunity = c),
|
||||
label: Text(dialogContext.l10n.community_selectCommunity),
|
||||
leadingIcon: const Icon(Icons.groups),
|
||||
expandedInsets: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
// Hashtag name input
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: TextField(
|
||||
@@ -726,13 +885,26 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
maxLength: 31,
|
||||
),
|
||||
),
|
||||
// Privacy hint for community hashtags
|
||||
if (!isRegularHashtag)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
dialogContext.l10n.community_hashtagPrivacyHint,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
var hashtag = hashtagController.text.trim();
|
||||
if (hashtag.isEmpty) {
|
||||
ScaffoldMessenger.of(dialogContext).showSnackBar(
|
||||
@@ -740,14 +912,38 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Normalize hashtag name
|
||||
final name = hashtag.startsWith('#') ? hashtag : '#$hashtag';
|
||||
final psk = Channel.derivePskFromHashtag(hashtag);
|
||||
Navigator.pop(dialogContext);
|
||||
connector.setChannel(nextIndex, name, psk);
|
||||
|
||||
// Normalize hashtag name (remove leading # if present)
|
||||
if (hashtag.startsWith('#')) {
|
||||
hashtag = hashtag.substring(1);
|
||||
}
|
||||
final channelName = '#$hashtag';
|
||||
|
||||
final Uint8List psk;
|
||||
if (isRegularHashtag) {
|
||||
// Regular hashtag - public derivation using SHA256
|
||||
psk = Channel.derivePskFromHashtag(hashtag);
|
||||
} else {
|
||||
// Community hashtag - HMAC derivation from community secret
|
||||
if (selectedCommunity == null) {
|
||||
ScaffoldMessenger.of(dialogContext).showSnackBar(
|
||||
SnackBar(content: Text(dialogContext.l10n.community_selectCommunity)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
psk = selectedCommunity!.deriveCommunityHashtagPsk(hashtag);
|
||||
// Track in community's hashtag list
|
||||
await _communityStore.addHashtagChannel(selectedCommunity!.id, hashtag);
|
||||
_loadCommunities();
|
||||
}
|
||||
|
||||
if (dialogContext.mounted) {
|
||||
Navigator.pop(dialogContext);
|
||||
}
|
||||
connector.setChannel(nextIndex, channelName, psk);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.channels_channelAdded(name))),
|
||||
SnackBar(content: Text(context.l10n.channels_channelAdded(channelName))),
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -760,6 +956,126 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
],
|
||||
);
|
||||
|
||||
case 4: // Scan Community QR
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FilledButton.icon(
|
||||
onPressed: () async {
|
||||
Navigator.pop(dialogContext);
|
||||
if (context.mounted) {
|
||||
await Navigator.push<Community>(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const CommunityQrScannerScreen(),
|
||||
),
|
||||
);
|
||||
// Refresh communities list when returning from scanner
|
||||
if (context.mounted) {
|
||||
_loadCommunities();
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.qr_code_scanner),
|
||||
label: Text(dialogContext.l10n.community_scanQr),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
case 5: // Create Community
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: TextField(
|
||||
controller: nameController,
|
||||
decoration: InputDecoration(
|
||||
labelText: dialogContext.l10n.community_name,
|
||||
hintText: dialogContext.l10n.community_enterName,
|
||||
border: const OutlineInputBorder(),
|
||||
prefixIcon: const Icon(Icons.groups),
|
||||
),
|
||||
maxLength: 31,
|
||||
),
|
||||
),
|
||||
CheckboxListTile(
|
||||
value: addPublicChannel,
|
||||
onChanged: (value) {
|
||||
setDialogState(() {
|
||||
addPublicChannel = value ?? true;
|
||||
});
|
||||
},
|
||||
title: Text(dialogContext.l10n.community_addPublicChannel),
|
||||
subtitle: Text(dialogContext.l10n.community_addPublicChannelHint),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FilledButton(
|
||||
onPressed: () async {
|
||||
final name = nameController.text.trim();
|
||||
if (name.isEmpty) {
|
||||
ScaffoldMessenger.of(dialogContext).showSnackBar(
|
||||
SnackBar(content: Text(dialogContext.l10n.community_enterName)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create community with random secret
|
||||
final community = Community.create(
|
||||
id: const Uuid().v4(),
|
||||
name: name,
|
||||
);
|
||||
|
||||
// Save to store
|
||||
await _communityStore.addCommunity(community);
|
||||
|
||||
// Optionally add the community public channel to the device
|
||||
if (addPublicChannel) {
|
||||
final psk = community.deriveCommunityPublicPsk();
|
||||
final channelName = '${community.name} Public';
|
||||
connector.setChannel(nextIndex, channelName, psk);
|
||||
}
|
||||
|
||||
if (dialogContext.mounted) {
|
||||
Navigator.pop(dialogContext);
|
||||
}
|
||||
|
||||
// Refresh communities list
|
||||
_loadCommunities();
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.community_created(name))),
|
||||
);
|
||||
|
||||
// Show QR code dialog
|
||||
await QrCodeShareDialog.show(
|
||||
context: context,
|
||||
data: community.toQrJson(),
|
||||
title: context.l10n.community_qrTitle,
|
||||
instructions: context.l10n.community_qrInstructions(name),
|
||||
embeddedImage: Image.asset('assets/images/mesh-icon.png', width: 40, height: 40),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(dialogContext.l10n.common_create),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -810,11 +1126,19 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
const Divider(height: 1),
|
||||
buildOptionTile(
|
||||
optionIndex: 4,
|
||||
icon: Icons.qr_code,
|
||||
title: dialogContext.l10n.channels_scanQrCode,
|
||||
subtitle: dialogContext.l10n.channels_scanQrCodeComingSoon,
|
||||
enabled: false,
|
||||
icon: Icons.qr_code_scanner,
|
||||
title: dialogContext.l10n.community_scanQr,
|
||||
subtitle: dialogContext.l10n.community_join,
|
||||
),
|
||||
if (selectedOption == 4) buildExpandedContent()!,
|
||||
const Divider(height: 1),
|
||||
buildOptionTile(
|
||||
optionIndex: 5,
|
||||
icon: Icons.groups,
|
||||
title: dialogContext.l10n.community_create,
|
||||
subtitle: dialogContext.l10n.community_createDesc,
|
||||
),
|
||||
if (selectedOption == 5) buildExpandedContent()!,
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -967,4 +1291,360 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _showManageCommunitiesDialog(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (sheetContext) => DraggableScrollableSheet(
|
||||
initialChildSize: 0.5,
|
||||
minChildSize: 0.3,
|
||||
maxChildSize: 0.9,
|
||||
expand: false,
|
||||
builder: (_, scrollController) => Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.groups, size: 28),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
context.l10n.community_manageCommunities,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Expanded(
|
||||
child: _communities.isEmpty
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.groups_outlined, size: 64, color: Colors.grey[400]),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
context.l10n.community_noCommunities,
|
||||
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
context.l10n.community_scanOrCreate,
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey[500]),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
controller: scrollController,
|
||||
itemCount: _communities.length,
|
||||
itemBuilder: (context, index) {
|
||||
final community = _communities[index];
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Colors.purple.withValues(alpha: 0.2),
|
||||
child: const Icon(Icons.groups, color: Colors.purple),
|
||||
),
|
||||
title: Text(community.name),
|
||||
subtitle: Text(
|
||||
'ID: ${community.shortCommunityId}...',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
trailing: PopupMenuButton<String>(
|
||||
onSelected: (value) {
|
||||
Navigator.pop(sheetContext);
|
||||
if (value == 'share') {
|
||||
_showCommunityQrDialog(context, community);
|
||||
} else if (value == 'regenerate') {
|
||||
_regenerateCommunitySecret(context, community);
|
||||
} else if (value == 'update') {
|
||||
_updateCommunitySecret(context, community);
|
||||
} else if (value == 'leave') {
|
||||
_confirmLeaveCommunity(context, community);
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: 'share',
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.qr_code),
|
||||
const SizedBox(width: 12),
|
||||
Text(context.l10n.community_showQr),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'regenerate',
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.refresh),
|
||||
const SizedBox(width: 12),
|
||||
Text(context.l10n.community_regenerateSecret),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'update',
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.qr_code_scanner),
|
||||
const SizedBox(width: 12),
|
||||
Text(context.l10n.community_updateSecret),
|
||||
],
|
||||
),
|
||||
),
|
||||
const PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
value: 'leave',
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.exit_to_app, color: Colors.red),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
context.l10n.community_delete,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(sheetContext);
|
||||
_showCommunityQrDialog(context, community);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showCommunityQrDialog(BuildContext context, Community community) {
|
||||
QrCodeShareDialog.show(
|
||||
context: context,
|
||||
data: community.toQrJson(),
|
||||
title: context.l10n.community_qrTitle,
|
||||
instructions: context.l10n.community_qrInstructions(community.name),
|
||||
embeddedImage: Image.asset('assets/images/mesh-icon.png', width: 40, height: 40),
|
||||
);
|
||||
}
|
||||
|
||||
/// Regenerate the community secret and update all associated channels
|
||||
void _regenerateCommunitySecret(BuildContext context, Community community) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(dialogContext.l10n.community_regenerateSecret),
|
||||
content: Text(dialogContext.l10n.community_regenerateSecretConfirm(community.name)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
child: Text(dialogContext.l10n.common_cancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(dialogContext);
|
||||
|
||||
final connector = context.read<MeshCoreConnector>();
|
||||
final newCommunity = community.withRegeneratedSecret();
|
||||
|
||||
// Update channel PSKs
|
||||
await _updateCommunityChannelPsks(connector, community, newCommunity);
|
||||
|
||||
// Save updated community
|
||||
await _communityStore.updateCommunity(newCommunity);
|
||||
_loadCommunities();
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.community_secretRegenerated(community.name))),
|
||||
);
|
||||
|
||||
// Show the new QR code
|
||||
_showCommunityQrDialog(context, newCommunity);
|
||||
}
|
||||
},
|
||||
child: Text(dialogContext.l10n.community_regenerate),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Update community secret from a scanned QR code
|
||||
void _updateCommunitySecret(BuildContext context, Community community) async {
|
||||
final result = await Navigator.push<String>(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _CommunitySecretScannerScreen(
|
||||
communityName: community.name,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null || !context.mounted) return;
|
||||
|
||||
final newSecret = Community.extractSecretFromQrData(result);
|
||||
if (newSecret == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.community_invalidQrCode)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final connector = context.read<MeshCoreConnector>();
|
||||
final newCommunity = community.withNewSecret(newSecret);
|
||||
|
||||
// Update channel PSKs
|
||||
await _updateCommunityChannelPsks(connector, community, newCommunity);
|
||||
|
||||
// Save updated community
|
||||
await _communityStore.updateCommunity(newCommunity);
|
||||
_loadCommunities();
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.community_secretUpdated(community.name))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update PSKs for all channels belonging to a community
|
||||
Future<void> _updateCommunityChannelPsks(
|
||||
MeshCoreConnector connector,
|
||||
Community oldCommunity,
|
||||
Community newCommunity,
|
||||
) async {
|
||||
// Find and update the public channel
|
||||
final oldPublicPskHex = Channel.formatPskHex(oldCommunity.deriveCommunityPublicPsk());
|
||||
final newPublicPsk = newCommunity.deriveCommunityPublicPsk();
|
||||
|
||||
for (final channel in connector.channels) {
|
||||
if (channel.pskHex == oldPublicPskHex) {
|
||||
await connector.setChannel(channel.index, channel.name, newPublicPsk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find and update hashtag channels
|
||||
for (final hashtag in oldCommunity.hashtagChannels) {
|
||||
final oldHashtagPskHex = Channel.formatPskHex(oldCommunity.deriveCommunityHashtagPsk(hashtag));
|
||||
final newHashtagPsk = newCommunity.deriveCommunityHashtagPsk(hashtag);
|
||||
|
||||
for (final channel in connector.channels) {
|
||||
if (channel.pskHex == oldHashtagPskHex) {
|
||||
await connector.setChannel(channel.index, channel.name, newHashtagPsk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _confirmLeaveCommunity(BuildContext context, Community community) {
|
||||
final connector = context.read<MeshCoreConnector>();
|
||||
|
||||
// Find all channels that belong to this community
|
||||
List<Channel> communityChannels = [];
|
||||
final publicPskHex = Channel.formatPskHex(community.deriveCommunityPublicPsk());
|
||||
|
||||
for (final channel in connector.channels) {
|
||||
// Check if it's the public channel
|
||||
if (channel.pskHex == publicPskHex) {
|
||||
communityChannels.add(channel);
|
||||
continue;
|
||||
}
|
||||
// Check if it's a hashtag channel
|
||||
for (final hashtag in community.hashtagChannels) {
|
||||
final hashtagPskHex = Channel.formatPskHex(community.deriveCommunityHashtagPsk(hashtag));
|
||||
if (channel.pskHex == hashtagPskHex) {
|
||||
communityChannels.add(channel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final channelCount = communityChannels.length;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(dialogContext.l10n.community_delete),
|
||||
content: Text(
|
||||
channelCount > 0
|
||||
? '${dialogContext.l10n.community_deleteConfirm(community.name)}\n\n${dialogContext.l10n.community_deleteChannelsWarning(channelCount)}'
|
||||
: dialogContext.l10n.community_deleteConfirm(community.name),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
child: Text(dialogContext.l10n.common_cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(dialogContext);
|
||||
|
||||
// Delete all community channels from the device
|
||||
for (final channel in communityChannels) {
|
||||
await connector.deleteChannel(channel.index);
|
||||
}
|
||||
|
||||
// Remove community from store
|
||||
await _communityStore.removeCommunity(community.id);
|
||||
_loadCommunities();
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.community_deleted(community.name))),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
dialogContext.l10n.community_delete,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple scanner screen for updating community secret
|
||||
class _CommunitySecretScannerScreen extends StatelessWidget {
|
||||
final String communityName;
|
||||
|
||||
const _CommunitySecretScannerScreen({required this.communityName});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.l10n.community_updateSecret),
|
||||
),
|
||||
body: QrScannerWidget(
|
||||
onScanned: (data) {
|
||||
Navigator.pop(context, data);
|
||||
},
|
||||
validator: (data) => Community.isValidQrData(data),
|
||||
onValidationFailed: (data) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.community_invalidQrCode)),
|
||||
);
|
||||
},
|
||||
instructions: context.l10n.community_scanToUpdateSecret(communityName),
|
||||
overlay: const ScannerCornerOverlay(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../connector/meshcore_connector.dart';
|
||||
import '../l10n/l10n.dart';
|
||||
import '../models/community.dart';
|
||||
import '../storage/community_store.dart';
|
||||
import '../widgets/qr_scanner_widget.dart';
|
||||
|
||||
/// Screen for scanning community QR codes to join communities.
|
||||
///
|
||||
/// After successful scan, the user can:
|
||||
/// 1. Join the community (saves to local storage)
|
||||
/// 2. Optionally add the Community Public Channel to the device
|
||||
class CommunityQrScannerScreen extends StatefulWidget {
|
||||
const CommunityQrScannerScreen({super.key});
|
||||
|
||||
@override
|
||||
State<CommunityQrScannerScreen> createState() =>
|
||||
_CommunityQrScannerScreenState();
|
||||
}
|
||||
|
||||
class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
|
||||
final CommunityStore _communityStore = CommunityStore();
|
||||
bool _isProcessing = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.l10n.community_scanQr),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: _isProcessing
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: QrScannerWidget(
|
||||
onScanned: (data) => _handleScannedData(context, data),
|
||||
validator: Community.isValidQrData,
|
||||
onValidationFailed: (_) => _showInvalidQrError(context),
|
||||
instructions: context.l10n.community_scanInstructions,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleScannedData(BuildContext context, String data) async {
|
||||
if (_isProcessing) return;
|
||||
|
||||
setState(() {
|
||||
_isProcessing = true;
|
||||
});
|
||||
|
||||
try {
|
||||
// Parse the community data
|
||||
final community = Community.fromQrData(const Uuid().v4(), data);
|
||||
|
||||
// Check if this community already exists
|
||||
final existing = await _communityStore.findByCommunityId(
|
||||
community.communityId,
|
||||
);
|
||||
|
||||
if (existing != null) {
|
||||
if (context.mounted) {
|
||||
_showAlreadyMemberDialog(context, existing);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Show confirmation dialog
|
||||
if (context.mounted) {
|
||||
await _showJoinConfirmationDialog(context, community);
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.community_invalidQrCode),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isProcessing = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _showInvalidQrError(BuildContext context) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.community_invalidQrCode),
|
||||
backgroundColor: Colors.orange,
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showAlreadyMemberDialog(BuildContext context, Community community) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(context.l10n.community_alreadyMember),
|
||||
content: Text(
|
||||
context.l10n.community_alreadyMemberMessage(community.name),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(dialogContext);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(context.l10n.common_ok),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showJoinConfirmationDialog(
|
||||
BuildContext context,
|
||||
Community community,
|
||||
) async {
|
||||
bool addPublicChannel = true;
|
||||
|
||||
final result = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => StatefulBuilder(
|
||||
builder: (dialogContext, setDialogState) => AlertDialog(
|
||||
title: Text(context.l10n.community_joinTitle),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(context.l10n.community_joinConfirmation(community.name)),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.groups,
|
||||
color: Theme.of(dialogContext).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
community.name,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
'ID: ${community.shortCommunityId}...',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(),
|
||||
const SizedBox(height: 8),
|
||||
CheckboxListTile(
|
||||
value: addPublicChannel,
|
||||
onChanged: (value) {
|
||||
setDialogState(() {
|
||||
addPublicChannel = value ?? true;
|
||||
});
|
||||
},
|
||||
title: Text(context.l10n.community_addPublicChannel),
|
||||
subtitle: Text(context.l10n.community_addPublicChannelHint),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext, false),
|
||||
child: Text(context.l10n.common_cancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(dialogContext, true),
|
||||
child: Text(context.l10n.community_join),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (result == true && context.mounted) {
|
||||
await _joinCommunity(context, community, addPublicChannel);
|
||||
} else if (context.mounted) {
|
||||
// User cancelled - go back
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _joinCommunity(
|
||||
BuildContext context,
|
||||
Community community,
|
||||
bool addPublicChannel,
|
||||
) async {
|
||||
// Save community to local storage
|
||||
await _communityStore.addCommunity(community);
|
||||
|
||||
// Optionally add the community public channel to the device
|
||||
if (addPublicChannel && context.mounted) {
|
||||
final connector = context.read<MeshCoreConnector>();
|
||||
final nextIndex = _findNextAvailableChannelIndex(connector);
|
||||
|
||||
if (nextIndex != null) {
|
||||
final psk = community.deriveCommunityPublicPsk();
|
||||
final channelName = '${community.name} Public';
|
||||
connector.setChannel(nextIndex, channelName, psk);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.community_joined(community.name)),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
|
||||
// Return to previous screen
|
||||
Navigator.pop(context, community);
|
||||
}
|
||||
}
|
||||
|
||||
int? _findNextAvailableChannelIndex(MeshCoreConnector connector) {
|
||||
final usedIndices = connector.channels.map((c) => c.index).toSet();
|
||||
for (int i = 0; i < connector.maxChannels; i++) {
|
||||
if (!usedIndices.contains(i)) return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import '../models/community.dart';
|
||||
import 'prefs_manager.dart';
|
||||
|
||||
/// Persists communities to local storage using SharedPreferences.
|
||||
///
|
||||
/// Communities are stored as a JSON array under a single key.
|
||||
/// Each community contains its secret K, so this data should
|
||||
/// be considered sensitive (though device encryption handles security).
|
||||
class CommunityStore {
|
||||
static const String _communitiesKey = 'communities_v1';
|
||||
|
||||
/// Load all communities from storage
|
||||
Future<List<Community>> loadCommunities() async {
|
||||
final prefs = PrefsManager.instance;
|
||||
final jsonString = prefs.getString(_communitiesKey);
|
||||
if (jsonString == null || jsonString.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
final jsonList = jsonDecode(jsonString) as List<dynamic>;
|
||||
return jsonList
|
||||
.map((json) => Community.fromJson(json as Map<String, dynamic>))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
// If JSON is corrupted, return empty list
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Save all communities to storage
|
||||
Future<void> saveCommunities(List<Community> communities) async {
|
||||
final prefs = PrefsManager.instance;
|
||||
final jsonList = communities.map((c) => c.toJson()).toList();
|
||||
await prefs.setString(_communitiesKey, jsonEncode(jsonList));
|
||||
}
|
||||
|
||||
/// Add a new community
|
||||
Future<void> addCommunity(Community community) async {
|
||||
final communities = await loadCommunities();
|
||||
|
||||
// Check if community with same ID already exists
|
||||
final existingIndex = communities.indexWhere((c) => c.id == community.id);
|
||||
if (existingIndex >= 0) {
|
||||
// Replace existing
|
||||
communities[existingIndex] = community;
|
||||
} else {
|
||||
communities.add(community);
|
||||
}
|
||||
|
||||
await saveCommunities(communities);
|
||||
}
|
||||
|
||||
/// Update an existing community
|
||||
Future<void> updateCommunity(Community community) async {
|
||||
final communities = await loadCommunities();
|
||||
final index = communities.indexWhere((c) => c.id == community.id);
|
||||
if (index >= 0) {
|
||||
communities[index] = community;
|
||||
await saveCommunities(communities);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a community by ID
|
||||
Future<void> removeCommunity(String communityId) async {
|
||||
final communities = await loadCommunities();
|
||||
communities.removeWhere((c) => c.id == communityId);
|
||||
await saveCommunities(communities);
|
||||
}
|
||||
|
||||
/// Get a community by ID
|
||||
Future<Community?> getCommunity(String communityId) async {
|
||||
final communities = await loadCommunities();
|
||||
try {
|
||||
return communities.firstWhere((c) => c.id == communityId);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a community with the same secret already exists
|
||||
/// (to prevent duplicate imports from QR scanning)
|
||||
Future<Community?> findByCommunityId(String cid) async {
|
||||
final communities = await loadCommunities();
|
||||
try {
|
||||
return communities.firstWhere((c) => c.communityId == cid);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a hashtag channel to a community
|
||||
Future<void> addHashtagChannel(
|
||||
String communityId,
|
||||
String hashtag,
|
||||
) async {
|
||||
final community = await getCommunity(communityId);
|
||||
if (community != null) {
|
||||
final updated = community.addHashtagChannel(hashtag);
|
||||
await updateCommunity(updated);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a hashtag channel from a community
|
||||
Future<void> removeHashtagChannel(
|
||||
String communityId,
|
||||
String hashtag,
|
||||
) async {
|
||||
final community = await getCommunity(communityId);
|
||||
if (community != null) {
|
||||
final updated = community.removeHashtagChannel(hashtag);
|
||||
await updateCommunity(updated);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
/// A reusable QR code display widget for sharing data.
|
||||
///
|
||||
/// Features:
|
||||
/// - Configurable size and colors
|
||||
/// - Optional logo/icon in center
|
||||
/// - Automatic theming (light/dark mode aware)
|
||||
/// - Title and instructions
|
||||
class QrCodeDisplay extends StatelessWidget {
|
||||
/// The data to encode in the QR code
|
||||
final String data;
|
||||
|
||||
/// Size of the QR code (width and height)
|
||||
final double size;
|
||||
|
||||
/// Optional widget to display in the center (e.g., app logo)
|
||||
final Widget? embeddedImage;
|
||||
|
||||
/// Size of the embedded image (if provided)
|
||||
final double embeddedImageSize;
|
||||
|
||||
/// Title displayed above the QR code
|
||||
final String? title;
|
||||
|
||||
/// Instructions displayed below the QR code
|
||||
final String? instructions;
|
||||
|
||||
/// Background color of the QR code (defaults to white)
|
||||
final Color? backgroundColor;
|
||||
|
||||
/// Foreground color of the QR code modules (defaults to black)
|
||||
final Color? foregroundColor;
|
||||
|
||||
/// Padding around the QR code
|
||||
final EdgeInsets padding;
|
||||
|
||||
/// Error correction level
|
||||
final int errorCorrectionLevel;
|
||||
|
||||
const QrCodeDisplay({
|
||||
super.key,
|
||||
required this.data,
|
||||
this.size = 200,
|
||||
this.embeddedImage,
|
||||
this.embeddedImageSize = 50,
|
||||
this.title,
|
||||
this.instructions,
|
||||
this.backgroundColor,
|
||||
this.foregroundColor,
|
||||
this.padding = const EdgeInsets.all(16),
|
||||
this.errorCorrectionLevel = QrErrorCorrectLevel.M,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final isDark = theme.brightness == Brightness.dark;
|
||||
|
||||
// Default colors based on theme
|
||||
final bgColor = backgroundColor ?? Colors.white;
|
||||
final fgColor = foregroundColor ?? Colors.black;
|
||||
|
||||
return Padding(
|
||||
padding: padding,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (title != null) ...[
|
||||
Text(
|
||||
title!,
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
|
||||
// QR code container with rounded corners
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: isDark
|
||||
? null
|
||||
: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.1),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: embeddedImage != null
|
||||
? _buildQrWithEmbeddedImage(fgColor, bgColor)
|
||||
: _buildSimpleQr(fgColor, bgColor),
|
||||
),
|
||||
|
||||
if (instructions != null) ...[
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
instructions!,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSimpleQr(Color fgColor, Color bgColor) {
|
||||
return QrImageView(
|
||||
data: data,
|
||||
version: QrVersions.auto,
|
||||
size: size,
|
||||
backgroundColor: bgColor,
|
||||
errorCorrectionLevel: errorCorrectionLevel,
|
||||
eyeStyle: QrEyeStyle(
|
||||
eyeShape: QrEyeShape.square,
|
||||
color: fgColor,
|
||||
),
|
||||
dataModuleStyle: QrDataModuleStyle(
|
||||
dataModuleShape: QrDataModuleShape.square,
|
||||
color: fgColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQrWithEmbeddedImage(Color fgColor, Color bgColor) {
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
QrImageView(
|
||||
data: data,
|
||||
version: QrVersions.auto,
|
||||
size: size,
|
||||
backgroundColor: bgColor,
|
||||
// Use higher error correction when embedding image
|
||||
errorCorrectionLevel: QrErrorCorrectLevel.H,
|
||||
eyeStyle: QrEyeStyle(
|
||||
eyeShape: QrEyeShape.square,
|
||||
color: fgColor,
|
||||
),
|
||||
dataModuleStyle: QrDataModuleStyle(
|
||||
dataModuleShape: QrDataModuleShape.square,
|
||||
color: fgColor,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: embeddedImageSize,
|
||||
height: embeddedImageSize,
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: embeddedImage,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Dialog to display a QR code for sharing
|
||||
class QrCodeShareDialog extends StatelessWidget {
|
||||
final String data;
|
||||
final String? title;
|
||||
final String? instructions;
|
||||
final Widget? embeddedImage;
|
||||
|
||||
const QrCodeShareDialog({
|
||||
super.key,
|
||||
required this.data,
|
||||
this.title,
|
||||
this.instructions,
|
||||
this.embeddedImage,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
QrCodeDisplay(
|
||||
data: data,
|
||||
size: 250,
|
||||
title: title,
|
||||
instructions: instructions,
|
||||
embeddedImage: embeddedImage,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: FilledButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Done'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Show the dialog
|
||||
static Future<void> show({
|
||||
required BuildContext context,
|
||||
required String data,
|
||||
String? title,
|
||||
String? instructions,
|
||||
Widget? embeddedImage,
|
||||
}) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (context) => QrCodeShareDialog(
|
||||
data: data,
|
||||
title: title,
|
||||
instructions: instructions,
|
||||
embeddedImage: embeddedImage,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||
|
||||
/// A reusable QR code scanner widget that can be embedded anywhere.
|
||||
///
|
||||
/// Features:
|
||||
/// - Configurable scan window overlay
|
||||
/// - Flash toggle button
|
||||
/// - Camera switch button (front/back)
|
||||
/// - Customizable callbacks for scan results
|
||||
/// - Optional validation function for QR data
|
||||
/// - Automatic pause when not visible
|
||||
/// - Debouncing to prevent duplicate scans
|
||||
class QrScannerWidget extends StatefulWidget {
|
||||
/// Called when a valid QR code is scanned
|
||||
final void Function(String data) onScanned;
|
||||
|
||||
/// Optional validator - return true if the QR data is valid
|
||||
final bool Function(String data)? validator;
|
||||
|
||||
/// Optional error callback when validation fails
|
||||
final void Function(String data)? onValidationFailed;
|
||||
|
||||
/// Whether to show the flash toggle button
|
||||
final bool showFlashButton;
|
||||
|
||||
/// Whether to show the camera switch button
|
||||
final bool showCameraSwitchButton;
|
||||
|
||||
/// Custom overlay widget (defaults to scan window frame)
|
||||
final Widget? overlay;
|
||||
|
||||
/// Instructions text shown below the scan window
|
||||
final String? instructions;
|
||||
|
||||
/// Whether to continue scanning after first successful scan
|
||||
final bool continuousScanning;
|
||||
|
||||
/// Debounce duration to prevent duplicate scans
|
||||
final Duration debounceDuration;
|
||||
|
||||
const QrScannerWidget({
|
||||
super.key,
|
||||
required this.onScanned,
|
||||
this.validator,
|
||||
this.onValidationFailed,
|
||||
this.showFlashButton = true,
|
||||
this.showCameraSwitchButton = true,
|
||||
this.overlay,
|
||||
this.instructions,
|
||||
this.continuousScanning = false,
|
||||
this.debounceDuration = const Duration(milliseconds: 500),
|
||||
});
|
||||
|
||||
@override
|
||||
State<QrScannerWidget> createState() => _QrScannerWidgetState();
|
||||
}
|
||||
|
||||
class _QrScannerWidgetState extends State<QrScannerWidget>
|
||||
with WidgetsBindingObserver {
|
||||
late MobileScannerController _controller;
|
||||
bool _hasScanned = false;
|
||||
String? _lastScannedData;
|
||||
DateTime? _lastScanTime;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_controller = MobileScannerController(
|
||||
detectionSpeed: DetectionSpeed.normal,
|
||||
facing: CameraFacing.back,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
// Handle app lifecycle changes - pause/resume scanner
|
||||
if (!_controller.value.hasCameraPermission) return;
|
||||
|
||||
switch (state) {
|
||||
case AppLifecycleState.resumed:
|
||||
_controller.start();
|
||||
break;
|
||||
case AppLifecycleState.inactive:
|
||||
case AppLifecycleState.paused:
|
||||
case AppLifecycleState.detached:
|
||||
case AppLifecycleState.hidden:
|
||||
_controller.stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleDetection(BarcodeCapture capture) {
|
||||
// Prevent duplicate scans
|
||||
if (_hasScanned && !widget.continuousScanning) return;
|
||||
|
||||
final List<Barcode> barcodes = capture.barcodes;
|
||||
for (final barcode in barcodes) {
|
||||
final String? rawValue = barcode.rawValue;
|
||||
if (rawValue == null || rawValue.isEmpty) continue;
|
||||
|
||||
// Debounce - ignore if same data scanned too quickly
|
||||
final now = DateTime.now();
|
||||
if (_lastScannedData == rawValue &&
|
||||
_lastScanTime != null &&
|
||||
now.difference(_lastScanTime!) < widget.debounceDuration) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_lastScannedData = rawValue;
|
||||
_lastScanTime = now;
|
||||
|
||||
// Validate if validator provided
|
||||
if (widget.validator != null && !widget.validator!(rawValue)) {
|
||||
widget.onValidationFailed?.call(rawValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Mark as scanned to prevent duplicates
|
||||
if (!widget.continuousScanning) {
|
||||
setState(() {
|
||||
_hasScanned = true;
|
||||
});
|
||||
_controller.stop();
|
||||
}
|
||||
|
||||
// Notify callback
|
||||
widget.onScanned(rawValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the scanner to allow scanning again
|
||||
void resetScanner() {
|
||||
setState(() {
|
||||
_hasScanned = false;
|
||||
_lastScannedData = null;
|
||||
_lastScanTime = null;
|
||||
});
|
||||
_controller.start();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
// Scanner view
|
||||
MobileScanner(
|
||||
controller: _controller,
|
||||
onDetect: _handleDetection,
|
||||
errorBuilder: (context, error, child) {
|
||||
return _buildErrorWidget(context, error);
|
||||
},
|
||||
),
|
||||
|
||||
// Overlay
|
||||
widget.overlay ?? _buildDefaultOverlay(context),
|
||||
|
||||
// Control buttons
|
||||
Positioned(
|
||||
bottom: 16,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: _buildControls(context),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDefaultOverlay(BuildContext context) {
|
||||
return ColorFiltered(
|
||||
colorFilter: ColorFilter.mode(
|
||||
Colors.black.withValues(alpha: 0.5),
|
||||
BlendMode.srcOut,
|
||||
),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.black,
|
||||
backgroundBlendMode: BlendMode.dstOut,
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
height: 250,
|
||||
width: 250,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red, // This color is used for cutout
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
if (widget.instructions != null) ...[
|
||||
const SizedBox(height: 24),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0.7),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
widget.instructions!,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildControls(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (widget.showFlashButton)
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _controller,
|
||||
builder: (context, state, child) {
|
||||
return IconButton.filled(
|
||||
onPressed: () => _controller.toggleTorch(),
|
||||
icon: Icon(
|
||||
state.torchState == TorchState.on
|
||||
? Icons.flash_on
|
||||
: Icons.flash_off,
|
||||
),
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.black54,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (widget.showFlashButton && widget.showCameraSwitchButton)
|
||||
const SizedBox(width: 24),
|
||||
if (widget.showCameraSwitchButton)
|
||||
IconButton.filled(
|
||||
onPressed: () => _controller.switchCamera(),
|
||||
icon: const Icon(Icons.cameraswitch),
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.black54,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorWidget(BuildContext context, MobileScannerException error) {
|
||||
String message;
|
||||
IconData icon;
|
||||
|
||||
switch (error.errorCode) {
|
||||
case MobileScannerErrorCode.permissionDenied:
|
||||
message = 'Camera permission denied.\nPlease enable camera access in settings.';
|
||||
icon = Icons.no_photography;
|
||||
break;
|
||||
case MobileScannerErrorCode.unsupported:
|
||||
message = 'Camera not supported on this device.';
|
||||
icon = Icons.videocam_off;
|
||||
break;
|
||||
default:
|
||||
message = 'Failed to start camera.\n${error.errorDetails?.message ?? ''}';
|
||||
icon = Icons.error_outline;
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 64, color: Colors.grey),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
message,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600],
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A simpler scanner overlay with just corner brackets
|
||||
class ScannerCornerOverlay extends StatelessWidget {
|
||||
final double scanWindowSize;
|
||||
final Color borderColor;
|
||||
final double borderWidth;
|
||||
final double cornerLength;
|
||||
|
||||
const ScannerCornerOverlay({
|
||||
super.key,
|
||||
this.scanWindowSize = 250,
|
||||
this.borderColor = Colors.white,
|
||||
this.borderWidth = 3,
|
||||
this.cornerLength = 30,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: scanWindowSize,
|
||||
height: scanWindowSize,
|
||||
child: CustomPaint(
|
||||
painter: _CornerPainter(
|
||||
color: borderColor,
|
||||
strokeWidth: borderWidth,
|
||||
cornerLength: cornerLength,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CornerPainter extends CustomPainter {
|
||||
final Color color;
|
||||
final double strokeWidth;
|
||||
final double cornerLength;
|
||||
|
||||
_CornerPainter({
|
||||
required this.color,
|
||||
required this.strokeWidth,
|
||||
required this.cornerLength,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = color
|
||||
..strokeWidth = strokeWidth
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeCap = StrokeCap.round;
|
||||
|
||||
final path = Path();
|
||||
|
||||
// Top-left corner
|
||||
path.moveTo(0, cornerLength);
|
||||
path.lineTo(0, 0);
|
||||
path.lineTo(cornerLength, 0);
|
||||
|
||||
// Top-right corner
|
||||
path.moveTo(size.width - cornerLength, 0);
|
||||
path.lineTo(size.width, 0);
|
||||
path.lineTo(size.width, cornerLength);
|
||||
|
||||
// Bottom-right corner
|
||||
path.moveTo(size.width, size.height - cornerLength);
|
||||
path.lineTo(size.width, size.height);
|
||||
path.lineTo(size.width - cornerLength, size.height);
|
||||
|
||||
// Bottom-left corner
|
||||
path.moveTo(cornerLength, size.height);
|
||||
path.lineTo(0, size.height);
|
||||
path.lineTo(0, size.height - cornerLength);
|
||||
|
||||
canvas.drawPath(path, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import Foundation
|
||||
|
||||
import flutter_blue_plus_darwin
|
||||
import flutter_local_notifications
|
||||
import mobile_scanner
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
@@ -16,6 +17,7 @@ import wakelock_plus
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin"))
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
|
||||
@@ -12,5 +12,7 @@
|
||||
<true/>
|
||||
<key>com.apple.security.device.bluetooth</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -30,5 +30,7 @@
|
||||
<string>NSApplication</string>
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>MeshCore needs Bluetooth to communicate with LoRa mesh devices</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app uses the camera to scan QR codes for joining communities.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -8,5 +8,7 @@
|
||||
<true/>
|
||||
<key>com.apple.security.device.bluetooth</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -453,6 +453,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
mobile_scanner:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mobile_scanner
|
||||
sha256: "0b466a0a8a211b366c2e87f3345715faef9b6011c7147556ad22f37de6ba3173"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.11"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -605,6 +613,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5+1"
|
||||
qr:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: qr
|
||||
sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
qr_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: qr_flutter
|
||||
sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -53,6 +53,8 @@ dependencies:
|
||||
wakelock_plus: ^1.2.8
|
||||
characters: ^1.4.0
|
||||
package_info_plus: ^8.0.0
|
||||
mobile_scanner: ^6.0.0 # QR/barcode scanning
|
||||
qr_flutter: ^4.1.0 # QR code generation
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -78,6 +80,9 @@ flutter:
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
assets:
|
||||
- assets/images/
|
||||
|
||||
flutter_launcher_icons:
|
||||
android: true
|
||||
ios: true
|
||||
|
||||
+121
-1
@@ -1 +1,121 @@
|
||||
{}
|
||||
{
|
||||
"bg": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"de": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"es": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"it": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"nl": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"pl": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"pt": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"sk": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"sl": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"sv": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
],
|
||||
|
||||
"zh": [
|
||||
"community_regenerateSecret",
|
||||
"community_regenerateSecretConfirm",
|
||||
"community_regenerate",
|
||||
"community_secretRegenerated",
|
||||
"community_updateSecret",
|
||||
"community_secretUpdated",
|
||||
"community_scanToUpdateSecret"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user