diff --git a/lib/l10n/intl_cy.arb b/lib/l10n/intl_cy.arb index 9291bcd3..63f903f3 100644 --- a/lib/l10n/intl_cy.arb +++ b/lib/l10n/intl_cy.arb @@ -1,6 +1,10 @@ { "@@locale": "cy", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index aa14e07a..72fb2661 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1,6 +1,10 @@ { "@@locale": "da", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 6e355995..fd5f1ce9 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,10 @@ { "@@locale": "de", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "localeIt": "Italienisch \/ Italiano", diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index fb518eb5..5a224ea5 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -1,6 +1,10 @@ { "@@locale": "el", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 87395b1d..705f18a7 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,10 @@ { "@@locale": "en", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index a2c38335..6f60cf98 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,10 @@ { "@@locale": "es", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "tooltipPinConversation": "Fija la conversación en la parte superior de \"Conversaciones\"", "errorDownloadDirectoryDoesNotExist": "No se puede habilitar el uso compartido de archivos porque la carpeta de descarga no se ha configurado o se configuró en una carpeta que no existe.", "acquiringTicketsFromServer": "Realizando Desafío Antispam", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 079dff13..2431fe99 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,10 @@ { "@@locale": "fr", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Le partage de fichiers ne peut pas être activé car le dossier de téléchargement n'a pas été défini ou est défini sur un dossier qui n'existe pas.", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index e403987d..30cb866c 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,10 @@ { "@@locale": "it", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/l10n/intl_lb.arb b/lib/l10n/intl_lb.arb index 260d347d..9ea37138 100644 --- a/lib/l10n/intl_lb.arb +++ b/lib/l10n/intl_lb.arb @@ -1,6 +1,10 @@ { "@@locale": "lb", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/l10n/intl_no.arb b/lib/l10n/intl_no.arb index 00b265b9..ffd05d43 100644 --- a/lib/l10n/intl_no.arb +++ b/lib/l10n/intl_no.arb @@ -1,6 +1,10 @@ { "@@locale": "no", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index e864744b..2fd714de 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,10 @@ { "@@locale": "pl", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index fed22cbe..f10a306b 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,10 @@ { "@@locale": "pt", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 0e912a7d..90aa65be 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,6 +1,10 @@ { "@@locale": "ro", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 396c57ef..38d282b6 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,10 @@ { "@@locale": "ru", - "@@last_modified": "2022-09-09T21:46:36+02:00", + "@@last_modified": "2022-09-09T23:36:56+02:00", + "experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes", + "enableExperimentQRCode": "QR Codes", + "shareMenuQRCode": "Show QR Code", + "shareProfileMenuTooltop": "Share profile via...", "acquiredTicketsFromServer": "Antispam Challenge Complete", "acquiringTicketsFromServer": "Performing Antispam Challenge", "errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.", diff --git a/lib/settings.dart b/lib/settings.dart index 986cce4f..fee77c72 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -15,6 +15,7 @@ const FileSharingExperiment = "filesharing"; const ImagePreviewsExperiment = "filesharing-images"; const ClickableLinksExperiment = "clickable-links"; const FormattingExperiment = "message-formatting"; +const QRCodeExperiment = "qrcode-support"; enum DualpaneMode { Single, diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index 5324564c..19acccde 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -19,9 +19,12 @@ import '../main.dart'; import '../settings.dart'; import 'addcontactview.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:qr_flutter/qr_flutter.dart'; import 'messageview.dart'; +enum ShareMenu { copyCode, qrcode } + class ContactsView extends StatefulWidget { const ContactsView({Key? key}) : super(key: key); @@ -163,16 +166,53 @@ class _ContactsViewState extends State { actions.add(Tooltip(message: AppLocalizations.of(context)!.blockUnknownConnectionsEnabledDescription, child: Icon(CwtchIcons.block_unknown))); } - // Copy profile onion - actions.add(IconButton( + if (Provider.of(context, listen: false).isExperimentEnabled(QRCodeExperiment)) { + actions.add(PopupMenuButton( icon: Icon(CwtchIcons.address_copy_2), - tooltip: AppLocalizations.of(context)!.copyAddress, + tooltip: AppLocalizations.of(context)!.shareProfileMenuTooltop, splashRadius: Material.defaultSplashRadius / 2, - onPressed: () { - Clipboard.setData(new ClipboardData(text: Provider.of(context, listen: false).onion)); - final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification)); - ScaffoldMessenger.of(context).showSnackBar(snackBar); - })); + onSelected: (ShareMenu item) { + switch(item) { + case ShareMenu.copyCode: + { + Clipboard.setData(new ClipboardData(text: Provider + .of(context, listen: false) + .onion)); + final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification)); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + break; + case ShareMenu.qrcode: { + _showQRCode("cwtch:" + Provider.of(context, listen: false).onion); + } + break; + } + }, + itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + value: ShareMenu.copyCode, + child: Text(AppLocalizations.of(context)!.copyAddress), + ), + PopupMenuItem( + value: ShareMenu.qrcode, + child: Text(AppLocalizations.of(context)!.shareMenuQRCode), + ), + ], + )); + } else { + actions.add(IconButton( + icon: Icon(CwtchIcons.address_copy_2), + tooltip: AppLocalizations.of(context)!.copyAddress, + splashRadius: Material.defaultSplashRadius / 2, + onPressed: () { + Clipboard.setData(new ClipboardData(text: Provider + .of(context, listen: false) + .onion)); + final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification)); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + })); + } + // Manage known Servers if (Provider.of(context, listen: false).isExperimentEnabled(TapirGroupsExperiment) || Provider.of(context, listen: false).isExperimentEnabled(ServerManagementExperiment)) { @@ -381,4 +421,25 @@ class _ContactsViewState extends State { ))); }); } + + void _showQRCode(String profile_code) { + showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return Wrap( children: [ + Center( child: + QrImage( + data: profile_code, + version: QrVersions.auto, + size: 400.0, + backgroundColor: Provider.of(context).theme.backgroundPaneColor, + foregroundColor: Provider.of(context).theme.mainTextColor, + gapless: false, + ), + )]); + + }, + ); + } + } diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index 6a0f353f..81366b01 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -468,6 +468,24 @@ class _GlobalSettingsViewState extends State { inactiveTrackColor: settings.theme.defaultButtonDisabledColor, secondary: Icon(Icons.link, color: settings.current().mainTextColor), )), + Visibility( + visible: settings.experimentsEnabled, + child: SwitchListTile( + title: Text(AppLocalizations.of(context)!.enableExperimentQRCode, style: TextStyle(color: settings.current().mainTextColor)), + subtitle: Text(AppLocalizations.of(context)!.experimentQRCodeDescription), + value: settings.isExperimentEnabled(QRCodeExperiment), + onChanged: (bool value) { + if (value) { + settings.enableExperiment(QRCodeExperiment); + } else { + settings.disableExperiment(QRCodeExperiment); + } + saveSettings(context); + }, + activeTrackColor: settings.theme.defaultButtonActiveColor, + inactiveTrackColor: settings.theme.defaultButtonDisabledColor, + secondary: Icon(Icons.qr_code, color: settings.current().mainTextColor), + )), AboutListTile( icon: appIcon, applicationIcon: Padding(padding: EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)), diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index 8ec9b0e4..ef65c2dd 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -294,10 +294,12 @@ class _MessageViewState extends State { // Do this after we trim to preserve enter-behaviour... bool isOffline = Provider.of(context, listen: false).isOnline() == false; - if (isOffline) { + bool performingAntiSpam = Provider.of(context).antispamTickets == 0; + if (isOffline || performingAntiSpam) { return; } + var isGroup = Provider.of(context, listen: false).contactList.getContact(Provider.of(context, listen: false).selectedConversation!)!.isGroup; // peers and groups currently have different length constraints (servers can store less)... diff --git a/pubspec.lock b/pubspec.lock index 644a5885..b1f454c3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -617,6 +617,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + qr: + dependency: transitive + description: + name: qr + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" screen_retriever: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d12d5ae6..871f6773 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.8.0+34 +version: 1.9.0+35 environment: sdk: ">=2.15.0 <3.0.0" @@ -46,6 +46,7 @@ dependencies: win_toast: ^0.0.2 flutter_local_notifications: ^9.6.1 desktop_notifications: ^0.6.3 + qr_flutter: ^4.0.0 dev_dependencies: msix: ^3.6.2