diff --git a/.drone.yml b/.drone.yml index 9122b097..45fc462b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -30,7 +30,7 @@ steps: path: /root/.pub-cache commands: - ./fetch-tor.sh - - echo `git describe --tags` > VERSION + - echo `git describe --tags --abbrev=1` > VERSION - echo `date +%G-%m-%d-%H-%M` > BUILDDATE - flutter pub get - mkdir deploy @@ -195,9 +195,9 @@ steps: - name: fetch image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc commands: - - powershell -command "Invoke-WebRequest -Uri https://dist.torproject.org/torbrowser/10.0.18/tor-win64-0.4.5.9.zip -OutFile tor.zip" - - powershell -command "if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '72764eb07ad8ab511603aba0734951ca003989f5f4686af91ba220217b4a8a4bcc5f571b59f52c847932f6efedf847b111621983050fcddbb8099d43ca66fb07' ) { Write-Error 'tor.zip sha512sum mismatch' }" - - git describe --tags > VERSION + - powershell -command "Invoke-WebRequest -Uri https://dist.torproject.org/torbrowser/10.5a17/tor-win64-0.4.6.5.zip -OutFile tor.zip" + - powershell -command "if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '7917561a7a063440a1ddfa9cb544ab9ffd09de84cea3dd66e3cc9cd349dd9f85b74a522ec390d7a974bc19b424c4d53af60e57bbc47e763d13cab6a203c4592f' ) { Write-Error 'tor.zip sha512sum mismatch' }" + - git describe --tags --abbrev=1 > VERSION - powershell -command "Get-Date -Format 'yyyy-MM-dd-HH-mm'" > BUILDDATE - .\fetch-libcwtch-go.ps1 diff --git a/README.md b/README.md index f95318aa..68d92554 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ In Lokalise, hit Download and make sure: * Format is set to "Flutter (.arb) * Output filename is set to `l10n/intl_%LANG_ISO%.%FORMAT%` * Empty translations is set to "Replace with base language" +* Order "Last Update" Build, download and unzip the output, overwriting `lib/l10n`. The next time Flwtch is built, Flutter will notice the changes and update `app_localizations.dart` accordingly (thanks to `generate:true` in `pubspec.yaml`). diff --git a/assets/core/Cwtch_knott_white.svg b/assets/core/Cwtch_knott_white.svg index 89a3e5cd..15ced0a8 100644 --- a/assets/core/Cwtch_knott_white.svg +++ b/assets/core/Cwtch_knott_white.svg @@ -1,6 +1,4 @@ - - image/svg+xml + inkscape:current-layer="Layer_1" + inkscape:document-rotation="0" /> \ No newline at end of file + diff --git a/cwtch.png b/cwtch.png index df58306b..93a84f6e 100644 Binary files a/cwtch.png and b/cwtch.png differ diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index e1a96c50..ebd3979c 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -68,7 +68,13 @@ class CwtchNotifier { } if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"]) == null) { profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], data["GroupID"], - authorization: ContactAuthorization.approved, imagePath: data["PicturePath"], nickname: data["GroupName"], status: status, server: data["GroupServer"], isGroup: true, lastMessageTime: DateTime.now())); + authorization: ContactAuthorization.approved, + imagePath: data["PicturePath"], + nickname: data["GroupName"], + status: status, + server: data["GroupServer"], + isGroup: true, + lastMessageTime: DateTime.now())); profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now()); } break; diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index fe721ecf..059c640f 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,8 @@ { "@@locale": "pl", - "@@last_modified": "2021-07-10T17:32:01+02:00", + "@@last_modified": "2021-07-14T01:50:27+02:00", + "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", diff --git a/lib/main.dart b/lib/main.dart index 204c8695..37638d61 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:cwtch/config.dart'; import 'package:cwtch/notification_manager.dart'; import 'package:cwtch/views/messageview.dart'; import 'package:cwtch/widgets/rightshiftfixer.dart'; @@ -28,7 +29,7 @@ var globalTorStatus = TorStatus(); var globalAppState = AppState(); void main() { - print("main()"); + print("Cwtch version: ${EnvironmentConfig.BUILD_VER} built on: ${EnvironmentConfig.BUILD_DATE}"); LicenseRegistry.addLicense(() => licenses()); WidgetsFlutterBinding.ensureInitialized(); print("runApp()"); diff --git a/lib/model.dart b/lib/model.dart index 086b2916..46a8b508 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -329,14 +329,10 @@ class ProfileInfoState extends ChangeNotifier { } } -enum ContactAuthorization { - unknown, - approved, - blocked -} +enum ContactAuthorization { unknown, approved, blocked } ContactAuthorization stringToContactAuthorization(String authStr) { - switch(authStr) { + switch (authStr) { case "approved": return ContactAuthorization.approved; case "blocked": @@ -346,7 +342,6 @@ ContactAuthorization stringToContactAuthorization(String authStr) { } } - class ContactInfoState extends ChangeNotifier { final String profileOnion; final String onion; diff --git a/lib/models/message.dart b/lib/models/message.dart index 1d5bafd3..b93ce649 100644 --- a/lib/models/message.dart +++ b/lib/models/message.dart @@ -23,8 +23,6 @@ const TorV3ContactHandleLength = 56; // Defines the length of a Cwtch v2 Group. const GroupConversationHandleLength = 32; - - abstract class Message { MessageMetadata getMetadata(); Widget getWidget(BuildContext context); diff --git a/lib/models/messages/quotedmessage.dart b/lib/models/messages/quotedmessage.dart index 4d71a2d9..3bb85961 100644 --- a/lib/models/messages/quotedmessage.dart +++ b/lib/models/messages/quotedmessage.dart @@ -10,6 +10,17 @@ import 'package:provider/provider.dart'; import '../../main.dart'; import '../../model.dart'; +class QuotedMessageStructure { + final String quotedHash; + final String body; + QuotedMessageStructure(this.quotedHash, this.body); + + Map toJson() => { + 'quotedHash': quotedHash, + 'body': body, + }; +} + class LocallyIndexedMessage { final dynamic message; final int index; diff --git a/lib/views/addeditprofileview.dart b/lib/views/addeditprofileview.dart index 26d2dcd7..46d9d6d4 100644 --- a/lib/views/addeditprofileview.dart +++ b/lib/views/addeditprofileview.dart @@ -136,7 +136,12 @@ class _AddEditProfileViewState extends State { // We only allow setting password types on profile creation Visibility( visible: Provider.of(context).onion.isEmpty, - child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + child: SizedBox( + height: 20, + )), + Visibility( + visible: Provider.of(context).onion.isEmpty, + child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Checkbox( value: usePassword, fillColor: MaterialStateProperty.all(theme.current().defaultButtonColor()), @@ -147,6 +152,16 @@ class _AddEditProfileViewState extends State { AppLocalizations.of(context)!.radioUsePassword, style: TextStyle(color: theme.current().mainTextColor()), ), + SizedBox( + height: 20, + ), + Flexible( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Text( + usePassword ? AppLocalizations.of(context)!.encryptedProfileDescription : AppLocalizations.of(context)!.plainProfileDescription, + textAlign: TextAlign.center, + ))) ])), SizedBox( height: 20, diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index aa782034..ce655ed3 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -34,6 +34,7 @@ class _GlobalSettingsViewState extends State { Widget _buildSettingsList() { return Consumer(builder: (context, settings, child) { return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { + var appIcon = Icon(Icons.info, color: settings.current().mainTextColor()); return Scrollbar( isAlwaysShown: true, child: SingleChildScrollView( @@ -177,12 +178,17 @@ class _GlobalSettingsViewState extends State { ], )), AboutListTile( - icon: Icon(Icons.info, color: settings.current().mainTextColor()), - applicationIcon: Padding(padding: EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)), - applicationName: "Cwtch (Flutter UI)", - applicationVersion: AppLocalizations.of(context)!.versionBuilddate.replaceAll("%1", EnvironmentConfig.BUILD_VER).replaceAll("%2", EnvironmentConfig.BUILD_DATE), - applicationLegalese: '\u{a9} 2021 Open Privacy Research Society', - ), + icon: appIcon, + applicationIcon: Padding(padding: EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)), + applicationName: "Cwtch (Flutter UI)", + applicationLegalese: '\u{a9} 2021 Open Privacy Research Society', + aboutBoxChildren: [ + Padding( + padding: EdgeInsets.fromLTRB( + 24.0 + 10.0 + (appIcon.size ?? 24.0), 16.0, 0.0, 0.0), // About has 24 padding (ln 389) and there appears to be another 10 of padding in the widget + child: SelectableText(AppLocalizations.of(context)!.versionBuilddate.replaceAll("%1", EnvironmentConfig.BUILD_VER).replaceAll("%2", EnvironmentConfig.BUILD_DATE)), + ) + ]), ])))); }); }); diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index 4370b0f6..5b88afa0 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/message.dart'; +import 'package:cwtch/models/messages/quotedmessage.dart'; import 'package:cwtch/widgets/malformedbubble.dart'; import 'package:cwtch/widgets/messageloadingbubble.dart'; import 'package:cwtch/widgets/profileimage.dart'; @@ -127,7 +128,7 @@ class _MessageViewState extends State { var bytes1 = utf8.encode(messageWrapper["PeerID"] + messageWrapper['Message']); var digest1 = sha256.convert(bytes1); var contentHash = base64Encode(digest1.bytes); - var quotedMessage = "{\"quotedHash\":\"" + contentHash + "\",\"body\":\"" + ctrlrCompose.value.text + "\"}"; + var quotedMessage = jsonEncode(QuotedMessageStructure(contentHash, ctrlrCompose.value.text)); ChatMessage cm = new ChatMessage(o: QuotedMessageOverlay, d: quotedMessage); Provider.of(context, listen: false) .cwtch @@ -206,7 +207,7 @@ class _MessageViewState extends State { suffixIcon: IconButton( icon: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of(context).theme.mainTextColor()), tooltip: AppLocalizations.of(context)!.sendMessage, - onPressed: isOffline ? null :_sendMessage, + onPressed: isOffline ? null : _sendMessage, ), )))), ), diff --git a/lib/views/torstatusview.dart b/lib/views/torstatusview.dart index e3f445da..0c6264fd 100644 --- a/lib/views/torstatusview.dart +++ b/lib/views/torstatusview.dart @@ -54,7 +54,7 @@ class _TorStatusView extends State { ), ListTile( title: Text(AppLocalizations.of(context)!.torVersion), - subtitle: Text(torStatus.version), + subtitle: SelectableText(torStatus.version), ), ])))); }); diff --git a/lib/widgets/contactrow.dart b/lib/widgets/contactrow.dart index f8c6f476..fced6b79 100644 --- a/lib/widgets/contactrow.dart +++ b/lib/widgets/contactrow.dart @@ -99,8 +99,6 @@ class _ContactRowState extends State { )); } - - void _btnApprove() { Provider.of(context, listen: false) .cwtch diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index a1d90482..2a822240 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -25,7 +25,7 @@ class MessageRowState extends State { @override Widget build(BuildContext context) { var fromMe = Provider.of(context).senderHandle == Provider.of(context).onion; - var isContact = Provider.of(context).getContact(Provider.of(context).senderHandle) != null; + var isContact = Provider.of(context).contactList.getContact(Provider.of(context).senderHandle) != null; var senderDisplayStr = ""; if (!fromMe) { @@ -39,6 +39,7 @@ class MessageRowState extends State { Widget wdgIcons = Visibility( visible: this.showMenu, + maintainSize: true, child: IconButton( tooltip: AppLocalizations.of(context)!.tooltipReplyToThisMessage, onPressed: () { @@ -127,10 +128,7 @@ class MessageRowState extends State { style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))), child: Text(AppLocalizations.of(context)!.addContact), onPressed: () { - Provider - .of(context, listen: false) - .cwtch - .ImportBundle(profileOnion, senderOnion); + Provider.of(context, listen: false).cwtch.ImportBundle(profileOnion, senderOnion); final snackBar = SnackBar( content: Text(AppLocalizations.of(context)!.successfullAddedContact), duration: Duration(seconds: 2), diff --git a/lib/widgets/profileimage.dart b/lib/widgets/profileimage.dart index 68e9ae11..1516f3e0 100644 --- a/lib/widgets/profileimage.dart +++ b/lib/widgets/profileimage.dart @@ -5,7 +5,8 @@ import 'package:provider/provider.dart'; import '../settings.dart'; class ProfileImage extends StatefulWidget { - ProfileImage({required this.imagePath, required this.diameter, required this.border, this.badgeCount = 0, required this.badgeColor, required this.badgeTextColor, this.maskOut = false, this.tooltip = ""}); + ProfileImage( + {required this.imagePath, required this.diameter, required this.border, this.badgeCount = 0, required this.badgeColor, required this.badgeTextColor, this.maskOut = false, this.tooltip = ""}); final double diameter; final String imagePath; final Color border; @@ -28,8 +29,8 @@ class _ProfileImageState extends State { // We need some theme specific blending here...we might want to consider making this a theme level attribute colorBlendMode: !widget.maskOut ? Provider.of(context).theme.identifier() == "dark" - ? BlendMode.softLight - : BlendMode.darken + ? BlendMode.softLight + : BlendMode.darken : BlendMode.srcOut, color: Provider.of(context).theme.backgroundHilightElementColor(), isAntiAlias: true, @@ -47,11 +48,7 @@ class _ProfileImageState extends State { color: widget.border, child: Padding( padding: const EdgeInsets.all(2.0), //border size - child: ClipOval( - clipBehavior: Clip.antiAlias, - child: widget.tooltip == "" ? image : Tooltip( - message: widget.tooltip, - child: image))))), + child: ClipOval(clipBehavior: Clip.antiAlias, child: widget.tooltip == "" ? image : Tooltip(message: widget.tooltip, child: image))))), Visibility( visible: widget.badgeCount > 0, child: Positioned( diff --git a/linux/cwtch.png b/linux/cwtch.png index df58306b..93a84f6e 100644 Binary files a/linux/cwtch.png and b/linux/cwtch.png differ diff --git a/windows/runner/resources/knot_128.ico b/windows/runner/resources/knot_128.ico index d35e28fb..d54f3b09 100644 Binary files a/windows/runner/resources/knot_128.ico and b/windows/runner/resources/knot_128.ico differ diff --git a/windows/runner/resources/knot_16.ico b/windows/runner/resources/knot_16.ico index 42fa6e96..82de681c 100644 Binary files a/windows/runner/resources/knot_16.ico and b/windows/runner/resources/knot_16.ico differ diff --git a/windows/runner/resources/knot_256.ico b/windows/runner/resources/knot_256.ico index 2e0f50a1..211bf9f0 100644 Binary files a/windows/runner/resources/knot_256.ico and b/windows/runner/resources/knot_256.ico differ diff --git a/windows/runner/resources/knot_32.ico b/windows/runner/resources/knot_32.ico index da125478..c9d7b1c4 100644 Binary files a/windows/runner/resources/knot_32.ico and b/windows/runner/resources/knot_32.ico differ diff --git a/windows/runner/resources/knot_48.ico b/windows/runner/resources/knot_48.ico index 169ba2c7..badd923f 100644 Binary files a/windows/runner/resources/knot_48.ico and b/windows/runner/resources/knot_48.ico differ diff --git a/windows/runner/resources/knot_64.ico b/windows/runner/resources/knot_64.ico index 74a28e03..a55c52aa 100644 Binary files a/windows/runner/resources/knot_64.ico and b/windows/runner/resources/knot_64.ico differ