diff --git a/LIBCWTCH-GO-MACOS.version b/LIBCWTCH-GO-MACOS.version index 3c6d2eca..dcda997e 100644 --- a/LIBCWTCH-GO-MACOS.version +++ b/LIBCWTCH-GO-MACOS.version @@ -1 +1 @@ -2021-12-08-00-32-v1.5.0-7-g28a13aa \ No newline at end of file +2021-12-11-02-00-v1.5.0-9-gaa102bd \ No newline at end of file diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index 33bf0526..8f72ef13 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -2021-12-08-05-32-v1.5.0-7-g28a13aa \ No newline at end of file +2021-12-11-07-00-v1.5.0-9-gaa102bd \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index b3793cca..b08f6de8 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,16 @@ { "@@locale": "de", - "@@last_modified": "2021-11-11T01:02:08+01:00", + "@@last_modified": "2021-11-21T17:42:07+01:00", + "manageKnownServersShort": "Servers", + "manageKnownServersLong": "Manage Known Servers", + "displayNameTooltip": "Please enter a display name", + "manageKnownServersButton": "Manage Known Servers", + "fieldDescriptionLabel": "Description", + "groupsOnThisServerLabel": "Groups I am in hosted on this server", + "importLocalServerButton": "Import %1", + "importLocalServerSelectText": "Select Local Server", + "importLocalServerLabel": "Import a locally hosted server", + "savePeerHistoryDescription": "Legt fest, ob ein mit dem anderen Nutzer verknüpfter Verlauf gelöscht werden soll oder nicht.", "newMessagesLabel": "New Messages", "localeRU": "Russian", "copyServerKeys": "Copy keys", @@ -55,7 +65,6 @@ "peerOfflineMessage": "Anderer Nutzer ist offline, Nachrichten können derzeit nicht zugestellt werden", "blockBtn": "Anderen Nutzer blockieren", "savePeerHistory": "Peer-Verlauf speichern", - "savePeerHistoryDescription": "Legt fest, ob ein mit dem anderen Nutzer verknüpfter Verlauf gelöscht werden soll oder nicht.", "dontSavePeerHistory": "Verlauf mit anderem Nutzer löschen", "unblockBtn": "Anderen Nutzer entsperren", "blockUnknownLabel": "Unbekannte Peers blockieren", @@ -190,7 +199,6 @@ "radioNoPassword": "Unverschlüsselt (kein Passwort)", "radioUsePassword": "Passwort", "copiedToClipboardNotification": "in die Zwischenablage kopiert", - "copyBtn": "Kopieren", "editProfile": "Profil bearbeiten", "newProfile": "Neues Profil", "defaultProfileName": "Alice", @@ -210,6 +218,7 @@ "acceptGroupInviteLabel": "Möchtest Du die Einladung annehmen", "newGroupBtn": "Neue Gruppe anlegen", "copiedClipboardNotification": "in die Zwischenablage kopiert", + "copyBtn": "Kopieren", "pendingLabel": "Bestätigung ausstehend", "acknowledgedLabel": "bestätigt", "couldNotSendMsgError": "Nachricht konnte nicht gesendet werden", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 70f13992..4069f313 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,16 @@ { "@@locale": "en", - "@@last_modified": "2021-11-11T01:02:08+01:00", + "@@last_modified": "2021-11-21T17:42:07+01:00", + "manageKnownServersShort": "Servers", + "manageKnownServersLong": "Manage Known Servers", + "displayNameTooltip": "Please enter a display name", + "manageKnownServersButton": "Manage Known Servers", + "fieldDescriptionLabel": "Description", + "groupsOnThisServerLabel": "Groups I am in hosted on this server", + "importLocalServerButton": "Import %1", + "importLocalServerSelectText": "Select Local Server", + "importLocalServerLabel": "Import a locally hosted server", + "savePeerHistoryDescription": "Determines whether to delete any history associated with the contact.", "newMessagesLabel": "New Messages", "localeRU": "Russian", "copyServerKeys": "Copy keys", @@ -55,7 +65,6 @@ "peerOfflineMessage": "Contact is offline, messages can't be delivered right now", "blockBtn": "Block Contact", "savePeerHistory": "Save History", - "savePeerHistoryDescription": "Determines whether or not to delete any history associated with the contact.", "dontSavePeerHistory": "Delete History", "unblockBtn": "Unblock Contact", "blockUnknownLabel": "Block Unknown Contacts", @@ -190,7 +199,6 @@ "radioNoPassword": "Unencrypted (No password)", "radioUsePassword": "Password", "copiedToClipboardNotification": "Copied to Clipboard", - "copyBtn": "Copy", "editProfile": "Edit Profille", "newProfile": "New Profile", "defaultProfileName": "Alice", @@ -210,6 +218,7 @@ "acceptGroupInviteLabel": "Do you want to accept the invitation to", "newGroupBtn": "Create new group", "copiedClipboardNotification": "Copied to clipboard", + "copyBtn": "Copy", "pendingLabel": "Pending", "acknowledgedLabel": "Acknowledged", "couldNotSendMsgError": "Could not send this message", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index ba0a54bc..bb62828f 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,16 @@ { "@@locale": "es", - "@@last_modified": "2021-11-11T01:02:08+01:00", + "@@last_modified": "2021-11-21T17:42:07+01:00", + "manageKnownServersShort": "Servers", + "manageKnownServersLong": "Manage Known Servers", + "displayNameTooltip": "Please enter a display name", + "manageKnownServersButton": "Manage Known Servers", + "fieldDescriptionLabel": "Description", + "groupsOnThisServerLabel": "Groups I am in hosted on this server", + "importLocalServerButton": "Import %1", + "importLocalServerSelectText": "Select Local Server", + "importLocalServerLabel": "Import a locally hosted server", + "savePeerHistoryDescription": "Determina si eliminar o no el historial asociado con el contacto.", "newMessagesLabel": "New Messages", "localeRU": "Russian", "copyServerKeys": "Copy keys", @@ -55,7 +65,6 @@ "peerOfflineMessage": "Este contacto no está en línea, los mensajes no pueden ser entregados en este momento", "blockBtn": "Bloquear contacto", "savePeerHistory": "Guardar el historial con contacto", - "savePeerHistoryDescription": "Determina si eliminar o no el historial asociado con el contacto.", "dontSavePeerHistory": "Eliminar historial de contacto", "unblockBtn": "Desbloquear contacto", "blockUnknownLabel": "Bloquear conexiones desconocidas", @@ -190,7 +199,6 @@ "radioNoPassword": "Sin cifrado (sin contraseña)", "radioUsePassword": "Contraseña", "copiedToClipboardNotification": "Copiado al portapapeles", - "copyBtn": "Copiar", "editProfile": "Editar perfil", "newProfile": "Nuevo perfil", "defaultProfileName": "Alicia", @@ -210,6 +218,7 @@ "acceptGroupInviteLabel": "¿Quieres aceptar la invitación a ", "newGroupBtn": "Crear un nuevo grupo de chat", "copiedClipboardNotification": "Copiado al portapapeles", + "copyBtn": "Copiar", "pendingLabel": "Pendiente", "acknowledgedLabel": "Reconocido", "couldNotSendMsgError": "No se pudo enviar este mensaje", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index bb6e8810..fdd9808f 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,8 +1,18 @@ { "@@locale": "fr", - "@@last_modified": "2021-11-11T01:02:08+01:00", - "newMessagesLabel": "New Messages", - "localeRU": "Russian", + "@@last_modified": "2021-11-21T17:42:07+01:00", + "manageKnownServersShort": "Servers", + "manageKnownServersLong": "Manage Known Servers", + "displayNameTooltip": "Veuillez entrer un nom d'usage s'il vous plaît", + "manageKnownServersButton": "Gérer les serveurs connus", + "fieldDescriptionLabel": "Description", + "groupsOnThisServerLabel": "Les groupes dont je fais partie sont hébergés sur ce serveur", + "importLocalServerButton": "Importer %1", + "importLocalServerSelectText": "Sélectionnez le serveur local", + "importLocalServerLabel": "Importer un serveur hébergé localement", + "savePeerHistoryDescription": "Détermine s'il faut ou non supprimer tout historique associé au contact.", + "newMessagesLabel": "Nouveaux messages", + "localeRU": "Russe", "copyServerKeys": "Copier les clés", "verfiyResumeButton": "Vérifier\/reprendre", "fileCheckingStatus": "Vérification de l'état du téléchargement", @@ -55,7 +65,6 @@ "peerOfflineMessage": "Le contact est hors ligne, les messages ne peuvent pas être transmis pour le moment.", "blockBtn": "Bloquer le contact", "savePeerHistory": "Enregistrer l'historique", - "savePeerHistoryDescription": "Détermine s'il faut ou non supprimer tout historique associé au contact.", "dontSavePeerHistory": "Supprimer l'historique", "unblockBtn": "Débloquer le contact", "blockUnknownLabel": "Bloquer les pairs inconnus", @@ -190,7 +199,6 @@ "radioNoPassword": "Non chiffré (pas de mot de passe)", "radioUsePassword": "Mot de passe", "copiedToClipboardNotification": "Copié dans le presse-papier", - "copyBtn": "Copier", "editProfile": "Modifier le profil", "newProfile": "Nouveau profil", "defaultProfileName": "Alice", @@ -210,6 +218,7 @@ "acceptGroupInviteLabel": "Voulez-vous accepter l'invitation au groupe", "newGroupBtn": "Créer un nouveau groupe", "copiedClipboardNotification": "Copié dans le presse-papier", + "copyBtn": "Copier", "pendingLabel": "En attente", "acknowledgedLabel": "Accusé de réception", "couldNotSendMsgError": "Impossible d'envoyer ce message", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index fce93d69..113411cd 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,52 +1,62 @@ { "@@locale": "it", - "@@last_modified": "2021-11-11T01:02:08+01:00", - "newMessagesLabel": "New Messages", - "localeRU": "Russian", - "copyServerKeys": "Copy keys", - "verfiyResumeButton": "Verify\/resume", - "fileCheckingStatus": "Checking download status", - "fileInterrupted": "Interrupted", - "fileSavedTo": "Saved to", - "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", - "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", - "deleteServerConfirmBtn": "Really delete server", - "deleteServerSuccess": "Successfully deleted server", - "enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server", - "copyAddress": "Copy Address", - "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", - "settingServers": "Hosting Servers", - "enterServerPassword": "Enter password to unlock server", - "unlockProfileTip": "Please create or unlock a profile to begin!", - "unlockServerTip": "Please create or unlock a server to begin!", - "addServerTooltip": "Add new server", - "serversManagerTitleShort": "Servers", - "serversManagerTitleLong": "Servers You Host", - "saveServerButton": "Save Server", - "serverAutostartDescription": "Controls if the application will automatically launch the server on start", - "serverAutostartLabel": "Autostart", - "serverEnabledDescription": "Start or stop the server", - "serverEnabled": "Server Enabled", - "serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared", - "serverDescriptionLabel": "Server Description", - "serverAddress": "Server Address", - "editServerTitle": "Edit Server", - "addServerTitle": "Add Server", - "titleManageProfilesShort": "Profiles", - "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", + "@@last_modified": "2021-11-21T17:42:07+01:00", + "manageKnownServersShort": "Servers", + "manageKnownServersLong": "Manage Known Servers", + "displayNameTooltip": "Please enter a display name", + "manageKnownServersButton": "Manage Known Servers", + "fieldDescriptionLabel": "Description", + "groupsOnThisServerLabel": "Groups I am in hosted on this server", + "importLocalServerButton": "Import %1", + "importLocalServerSelectText": "Select Local Server", + "importLocalServerLabel": "Import a locally hosted server", + "savePeerHistoryDescription": "Determina se eliminare o meno ogni cronologia eventualmente associata al peer.", + "newMessagesLabel": "Nuovi messaggi", + "localeRU": "Russo", + "copyServerKeys": "Copia chiavi", + "verfiyResumeButton": "Verifica\/riprendi", + "fileCheckingStatus": "Controllo dello stato del download", + "fileInterrupted": "Interrotto", + "fileSavedTo": "Salvato in", + "plainServerDescription": "Ti raccomandiamo di proteggere i tuoi server Cwtch con una password. Se non imposti una password su questo server, chiunque abbia accesso a questo dispositivo potrebbe essere in grado di accedere alle relativ informazioni, compresi dati sensibili come le chiavi crittografiche.", + "encryptedServerDescription": "Criptare un server con una password lo protegge da altre persone che potrebbero usare questo dispositivo. I server criptati non possono essere decriptati, visualizzati o accessibili finché non viene inserita la password corretta per sbloccarli.", + "deleteServerConfirmBtn": "Elimina davvero il server", + "deleteServerSuccess": "Server eliminato con successo", + "enterCurrentPasswordForDeleteServer": "Inserisci la password attuale per eliminare questo server", + "copyAddress": "Copia indirizzo", + "settingServersDescription": "L'esperimento dei server di hosting permette di allocare e gestire i server Cwtch", + "settingServers": "Server di hosting", + "enterServerPassword": "Inserisci la password per sbloccare il server", + "unlockProfileTip": "Crea o sblocca un profilo per iniziare!", + "unlockServerTip": "Crea o sblocca un server per iniziare!", + "addServerTooltip": "Aggiungi nuovo server", + "serversManagerTitleShort": "Gestisci i server", + "serversManagerTitleLong": "Server che gestisci", + "saveServerButton": "Salva il server", + "serverAutostartDescription": "Controlla se l'applicazione avvierà automaticamente il server all'avvio", + "serverAutostartLabel": "Avvio automatico", + "serverEnabledDescription": "Avvia o arresta il server", + "serverEnabled": "Server abilitato", + "serverDescriptionDescription": "La tua descrizione del server solo per gestione personale, non sarà mai condivisa", + "serverDescriptionLabel": "Descrizione del server", + "serverAddress": "Indirizzo del server", + "editServerTitle": "Modifica il server", + "addServerTitle": "Aggiungi server", + "titleManageProfilesShort": "Profili", + "descriptionStreamerMode": "Se attivata, questa opzione rende l'applicazione visivamente più privata per lo streaming o la presentazione, ad esempio nascondendo il profilo e gli indirizzi di contatto", "descriptionFileSharing": "L'esperimento di condivisione dei file ti consente di inviare e ricevere file dai contatti e dai gruppi di Cwtch. Tieni presente che la condivisione di un file con un gruppo farà sì che i membri di quel gruppo si colleghino con te direttamente su Cwtch per scaricarlo.", "settingFileSharing": "Condivisione file", "tooltipSendFile": "Invia file", "messageFileOffered": "Il contatto offre l'invio di un file", - "messageFileSent": "You sent a file", - "messageEnableFileSharing": "Enable the file sharing experiment to view this message.", - "labelFilesize": "Size", - "labelFilename": "Filename", - "downloadFileButton": "Download", - "openFolderButton": "Open Folder", - "retrievingManifestMessage": "Retrieving file information...", - "streamerModeLabel": "Streamer\/Presentation Mode", - "archiveConversation": "Archive this Conversation", + "messageFileSent": "Hai inviato un file", + "messageEnableFileSharing": "Abilita l'esperimento di condivisione dei file per visualizzare questo messaggio.", + "labelFilesize": "Dimensione", + "labelFilename": "Nome del file", + "downloadFileButton": "Scarica", + "openFolderButton": "Apri cartella", + "retrievingManifestMessage": "Recupero delle informazioni sul file in corso...", + "streamerModeLabel": "Modalità Streamer\/Presentazione", + "archiveConversation": "Archivia questa conversazione", "profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi", "addPeerTab": "Aggiungi un peer", "addPeer": "Aggiungi peer", @@ -55,29 +65,28 @@ "peerOfflineMessage": "Il peer è offline, i messaggi non possono essere recapitati in questo momento", "blockBtn": "Blocca il peer", "savePeerHistory": "Salva cronologia peer", - "savePeerHistoryDescription": "Determina se eliminare o meno ogni cronologia eventualmente associata al peer.", "dontSavePeerHistory": "Elimina cronologia dei peer", "unblockBtn": "Sblocca il peer", "blockUnknownLabel": "Blocca peer sconosciuti", - "blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings", + "blockUnknownConnectionsEnabledDescription": "Le connessioni da contatti sconosciuti sono bloccate. Puoi modificare questa impostazione in Impostazioni", "networkStatusConnecting": "Connessione alla rete e ai peer ...", - "showMessageButton": "Show Message", - "blockedMessageMessage": "This message is from a profile you have blocked.", - "placeholderEnterMessage": "Type a message...", - "plainProfileDescription": "We recommend that you protect your Cwtch profiles with a password. If you do not set a password on this profile then anyone who has access to this device may be able to access information about this profile, including contacts, messages and sensitive cryptographic keys.", - "encryptedProfileDescription": "Encrypting a profile with a password protects it from other people who may also use this device. Encrypted profiles cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", - "addContactConfirm": "Add contact %1", - "addContact": "Add contact", - "contactGoto": "Go to conversation with %1", - "settingUIColumnOptionSame": "Same as portrait mode setting", - "settingUIColumnDouble14Ratio": "Double (1:4)", - "settingUIColumnDouble12Ratio": "Double (1:2)", - "settingUIColumnSingle": "Single", - "settingUIColumnLandscape": "UI Columns in Landscape Mode", - "settingUIColumnPortrait": "UI Columns in Portrait Mode", - "localePl": "Polish", - "tooltipRemoveThisQuotedMessage": "Remove quoted message.", - "tooltipReplyToThisMessage": "Reply to this message", + "showMessageButton": "Mostra il messaggio", + "blockedMessageMessage": "Questo messaggio proviene da un profilo che hai bloccato.", + "placeholderEnterMessage": "Scrivi un messaggio...", + "plainProfileDescription": "Ti raccomandiamo di proteggere i tuoi profili Cwtch con una password. Se non imposti una password su questo profilo, chiunque abbia accesso a questo dispositivo potrebbe essere in grado di accedere alle relative informazioni, compresi contatti, messaggi e altri dati sensibili come le chiavi crittografiche.", + "encryptedProfileDescription": "Criptare un profilo con una password lo protegge da altre persone che potrebbero usare questo dispositivo. I profili criptati non possono essere decriptati, visualizzati o accessibili finché non viene inserita la password corretta per sbloccarli.", + "addContactConfirm": "Aggiungi %1 come contatto", + "addContact": "Aggiungi contatto", + "contactGoto": "Vai alla conversazione con %1", + "settingUIColumnOptionSame": "Stessa impostazione della modalità verticale", + "settingUIColumnDouble14Ratio": "Doppia (1:4)", + "settingUIColumnDouble12Ratio": "Doppia (1:2)", + "settingUIColumnSingle": "Singola", + "settingUIColumnLandscape": "Colonne dell'interfaccia utente in modalità orizzontale", + "settingUIColumnPortrait": "Colonne dell'interfaccia utente in modalità verticale", + "localePl": "Polacco", + "tooltipRemoveThisQuotedMessage": "Rimuovi il messaggio citato.", + "tooltipReplyToThisMessage": "Rispondi a questo messaggio", "tooltipRejectContactRequest": "Rifiuta questa richiesta di contatto", "tooltipAcceptContactRequest": "Accetta questa richiesta di contatto.", "notificationNewMessageFromGroup": "Nuovo messaggio in un gruppo!", @@ -190,7 +199,6 @@ "radioNoPassword": "Non criptato (senza password)", "radioUsePassword": "Password", "copiedToClipboardNotification": "Copiato negli Appunti", - "copyBtn": "Copia", "editProfile": "Modifica profilo", "newProfile": "Nuovo profilo", "defaultProfileName": "Alice", @@ -210,6 +218,7 @@ "acceptGroupInviteLabel": "Vuoi accettare l'invito a", "newGroupBtn": "Crea un nuovo gruppo", "copiedClipboardNotification": "Copiato negli Appunti", + "copyBtn": "Copia", "pendingLabel": "In corso", "acknowledgedLabel": "Riconosciuto", "couldNotSendMsgError": "Impossibile inviare questo messaggio", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index aa2e5366..a8fd1a3e 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,8 +1,18 @@ { "@@locale": "pl", - "@@last_modified": "2021-11-11T01:02:08+01:00", - "newMessagesLabel": "New Messages", - "localeRU": "Russian", + "@@last_modified": "2021-11-21T17:42:07+01:00", + "manageKnownServersShort": "Servers", + "manageKnownServersLong": "Manage Known Servers", + "displayNameTooltip": "Please enter a display name", + "manageKnownServersButton": "Manage Known Servers", + "fieldDescriptionLabel": "Description", + "groupsOnThisServerLabel": "Groups I am in hosted on this server", + "importLocalServerButton": "Import %1", + "importLocalServerSelectText": "Select Local Server", + "importLocalServerLabel": "Import a locally hosted server", + "savePeerHistoryDescription": "Determines whether to delete any history associated with the contact.", + "newMessagesLabel": "Nowe wiadomości", + "localeRU": "Rosyjski", "copyServerKeys": "Kopiuj klucze", "verfiyResumeButton": "Zweryfikuj\/wznów", "fileCheckingStatus": "Sprawdzanie stanu pobierania", @@ -12,26 +22,26 @@ "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", "deleteServerConfirmBtn": "Naprawdę usuń serwer", "deleteServerSuccess": "Pomyślnie usunięto serwer", - "enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server", + "enterCurrentPasswordForDeleteServer": "Wprowadź aktualne hasło, aby usunąć ten serwer", "copyAddress": "Skopiuj adres", "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", - "settingServers": "Hosting Servers", - "enterServerPassword": "Enter password to unlock server", + "settingServers": "Hosting serwerów", + "enterServerPassword": "Wprowadź hasło, aby odblokować serwer", "unlockProfileTip": "Please create or unlock a profile to begin!", "unlockServerTip": "Please create or unlock a server to begin!", - "addServerTooltip": "Add new server", - "serversManagerTitleShort": "Servers", - "serversManagerTitleLong": "Servers You Host", - "saveServerButton": "Save Server", + "addServerTooltip": "Dodaj nowy serwer", + "serversManagerTitleShort": "Serwery", + "serversManagerTitleLong": "Serwery, które hostujesz", + "saveServerButton": "Zapisz serwer", "serverAutostartDescription": "Controls if the application will automatically launch the server on start", "serverAutostartLabel": "Autostart", - "serverEnabledDescription": "Start or stop the server", - "serverEnabled": "Server Enabled", + "serverEnabledDescription": "Uruchom lub zatrzymaj serwer", + "serverEnabled": "Serwer włączony", "serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared", - "serverDescriptionLabel": "Server Description", - "serverAddress": "Server Address", - "editServerTitle": "Edit Server", - "addServerTitle": "Add Server", + "serverDescriptionLabel": "Opis serwera", + "serverAddress": "Adres serwera", + "editServerTitle": "Edytuj serwer", + "addServerTitle": "Dodaj serwer", "titleManageProfilesShort": "Profile", "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "Eksperyment udostępniania plików pozwala na wysyłanie i odbieranie plików od kontaktów i grup Cwtch. Zauważ, że udostępnienie pliku grupie spowoduje, że członkowie tej grupy połączą się z Tobą bezpośrednio przez Cwtch, aby go pobrać.", @@ -55,70 +65,69 @@ "peerOfflineMessage": "Contact is offline, messages can't be delivered right now", "blockBtn": "Block Contact", "savePeerHistory": "Save History", - "savePeerHistoryDescription": "Determines whether or not to delete any history associated with the contact.", "dontSavePeerHistory": "Delete History", "unblockBtn": "Unblock Contact", "blockUnknownLabel": "Block Unknown Contacts", "blockUnknownConnectionsEnabledDescription": "Połączenia od nieznanych kontaktów są blokowane. Można to zmienić w Ustawieniach", "networkStatusConnecting": "Connecting to network and contacts...", - "showMessageButton": "Show Message", - "blockedMessageMessage": "This message is from a profile you have blocked.", - "placeholderEnterMessage": "Type a message...", + "showMessageButton": "Pokaż wiadomość", + "blockedMessageMessage": "Ta wiadomość pochodzi z profilu, który został przez Ciebie zablokowany.", + "placeholderEnterMessage": "Wpisz wiadomość...", "plainProfileDescription": "We recommend that you protect your Cwtch profiles with a password. If you do not set a password on this profile then anyone who has access to this device may be able to access information about this profile, including contacts, messages and sensitive cryptographic keys.", "encryptedProfileDescription": "Encrypting a profile with a password protects it from other people who may also use this device. Encrypted profiles cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", - "addContactConfirm": "Add contact %1", - "addContact": "Add contact", - "contactGoto": "Go to conversation with %1", - "settingUIColumnOptionSame": "Same as portrait mode setting", - "settingUIColumnDouble14Ratio": "Double (1:4)", - "settingUIColumnDouble12Ratio": "Double (1:2)", - "settingUIColumnSingle": "Single", + "addContactConfirm": "Dodaj kontakt %1", + "addContact": "Dodaj kontakt", + "contactGoto": "Przejdź do rozmowy z %1", + "settingUIColumnOptionSame": "Tak samo jak w przypadku trybu portretowego", + "settingUIColumnDouble14Ratio": "Podwójny (1:4)", + "settingUIColumnDouble12Ratio": "Podwójny (1:2)", + "settingUIColumnSingle": "Pojedynczy", "settingUIColumnLandscape": "UI Columns in Landscape Mode", "settingUIColumnPortrait": "UI Columns in Portrait Mode", - "localePl": "Polish", - "tooltipRemoveThisQuotedMessage": "Remove quoted message.", - "tooltipReplyToThisMessage": "Reply to this message", - "tooltipRejectContactRequest": "Reject this contact request", - "tooltipAcceptContactRequest": "Accept this contact request.", - "notificationNewMessageFromGroup": "New message in a group!", - "notificationNewMessageFromPeer": "New message from a contact!", - "tooltipHidePassword": "Hide Password", - "tooltipShowPassword": "Show Password", + "localePl": "Polski", + "tooltipRemoveThisQuotedMessage": "Usuń cytowaną wiadomość.", + "tooltipReplyToThisMessage": "Odpowiedz na tę wiadomość", + "tooltipRejectContactRequest": "Odrzuć tę prośbę o kontakt", + "tooltipAcceptContactRequest": "Zaakceptuj tę prośbę o kontakt.", + "notificationNewMessageFromGroup": "Nowa wiadomość w grupie!", + "notificationNewMessageFromPeer": "Nowa wiadomość od kontaktu!", + "tooltipHidePassword": "Ukryj hasło", + "tooltipShowPassword": "Pokaż hasło", "serverNotSynced": "Syncing New Messages (This can take some time)...", "groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.", - "shutdownCwtchAction": "Shutdown Cwtch", + "shutdownCwtchAction": "Zamknij Cwtch", "shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.", - "shutdownCwtchDialogTitle": "Shutdown Cwtch?", - "shutdownCwtchTooltip": "Shutdown Cwtch", - "malformedMessage": "Malformed message", - "profileDeleteSuccess": "Successfully deleted profile", - "debugLog": "Turn on console debug logging", - "torNetworkStatus": "Tor network status", + "shutdownCwtchDialogTitle": "Zamknąć Cwtch?", + "shutdownCwtchTooltip": "Zamknij Cwtch", + "malformedMessage": "Źle sformatowana wiadomość", + "profileDeleteSuccess": "Pomyślnie usunięto profil", + "debugLog": "Włącz logowanie debugowania konsoli", + "torNetworkStatus": "Stan sieci Tor", "addContactFirst": "Add or pick a contact to begin chatting.", "createProfileToBegin": "Please create or unlock a profile to begin", - "nickChangeSuccess": "Profile nickname changed successfully", + "nickChangeSuccess": "Nick w profilu został zmieniony pomyślnie", "addServerFirst": "You need to add a server before you can create a group", - "deleteProfileSuccess": "Successfully deleted profile", - "sendInvite": "Send a contact or group invite", - "sendMessage": "Send Message", - "cancel": "Cancel", + "deleteProfileSuccess": "Pomyślnie usunięto profil", + "sendInvite": "Wyślij kontakt lub zaproszenie do grupy", + "sendMessage": "Wyślij wiadomość", + "cancel": "Anuluj", "resetTor": "Reset", - "torStatus": "Tor Status", - "torVersion": "Tor Version", + "torStatus": "Status Tor", + "torVersion": "Wersja Tor", "sendAnInvitation": "You sent an invitation for: ", "contactSuggestion": "This is a contact suggestion for: ", - "rejected": "Rejected!", - "accepted": "Accepted!", + "rejected": "Odrzucone!", + "accepted": "Przyjęte!", "chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.", - "newPassword": "New Password", - "yesLeave": "Yes, Leave This Conversation", + "newPassword": "Nowe hasło", + "yesLeave": "Tak, wyjdź z tej rozmowy", "reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.", - "leaveGroup": "Leave This Conversation", + "leaveGroup": "Wyjdź z tej rozmowy", "inviteToGroup": "You have been invited to join a group:", "pasteAddressToAddContact": "Paste a cwtch address, invitation or key bundle here to add a new conversation", "tooltipAddContact": "Add a new contact or conversation", "titleManageContacts": "Conversations", - "titleManageServers": "Manage Servers", + "titleManageServers": "Zarządzaj serwerami", "dateNever": "Never", "dateLastYear": "Last Year", "dateYesterday": "Yesterday", @@ -190,7 +199,6 @@ "radioNoPassword": "Unencrypted (No password)", "radioUsePassword": "Password", "copiedToClipboardNotification": "Copied to Clipboard", - "copyBtn": "Copy", "editProfile": "Edit Profille", "newProfile": "New Profile", "defaultProfileName": "Alice", @@ -210,6 +218,7 @@ "acceptGroupInviteLabel": "Do you want to accept the invitation to", "newGroupBtn": "Create new group", "copiedClipboardNotification": "Copied to clipboard", + "copyBtn": "Copy", "pendingLabel": "Pending", "acknowledgedLabel": "Acknowledged", "couldNotSendMsgError": "Could not send this message", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 137e8a2b..533631d0 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,16 @@ { "@@locale": "pt", - "@@last_modified": "2021-11-11T01:02:08+01:00", + "@@last_modified": "2021-11-21T17:42:07+01:00", + "manageKnownServersShort": "Servers", + "manageKnownServersLong": "Manage Known Servers", + "displayNameTooltip": "Please enter a display name", + "manageKnownServersButton": "Manage Known Servers", + "fieldDescriptionLabel": "Description", + "groupsOnThisServerLabel": "Groups I am in hosted on this server", + "importLocalServerButton": "Import %1", + "importLocalServerSelectText": "Select Local Server", + "importLocalServerLabel": "Import a locally hosted server", + "savePeerHistoryDescription": "Determines whether to delete any history associated with the contact.", "newMessagesLabel": "New Messages", "localeRU": "Russian", "copyServerKeys": "Copy keys", @@ -55,7 +65,6 @@ "peerOfflineMessage": "Contact is offline, messages can't be delivered right now", "blockBtn": "Block Contact", "savePeerHistory": "Save History", - "savePeerHistoryDescription": "Determines whether or not to delete any history associated with the contact.", "dontSavePeerHistory": "Delete History", "unblockBtn": "Unblock Contact", "blockUnknownLabel": "Block Unknown Contacts", @@ -190,7 +199,6 @@ "radioNoPassword": "Unencrypted (No password)", "radioUsePassword": "Password", "copiedToClipboardNotification": "Copiado", - "copyBtn": "Copiar", "editProfile": "Edit Profille", "newProfile": "New Profile", "defaultProfileName": "Alice", @@ -210,6 +218,7 @@ "acceptGroupInviteLabel": "Você quer aceitar o convite para", "newGroupBtn": "Criar novo grupo", "copiedClipboardNotification": "Copiado", + "copyBtn": "Copiar", "pendingLabel": "Pendente", "acknowledgedLabel": "Confirmada", "couldNotSendMsgError": "Não deu para enviar esta mensagem", diff --git a/lib/model.dart b/lib/model.dart index 87ac2f36..82246ee0 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -120,6 +120,7 @@ class ProfileListState extends ChangeNotifier { } class ContactListState extends ChangeNotifier { + ProfileServerListState? servers; List _contacts = []; String _filter = ""; int get num => _contacts.length; @@ -131,6 +132,10 @@ class ContactListState extends ChangeNotifier { notifyListeners(); } + void connectServers(ProfileServerListState servers) { + this.servers = servers; + } + List filteredList() { if (!isFiltered) return contacts; return _contacts.where((ContactInfoState c) => c.onion.toLowerCase().startsWith(_filter) || (c.nickname.toLowerCase().contains(_filter))).toList(); @@ -138,11 +143,20 @@ class ContactListState extends ChangeNotifier { void addAll(Iterable newContacts) { _contacts.addAll(newContacts); + servers?.clearGroups(); + _contacts.forEach((contact) { + if (contact.isGroup) { + servers?.addGroup(contact); + } + }); notifyListeners(); } void add(ContactInfoState newContact) { _contacts.add(newContact); + if (newContact.isGroup) { + servers?.addGroup(newContact); + } notifyListeners(); } @@ -213,8 +227,8 @@ class ContactListState extends ChangeNotifier { } class ProfileInfoState extends ChangeNotifier { - ContactListState _contacts = ContactListState(); ProfileServerListState _servers = ProfileServerListState(); + ContactListState _contacts = ContactListState(); final String onion; String _nickname = ""; String _imagePath = ""; @@ -242,7 +256,11 @@ class ProfileInfoState extends ChangeNotifier { this._online = online; this._encrypted = encrypted; + _contacts.connectServers(this._servers); + if (contactsJson != null && contactsJson != "" && contactsJson != "null") { + this.replaceServers(serversJson); + List contacts = jsonDecode(contactsJson); this._contacts.addAll(contacts.map((contact) { return ContactInfoState(this.onion, contact["identifier"], contact["onion"], @@ -265,7 +283,7 @@ class ProfileInfoState extends ChangeNotifier { } } - this.replaceServers(serversJson); + } // Parse out the server list json into our server info state struct... @@ -274,15 +292,22 @@ class ProfileInfoState extends ChangeNotifier { List servers = jsonDecode(serversJson); this._servers.replace(servers.map((server) { // TODO Keys... - return RemoteServerInfoState(onion: server["onion"], status: server["status"]); + return RemoteServerInfoState(onion: server["onion"], identifier: server["identifier"], description: server["description"], status: server["status"]); })); + + this._contacts.contacts.forEach((contact) { + if (contact.isGroup) { + _servers.addGroup(contact); + } + }); + notifyListeners(); } } // void updateServerStatusCache(String server, String status) { - this._servers.updateServerCache(server, status); + this._servers.updateServerState(server, status); notifyListeners(); } diff --git a/lib/models/profileservers.dart b/lib/models/profileservers.dart index cb86d392..5f422538 100644 --- a/lib/models/profileservers.dart +++ b/lib/models/profileservers.dart @@ -1,3 +1,4 @@ +import 'package:cwtch/model.dart'; import 'package:flutter/material.dart'; class ProfileServerListState extends ChangeNotifier { @@ -6,6 +7,7 @@ class ProfileServerListState extends ChangeNotifier { void replace(Iterable newServers) { _servers.clear(); _servers.addAll(newServers); + resort(); notifyListeners(); } @@ -14,23 +16,78 @@ class ProfileServerListState extends ChangeNotifier { return idx >= 0 ? _servers[idx] : null; } - void updateServerCache(String onion, String status) { + void updateServerState(String onion, String status) { int idx = _servers.indexWhere((element) => element.onion == onion); if (idx >= 0) { - _servers[idx] = RemoteServerInfoState(onion: onion, status: status); + _servers[idx].status = status; } else { print("Tried to update server cache without a starting state...this is probably an error"); } + resort(); notifyListeners(); } + void resort() { + _servers.sort((RemoteServerInfoState a, RemoteServerInfoState b) { + // return -1 = a first in list + // return 1 = b first in list + + // online v offline + if (a.status == "Synced" && b.status != "Synced") { + return -1; + } else if (a.status != "Synced" && b.status == "Synced") { + return 1; + } + + // num of groups + if (a.groups.length > b.groups.length) { + return -1; + } else if (b.groups.length > a.groups.length) { + return 1; + } + + return 0; + }); + } + + void clearGroups() { + _servers.map((server) => server.clearGroups()); + } + + void addGroup(ContactInfoState group) { + int idx = _servers.indexWhere((element) => element.onion == group.server); + if (idx >= 0) { + _servers[idx].addGroup(group); + } + } + List get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier } class RemoteServerInfoState extends ChangeNotifier { final String onion; - final String status; + final int identifier; + String status; + String description; + List _groups = []; + + RemoteServerInfoState({required this.onion, required this.identifier, required this.description, required this.status}); + + void updateDescription(String newDescription) { + this.description = newDescription; + notifyListeners(); + } + + void clearGroups() { + _groups = []; + } + + void addGroup(ContactInfoState group) { + _groups.add(group); + notifyListeners(); + } + + List get groups => _groups.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier - RemoteServerInfoState({required this.onion, required this.status}); } diff --git a/lib/views/addeditprofileview.dart b/lib/views/addeditprofileview.dart index 2355fd57..03084ca2 100644 --- a/lib/views/addeditprofileview.dart +++ b/lib/views/addeditprofileview.dart @@ -107,8 +107,7 @@ class _AddEditProfileViewState extends State { labelText: AppLocalizations.of(context)!.yourDisplayName, validator: (value) { if (value.isEmpty) { - // TODO l10n ize - return "Please enter a display name"; + return AppLocalizations.of(context)!.displayNameTooltip; } return null; }, diff --git a/lib/views/addeditservers.dart b/lib/views/addeditservers.dart index 2a1c8f46..b2a5cacf 100644 --- a/lib/views/addeditservers.dart +++ b/lib/views/addeditservers.dart @@ -110,7 +110,7 @@ class _AddEditServerViewState extends State { ), CwtchTextField( controller: ctrlrDesc, - labelText: "Description", + labelText: AppLocalizations.of(context)!.fieldDescriptionLabel, autofocus: false, ) ]), diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index 990899f3..a05ef4df 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -1,4 +1,5 @@ import 'package:cwtch/cwtch_icons_icons.dart'; +import 'package:cwtch/views/profileserversview.dart'; import 'package:flutter/material.dart'; import 'package:cwtch/views/torstatusview.dart'; import 'package:cwtch/widgets/contactrow.dart'; @@ -112,7 +113,15 @@ class _ContactsViewState extends State { Clipboard.setData(new ClipboardData(text: Provider.of(context, listen: false).onion)); })); - // TODO servers + // Manage known Servers + if (Provider.of(context, listen: false).isExperimentEnabled(ServerManagementExperiment)) { + actions.add(IconButton( + icon: Icon(CwtchIcons.dns_24px), + tooltip: AppLocalizations.of(context)!.manageKnownServersButton, + onPressed: () { + _pushServers(); + })); + } // Search contacts actions.add(IconButton( @@ -162,12 +171,13 @@ class _ContactsViewState extends State { )); } - void _pushTorStatus() { + void _pushServers() { + var profile = Provider.of(context); Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) { return MultiProvider( - providers: [Provider.value(value: Provider.of(context))], - child: TorStatusView(), + providers: [ChangeNotifierProvider(create: (context) => profile), Provider.value(value: Provider.of(context))], + child: ProfileServersView(), ); }, )); diff --git a/lib/views/profileserversview.dart b/lib/views/profileserversview.dart new file mode 100644 index 00000000..7ac06520 --- /dev/null +++ b/lib/views/profileserversview.dart @@ -0,0 +1,155 @@ +import 'package:cwtch/models/profileservers.dart'; +import 'package:cwtch/models/servers.dart'; +import 'package:cwtch/widgets/remoteserverrow.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; + +import '../cwtch_icons_icons.dart'; +import '../main.dart'; +import '../model.dart'; +import '../settings.dart'; + + +class ProfileServersView extends StatefulWidget { + @override + _ProfileServersView createState() => _ProfileServersView(); +} + +class _ProfileServersView extends State { + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + + var knownServers = Provider.of(context).serverList.servers.map((RemoteServerInfoState remoteServer) { return remoteServer.onion + ".onion"; }).toSet(); + var importServerList = Provider.of(context).servers.where((server) => !knownServers.contains(server.onion) ).map>((ServerInfoState serverInfo) { + return DropdownMenuItem( + value: serverInfo.onion, + child: Text( + serverInfo.description.isNotEmpty ? serverInfo.description : serverInfo.onion, + overflow: TextOverflow.ellipsis, + ), + ); + }).toList(); + + importServerList.insert(0, DropdownMenuItem( + value: "", + child: Text(AppLocalizations.of(context)!.importLocalServerSelectText))); + + return Scaffold( + appBar: AppBar( + title: Text(MediaQuery + .of(context) + .size + .width > 600 ? AppLocalizations.of(context)!.manageKnownServersLong : AppLocalizations.of(context)!.manageKnownServersShort), + ), + body: Consumer(builder: (context, profile, child) { + ProfileServerListState servers = profile.serverList; + final tiles = servers.servers.map((RemoteServerInfoState server) { + return ChangeNotifierProvider.value( + value: server, + builder: (context, child) => RepaintBoundary(child: RemoteServerRow()), + ); + }, + ); + + final divided = ListTile.divideTiles( + context: context, + tiles: tiles, + ).toList(); + + final importCard = Card( child: ListTile( + title: Text(AppLocalizations.of(context)!.importLocalServerLabel), + leading: Icon(CwtchIcons.add_circle_24px , color: Provider.of(context).current().mainTextColor()), + trailing: DropdownButton( + onChanged: (String? importServer) { + if (importServer!.isNotEmpty) { + var server = Provider.of(context).getServer(importServer)!; + showImportConfirm(context, profile.onion, server.onion, server.description, server.serverBundle); + } + + }, + value: "", + items: importServerList, + + ))); + + return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { + return Scrollbar( + isAlwaysShown: true, + child: SingleChildScrollView( + clipBehavior: Clip.antiAlias, + child: + Container( + margin: EdgeInsets.fromLTRB(5, 0, 5, 10), + padding: EdgeInsets.fromLTRB(5, 0, 5, 10), + child: Column(children: [ + + if (importServerList.length > 1) importCard, + + Column( children: divided ) + ]))));}); + + return ListView(children: divided); + }, + )); + } + + showImportConfirm(BuildContext context, String profileHandle, String serverHandle, String serverDesc, String bundle) { + var serverLabel = serverDesc.isNotEmpty ? serverDesc : serverHandle; + serverHandle = serverHandle.substring(0, serverHandle.length-6 ); // remove '.onion' + // set up the buttons + Widget cancelButton = ElevatedButton( + child: Text(AppLocalizations.of(context)!.cancel), + onPressed: () { + Navigator.of(context).pop(); // dismiss dialog + }, + ); + Widget continueButton = ElevatedButton( + child: Text(AppLocalizations.of(context)!.importLocalServerButton.replaceAll("%1", serverLabel)), + onPressed: () { + Provider.of(context, listen: false).cwtch.ImportBundle(profileHandle, bundle); + // Wait 500ms and hope the server is imported and add it's description in the UI and as an attribute + Future.delayed(const Duration(milliseconds: 500), () { + var profile = Provider.of(context); + if (profile.serverList.getServer(serverHandle) != null) { + profile.serverList.getServer(serverHandle)?.updateDescription( + serverDesc); + + Provider + .of(context, listen: false) + .cwtch + .SetConversationAttribute(profile.onion, profile.serverList + .getServer(serverHandle) + !.identifier, "server.description", serverDesc); + } + }); + Navigator.of(context).pop(); + }); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + title: Text(AppLocalizations.of(context)!.importLocalServerButton.replaceAll("%1", serverLabel)), + actions: [ + cancelButton, + continueButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } + + + +} \ No newline at end of file diff --git a/lib/views/remoteserverview.dart b/lib/views/remoteserverview.dart new file mode 100644 index 00000000..bc940807 --- /dev/null +++ b/lib/views/remoteserverview.dart @@ -0,0 +1,151 @@ +import 'dart:convert'; +import 'package:cwtch/cwtch/cwtch.dart'; +import 'package:cwtch/cwtch_icons_icons.dart'; +import 'package:cwtch/models/profileservers.dart'; +import 'package:cwtch/models/servers.dart'; +import 'package:cwtch/widgets/buttontextfield.dart'; +import 'package:cwtch/widgets/contactrow.dart'; +import 'package:cwtch/widgets/cwtchlabel.dart'; +import 'package:cwtch/widgets/passwordfield.dart'; +import 'package:cwtch/widgets/textfield.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:flutter/material.dart'; +import 'package:cwtch/settings.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import '../errorHandler.dart'; +import '../main.dart'; +import '../config.dart'; +import '../model.dart'; + +/// Pane to add or edit a server +class RemoteServerView extends StatefulWidget { + const RemoteServerView(); + + @override + _RemoteServerViewState createState() => _RemoteServerViewState(); +} + +class _RemoteServerViewState extends State { + final _formKey = GlobalKey(); + + final ctrlrDesc = TextEditingController(text: ""); + + @override + void initState() { + super.initState(); + var serverInfoState = Provider.of(context, listen: false); + if (serverInfoState.description.isNotEmpty) { + ctrlrDesc.text = serverInfoState.description; + } + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Consumer3(builder: (context, profile, serverInfoState, settings, child) { + return Scaffold( + appBar: AppBar( + title: Text(ctrlrDesc.text.isNotEmpty ? ctrlrDesc.text : serverInfoState.onion) + ), + body: Container( + margin: EdgeInsets.fromLTRB(30, 0, 30, 10), + padding: EdgeInsets.fromLTRB(20, 0, 20, 10), + child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ + SizedBox( + height: 20, + ), + CwtchLabel(label: AppLocalizations.of(context)!.serverAddress), + SizedBox( + height: 20, + ), + SelectableText( + serverInfoState.onion + ), + + // Description + SizedBox( + height: 20, + ), + CwtchLabel(label: AppLocalizations.of(context)!.serverDescriptionLabel), + Text(AppLocalizations.of(context)!.serverDescriptionDescription), + SizedBox( + height: 20, + ), + CwtchButtonTextField( + controller: ctrlrDesc, + readonly: false, + tooltip: AppLocalizations.of(context)!.saveBtn, + labelText: AppLocalizations.of(context)!.fieldDescriptionLabel, + icon: Icon(Icons.save), + onPressed: () { + Provider.of(context, listen: false).cwtch.SetConversationAttribute(profile.onion, serverInfoState.identifier, "server.description", ctrlrDesc.text); + serverInfoState.updateDescription(ctrlrDesc.text); + }, + ), + + SizedBox( + height: 20, + ), + + Padding(padding: EdgeInsets.all(8), child: Text( AppLocalizations.of(context)!.groupsOnThisServerLabel),), + Expanded(child: _buildGroupsList(serverInfoState)) + ]))); + + }); + } + + Widget _buildGroupsList(RemoteServerInfoState serverInfoState) { + final tiles = serverInfoState.groups.map((ContactInfoState group) { + return ChangeNotifierProvider.value( + value: group, + builder: (context, child) => RepaintBoundary(child: _buildGroupRow(group)), // ServerRow()), + ); + }, + ); + + final divided = ListTile.divideTiles( + context: context, + tiles: tiles, + ).toList(); + + var size = MediaQuery.of(context).size; + + int cols = ((size.width - 50) / 500).ceil(); + final double itemHeight = 60; // magic arbitary + final double itemWidth = (size.width - 50 /* magic padding guess */) / cols; + + return GridView.count(crossAxisCount: cols, childAspectRatio: (itemWidth / itemHeight), children: divided); + } + + Widget _buildGroupRow(ContactInfoState group) { + return Padding( + padding: const EdgeInsets.all(6.0), //border size + child: Column( + children: [ + Text( + group.nickname, + style: Provider.of(context).biggerFont.apply(color: Provider.of(context).theme.portraitOnlineBorderColor()), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + Visibility( + visible: !Provider.of(context).streamerMode, + child: ExcludeSemantics( + child: Text( + group.onion, + softWrap: true, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: Provider.of(context).theme.portraitOnlineBorderColor()), + ))) + ]) + ); + } + +} + diff --git a/lib/widgets/buttontextfield.dart b/lib/widgets/buttontextfield.dart index 46e88796..cd1cdb09 100644 --- a/lib/widgets/buttontextfield.dart +++ b/lib/widgets/buttontextfield.dart @@ -5,12 +5,13 @@ import 'package:provider/provider.dart'; // Provides a styled Text Field for use in Form Widgets. // Callers must provide a text controller, label helper text and a validator. class CwtchButtonTextField extends StatefulWidget { - CwtchButtonTextField({required this.controller, required this.onPressed, required this.icon, required this.tooltip, this.readonly = true}); + CwtchButtonTextField({required this.controller, required this.onPressed, required this.icon, required this.tooltip, this.readonly = true, this.labelText}); final TextEditingController controller; final Function()? onPressed; final Icon icon; final String tooltip; final bool readonly; + String? labelText; @override _CwtchButtonTextFieldState createState() => _CwtchButtonTextFieldState(); @@ -39,6 +40,8 @@ class _CwtchButtonTextFieldState extends State { focusNode: _focusNode, enableIMEPersonalizedLearning: false, decoration: InputDecoration( + labelText: widget.labelText, + labelStyle: TextStyle(color: theme.current().mainTextColor(), backgroundColor: theme.current().textfieldBackgroundColor()), suffixIcon: IconButton( onPressed: widget.onPressed, icon: widget.icon, diff --git a/lib/widgets/remoteserverrow.dart b/lib/widgets/remoteserverrow.dart new file mode 100644 index 00000000..339a170d --- /dev/null +++ b/lib/widgets/remoteserverrow.dart @@ -0,0 +1,78 @@ +import 'package:cwtch/main.dart'; +import 'package:cwtch/models/profileservers.dart'; +import 'package:cwtch/models/servers.dart'; +import 'package:cwtch/views/addeditservers.dart'; +import 'package:cwtch/views/remoteserverview.dart'; +import 'package:cwtch/widgets/profileimage.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import '../cwtch_icons_icons.dart'; +import '../errorHandler.dart'; +import '../model.dart'; +import '../settings.dart'; + +class RemoteServerRow extends StatefulWidget { + @override + _RemoteServerRowState createState() => _RemoteServerRowState(); +} + +class _RemoteServerRowState extends State { + @override + Widget build(BuildContext context) { + var server = Provider.of(context); + var description = server.description.isNotEmpty ? server.description : server.onion; + var running = server.status == "Synced"; + return Consumer( + builder: (context, profile, child) { + return Card(clipBehavior: Clip.antiAlias, + margin: EdgeInsets.all(0.0), + child: InkWell( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.all(6.0), //border size + child: Icon(CwtchIcons.dns_24px, + color: running ? Provider.of(context).theme.portraitOnlineBorderColor() : Provider.of(context).theme.portraitOfflineBorderColor(), + size: 64) + + ), + Expanded( + child: Column( + children: [ + Text( + description, + semanticsLabel: description, + style: Provider.of(context).biggerFont.apply(color: running ? Provider.of(context).theme.portraitOnlineBorderColor() : Provider.of(context).theme.portraitOfflineBorderColor()), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + Visibility( + visible: !Provider.of(context).streamerMode, + child: ExcludeSemantics( + child: Text( + server.onion, + softWrap: true, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: running ? Provider.of(context).theme.portraitOnlineBorderColor() : Provider.of(context).theme.portraitOfflineBorderColor()), + ))) + ], + )), + + ]), + onTap: () { + Navigator.of(context).push(MaterialPageRoute( + settings: RouteSettings(name: "remoteserverview"), + builder: (BuildContext context) { + return MultiProvider( + providers: [Provider.value(value: profile), ChangeNotifierProvider(create: (context) => server), Provider.value(value: Provider.of(context))], + child: RemoteServerView(), + ); + })); + } + ));}); + } +} diff --git a/lib/widgets/serverrow.dart b/lib/widgets/serverrow.dart index 9c12f477..a4e8bb05 100644 --- a/lib/widgets/serverrow.dart +++ b/lib/widgets/serverrow.dart @@ -73,7 +73,11 @@ class _ServerRowState extends State { _pushEditServer(server); }, ) - ]))); + ]), + onTap: () { + _pushEditServer(server); + } + )); } void _pushEditServer(ServerInfoState server) {