diff --git a/integration_test/gherkin_suite_test.editable.dart b/integration_test/gherkin_suite_test.editable.dart index 262fb470..fef4f14d 100644 --- a/integration_test/gherkin_suite_test.editable.dart +++ b/integration_test/gherkin_suite_test.editable.dart @@ -1,13 +1,11 @@ //import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart'; // notice new import name import 'package:flutter_gherkin/flutter_gherkin.dart'; -import 'package:flutter_test/flutter_test.dart'; import 'package:gherkin/gherkin.dart'; import 'dart:io'; // The application under test. import 'package:cwtch/main.dart' as app; -import 'package:glob/glob.dart'; import 'gherkin_suite_test.dart'; import 'hooks/env.dart'; diff --git a/integration_test/steps/chat.dart b/integration_test/steps/chat.dart index aa16f168..d735aec2 100644 --- a/integration_test/steps/chat.dart +++ b/integration_test/steps/chat.dart @@ -1,17 +1,8 @@ -import 'package:cwtch/main.dart'; -import 'package:cwtch/widgets/messagebubble.dart'; -import 'package:cwtch/widgets/profilerow.dart'; import 'package:cwtch/widgets/quotedmessage.dart'; -import 'package:cwtch/widgets/tor_icon.dart'; -import 'package:cwtch/views/profilemgrview.dart'; import 'package:flutter_gherkin/flutter_gherkin.dart'; -import 'package:flutter_gherkin/src/flutter/parameters/existence_parameter.dart'; -import 'package:flutter_gherkin/src/flutter/parameters/swipe_direction_parameter.dart'; import 'package:gherkin/gherkin.dart'; -import 'package:flutter/material.dart'; -import 'overrides.dart'; StepDefinitionGeneric ExpectReply() { return given3( diff --git a/integration_test/steps/form_elements.dart b/integration_test/steps/form_elements.dart index afda0d2e..3afdbcda 100644 --- a/integration_test/steps/form_elements.dart +++ b/integration_test/steps/form_elements.dart @@ -1,10 +1,7 @@ -import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:flutter_driver/flutter_driver.dart'; import 'package:flutter_gherkin/flutter_gherkin.dart'; import 'package:gherkin/gherkin.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; enum SwitchState { checked, unchecked } @@ -17,6 +14,7 @@ class SwitchStateParameter extends CustomParameter { case "unchecked": return SwitchState.unchecked; } + return null; }); } diff --git a/integration_test/steps/overrides.dart b/integration_test/steps/overrides.dart index 26acaa66..5760bfa7 100644 --- a/integration_test/steps/overrides.dart +++ b/integration_test/steps/overrides.dart @@ -149,7 +149,7 @@ StepDefinitionGeneric ExpectWidgetWithTextWithin() { ); } }() - .timeout(Duration(seconds: 120)); + .timeout(const Duration(seconds: 120)); }, configuration: StepDefinitionConfiguration()..timeout = const Duration(days: 1), ); @@ -173,7 +173,7 @@ StepDefinitionGeneric WaitUntilTextExists() { )); } }() - .timeout(Duration(seconds: 120)); + .timeout(const Duration(seconds: 120)); }, configuration: StepDefinitionConfiguration()..timeout = const Duration(days: 1), ); @@ -195,7 +195,7 @@ StepDefinitionGeneric WaitUntilTooltipExists() { context.world.appDriver.findBy(ofType, FindType.tooltip), ); }, - timeout: Duration(seconds: 120), + timeout: const Duration(seconds: 120), ); }, configuration: StepDefinitionConfiguration()..timeout = const Duration(days: 1), @@ -213,7 +213,7 @@ mixin _SwipeHelper on When4WithWorld lines = file.openRead().transform(utf8.decoder).transform(LineSplitter()); + Stream lines = file.openRead().transform(utf8.decoder).transform(const LineSplitter()); try { await for (var line in lines) { if (line.startsWith("wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-")) { diff --git a/integration_test/steps/utils.dart b/integration_test/steps/utils.dart index e23c8f91..05c9bafb 100644 --- a/integration_test/steps/utils.dart +++ b/integration_test/steps/utils.dart @@ -10,7 +10,7 @@ StepDefinitionGeneric TakeScreenshot() { final bytes = await context.world.appDriver.screenshot(); final screenshotData = base64Encode(bytes); print("EMBEDDING SCREENSHOT...."); - print("$screenshotData"); + print(screenshotData); context.world.attach(screenshotData, 'image/png', 'And I take a screenshot'); } catch (e, st) { print("FAILED TO EMBED??? $e $st"); diff --git a/lib/controllers/open_link_modal.dart b/lib/controllers/open_link_modal.dart index c770bcb6..29c448a8 100644 --- a/lib/controllers/open_link_modal.dart +++ b/lib/controllers/open_link_modal.dart @@ -1,6 +1,5 @@ import 'package:cwtch/themes/opaque.dart'; import 'package:cwtch/third_party/linkify/linkify.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -13,11 +12,11 @@ void modalOpenLink(BuildContext ctx, LinkableElement link) { showModalBottomSheet( context: ctx, builder: (BuildContext bcontext) { - return Container( + return SizedBox( height: 200, child: Center( child: Padding( - padding: EdgeInsets.all(30.0), + padding: const EdgeInsets.all(30.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, @@ -28,12 +27,12 @@ void modalOpenLink(BuildContext ctx, LinkableElement link) { ), Flex(direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.center, children: [ Container( - margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10), + margin: const EdgeInsets.symmetric(vertical: 20, horizontal: 10), child: ElevatedButton( child: Text(AppLocalizations.of(bcontext)!.clickableLinksCopy, style: Provider.of(bcontext).scaleFonts(defaultTextButtonStyle), semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinksCopy), onPressed: () { - Clipboard.setData(new ClipboardData(text: link.url)); + Clipboard.setData(ClipboardData(text: link.url)); final snackBar = SnackBar( content: Text( @@ -48,7 +47,7 @@ void modalOpenLink(BuildContext ctx, LinkableElement link) { ), ), Container( - margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10), + margin: const EdgeInsets.symmetric(vertical: 20, horizontal: 10), child: ElevatedButton( child: Text(AppLocalizations.of(bcontext)!.clickableLinkOpen, style: Provider.of(bcontext).scaleFonts(defaultTextButtonStyle), semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinkOpen), diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 4448095f..f3fd25af 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -3,15 +3,10 @@ import 'package:cwtch/cwtch/cwtch.dart'; import 'package:cwtch/main.dart'; import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/contact.dart'; -import 'package:cwtch/models/message.dart'; import 'package:cwtch/models/profilelist.dart'; -import 'package:cwtch/models/profileservers.dart'; import 'package:cwtch/models/remoteserver.dart'; import 'package:cwtch/models/servers.dart'; import 'package:cwtch/notification_manager.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; -import 'package:provider/provider.dart'; import 'package:cwtch/torstatus.dart'; @@ -111,7 +106,7 @@ class CwtchNotifier { defaultImagePath: data["defaultPicture"], blocked: data["blocked"] == "true", accepted: data["accepted"] == "true", - savePeerHistory: data["saveConversationHistory"] == null ? "DeleteHistoryConfirmed" : data["saveConversationHistory"], + savePeerHistory: data["saveConversationHistory"] ?? "DeleteHistoryConfirmed", numMessages: int.parse(data["numMessages"]), numUnread: int.parse(data["unread"]), isGroup: false, // by definition @@ -254,7 +249,7 @@ class CwtchNotifier { var notification = data["notification"]; // Only bother to do anything if we know about the group and the provided index is greater than our current total... - if (currentTotal != null && idx >= currentTotal) { + if (idx >= currentTotal) { // TODO: There are 2 timestamps associated with a new group message - time sent and time received. // Sent refers to the time a profile alleges they sent a message // Received refers to the time we actually saw the message from the server @@ -311,7 +306,7 @@ class CwtchNotifier { // local.conversation.filekey.path List keyparts = data["Key"].toString().split("."); if (keyparts.length == 5) { - String filekey = keyparts[2] + "." + keyparts[3]; + String filekey = "${keyparts[2]}.${keyparts[3]}"; profileCN.getProfile(data["ProfileOnion"])?.downloadSetPathForSender(filekey, data["Data"]); } } @@ -343,10 +338,10 @@ class CwtchNotifier { try { List associatedGroups = jsonDecode(data["Data"]); int count = int.parse(data["ServerTokenCount"]); - associatedGroups.forEach((identifier) { + for (var identifier in associatedGroups) { profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(int.parse(identifier.toString()))!.antispamTickets = count; - }); - EnvironmentConfig.debugLog("update server token count for ${associatedGroups}, $count"); + } + EnvironmentConfig.debugLog("update server token count for $associatedGroups, $count"); } catch (e) { // No tokens in data... } @@ -354,7 +349,7 @@ class CwtchNotifier { case "NewGroup": String invite = data["GroupInvite"].toString(); if (invite.startsWith("torv3")) { - String inviteJson = new String.fromCharCodes(base64Decode(invite.substring(5))); + String inviteJson = String.fromCharCodes(base64Decode(invite.substring(5))); dynamic groupInvite = jsonDecode(inviteJson); // Retrieve Server Status from Cache... @@ -396,7 +391,7 @@ class CwtchNotifier { break; case "UpdatedConversationAttribute": if (data["Path"] == "profile.name") { - if (data["Data"].toString().trim().length > 0) { + if (data["Data"].toString().trim().isNotEmpty) { // Update locally on the UI... if (profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]) != null) { profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"])!.nickname = data["Data"]; diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index de464a92..3baab010 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; -import 'dart:io' show Platform; import 'package:cwtch/cwtch/cwtchNotifier.dart'; import 'package:path/path.dart' as path; @@ -13,7 +12,6 @@ import 'package:cwtch/cwtch/cwtch.dart'; import '../config.dart'; import "package:path/path.dart" show dirname, join; -import 'dart:io' show Platform; ///////////////////// /// Cwtch API /// @@ -132,7 +130,7 @@ class CwtchFfi implements Cwtch { late DynamicLibrary library; late CwtchNotifier cwtchNotifier; late Isolate cwtchIsolate; - ReceivePort _receivePort = ReceivePort(); + final ReceivePort _receivePort = ReceivePort(); bool _isL10nInit = false; String _assetsDir = path.join(Directory.current.path, "data", "flutter_assets"); String _cwtchDir = ""; @@ -162,10 +160,11 @@ class CwtchFfi implements Cwtch { } library = DynamicLibrary.open(libraryPath); cwtchNotifier = _cwtchNotifier; - cwtchNotifier.setMessageSeenCallback((String profile, int conversation, DateTime time) => {this.SetConversationAttribute(profile, conversation, LastMessageSeenTimeKey, time.toIso8601String())}); + cwtchNotifier.setMessageSeenCallback((String profile, int conversation, DateTime time) => {SetConversationAttribute(profile, conversation, LastMessageSeenTimeKey, time.toIso8601String())}); } // ignore: non_constant_identifier_names + @override Future Start() async { String home = ""; String bundledTor = ""; @@ -264,15 +263,18 @@ class CwtchFfi implements Cwtch { }); } + @override String getAssetsDir() { return _assetsDir; } + @override Future getCwtchDir() async { return _cwtchDir; } // ignore: non_constant_identifier_names + @override Future ReconnectCwtchForeground() async { var reconnectCwtch = library.lookup>("c_ReconnectCwtchForeground"); // ignore: non_constant_identifier_names @@ -310,13 +312,13 @@ class CwtchFfi implements Cwtch { final Free = free.asFunction(); // ignore: non_constant_identifier_names - final GetAppBusEvent = () { + GetAppBusEvent() { // ignore: non_constant_identifier_names Pointer result = GetAppbusEvent(); String event = result.toDartString(); Free(result); return event; - }; + } while (true) { final event = GetAppBusEvent(); @@ -330,6 +332,7 @@ class CwtchFfi implements Cwtch { } // ignore: non_constant_identifier_names + @override void CreateProfile(String nick, String pass, bool autostart) { var createProfileC = library.lookup>("c_CreateProfile"); // ignore: non_constant_identifier_names @@ -342,6 +345,7 @@ class CwtchFfi implements Cwtch { } // ignore: non_constant_identifier_names + @override void ActivatePeerEngine(String profile) { var activatePeerEngineC = library.lookup>("c_ActivatePeerEngine"); final ActivatePeerEngine = activatePeerEngineC.asFunction(); @@ -351,6 +355,7 @@ class CwtchFfi implements Cwtch { } // ignore: non_constant_identifier_names + @override void DeactivatePeerEngine(String profile) { var deactivatePeerEngineC = library.lookup>("c_DeactivatePeerEngine"); final DeactivatePeerEngine = deactivatePeerEngineC.asFunction(); @@ -360,6 +365,7 @@ class CwtchFfi implements Cwtch { } // ignore: non_constant_identifier_names + @override void LoadProfiles(String pass) { var loadProfileC = library.lookup>("c_LoadProfiles"); // ignore: non_constant_identifier_names @@ -370,6 +376,7 @@ class CwtchFfi implements Cwtch { } // ignore: non_constant_identifier_names + @override Future GetMessage(String profile, int handle, int index) async { var getMessageC = library.lookup>("c_GetMessage"); // ignore: non_constant_identifier_names @@ -383,6 +390,7 @@ class CwtchFfi implements Cwtch { } // ignore: non_constant_identifier_names + @override Future GetMessages(String profile, int handle, int index, int count) async { var getMessagesC = library.lookup>("c_GetMessages"); // ignore: non_constant_identifier_names @@ -499,6 +507,7 @@ class CwtchFfi implements Cwtch { } // ignore: non_constant_identifier_names + @override void ExportPreviewedFile(String sourceFile, String suggestion) { // android only - do nothing } diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index 164721fa..042701c1 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -22,8 +22,8 @@ Future startCwtch() async { */ class CwtchGomobile implements Cwtch { - static const appInfoPlatform = const MethodChannel('test.flutter.dev/applicationInfo'); - static const cwtchPlatform = const MethodChannel('cwtch'); + static const appInfoPlatform = MethodChannel('test.flutter.dev/applicationInfo'); + static const cwtchPlatform = MethodChannel('cwtch'); final appbusEventChannelName = 'test.flutter.dev/eventBus'; @@ -54,30 +54,33 @@ class CwtchGomobile implements Cwtch { CwtchGomobile(CwtchNotifier _cwtchNotifier) { print("gomobile.dart: CwtchGomobile()"); cwtchNotifier = _cwtchNotifier; - cwtchNotifier.setMessageSeenCallback((String profile, int conversation, DateTime time) => {this.SetConversationAttribute(profile, conversation, LastMessageSeenTimeKey, time.toIso8601String())}); + cwtchNotifier.setMessageSeenCallback((String profile, int conversation, DateTime time) => {SetConversationAttribute(profile, conversation, LastMessageSeenTimeKey, time.toIso8601String())}); androidHomeDirectory = getApplicationDocumentsDirectory(); androidLibraryDir = appInfoPlatform.invokeMethod('getNativeLibDir'); // Method channel to receive libcwtch-go events via Kotlin and dispatch them to _handleAppbusEvent (sends to cwtchNotifier) final appbusEventChannel = MethodChannel(appbusEventChannelName); - appbusEventChannel.setMethodCallHandler(this._handleAppbusEvent); + appbusEventChannel.setMethodCallHandler(_handleAppbusEvent); } + @override String getAssetsDir() { // TODO return ""; } // Requires Start() to have been run to initialize + @override Future getCwtchDir() async { return _cwtchDir; } // ignore: non_constant_identifier_names + @override Future Start() async { print("gomobile.dart: Start()..."); androidHomeDirectoryStr = (await androidHomeDirectory).path; - _cwtchDir = path.join(await androidHomeDirectoryStr, ".cwtch"); + _cwtchDir = path.join(androidHomeDirectoryStr, ".cwtch"); if (EnvironmentConfig.BUILD_VER == dev_version) { _cwtchDir = path.join(_cwtchDir, "dev"); } @@ -100,41 +103,49 @@ class CwtchGomobile implements Cwtch { } // ignore: non_constant_identifier_names + @override void CreateProfile(String nick, String pass, bool autostart) { cwtchPlatform.invokeMethod("CreateProfile", {"nick": nick, "pass": pass, "autostart": autostart}); } // ignore: non_constant_identifier_names + @override void ActivatePeerEngine(String profile) { cwtchPlatform.invokeMethod("ActivatePeerEngine", {"profile": profile}); } // ignore: non_constant_identifier_names + @override void DeactivatePeerEngine(String profile) { cwtchPlatform.invokeMethod("DeactivatePeerEngine", {"profile": profile}); } // ignore: non_constant_identifier_names + @override void LoadProfiles(String pass) { cwtchPlatform.invokeMethod("LoadProfiles", {"pass": pass}); } // ignore: non_constant_identifier_names + @override void DeleteProfile(String onion, String pass) { cwtchPlatform.invokeMethod("DeleteProfile", {"ProfileOnion": onion, "pass": pass}); } // ignore: non_constant_identifier_names + @override Future GetMessage(String profile, int conversation, int index) { return cwtchPlatform.invokeMethod("GetMessage", {"ProfileOnion": profile, "conversation": conversation, "index": index}); } // ignore: non_constant_identifier_names + @override Future GetMessageByID(String profile, int conversation, int id) { return cwtchPlatform.invokeMethod("GetMessageByID", {"ProfileOnion": profile, "conversation": conversation, "id": id}); } // ignore: non_constant_identifier_names + @override Future GetMessages(String profile, int conversation, int index, int count) { return cwtchPlatform.invokeMethod("GetMessages", {"ProfileOnion": profile, "conversation": conversation, "index": index, "count": count}); } @@ -197,12 +208,14 @@ class CwtchGomobile implements Cwtch { } // ignore: non_constant_identifier_names + @override void CreateDownloadableFile(String profileOnion, int conversation, String filenameSuggestion, String filekey, String manifestpath) { cwtchPlatform .invokeMethod("CreateDownloadableFile", {"ProfileOnion": profileOnion, "conversation": conversation, "manifestpath": manifestpath, "filename": filenameSuggestion, "filekey": filekey}); } // ignore: non_constant_identifier_names + @override void ExportPreviewedFile(String sourceFile, String suggestion) { cwtchPlatform.invokeMethod("ExportPreviewedFile", { "Path": sourceFile, @@ -335,7 +348,7 @@ class CwtchGomobile implements Cwtch { @override String? defaultDownloadPath() { - return this.androidHomeDirectoryStr; + return androidHomeDirectoryStr; } @override diff --git a/lib/l10n/custom_material_delegate.dart b/lib/l10n/custom_material_delegate.dart index 59041c9c..b4838d27 100644 --- a/lib/l10n/custom_material_delegate.dart +++ b/lib/l10n/custom_material_delegate.dart @@ -490,7 +490,7 @@ class MaterialLocalizationLu extends MaterialLocalizations { @override String aboutListTileTitle(String applicationName) { - return aboutListTileTitleRaw.replaceFirst("$applicationName", applicationName); + return aboutListTileTitleRaw.replaceFirst(applicationName, applicationName); } @override diff --git a/lib/licenses.dart b/lib/licenses.dart index 49593ad5..018fc846 100644 --- a/lib/licenses.dart +++ b/lib/licenses.dart @@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart'; Stream licenses() async* { /// Open Privacy Code - yield LicenseEntryWithLineBreaks(["cwtch", "tapir", "connectivity", "log"], '''MIT License + yield const LicenseEntryWithLineBreaks(["cwtch", "tapir", "connectivity", "log"], '''MIT License Copyright (c) 2018-2023 Open Privacy Research Society @@ -13,7 +13,7 @@ Stream licenses() async* { THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.'''); /// Ristretto Code - yield LicenseEntryWithLineBreaks(["ristretto255"], '''Copyright (c) 2009 The Go Authors. All rights reserved. + yield const LicenseEntryWithLineBreaks(["ristretto255"], '''Copyright (c) 2009 The Go Authors. All rights reserved. Copyright (c) 2017 George Tankersley. All rights reserved. Copyright (c) 2019 Henry de Valence. All rights reserved. @@ -44,7 +44,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.'''); /// Package pretty provides pretty-printing for Go values. (via Cwtch) - yield LicenseEntryWithLineBreaks(["pretty"], '''Copyright 2012 Keith Rarick + yield const LicenseEntryWithLineBreaks(["pretty"], '''Copyright 2012 Keith Rarick Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -64,7 +64,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.'''); - yield LicenseEntryWithLineBreaks(["pidusage"], '''MIT License + yield const LicenseEntryWithLineBreaks(["pidusage"], '''MIT License Copyright (c) 2017 David 大伟 @@ -87,7 +87,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.'''); /// Go Standard Lib - yield LicenseEntryWithLineBreaks(["crypto, net"], '''Copyright (c) 2009 The Go Authors. All rights reserved. + yield const LicenseEntryWithLineBreaks(["crypto, net"], '''Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -115,9 +115,9 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.'''); - yield LicenseEntryWithLineBreaks(["flaticons"], "Icons made by Freepik (https://www.freepik.com) from Flaticon (www.flaticon.com)"); + yield const LicenseEntryWithLineBreaks(["flaticons"], "Icons made by Freepik (https://www.freepik.com) from Flaticon (www.flaticon.com)"); - yield LicenseEntryWithLineBreaks(["flutter_linkify", "linkify"], '''MIT License + yield const LicenseEntryWithLineBreaks(["flutter_linkify", "linkify"], '''MIT License Copyright (c) 2019/2020 Charles-William Crete @@ -139,7 +139,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.'''); - yield LicenseEntryWithLineBreaks(["connectivity_plus", "connectivity_plus_platform_interface"], ''' + yield const LicenseEntryWithLineBreaks(["connectivity_plus", "connectivity_plus_platform_interface"], ''' Copyright 2017 The Chromium Authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -168,14 +168,14 @@ SOFTWARE.'''); (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.'''); - yield LicenseEntryWithLineBreaks(["connectivity_plus"], ''' + yield const LicenseEntryWithLineBreaks(["connectivity_plus"], ''' * Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree'''); - yield LicenseEntryWithLineBreaks(["nm"], '''Mozilla Public License Version 2.0 + yield const LicenseEntryWithLineBreaks(["nm"], '''Mozilla Public License Version 2.0 ================================== 1. Definitions @@ -549,7 +549,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.'''); - yield LicenseEntryWithLineBreaks(["Inter Fonts"], ''' + yield const LicenseEntryWithLineBreaks(["Inter Fonts"], ''' Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) This Font Software is licensed under the SIL Open Font License, Version 1.1. @@ -645,7 +645,7 @@ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. '''); - yield LicenseEntryWithLineBreaks(["Noto Color Emoji Fonts"], ''' + yield const LicenseEntryWithLineBreaks(["Noto Color Emoji Fonts"], ''' Copyright 2021 Google Inc. All Rights Reserved. This Font Software is licensed under the SIL Open Font License, Version 1.1. @@ -741,7 +741,7 @@ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. '''); - yield LicenseEntryWithLineBreaks(["Roboto fonts"], ''' + yield const LicenseEntryWithLineBreaks(["Roboto fonts"], ''' Apache License Version 2.0, January 2004 diff --git a/lib/main.dart b/lib/main.dart index f3d888a0..aee6f150 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:isolate'; import 'package:cwtch/config.dart'; import 'package:cwtch/notification_manager.dart'; import 'package:cwtch/themes/cwtch.dart'; @@ -33,9 +32,7 @@ import 'themes/opaque.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:intl/intl.dart' as intl; - -var globalSettings = Settings(Locale("en", ''), CwtchDark()); +var globalSettings = Settings(const Locale("en", ''), CwtchDark()); var globalErrorHandler = ErrorHandler(); var globalTorStatus = TorStatus(); var globalAppState = AppState(); @@ -54,6 +51,8 @@ Future main() async { class Flwtch extends StatefulWidget { final Key flwtch = GlobalKey(); + Flwtch({super.key}); + @override FlwtchState createState() => FlwtchState(); } @@ -63,9 +62,9 @@ enum ConnectivityState { assumed_online, confirmed_offline, confirmed_online } class FlwtchState extends State with WindowListener { late Cwtch cwtch; late ProfileListState profs; - final MethodChannel notificationClickChannel = MethodChannel('im.cwtch.flwtch/notificationClickHandler'); - final MethodChannel shutdownMethodChannel = MethodChannel('im.cwtch.flwtch/shutdownClickHandler'); - final MethodChannel shutdownLinuxMethodChannel = MethodChannel('im.cwtch.linux.shutdown'); + final MethodChannel notificationClickChannel = const MethodChannel('im.cwtch.flwtch/notificationClickHandler'); + final MethodChannel shutdownMethodChannel = const MethodChannel('im.cwtch.flwtch/shutdownClickHandler'); + final MethodChannel shutdownLinuxMethodChannel = const MethodChannel('im.cwtch.linux.shutdown'); late StreamSubscription? connectivityStream; ConnectivityState connectivityState = ConnectivityState.assumed_online; @@ -80,7 +79,7 @@ class FlwtchState extends State with WindowListener { @override initState() { print("initState() started, setting up handlers"); - globalSettings = Settings(Locale("en", ''), CwtchDark()); + globalSettings = Settings(const Locale("en", ''), CwtchDark()); globalErrorHandler = ErrorHandler(); globalTorStatus = TorStatus(); globalAppState = AppState(); @@ -96,15 +95,13 @@ class FlwtchState extends State with WindowListener { shutdownLinuxMethodChannel.setMethodCallHandler(shutdownDirect); print("initState: creating cwtchnotifier, ffi"); if (Platform.isAndroid) { - var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState, globalServersList, this); + var cwtchNotifier = CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState, globalServersList, this); cwtch = CwtchGomobile(cwtchNotifier); } else if (Platform.isLinux) { - var cwtchNotifier = - new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList, this); + var cwtchNotifier = CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList, this); cwtch = CwtchFfi(cwtchNotifier); } else { - var cwtchNotifier = - new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList, this); + var cwtchNotifier = CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList, this); cwtch = CwtchFfi(cwtchNotifier); } print("initState: invoking cwtch.Start()"); @@ -128,7 +125,7 @@ class FlwtchState extends State with WindowListener { // gracefully fails and NOPs, as it's not a required functionality startConnectivityListener() async { try { - connectivityStream = await Connectivity().onConnectivityChanged.listen((ConnectivityResult result) { + connectivityStream = Connectivity().onConnectivityChanged.listen((ConnectivityResult result) { // Got a new connectivity status! if (result == ConnectivityResult.none) { connectivityState = ConnectivityState.confirmed_offline; @@ -174,11 +171,11 @@ class FlwtchState extends State with WindowListener { builder: (context, widget) { // in test mode...rebuild everything every second...if cwtch isn't loaded... if (EnvironmentConfig.TEST_MODE && cwtch.IsLoaded() == false) { - Timer t = new Timer.periodic(Duration(seconds: 1), (Timer t) => setState(() {})); + Timer t = Timer.periodic(const Duration(seconds: 1), (Timer t) => setState(() {})); } return Consumer2( builder: (context, settings, appState, child) => MaterialApp( - key: Key('app'), + key: const Key('app'), navigatorKey: navKey, locale: settings.locale, showPerformanceOverlay: settings.profileMode, @@ -193,7 +190,7 @@ class FlwtchState extends State with WindowListener { title: 'Cwtch', showSemanticsDebugger: settings.useSemanticDebugger, theme: mkThemeData(settings), - home: (!appState.cwtchInit || appState.modalState != ModalState.none) || !cwtch.IsLoaded() ? SplashView() : ProfileMgrView(), + home: (!appState.cwtchInit || appState.modalState != ModalState.none) || !cwtch.IsLoaded() ? const SplashView() : const ProfileMgrView(), ), ); }, @@ -256,7 +253,6 @@ class FlwtchState extends State with WindowListener { exit(0); } } - ; } // Invoked via notificationClickChannel by MyBroadcastReceiver in MainActivity.kt @@ -286,7 +282,7 @@ class FlwtchState extends State with WindowListener { Navigator.of(navKey.currentContext!).push( PageRouteBuilder( - settings: RouteSettings(name: "conversations"), + settings: const RouteSettings(name: "conversations"), pageBuilder: (c, a1, a2) { return OrientationBuilder(builder: (orientationBuilderContext, orientation) { return MultiProvider( @@ -294,12 +290,12 @@ class FlwtchState extends State with WindowListener { builder: (innercontext, widget) { var appState = Provider.of(navKey.currentContext!); var settings = Provider.of(navKey.currentContext!); - return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? DoubleColumnView() : MessageView(); + return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? const DoubleColumnView() : const MessageView(); }); }); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); // On Gnome follows up a clicked notification with a "Cwtch is ready" notification that takes you to the app. AFAICT just because Gnome is bad @@ -320,6 +316,7 @@ class FlwtchState extends State with WindowListener { globalAppState.focus = false; } + @override void onWindowClose() {} @override diff --git a/lib/main_test.dart b/lib/main_test.dart index a6abe145..79478f82 100644 --- a/lib/main_test.dart +++ b/lib/main_test.dart @@ -8,16 +8,14 @@ import 'package:glob/list_local_fs.dart'; import 'config.dart'; import 'licenses.dart'; import 'main.dart'; -import 'themes/opaque.dart'; import 'dart:convert'; import 'dart:io'; -import 'dart:typed_data'; import "package:flutter_driver/driver_extension.dart"; import 'package:flutter_test/flutter_test.dart'; import 'package:glob/glob.dart'; -var globalSettings = Settings(Locale("en", ''), CwtchDark()); +var globalSettings = Settings(const Locale("en", ''), CwtchDark()); var globalErrorHandler = ErrorHandler(); Future main() async { diff --git a/lib/models/appstate.dart b/lib/models/appstate.dart index 9d8f54b9..c4e7bcc2 100644 --- a/lib/models/appstate.dart +++ b/lib/models/appstate.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:cwtch/config.dart'; import 'package:flutter/widgets.dart'; @@ -19,7 +18,7 @@ class AppState extends ChangeNotifier { bool _disableFilePicker = false; bool _focus = true; - StreamController _profilesUnreadNotifyControler = StreamController(); + final StreamController _profilesUnreadNotifyControler = StreamController(); late Stream profilesUnreadNotify; AppState() { @@ -33,50 +32,50 @@ class AppState extends ChangeNotifier { void SetAppError(String error) { appError = error; - EnvironmentConfig.debugLog("App Error: ${appError}"); + EnvironmentConfig.debugLog("App Error: $appError"); notifyListeners(); } void SetModalState(ModalState newState) { modalState = newState; - EnvironmentConfig.debugLog("Modal State: ${newState}"); + EnvironmentConfig.debugLog("Modal State: $newState"); notifyListeners(); } String? get selectedProfile => _selectedProfile; set selectedProfile(String? newVal) { - this._selectedConversation = null; - this._selectedProfile = newVal; + _selectedConversation = null; + _selectedProfile = newVal; notifyListeners(); } int? get selectedConversation => _selectedConversation; set selectedConversation(int? newVal) { - this._selectedConversation = newVal; + _selectedConversation = newVal; notifyListeners(); } int? get selectedSearchMessage => _selectedSearchMessage; set selectedSearchMessage(int? newVal) { - this._selectedSearchMessage = newVal; + _selectedSearchMessage = newVal; notifyListeners(); } bool get disableFilePicker => _disableFilePicker; set disableFilePicker(bool newVal) { - this._disableFilePicker = newVal; + _disableFilePicker = newVal; notifyListeners(); } bool get unreadMessagesBelow => _unreadMessagesBelow; set unreadMessagesBelow(bool newVal) { - this._unreadMessagesBelow = newVal; + _unreadMessagesBelow = newVal; notifyListeners(); } int get initialScrollIndex => _initialScrollIndex; set initialScrollIndex(int newVal) { - this._initialScrollIndex = newVal; + _initialScrollIndex = newVal; notifyListeners(); } diff --git a/lib/models/contact.dart b/lib/models/contact.dart index 2dfae7f2..e9eb5e84 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -1,5 +1,3 @@ -import 'dart:ffi'; - import 'package:cwtch/main.dart'; import 'package:cwtch/models/message_draft.dart'; import 'package:cwtch/models/profile.dart'; @@ -13,7 +11,6 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'message.dart'; import 'messagecache.dart'; enum ConversationNotificationPolicy { @@ -57,7 +54,7 @@ class ContactInfoState extends ChangeNotifier { late Map> keys; int _newMarkerMsgIndex = -1; late MessageCache messageCache; - ItemScrollController messageScrollController = new ItemScrollController(); + ItemScrollController messageScrollController = ItemScrollController(); // todo: a nicer way to model contacts, groups and other "entities" late bool _isGroup; @@ -98,55 +95,55 @@ class ContactInfoState extends ChangeNotifier { notificationPolicy = "ConversationNotificationPolicy.Default", pinned = false, }) { - this._nickname = nickname; - this._localNickname = localNickname; - this._isGroup = isGroup; - this._accepted = accepted; - this._blocked = blocked; - this._status = status; - this._imagePath = imagePath; - this._defaultImagePath = defaultImagePath; - this._totalMessages = numMessages; - this._unreadMessages = numUnread; - this._savePeerHistory = savePeerHistory; - this._lastMessageReceivedTime = lastMessageTime == null ? DateTime.fromMillisecondsSinceEpoch(0) : lastMessageTime; - this._lastMessageSentTime = _lastMessageReceivedTime; - this._server = server; - this._archived = archived; - this._notificationPolicy = notificationPolicyFromString(notificationPolicy); - this.messageCache = new MessageCache(_totalMessages); - this._pinned = pinned; - keys = Map>(); + _nickname = nickname; + _localNickname = localNickname; + _isGroup = isGroup; + _accepted = accepted; + _blocked = blocked; + _status = status; + _imagePath = imagePath; + _defaultImagePath = defaultImagePath; + _totalMessages = numMessages; + _unreadMessages = numUnread; + _savePeerHistory = savePeerHistory; + _lastMessageReceivedTime = lastMessageTime ?? DateTime.fromMillisecondsSinceEpoch(0); + _lastMessageSentTime = _lastMessageReceivedTime; + _server = server; + _archived = archived; + _notificationPolicy = notificationPolicyFromString(notificationPolicy); + messageCache = MessageCache(_totalMessages); + _pinned = pinned; + keys = >{}; } String get nickname { - if (this._localNickname != "") { - return this._localNickname; + if (_localNickname != "") { + return _localNickname; } - return this._nickname; + return _nickname; } - String get savePeerHistory => this._savePeerHistory; + String get savePeerHistory => _savePeerHistory; - String? get acnCircuit => this._acnCircuit; + String? get acnCircuit => _acnCircuit; - MessageDraft get messageDraft => this._messageDraft; + MessageDraft get messageDraft => _messageDraft; - DateTime get lastRetryTime => this._lastRetryTime; + DateTime get lastRetryTime => _lastRetryTime; set lastRetryTime(DateTime lastRetryTime) { - this._lastRetryTime = lastRetryTime; + _lastRetryTime = lastRetryTime; notifyListeners(); } set antispamTickets(int antispamTickets) { - this._antispamTickets = antispamTickets; + _antispamTickets = antispamTickets; notifyListeners(); } - int get antispamTickets => this._antispamTickets; + int get antispamTickets => _antispamTickets; set acnCircuit(String? acnCircuit) { - this._acnCircuit = acnCircuit; + _acnCircuit = acnCircuit; notifyListeners(); } @@ -154,58 +151,58 @@ class ContactInfoState extends ChangeNotifier { // be moved to the very bottom of the active conversations list until // new messages appear set isArchived(bool archived) { - this._archived = archived; + _archived = archived; notifyListeners(); } - bool get isArchived => this._archived; + bool get isArchived => _archived; set savePeerHistory(String newVal) { - this._savePeerHistory = newVal; + _savePeerHistory = newVal; notifyListeners(); } set nickname(String newVal) { - this._nickname = newVal; + _nickname = newVal; notifyListeners(); } set localNickname(String newVal) { - this._localNickname = newVal; + _localNickname = newVal; notifyListeners(); } - bool get isGroup => this._isGroup; + bool get isGroup => _isGroup; set isGroup(bool newVal) { - this._isGroup = newVal; + _isGroup = newVal; notifyListeners(); } - bool get isBlocked => this._blocked; + bool get isBlocked => _blocked; - bool get isInvitation => !this._blocked && !this._accepted; + bool get isInvitation => !_blocked && !_accepted; set accepted(bool newVal) { - this._accepted = newVal; + _accepted = newVal; notifyListeners(); } set blocked(bool newVal) { - this._blocked = newVal; + _blocked = newVal; notifyListeners(); } - String get status => this._status; + String get status => _status; set status(String newVal) { - this._status = newVal; - this.contactEvents.add(ContactEvent("Update Peer Status Received: $newVal")); + _status = newVal; + contactEvents.add(ContactEvent("Update Peer Status Received: $newVal")); notifyListeners(); } set messageDraft(MessageDraft newVal) { - this._messageDraft = newVal; + _messageDraft = newVal; notifyListeners(); } @@ -214,89 +211,89 @@ class ContactInfoState extends ChangeNotifier { } void selected() { - this._newMarkerMsgIndex = this._unreadMessages - 1; - this._unreadMessages = 0; + _newMarkerMsgIndex = _unreadMessages - 1; + _unreadMessages = 0; } void unselected() { - this._newMarkerMsgIndex = -1; + _newMarkerMsgIndex = -1; } - int get unreadMessages => this._unreadMessages; + int get unreadMessages => _unreadMessages; set unreadMessages(int newVal) { - this._unreadMessages = newVal; + _unreadMessages = newVal; notifyListeners(); } int get newMarkerMsgIndex { - return this._newMarkerMsgIndex; + return _newMarkerMsgIndex; } - int get totalMessages => this._totalMessages; + int get totalMessages => _totalMessages; set totalMessages(int newVal) { - this._totalMessages = newVal; - this.messageCache.storageMessageCount = newVal; + _totalMessages = newVal; + messageCache.storageMessageCount = newVal; notifyListeners(); } String get imagePath { // don't show custom images for blocked contacts.. - if (!this.isBlocked) { - return this._imagePath; + if (!isBlocked) { + return _imagePath; } - return this.defaultImagePath; + return defaultImagePath; } set imagePath(String newVal) { - this._imagePath = newVal; + _imagePath = newVal; notifyListeners(); } - String get defaultImagePath => this._defaultImagePath; + String get defaultImagePath => _defaultImagePath; set defaultImagePath(String newVal) { - this._defaultImagePath = newVal; + _defaultImagePath = newVal; notifyListeners(); } // This is last message received time (local) and to be used for sorting only // for instance, group sync, we want to pop to the top, so we set to time.Now() for new messages // but it should not be used for display - DateTime get lastMessageReceivedTime => this._lastMessageReceivedTime; + DateTime get lastMessageReceivedTime => _lastMessageReceivedTime; set lastMessageReceivedTime(DateTime newVal) { - this._lastMessageReceivedTime = newVal; + _lastMessageReceivedTime = newVal; notifyListeners(); } // This is last message sent time and is based on message reports of sent times // this can be used to display in the contact list a last time a message was received - DateTime get lastMessageSentTime => this._lastMessageSentTime; + DateTime get lastMessageSentTime => _lastMessageSentTime; set lastMessageSentTime(DateTime newVal) { - this._lastMessageSentTime = newVal; + _lastMessageSentTime = newVal; notifyListeners(); } // we only allow callers to fetch the server - String? get server => this._server; + String? get server => _server; bool isOnline() { - if (this.isGroup == true) { + if (isGroup == true) { // We now have an out of sync warning so we will mark these as online... - return this.status == "Authenticated" || this.status == "Synced"; + return status == "Authenticated" || status == "Synced"; } else { - return this.status == "Authenticated"; + return status == "Authenticated"; } } bool canSend() { - if (this.isGroup == true) { + if (isGroup == true) { // We now have an out of sync warning so we will mark these as online... - return this.status == "Synced" && this.antispamTickets > 0; + return status == "Synced" && antispamTickets > 0; } else { - return this.isOnline(); + return isOnline(); } } @@ -308,7 +305,7 @@ class ContactInfoState extends ChangeNotifier { } GlobalKey getMessageKey(int conversation, int message) { - String index = "c: " + conversation.toString() + " m:" + message.toString(); + String index = "c: $conversation m:$message"; if (keys[index] == null) { keys[index] = GlobalKey(); } @@ -317,7 +314,7 @@ class ContactInfoState extends ChangeNotifier { } GlobalKey? getMessageKeyOrFail(int conversation, int message) { - String index = "c: " + conversation.toString() + " m:" + message.toString(); + String index = "c: $conversation m:$message"; if (keys[index] == null) { return null; @@ -338,10 +335,10 @@ class ContactInfoState extends ChangeNotifier { _newMarkerMsgIndex++; } - this._lastMessageReceivedTime = timestamp; - this._lastMessageSentTime = timestamp; - this.messageCache.addNew(profileOnion, identifier, messageID, timestamp, senderHandle, senderImage, isAuto, data, contenthash); - this.totalMessages += 1; + _lastMessageReceivedTime = timestamp; + _lastMessageSentTime = timestamp; + messageCache.addNew(profileOnion, identifier, messageID, timestamp, senderHandle, senderImage, isAuto, data, contenthash); + totalMessages += 1; // We only ever see messages from authenticated peers. // If the contact is marked as offline then override this - can happen when the contact is removed from the front @@ -353,12 +350,12 @@ class ContactInfoState extends ChangeNotifier { } void ackCache(int messageID) { - this.messageCache.ackCache(messageID); + messageCache.ackCache(messageID); notifyListeners(); } void errCache(int messageID) { - this.messageCache.errCache(messageID); + messageCache.errCache(messageID); notifyListeners(); } @@ -408,14 +405,14 @@ class ContactInfoState extends ChangeNotifier { } void updateTranslationEvent(int messageID, String translation) { - this.messageCache.updateTranslationEvent(messageID, translation); + messageCache.updateTranslationEvent(messageID, translation); notifyListeners(); } // Contact Attributes. Can be set in Profile Edit View... List attributes = [null, null, null]; void setAttribute(int i, String? value) { - this.attributes[i] = value; + attributes[i] = value; notifyListeners(); } @@ -438,11 +435,11 @@ class ContactInfoState extends ChangeNotifier { } Color getBorderColor(OpaqueThemeType theme) { - if (this.isBlocked) { + if (isBlocked) { return theme.portraitBlockedBorderColor; } - if (this.isOnline()) { - switch (this.availabilityStatus) { + if (isOnline()) { + switch (availabilityStatus) { case ProfileStatusMenu.available: return theme.portraitOnlineBorderColor; case ProfileStatusMenu.away: @@ -458,26 +455,26 @@ class ContactInfoState extends ChangeNotifier { } String augmentedNickname(BuildContext context) { - var nick = redactedNick(context, this.onion, this.nickname); - return nick + (this.availabilityStatus == ProfileStatusMenu.available ? "" : " (" + this.statusString(context) + ")"); + var nick = redactedNick(context, onion, nickname); + return nick + (availabilityStatus == ProfileStatusMenu.available ? "" : " (${statusString(context)})"); } // Never use this for message lookup - can be a non-indexed value // e.g. -1 int get hoveredIndex => _hoveredIndex; set hoveredIndex(int newVal) { - this._hoveredIndex = newVal; + _hoveredIndex = newVal; notifyListeners(); } int get pendingScroll => _pendingScroll; set pendingScroll(int newVal) { - this._pendingScroll = newVal; + _pendingScroll = newVal; notifyListeners(); } String statusString(BuildContext context) { - switch (this.availabilityStatus) { + switch (availabilityStatus) { case ProfileStatusMenu.available: return AppLocalizations.of(context)!.availabilityStatusAvailable; case ProfileStatusMenu.away: @@ -494,6 +491,6 @@ class ContactEvent { String summary; late DateTime timestamp; ContactEvent(this.summary) { - this.timestamp = DateTime.now(); + timestamp = DateTime.now(); } } diff --git a/lib/models/contactlist.dart b/lib/models/contactlist.dart index e8709508..8fa8593a 100644 --- a/lib/models/contactlist.dart +++ b/lib/models/contactlist.dart @@ -6,7 +6,7 @@ import 'profileservers.dart'; class ContactListState extends ChangeNotifier { ProfileServerListState? servers; - List _contacts = []; + final List _contacts = []; String _filter = ""; int get num => _contacts.length; int get numFiltered => isFiltered ? filteredList().length : num; @@ -29,11 +29,11 @@ class ContactListState extends ChangeNotifier { void addAll(Iterable newContacts) { _contacts.addAll(newContacts); servers?.clearGroups(); - _contacts.forEach((contact) { + for (var contact in _contacts) { if (contact.isGroup) { servers?.addGroup(contact); } - }); + } resort(); notifyListeners(); } @@ -54,7 +54,7 @@ class ContactListState extends ChangeNotifier { if (otherGroups != null && otherGroups.isNotEmpty) { EnvironmentConfig.debugLog("sharing antispam tickets to new group. FIXME: in Cwtch 1.14"); var antispamTickets = otherGroups[0].antispamTickets; - _contacts.last!.antispamTickets = antispamTickets; + _contacts.last.antispamTickets = antispamTickets; } servers?.addGroup(newContact); } diff --git a/lib/models/filedownloadprogress.dart b/lib/models/filedownloadprogress.dart index 6b0a1935..95828c43 100644 --- a/lib/models/filedownloadprogress.dart +++ b/lib/models/filedownloadprogress.dart @@ -7,7 +7,7 @@ class FileDownloadProgress { // we keep track of both an explicit interrupt flag (for when a request fails or is explicitly cancelled) set interrupted(isInterrupted) { - this._interrupted = isInterrupted; + _interrupted = isInterrupted; } // but we have a fuzzy get which depends on lastUpdate, if the file isn't complete, but the last update was more @@ -33,12 +33,12 @@ class FileDownloadProgress { String prettyBytes(int bytes) { if (bytes > 1000000000) { - return (1.0 * bytes / 1000000000).toStringAsFixed(1) + " GB"; + return "${(1.0 * bytes / 1000000000).toStringAsFixed(1)} GB"; } else if (bytes > 1000000) { - return (1.0 * bytes / 1000000).toStringAsFixed(1) + " MB"; + return "${(1.0 * bytes / 1000000).toStringAsFixed(1)} MB"; } else if (bytes > 1000) { - return (1.0 * bytes / 1000).toStringAsFixed(1) + " kB"; + return "${(1.0 * bytes / 1000).toStringAsFixed(1)} kB"; } else { - return bytes.toString() + " B"; + return "$bytes B"; } } diff --git a/lib/models/message.dart b/lib/models/message.dart index 1557e16d..7981191a 100644 --- a/lib/models/message.dart +++ b/lib/models/message.dart @@ -76,6 +76,7 @@ class ByIndex implements CacheHandler { return msg; } + @override Future get(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache) async { // if in cache, get. But if the cache has unsynced or not in cache, we'll have to do a fetch if (index < cache.cacheByIndex.length) { @@ -132,7 +133,7 @@ class ByIndex implements CacheHandler { cache.addIndexed(messageInfo, start + i); } } catch (e, stacktrace) { - EnvironmentConfig.debugLog("Error: Getting indexed messages $start to ${start + amount} failed parsing: " + e.toString() + " " + stacktrace.toString()); + EnvironmentConfig.debugLog("Error: Getting indexed messages $start to ${start + amount} failed parsing: $e $stacktrace"); } finally { if (i != amount) { cache.malformIndexes(start + i, start + amount); @@ -165,6 +166,7 @@ class ById implements CacheHandler { return Future.value(messageInfo); } + @override Future get(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache) async { var messageInfo = await lookup(cache); if (messageInfo != null) { @@ -193,6 +195,7 @@ class ByContentHash implements CacheHandler { return Future.value(messageInfo); } + @override Future get(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache) async { var messageInfo = await lookup(cache); if (messageInfo != null) { @@ -274,7 +277,7 @@ MessageInfo? messageJsonToInfo(String profileOnion, int conversationIdentifier, return messageWrapperToInfo(profileOnion, conversationIdentifier, messageWrapper); } catch (e, stacktrace) { - EnvironmentConfig.debugLog("message handler exception on parse message and cache: " + e.toString() + " " + stacktrace.toString()); + EnvironmentConfig.debugLog("message handler exception on parse message and cache: $e $stacktrace"); return null; } } @@ -291,7 +294,7 @@ MessageInfo messageWrapperToInfo(String profileOnion, int conversationIdentifier var signature = messageWrapper['Signature']; var contenthash = messageWrapper['ContentHash']; var metadata = MessageMetadata(profileOnion, conversationIdentifier, messageID, timestamp, senderHandle, senderImage, signature, attributes, ackd, error, false, contenthash); - var messageInfo = new MessageInfo(metadata, messageWrapper['Message']); + var messageInfo = MessageInfo(metadata, messageWrapper['Message']); return messageInfo; } @@ -313,9 +316,9 @@ class MessageMetadata extends ChangeNotifier { final String? signature; final String contenthash; - dynamic get attributes => this._attributes; + dynamic get attributes => _attributes; - bool get ackd => this._ackd; + bool get ackd => _ackd; String translation = ""; void updateTranslationEvent(String translation) { @@ -324,14 +327,14 @@ class MessageMetadata extends ChangeNotifier { } set ackd(bool newVal) { - this._ackd = newVal; + _ackd = newVal; notifyListeners(); } - bool get error => this._error; + bool get error => _error; set error(bool newVal) { - this._error = newVal; + _error = newVal; notifyListeners(); } diff --git a/lib/models/message_draft.dart b/lib/models/message_draft.dart index 23ffb6ff..4c569c7f 100644 --- a/lib/models/message_draft.dart +++ b/lib/models/message_draft.dart @@ -1,5 +1,4 @@ import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; /// A "MessageDraft" structure that stores information about in-progress message drafts. /// MessageDraft stores text, quoted replies, and attached images. @@ -14,43 +13,43 @@ class MessageDraft extends ChangeNotifier { } bool isEmpty() { - return (this._quotedReference == null) || (this.messageText.isEmpty); + return (_quotedReference == null) || (messageText.isEmpty); } String get messageText => ctrlCompose.text; set messageText(String text) { - this.ctrlCompose.text = text; + ctrlCompose.text = text; notifyListeners(); } set quotedReference(int index) { - this._quotedReference = QuotedReference(index); + _quotedReference = QuotedReference(index); notifyListeners(); } void attachInvite(int handle) { - this._inviteHandle = handle; + _inviteHandle = handle; notifyListeners(); } int? getInviteHandle() { - return this._inviteHandle; + return _inviteHandle; } QuotedReference? getQuotedMessage() { - return this._quotedReference; + return _quotedReference; } void clearQuotedReference() { - this._quotedReference = null; + _quotedReference = null; notifyListeners(); } void clearDraft() { - this._quotedReference = null; - this.ctrlCompose.clear(); - this._inviteHandle = null; + _quotedReference = null; + ctrlCompose.clear(); + _inviteHandle = null; notifyListeners(); } @@ -61,7 +60,7 @@ class MessageDraft extends ChangeNotifier { } void clearInvite() { - this._inviteHandle = null; + _inviteHandle = null; } } diff --git a/lib/models/messagecache.dart b/lib/models/messagecache.dart index 94bb834e..ab68d537 100644 --- a/lib/models/messagecache.dart +++ b/lib/models/messagecache.dart @@ -26,10 +26,7 @@ class LocalIndexMessage { late int? messageId; - LocalIndexMessage(int? messageId, {cacheOnly = false, isLoading = false}) { - this.messageId = messageId; - this.cacheOnly = cacheOnly; - this.isLoading = isLoading; + LocalIndexMessage(this.messageId, {this.cacheOnly = false, this.isLoading = false}) { loader = Completer(); loaded = loader.future; if (!isLoading) { @@ -46,7 +43,7 @@ class LocalIndexMessage { } void failLoad() { - this.messageId = null; + messageId = null; if (!loader.isCompleted) { isLoading = false; loader.complete(true); @@ -92,17 +89,17 @@ class MessageCache extends ChangeNotifier { cache = {}; cacheByIndex = List.empty(growable: true); cacheByHash = {}; - this._storageMessageCount = storageMessageCount; + _storageMessageCount = storageMessageCount; } int get storageMessageCount => _storageMessageCount; set storageMessageCount(int newval) { - this._storageMessageCount = newval; + _storageMessageCount = newval; } // On android reconnect, if backend supplied message count > UI message count, add the difference to the front of the index void addFrontIndexGap(int count) { - this._indexUnsynced = count; + _indexUnsynced = count; } int get indexUnsynced => _indexUnsynced; @@ -127,10 +124,10 @@ class MessageCache extends ChangeNotifier { MessageInfo? getByContentHash(String contenthash) => cache[cacheByHash[contenthash]]; void addNew(String profileOnion, int conversation, int messageID, DateTime timestamp, String senderHandle, String senderImage, bool isAuto, String data, String contenthash) { - this.cache[messageID] = MessageInfo(MessageMetadata(profileOnion, conversation, messageID, timestamp, senderHandle, senderImage, "", {}, false, false, isAuto, contenthash), data); - this.cacheByIndex.insert(0, LocalIndexMessage(messageID)); - if (contenthash != null && contenthash != "") { - this.cacheByHash[contenthash] = messageID; + cache[messageID] = MessageInfo(MessageMetadata(profileOnion, conversation, messageID, timestamp, senderHandle, senderImage, "", {}, false, false, isAuto, contenthash), data); + cacheByIndex.insert(0, LocalIndexMessage(messageID)); + if (contenthash != "") { + cacheByHash[contenthash] = messageID; } } @@ -139,35 +136,35 @@ class MessageCache extends ChangeNotifier { // this prevents successive ui message build requests from triggering multiple GetMesssage requests to the backend, as the first one locks a block of messages and the rest wait on that void lockIndexes(int start, int end) { for (var i = start; i < end; i++) { - this.cacheByIndex.insert(i, LocalIndexMessage(null, isLoading: true)); + cacheByIndex.insert(i, LocalIndexMessage(null, isLoading: true)); // if there are unsynced messages on the index cache it means there are messages at the front, and by the logic in message/ByIndex/get() we will be loading those // there for we can decrement the count as this will be one of them - if (this._indexUnsynced > 0) { - this._indexUnsynced--; + if (_indexUnsynced > 0) { + _indexUnsynced--; } } } void malformIndexes(int start, int end) { for (var i = start; i < end; i++) { - this.cacheByIndex[i].failLoad(); + cacheByIndex[i].failLoad(); } } void addIndexed(MessageInfo messageInfo, int index) { - this.cache[messageInfo.metadata.messageID] = messageInfo; - if (index < this.cacheByIndex.length) { - this.cacheByIndex[index].finishLoad(messageInfo.metadata.messageID); + cache[messageInfo.metadata.messageID] = messageInfo; + if (index < cacheByIndex.length) { + cacheByIndex[index].finishLoad(messageInfo.metadata.messageID); } else { - this.cacheByIndex.insert(index, LocalIndexMessage(messageInfo.metadata.messageID)); + cacheByIndex.insert(index, LocalIndexMessage(messageInfo.metadata.messageID)); } - this.cacheByHash[messageInfo.metadata.contenthash] = messageInfo.metadata.messageID; + cacheByHash[messageInfo.metadata.contenthash] = messageInfo.metadata.messageID; } void addUnindexed(MessageInfo messageInfo) { - this.cache[messageInfo.metadata.messageID] = messageInfo; + cache[messageInfo.metadata.messageID] = messageInfo; if (messageInfo.metadata.contenthash != "") { - this.cacheByHash[messageInfo.metadata.contenthash] = messageInfo.metadata.messageID; + cacheByHash[messageInfo.metadata.contenthash] = messageInfo.metadata.messageID; } } diff --git a/lib/models/messages/filemessage.dart b/lib/models/messages/filemessage.dart index bb293e9e..2f57abff 100644 --- a/lib/models/messages/filemessage.dart +++ b/lib/models/messages/filemessage.dart @@ -20,30 +20,30 @@ class FileMessage extends Message { @override Widget getWidget(BuildContext context, Key key, int index) { return ChangeNotifierProvider.value( - value: this.metadata, + value: metadata, builder: (bcontext, child) { try { - dynamic shareObj = jsonDecode(this.content); + dynamic shareObj = jsonDecode(content); if (shareObj == null) { - return MessageRow(MalformedBubble(), index); + return MessageRow(const MalformedBubble(), index); } String nameSuggestion = shareObj['f'] as String; String rootHash = shareObj['h'] as String; String nonce = shareObj['n'] as String; int fileSize = shareObj['s'] as int; - String fileKey = rootHash + "." + nonce; + String fileKey = "$rootHash.$nonce"; if (!Provider.of(context, listen: false).downloadKnown(fileKey)) { Provider.of(context, listen: false).cwtch.CheckDownloadStatus(Provider.of(context, listen: false).onion, fileKey); } if (!validHash(rootHash, nonce)) { - return MessageRow(MalformedBubble(), index); + return MessageRow(const MalformedBubble(), index); } return MessageRow(FileBubble(nameSuggestion, rootHash, nonce, fileSize, isAuto: metadata.isAuto), index, key: key); } catch (e) { - return MessageRow(MalformedBubble(), index); + return MessageRow(const MalformedBubble(), index); } }); } @@ -51,24 +51,24 @@ class FileMessage extends Message { @override Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}) { return ChangeNotifierProvider.value( - value: this.metadata, + value: metadata, builder: (bcontext, child) { - dynamic shareObj = jsonDecode(this.content); + dynamic shareObj = jsonDecode(content); if (shareObj == null) { - return MalformedBubble(); + return const MalformedBubble(); } String nameSuggestion = shareObj['f'] as String; String rootHash = shareObj['h'] as String; String nonce = shareObj['n'] as String; int fileSize = shareObj['s'] as int; if (!validHash(rootHash, nonce)) { - return MalformedBubble(); + return const MalformedBubble(); } return Container( - padding: EdgeInsets.all(1.0), - decoration: BoxDecoration(), + padding: const EdgeInsets.all(1.0), + decoration: const BoxDecoration(), clipBehavior: Clip.antiAliasWithSaveLayer, - constraints: BoxConstraints(minHeight: 50, maxHeight: 50, minWidth: 50, maxWidth: 300), + constraints: const BoxConstraints(minHeight: 50, maxHeight: 50, minWidth: 50, maxWidth: 300), alignment: Alignment.centerLeft, child: FileBubble( nameSuggestion, @@ -84,7 +84,7 @@ class FileMessage extends Message { @override MessageMetadata getMetadata() { - return this.metadata; + return metadata; } bool validHash(String hash, String nonce) { diff --git a/lib/models/messages/invitemessage.dart b/lib/models/messages/invitemessage.dart index e2f339f6..bbb39723 100644 --- a/lib/models/messages/invitemessage.dart +++ b/lib/models/messages/invitemessage.dart @@ -19,28 +19,28 @@ class InviteMessage extends Message { @override Widget getWidget(BuildContext context, Key key, int index) { return ChangeNotifierProvider.value( - value: this.metadata, + value: metadata, builder: (bcontext, child) { String inviteTarget; String inviteNick; - String invite = this.content; + String invite = content; - if (this.content.length == TorV3ContactHandleLength) { - inviteTarget = this.content; + if (content.length == TorV3ContactHandleLength) { + inviteTarget = content; var targetContact = Provider.of(context).contactList.findContact(inviteTarget); - inviteNick = targetContact == null ? this.content : targetContact.nickname; + inviteNick = targetContact == null ? content : targetContact.nickname; } else { - var parts = this.content.toString().split("||"); + var parts = content.toString().split("||"); if (parts.length == 2) { try { var jsonObj = jsonDecode(utf8.fuse(base64).decode(parts[1].substring(5))); inviteTarget = jsonObj['GroupID']; inviteNick = jsonObj['GroupName']; } catch (e) { - return MessageRow(MalformedBubble(), index); + return MessageRow(const MalformedBubble(), index); } } else { - return MessageRow(MalformedBubble(), index); + return MessageRow(const MalformedBubble(), index); } } return MessageRow(InvitationBubble(overlay, inviteTarget, inviteNick, invite), index, key: key); @@ -50,27 +50,27 @@ class InviteMessage extends Message { @override Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}) { return ChangeNotifierProvider.value( - value: this.metadata, + value: metadata, builder: (bcontext, child) { String inviteTarget; String inviteNick; - String invite = this.content; - if (this.content.length == TorV3ContactHandleLength) { - inviteTarget = this.content; + String invite = content; + if (content.length == TorV3ContactHandleLength) { + inviteTarget = content; var targetContact = Provider.of(context).contactList.findContact(inviteTarget); - inviteNick = targetContact == null ? this.content : targetContact.nickname; + inviteNick = targetContact == null ? content : targetContact.nickname; } else { - var parts = this.content.toString().split("||"); + var parts = content.toString().split("||"); if (parts.length == 2) { try { var jsonObj = jsonDecode(utf8.fuse(base64).decode(parts[1].substring(5))); inviteTarget = jsonObj['GroupID']; inviteNick = jsonObj['GroupName']; } catch (e) { - return MalformedBubble(); + return const MalformedBubble(); } } else { - return MalformedBubble(); + return const MalformedBubble(); } } return InvitationBubble(overlay, inviteTarget, inviteNick, invite); @@ -79,6 +79,6 @@ class InviteMessage extends Message { @override MessageMetadata getMetadata() { - return this.metadata; + return metadata; } } diff --git a/lib/models/messages/malformedmessage.dart b/lib/models/messages/malformedmessage.dart index ece8338c..eb143903 100644 --- a/lib/models/messages/malformedmessage.dart +++ b/lib/models/messages/malformedmessage.dart @@ -11,23 +11,23 @@ class MalformedMessage extends Message { @override Widget getWidget(BuildContext context, Key key, int index) { return ChangeNotifierProvider.value( - value: this.metadata, + value: metadata, builder: (context, child) { - return MessageRow(MalformedBubble(), index, key: key); + return MessageRow(const MalformedBubble(), index, key: key); }); } @override Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}) { return ChangeNotifierProvider.value( - value: this.metadata, + value: metadata, builder: (bcontext, child) { - return MalformedBubble(); + return const MalformedBubble(); }); } @override MessageMetadata getMetadata() { - return this.metadata; + return metadata; } } diff --git a/lib/models/messages/quotedmessage.dart b/lib/models/messages/quotedmessage.dart index 95b1db5d..37456ca0 100644 --- a/lib/models/messages/quotedmessage.dart +++ b/lib/models/messages/quotedmessage.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:cwtch/config.dart'; import 'package:cwtch/models/message.dart'; import 'package:cwtch/models/messages/malformedmessage.dart'; import 'package:cwtch/widgets/malformedbubble.dart'; @@ -11,7 +10,6 @@ import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; import '../../settings.dart'; -import '../../third_party/linkify/flutter_linkify.dart'; class QuotedMessageStructure { final String quotedHash; @@ -32,7 +30,7 @@ class QuotedMessage extends Message { @override Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}) { return ChangeNotifierProvider.value( - value: this.metadata, + value: metadata, builder: (bcontext, child) { try { dynamic message = jsonDecode( @@ -42,33 +40,33 @@ class QuotedMessage extends Message { var formatMessages = Provider.of(bcontext).isExperimentEnabled(FormattingExperiment); return compileMessageContentWidget(context, constraints ?? BoxConstraints.loose(MediaQuery.sizeOf(context)), false, content, FocusNode(), formatMessages, false); } catch (e) { - return MalformedBubble(); + return const MalformedBubble(); } }); } @override MessageMetadata getMetadata() { - return this.metadata; + return metadata; } @override Widget getWidget(BuildContext context, Key key, int index) { try { - dynamic message = jsonDecode(this.content); + dynamic message = jsonDecode(content); if (message["body"] == null || message["quotedHash"] == null) { - return MalformedMessage(this.metadata).getWidget(context, key, index); + return MalformedMessage(metadata).getWidget(context, key, index); } return ChangeNotifierProvider.value( - value: this.metadata, + value: metadata, builder: (bcontext, child) { return MessageRow(QuotedMessageBubble(message["body"], messageHandler(bcontext, metadata.profileOnion, metadata.conversationIdentifier, ByContentHash(message["quotedHash"]))), index, key: key); }); } catch (e) { - return MalformedMessage(this.metadata).getWidget(context, key, index); + return MalformedMessage(metadata).getWidget(context, key, index); } } } diff --git a/lib/models/messages/textmessage.dart b/lib/models/messages/textmessage.dart index bb9d0a7c..c6185c37 100644 --- a/lib/models/messages/textmessage.dart +++ b/lib/models/messages/textmessage.dart @@ -1,17 +1,11 @@ -import 'dart:math'; - import 'package:cwtch/models/message.dart'; -import 'package:cwtch/models/messages/malformedmessage.dart'; -import 'package:cwtch/widgets/malformedbubble.dart'; import 'package:cwtch/widgets/messagebubble.dart'; -import 'package:cwtch/widgets/messageloadingbubble.dart'; import 'package:cwtch/widgets/messagerow.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; import '../../settings.dart'; -import '../../third_party/linkify/flutter_linkify.dart'; import '../../widgets/messageBubbleWidgetHelpers.dart'; class TextMessage extends Message { @@ -23,26 +17,25 @@ class TextMessage extends Message { @override Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}) { return ChangeNotifierProvider.value( - value: this.metadata, + value: metadata, builder: (bcontext, child) { var formatMessages = Provider.of(bcontext).isExperimentEnabled(FormattingExperiment); return compileMessageContentWidget(context, constraints ?? BoxConstraints.loose(MediaQuery.sizeOf(context)), false, content, FocusNode(), formatMessages, false); - ; }); } @override MessageMetadata getMetadata() { - return this.metadata; + return metadata; } @override Widget getWidget(BuildContext context, Key key, int index) { return ChangeNotifierProvider.value( - value: this.metadata, + value: metadata, builder: (bcontext, child) { return MessageRow( - MessageBubble(this.content), + MessageBubble(content), index, key: key, ); diff --git a/lib/models/profile.dart b/lib/models/profile.dart index 746bcad4..24055d8a 100644 --- a/lib/models/profile.dart +++ b/lib/models/profile.dart @@ -14,22 +14,20 @@ import '../views/contactsview.dart'; import 'contact.dart'; import 'contactlist.dart'; import 'filedownloadprogress.dart'; -import 'message.dart'; -import 'messagecache.dart'; import 'profileservers.dart'; class ProfileInfoState extends ChangeNotifier { - ProfileServerListState _servers = ProfileServerListState(); - ContactListState _contacts = ContactListState(); + final ProfileServerListState _servers = ProfileServerListState(); + final ContactListState _contacts = ContactListState(); final String onion; String _nickname = ""; String _imagePath = ""; String _defaultImagePath = ""; int _unreadMessages = 0; bool _online = false; - Map _downloads = Map(); - Map _downloadTriggers = Map(); - ItemScrollController contactListScrollController = new ItemScrollController(); + final Map _downloads = {}; + final Map _downloadTriggers = {}; + ItemScrollController contactListScrollController = ItemScrollController(); // assume profiles are encrypted...this will be set to false // in the constructor if the profile is encrypted with the defacto password. bool _encrypted = true; @@ -53,30 +51,30 @@ class ProfileInfoState extends ChangeNotifier { appearOffline = false, String, }) { - this._nickname = nickname; - this._imagePath = imagePath; - this._defaultImagePath = defaultImagePath; - this._unreadMessages = unreadMessages; - this._online = online; - this._enabled = _enabled; - this._autostart = autostart; + _nickname = nickname; + _imagePath = imagePath; + _defaultImagePath = defaultImagePath; + _unreadMessages = unreadMessages; + _online = online; + _enabled = _enabled; + _autostart = autostart; if (autostart) { - this._enabled = true; + _enabled = true; } - this._appearOffline = appearOffline; - this._appearOfflineAtStartup = appearOffline; - this._encrypted = encrypted; + _appearOffline = appearOffline; + _appearOfflineAtStartup = appearOffline; + _encrypted = encrypted; - _contacts.connectServers(this._servers); + _contacts.connectServers(_servers); if (contactsJson != null && contactsJson != "" && contactsJson != "null") { - this.replaceServers(serversJson); + replaceServers(serversJson); List contacts = jsonDecode(contactsJson); - this._contacts.addAll(contacts.map((contact) { - this._unreadMessages += contact["numUnread"] as int; + _contacts.addAll(contacts.map((contact) { + _unreadMessages += contact["numUnread"] as int; - return ContactInfoState(this.onion, contact["identifier"], contact["onion"], + return ContactInfoState(onion, contact["identifier"], contact["onion"], nickname: contact["name"], localNickname: contact["attributes"]?["local.profile.name"] ?? "", // contact may not have a local name status: contact["status"], @@ -96,8 +94,8 @@ class ProfileInfoState extends ChangeNotifier { })); // dummy set to invoke sort-on-load - if (this._contacts.num > 0) { - this._contacts.updateLastMessageReceivedTime(this._contacts.contacts.first.identifier, this._contacts.contacts.first.lastMessageReceivedTime); + if (_contacts.num > 0) { + _contacts.updateLastMessageReceivedTime(_contacts.contacts.first.identifier, _contacts.contacts.first.lastMessageReceivedTime); } } } @@ -108,7 +106,7 @@ class ProfileInfoState extends ChangeNotifier { void newSearch(String activeSearchID) { this.activeSearchID = activeSearchID; - this.activeSearchResults.clear(); + activeSearchResults.clear(); notifyListeners(); } @@ -123,18 +121,18 @@ class ProfileInfoState extends ChangeNotifier { void replaceServers(String serversJson) { if (serversJson != "" && serversJson != "null") { List servers = jsonDecode(serversJson); - this._servers.replace(servers.map((server) { + _servers.replace(servers.map((server) { // TODO Keys... var preSyncStartTime = DateTime.tryParse(server["syncProgress"]["startTime"]); var lastMessageTime = DateTime.tryParse(server["syncProgress"]["lastMessageTime"]); return RemoteServerInfoState(server["onion"], server["identifier"], server["description"], server["status"], lastPreSyncMessageTime: preSyncStartTime, mostRecentMessageTime: lastMessageTime); })); - this._contacts.contacts.forEach((contact) { + for (var contact in _contacts.contacts) { if (contact.isGroup) { _servers.addGroup(contact); } - }); + } notifyListeners(); } @@ -142,110 +140,105 @@ class ProfileInfoState extends ChangeNotifier { // void updateServerStatusCache(String server, String status) { - this._servers.updateServerState(server, status); + _servers.updateServerState(server, status); notifyListeners(); } // Getters and Setters for Online Status - bool get isOnline => this._online; + bool get isOnline => _online; set isOnline(bool newValue) { - this._online = newValue; + _online = newValue; notifyListeners(); } // Check encrypted status for profile info screen - bool get isEncrypted => this._encrypted; + bool get isEncrypted => _encrypted; set isEncrypted(bool newValue) { - this._encrypted = newValue; + _encrypted = newValue; notifyListeners(); } - String get nickname => this._nickname; + String get nickname => _nickname; set nickname(String newValue) { - this._nickname = newValue; + _nickname = newValue; notifyListeners(); } - String get imagePath => this._imagePath; + String get imagePath => _imagePath; set imagePath(String newVal) { - this._imagePath = newVal; + _imagePath = newVal; notifyListeners(); } - bool get enabled => this._enabled; + bool get enabled => _enabled; set enabled(bool newVal) { - this._enabled = newVal; + _enabled = newVal; notifyListeners(); } - bool get autostart => this._autostart; + bool get autostart => _autostart; set autostart(bool newVal) { - this._autostart = newVal; + _autostart = newVal; notifyListeners(); } - bool get appearOfflineAtStartup => this._appearOfflineAtStartup; + bool get appearOfflineAtStartup => _appearOfflineAtStartup; set appearOfflineAtStartup(bool newVal) { - this._appearOfflineAtStartup = newVal; + _appearOfflineAtStartup = newVal; notifyListeners(); } - bool get appearOffline => this._appearOffline; + bool get appearOffline => _appearOffline; set appearOffline(bool newVal) { - this._appearOffline = newVal; + _appearOffline = newVal; notifyListeners(); } - String get defaultImagePath => this._defaultImagePath; + String get defaultImagePath => _defaultImagePath; set defaultImagePath(String newVal) { - this._defaultImagePath = newVal; + _defaultImagePath = newVal; notifyListeners(); } - int get unreadMessages => this._unreadMessages; + int get unreadMessages => _unreadMessages; set unreadMessages(int newVal) { - this._unreadMessages = newVal; + _unreadMessages = newVal; notifyListeners(); } void recountUnread() { - this._unreadMessages = _contacts.contacts.fold(0, (i, c) => i + c.unreadMessages); + _unreadMessages = _contacts.contacts.fold(0, (i, c) => i + c.unreadMessages); } // Remove a contact from a list. Currently only used when rejecting a group invitation. // Eventually will also be used for other removals. void removeContact(String handle) { - this.contactList.removeContactByHandle(handle); + contactList.removeContactByHandle(handle); notifyListeners(); } - ContactListState get contactList => this._contacts; + ContactListState get contactList => _contacts; - ProfileServerListState get serverList => this._servers; - - @override - void dispose() { - super.dispose(); - } + ProfileServerListState get serverList => _servers; void updateFrom(String onion, String name, String picture, String contactsJson, String serverJson, bool online) { - this._nickname = name; - this._imagePath = picture; - this._online = online; - this._unreadMessages = 0; - this.replaceServers(serverJson); + _nickname = name; + _imagePath = picture; + _online = online; + _unreadMessages = 0; + replaceServers(serverJson); - if (contactsJson != null && contactsJson != "" && contactsJson != "null") { + if (contactsJson != "" && contactsJson != "null") { List contacts = jsonDecode(contactsJson); - contacts.forEach((contact) { - var profileContact = this._contacts.getContact(contact["identifier"]); - this._unreadMessages += contact["numUnread"] as int; + for (var contact in contacts) { + var profileContact = _contacts.getContact(contact["identifier"]); + _unreadMessages += contact["numUnread"] as int; if (profileContact != null) { profileContact.status = contact["status"]; @@ -271,28 +264,28 @@ class ProfileInfoState extends ChangeNotifier { profileContact.unreadMessages = contact["numUnread"]; profileContact.lastMessageReceivedTime = DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])); } else { - this._contacts.add(ContactInfoState( - this.onion, - contact["identifier"], - contact["onion"], - nickname: contact["name"], - defaultImagePath: contact["defaultPicture"], - status: contact["status"], - imagePath: contact["picture"], - accepted: contact["accepted"], - blocked: contact["blocked"], - savePeerHistory: contact["saveConversationHistory"], - numMessages: contact["numMessages"], - numUnread: contact["numUnread"], - isGroup: contact["isGroup"], - server: contact["groupServer"], - lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])), - notificationPolicy: contact["notificationPolicy"] ?? "ConversationNotificationPolicy.Default", - )); + _contacts.add(ContactInfoState( + this.onion, + contact["identifier"], + contact["onion"], + nickname: contact["name"], + defaultImagePath: contact["defaultPicture"], + status: contact["status"], + imagePath: contact["picture"], + accepted: contact["accepted"], + blocked: contact["blocked"], + savePeerHistory: contact["saveConversationHistory"], + numMessages: contact["numMessages"], + numUnread: contact["numUnread"], + isGroup: contact["isGroup"], + server: contact["groupServer"], + lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])), + notificationPolicy: contact["notificationPolicy"] ?? "ConversationNotificationPolicy.Default", + )); } - }); + } } - this._contacts.resort(); + _contacts.resort(); } void newMessage( @@ -306,33 +299,33 @@ class ProfileInfoState extends ChangeNotifier { } void downloadInit(String fileKey, int numChunks) { - this._downloads[fileKey] = FileDownloadProgress(numChunks, DateTime.now()); + _downloads[fileKey] = FileDownloadProgress(numChunks, DateTime.now()); notifyListeners(); } void downloadUpdate(String fileKey, int progress, int numChunks) { if (!downloadActive(fileKey)) { - this._downloads[fileKey] = FileDownloadProgress(numChunks, DateTime.now()); + _downloads[fileKey] = FileDownloadProgress(numChunks, DateTime.now()); if (progress < 0) { - this._downloads[fileKey]!.interrupted = true; + _downloads[fileKey]!.interrupted = true; } } else { - if (this._downloads[fileKey]!.interrupted) { - this._downloads[fileKey]!.interrupted = false; + if (_downloads[fileKey]!.interrupted) { + _downloads[fileKey]!.interrupted = false; } - this._downloads[fileKey]!.chunksDownloaded = progress; - this._downloads[fileKey]!.chunksTotal = numChunks; - this._downloads[fileKey]!.markUpdate(); + _downloads[fileKey]!.chunksDownloaded = progress; + _downloads[fileKey]!.chunksTotal = numChunks; + _downloads[fileKey]!.markUpdate(); } notifyListeners(); } void downloadMarkManifest(String fileKey) { if (!downloadActive(fileKey)) { - this._downloads[fileKey] = FileDownloadProgress(1, DateTime.now()); + _downloads[fileKey] = FileDownloadProgress(1, DateTime.now()); } - this._downloads[fileKey]!.gotManifest = true; - this._downloads[fileKey]!.markUpdate(); + _downloads[fileKey]!.gotManifest = true; + _downloads[fileKey]!.markUpdate(); notifyListeners(); } @@ -341,46 +334,46 @@ class ProfileInfoState extends ChangeNotifier { // happens as a result of a CheckDownloadStatus call, // invoked from a historical (timeline) download message // so setting numChunks correctly shouldn't matter - this.downloadInit(fileKey, 1); + downloadInit(fileKey, 1); } // Update the contact with a custom profile image if we are // waiting for one... - if (this._downloadTriggers.containsKey(fileKey)) { - int identifier = this._downloadTriggers[fileKey]!; - this.contactList.getContact(identifier)!.imagePath = finalPath; + if (_downloadTriggers.containsKey(fileKey)) { + int identifier = _downloadTriggers[fileKey]!; + contactList.getContact(identifier)!.imagePath = finalPath; notifyListeners(); } // only update if different - if (!this._downloads[fileKey]!.complete) { - this._downloads[fileKey]!.timeEnd = DateTime.now(); - this._downloads[fileKey]!.downloadedTo = finalPath; - this._downloads[fileKey]!.complete = true; - this._downloads[fileKey]!.markUpdate(); + if (!_downloads[fileKey]!.complete) { + _downloads[fileKey]!.timeEnd = DateTime.now(); + _downloads[fileKey]!.downloadedTo = finalPath; + _downloads[fileKey]!.complete = true; + _downloads[fileKey]!.markUpdate(); notifyListeners(); } } bool downloadKnown(String fileKey) { - return this._downloads.containsKey(fileKey); + return _downloads.containsKey(fileKey); } bool downloadActive(String fileKey) { - return this._downloads.containsKey(fileKey) && !this._downloads[fileKey]!.interrupted; + return _downloads.containsKey(fileKey) && !_downloads[fileKey]!.interrupted; } bool downloadGotManifest(String fileKey) { - return this._downloads.containsKey(fileKey) && this._downloads[fileKey]!.gotManifest; + return _downloads.containsKey(fileKey) && _downloads[fileKey]!.gotManifest; } bool downloadComplete(String fileKey) { - return this._downloads.containsKey(fileKey) && this._downloads[fileKey]!.complete; + return _downloads.containsKey(fileKey) && _downloads[fileKey]!.complete; } bool downloadInterrupted(String fileKey) { - if (this._downloads.containsKey(fileKey)) { - if (this._downloads[fileKey]!.interrupted) { + if (_downloads.containsKey(fileKey)) { + if (_downloads[fileKey]!.interrupted) { return true; } } @@ -388,22 +381,22 @@ class ProfileInfoState extends ChangeNotifier { } void downloadMarkResumed(String fileKey) { - if (this._downloads.containsKey(fileKey)) { - this._downloads[fileKey]!.interrupted = false; - this._downloads[fileKey]!.requested = DateTime.now(); - this._downloads[fileKey]!.markUpdate(); + if (_downloads.containsKey(fileKey)) { + _downloads[fileKey]!.interrupted = false; + _downloads[fileKey]!.requested = DateTime.now(); + _downloads[fileKey]!.markUpdate(); notifyListeners(); } } double downloadProgress(String fileKey) { - return this._downloads.containsKey(fileKey) ? this._downloads[fileKey]!.progress() : 0.0; + return _downloads.containsKey(fileKey) ? _downloads[fileKey]!.progress() : 0.0; } // used for loading interrupted download info; use downloadMarkFinished for successful downloads void downloadSetPath(String fileKey, String path) { - if (this._downloads.containsKey(fileKey)) { - this._downloads[fileKey]!.downloadedTo = path; + if (_downloads.containsKey(fileKey)) { + _downloads[fileKey]!.downloadedTo = path; notifyListeners(); } } @@ -413,28 +406,28 @@ class ProfileInfoState extends ChangeNotifier { // we may trigger this event for auto-downloaded receivers too, // as such we don't assume anything else about the file...other than that // it exists. - if (!this._downloads.containsKey(fileKey)) { + if (!_downloads.containsKey(fileKey)) { // this will be overwritten by download update if the file is being downloaded - this._downloads[fileKey] = FileDownloadProgress(1, DateTime.now()); + _downloads[fileKey] = FileDownloadProgress(1, DateTime.now()); } - this._downloads[fileKey]!.downloadedTo = path; + _downloads[fileKey]!.downloadedTo = path; notifyListeners(); } String? downloadFinalPath(String fileKey) { - return this._downloads.containsKey(fileKey) ? this._downloads[fileKey]!.downloadedTo : null; + return _downloads.containsKey(fileKey) ? _downloads[fileKey]!.downloadedTo : null; } String downloadSpeed(String fileKey) { - if (!downloadActive(fileKey) || this._downloads[fileKey]!.chunksDownloaded == 0) { + if (!downloadActive(fileKey) || _downloads[fileKey]!.chunksDownloaded == 0) { return "0 B/s"; } - var bytes = this._downloads[fileKey]!.chunksDownloaded * 4096; - var seconds = (this._downloads[fileKey]!.timeEnd ?? DateTime.now()).difference(this._downloads[fileKey]!.timeStart!).inSeconds; + var bytes = _downloads[fileKey]!.chunksDownloaded * 4096; + var seconds = (_downloads[fileKey]!.timeEnd ?? DateTime.now()).difference(_downloads[fileKey]!.timeStart!).inSeconds; if (seconds == 0) { return "0 B/s"; } - return prettyBytes((bytes / seconds).round()) + "/s"; + return "${prettyBytes((bytes / seconds).round())}/s"; } void waitForDownloadComplete(int identifier, String fileKey) { @@ -447,14 +440,14 @@ class ProfileInfoState extends ChangeNotifier { } void downloadReset(String fileKey) { - this._downloads.remove(fileKey); + _downloads.remove(fileKey); notifyListeners(); } // Profile Attributes. Can be set in Profile Edit View... List attributes = [null, null, null]; void setAttribute(int i, String? value) { - this.attributes[i] = value; + attributes[i] = value; notifyListeners(); } @@ -477,7 +470,7 @@ class ProfileInfoState extends ChangeNotifier { } Color getBorderColor(OpaqueThemeType theme) { - switch (this.availabilityStatus) { + switch (availabilityStatus) { case ProfileStatusMenu.available: return theme.portraitOnlineBorderColor; case ProfileStatusMenu.away: @@ -494,13 +487,13 @@ class ProfileInfoState extends ChangeNotifier { // FIXME: Cwtch should be sending these events prior to shutting down the engine... void deactivatePeerEngine(BuildContext context) { Provider.of(context, listen: false).cwtch.DeactivatePeerEngine(onion); - this.contactList.contacts.forEach((element) { + for (var element in contactList.contacts) { element.status = "Disconnected"; // reset retry time to allow for instant reconnection... element.lastRetryTime = element.loaded; - }); - this.serverList.servers.forEach((element) { + } + for (var element in serverList.servers) { element.status = "Disconnected"; - }); + } } } diff --git a/lib/models/profilelist.dart b/lib/models/profilelist.dart index 83d5363f..536b8243 100644 --- a/lib/models/profilelist.dart +++ b/lib/models/profilelist.dart @@ -1,13 +1,10 @@ -import 'dart:ui'; - -import 'package:cwtch/config.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/widgets.dart'; import 'profile.dart'; class ProfileListState extends ChangeNotifier { - List _profiles = []; + final List _profiles = []; int get num => _profiles.length; void add(String onion, String name, String picture, String defaultPicture, String contactsJson, String serverJson, bool online, bool autostart, bool encrypted, bool appearOffline) { diff --git a/lib/models/profileservers.dart b/lib/models/profileservers.dart index 61ee76d1..c834150d 100644 --- a/lib/models/profileservers.dart +++ b/lib/models/profileservers.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'contact.dart'; class ProfileServerListState extends ChangeNotifier { - List _servers = []; + final List _servers = []; void replace(Iterable newServers) { _servers.clear(); diff --git a/lib/models/redaction.dart b/lib/models/redaction.dart index 506911e6..5cc7a965 100644 --- a/lib/models/redaction.dart +++ b/lib/models/redaction.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter/cupertino.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -10,7 +8,7 @@ String redactedNick(BuildContext context, String handle, String nick) { var settings = Provider.of(context, listen: false); if (settings.streamerMode) { if (handle == nick) { - return handle.substring(0, 7) + "..."; + return "${handle.substring(0, 7)}..."; } } return nick; diff --git a/lib/models/remoteserver.dart b/lib/models/remoteserver.dart index cbe6b3b8..c283d80b 100644 --- a/lib/models/remoteserver.dart +++ b/lib/models/remoteserver.dart @@ -1,4 +1,3 @@ -import 'package:cwtch/config.dart'; import 'package:flutter/cupertino.dart'; import 'contact.dart'; @@ -11,17 +10,17 @@ class RemoteServerInfoState extends ChangeNotifier { List _groups = []; double syncProgress = 0; - DateTime lastPreSyncMessagTime = new DateTime(2020); + DateTime lastPreSyncMessagTime = DateTime(2020); RemoteServerInfoState(this.onion, this.identifier, this.description, this._status, {lastPreSyncMessageTime, mostRecentMessageTime}) { if (_status == "Authenticated" || _status == "Synced") { - this.lastPreSyncMessagTime = lastPreSyncMessageTime; + lastPreSyncMessagTime = lastPreSyncMessageTime; updateSyncProgressFor(mostRecentMessageTime); } } void updateDescription(String newDescription) { - this.description = newDescription; + description = newDescription; notifyListeners(); } @@ -39,11 +38,11 @@ class RemoteServerInfoState extends ChangeNotifier { _status = newStatus; if (status == "Authenticated") { // syncing, set lastPreSyncMessageTime - _groups.forEach((g) { + for (var g in _groups) { if (g.lastMessageReceivedTime.isAfter(lastPreSyncMessagTime)) { lastPreSyncMessagTime = g.lastMessageReceivedTime; } - }); + } } notifyListeners(); } @@ -57,7 +56,7 @@ class RemoteServerInfoState extends ChangeNotifier { // ! is Negative cus all the duration's we're calculating incidently are negative // this message is from before we think we should be syncing with the server // Can be because of a new server or a full resync, either way, use this (oldest message) as our lastPreSyncMessageTime - this.lastPreSyncMessagTime = point; + lastPreSyncMessagTime = point; pointFromStart = lastPreSyncMessagTime.toUtc().difference(point.toUtc()); } syncProgress = pointFromStart.inSeconds / range.inSeconds; diff --git a/lib/models/servers.dart b/lib/models/servers.dart index c08290fd..d81d91e3 100644 --- a/lib/models/servers.dart +++ b/lib/models/servers.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class ServerListState extends ChangeNotifier { - List _servers = []; + final List _servers = []; void replace(Iterable newServers) { _servers.clear(); diff --git a/lib/notification_manager.dart b/lib/notification_manager.dart index 81f21fd2..b1371402 100644 --- a/lib/notification_manager.dart +++ b/lib/notification_manager.dart @@ -3,13 +3,8 @@ import 'dart:convert'; import 'dart:io'; import 'package:cwtch/main.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:win_toast/win_toast.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:flutter_local_notifications_linux/flutter_local_notifications_linux.dart'; -import 'package:flutter_local_notifications_linux/src/model/hint.dart'; -import 'package:flutter_local_notifications_linux/src/model/icon.dart'; import 'package:path/path.dart' as path; @@ -33,8 +28,7 @@ class WindowsNotificationManager implements NotificationsManager { bool initialized = false; late Future Function(String, int) notificationSelectConvo; - WindowsNotificationManager(Future Function(String, int) notificationSelectConvo) { - this.notificationSelectConvo = notificationSelectConvo; + WindowsNotificationManager(this.notificationSelectConvo) { scheduleMicrotask(() async { initialized = await WinToast.instance().initialize(appName: 'cwtch', productName: 'Cwtch', companyName: 'Open Privacy Research Society'); /* @@ -61,6 +55,7 @@ class WindowsNotificationManager implements NotificationsManager { }); } + @override Future notify(String message, String profile, int conversationId) async { if (initialized && !globalAppState.focus) { if (!active) { @@ -137,7 +132,7 @@ class NixNotificationManager implements NotificationsManager { Future detectLinuxAssetsPath() async { var devStat = FileStat.stat("assets"); var localStat = FileStat.stat("data/flutter_assets"); - var homeStat = FileStat.stat((Platform.environment["HOME"] ?? "") + "/.local/share/cwtch/data/flutter_assets"); + var homeStat = FileStat.stat("${Platform.environment["HOME"] ?? ""}/.local/share/cwtch/data/flutter_assets"); var rootStat = FileStat.stat("/usr/share/cwtch/data/flutter_assets"); if ((await devStat).type == FileSystemEntityType.directory) { @@ -145,15 +140,14 @@ class NixNotificationManager implements NotificationsManager { } else if ((await localStat).type == FileSystemEntityType.directory) { return path.join(Directory.current.path, "data/flutter_assets/"); } else if ((await homeStat).type == FileSystemEntityType.directory) { - return (Platform.environment["HOME"] ?? "") + "/.local/share/cwtch/data/flutter_assets/"; + return "${Platform.environment["HOME"] ?? ""}/.local/share/cwtch/data/flutter_assets/"; } else if ((await rootStat).type == FileSystemEntityType.directory) { return "/usr/share/cwtch/data/flutter_assets/"; } return ""; } - NixNotificationManager(Future Function(String, int) notificationSelectConvo) { - this.notificationSelectConvo = notificationSelectConvo; + NixNotificationManager(this.notificationSelectConvo) { flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); scheduleMicrotask(() async { @@ -168,7 +162,7 @@ class NixNotificationManager implements NotificationsManager { final LinuxInitializationSettings initializationSettingsLinux = LinuxInitializationSettings(defaultActionName: 'Open notification', defaultIcon: linuxIcon, defaultSuppressSound: true); final InitializationSettings initializationSettings = - InitializationSettings(android: null, iOS: null, macOS: DarwinInitializationSettings(defaultPresentSound: false), linux: initializationSettingsLinux); + InitializationSettings(android: null, iOS: null, macOS: const DarwinInitializationSettings(defaultPresentSound: false), linux: initializationSettingsLinux); flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation()?.requestPermissions( alert: true, @@ -182,6 +176,7 @@ class NixNotificationManager implements NotificationsManager { }); } + @override Future notify(String message, String profile, int conversationId) async { if (!globalAppState.focus) { // Warning: Only use title field on Linux, body field will render links as clickable diff --git a/lib/settings.dart b/lib/settings.dart index 786171ac..7640adc8 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -1,8 +1,6 @@ import 'dart:collection'; -import 'dart:ui'; import 'dart:core'; -import 'package:cwtch/themes/cwtch.dart'; import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -64,7 +62,7 @@ class Settings extends ChangeNotifier { String _customTorConfig = ""; int _socksPort = -1; int _controlPort = -1; - String _customTorAuth = ""; + final String _customTorAuth = ""; bool _useTorCache = false; String _torCacheDir = ""; bool _useSemanticDebugger = false; @@ -77,12 +75,12 @@ class Settings extends ChangeNotifier { bool get profileMode => _profileMode; set profileMode(bool newval) { - this._profileMode = newval; + _profileMode = newval; notifyListeners(); } set useSemanticDebugger(bool newval) { - this._useSemanticDebugger = newval; + _useSemanticDebugger = newval; notifyListeners(); } @@ -107,20 +105,20 @@ class Settings extends ChangeNotifier { /// isExperimentEnabled can be used to safely check whether a particular /// experiment is enabled bool isExperimentEnabled(String experiment) { - if (this.experimentsEnabled) { - if (this.experiments.containsKey(experiment)) { + if (experimentsEnabled) { + if (experiments.containsKey(experiment)) { // We now know it cannot be null... - return this.experiments[experiment]! == true; + return experiments[experiment]! == true; } } // allow message formatting to be turned off even when experiments are // disabled... if (experiment == FormattingExperiment) { - if (this.experiments.containsKey(FormattingExperiment)) { + if (experiments.containsKey(FormattingExperiment)) { // If message formatting has not explicitly been turned off, then // turn it on by default (even when experiments are disabled) - return this.experiments[experiment]! == true; + return experiments[experiment]! == true; } else { return true; // enable by default } @@ -133,7 +131,7 @@ class Settings extends ChangeNotifier { /// be sent to the function and new settings will be instantiated based on the contents. handleUpdate(dynamic settings) { // Set Theme and notify listeners - this.setTheme(settings["Theme"], settings["ThemeMode"] ?? mode_dark); + setTheme(settings["Theme"], settings["ThemeMode"] ?? mode_dark); _themeImages = settings["ThemeImages"] ?? false; // Set Locale and notify listeners @@ -141,9 +139,7 @@ class Settings extends ChangeNotifier { // Decide whether to enable Experiments var fontScale = settings["FontScaling"]; - if (fontScale == null) { - fontScale = 1.0; - } + fontScale ??= 1.0; _fontScaling = double.parse(fontScale.toString()).clamp(0.5, 2.0); blockUnknownConnections = settings["BlockUnknownConnections"] ?? false; @@ -154,7 +150,7 @@ class Settings extends ChangeNotifier { preserveHistoryByDefault = settings["DefaultSaveHistory"] ?? false; // Set the internal experiments map. Casting from the Map that we get from JSON - experiments = new HashMap.from(settings["Experiments"]); + experiments = HashMap.from(settings["Experiments"]); // single pane vs dual pane preferences _uiColumnModePortrait = uiColumnModeFromString(settings["UIColumnModePortrait"]); @@ -192,15 +188,15 @@ class Settings extends ChangeNotifier { switchLocaleByCode(String languageCode) { var code = languageCode.split("_"); if (code.length == 1) { - this.switchLocale(Locale(languageCode)); + switchLocale(Locale(languageCode)); } else { - this.switchLocale(Locale(code[0], code[1])); + switchLocale(Locale(code[0], code[1])); } } /// Handle Font Scaling set fontScaling(double newFontScaling) { - this._fontScaling = newFontScaling; + _fontScaling = newFontScaling; notifyListeners(); } @@ -208,7 +204,7 @@ class Settings extends ChangeNotifier { // a convenience function to scale fonts dynamically... TextStyle scaleFonts(TextStyle input) { - return input.copyWith(fontSize: (input.fontSize ?? 12) * this.fontScaling); + return input.copyWith(fontSize: (input.fontSize ?? 12) * fontScaling); } /// Switch the Locale of the App @@ -280,28 +276,28 @@ class Settings extends ChangeNotifier { DualpaneMode get uiColumnModePortrait => _uiColumnModePortrait; set uiColumnModePortrait(DualpaneMode newval) { - this._uiColumnModePortrait = newval; + _uiColumnModePortrait = newval; notifyListeners(); } DualpaneMode get uiColumnModeLandscape => _uiColumnModeLandscape; set uiColumnModeLandscape(DualpaneMode newval) { - this._uiColumnModeLandscape = newval; + _uiColumnModeLandscape = newval; notifyListeners(); } NotificationPolicy get notificationPolicy => _notificationPolicy; set notificationPolicy(NotificationPolicy newpol) { - this._notificationPolicy = newpol; + _notificationPolicy = newpol; notifyListeners(); } NotificationContent get notificationContent => _notificationContent; set notificationContent(NotificationContent newcon) { - this._notificationContent = newcon; + _notificationContent = newcon; notifyListeners(); } @@ -320,15 +316,16 @@ class Settings extends ChangeNotifier { } static List uiColumnModeOptions(bool isLandscape) { - if (isLandscape) + if (isLandscape) { return [ DualpaneMode.CopyPortrait, DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4, ]; - else + } else { return [DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4]; + } } static DualpaneMode uiColumnModeFromString(String m) { @@ -477,7 +474,7 @@ class Settings extends ChangeNotifier { /// event bus. dynamic asJson() { return { - "Locale": this.locale.toString(), + "Locale": locale.toString(), "Theme": theme.theme, "ThemeMode": theme.mode, "ThemeImages": _themeImages, @@ -486,7 +483,7 @@ class Settings extends ChangeNotifier { "NotificationPolicy": _notificationPolicy.toString(), "NotificationContent": _notificationContent.toString(), "StreamerMode": streamerMode, - "ExperimentsEnabled": this.experimentsEnabled, + "ExperimentsEnabled": experimentsEnabled, "Experiments": experiments, "StateRootPane": 0, "FirstTime": false, diff --git a/lib/themes/cwtch.dart b/lib/themes/cwtch.dart index ffdd8ba7..fec235ac 100644 --- a/lib/themes/cwtch.dart +++ b/lib/themes/cwtch.dart @@ -11,23 +11,23 @@ import 'opaque.dart'; const cwtch_theme = "cwtch"; -final Color darkGreyPurple = Color(0xFF281831); -final Color deepPurple = Color(0xFF422850); -final Color mauvePurple = Color(0xFF8E64A5); -final Color whiteishPurple = Color(0xFFE3DFE4); -final Color lightGrey = Color(0xFF9E9E9E); -final Color softGreen = Color(0xFFA0FFB0); -final Color softRed = Color(0xFFFFA0B0); +const Color darkGreyPurple = Color(0xFF281831); +const Color deepPurple = Color(0xFF422850); +const Color mauvePurple = Color(0xFF8E64A5); +const Color whiteishPurple = Color(0xFFE3DFE4); +const Color lightGrey = Color(0xFF9E9E9E); +const Color softGreen = Color(0xFFA0FFB0); +const Color softRed = Color(0xFFFFA0B0); -final Color whitePurple = Color(0xFFFFFDFF); -final Color softPurple = Color(0xFFFDF3FC); -final Color purple = Color(0xFFDFB9DE); -final Color brightPurple = Color(0xFFD1B0E0); // not in new: portrait badge color -final Color darkPurple = Color(0xFF350052); -final Color greyPurple = Color(0xFF775F84); // not in new: portrait borders -final Color pink = Color(0xFFE85DA1); // not in new: active button color -final Color hotPink = Color(0xFFD20070); // Color(0xFFD01972); -final Color softGrey = Color(0xFFB3B6B3); // not in new theme: blocked +const Color whitePurple = Color(0xFFFFFDFF); +const Color softPurple = Color(0xFFFDF3FC); +const Color purple = Color(0xFFDFB9DE); +const Color brightPurple = Color(0xFFD1B0E0); // not in new: portrait badge color +const Color darkPurple = Color(0xFF350052); +const Color greyPurple = Color(0xFF775F84); // not in new: portrait borders +const Color pink = Color(0xFFE85DA1); // not in new: active button color +const Color hotPink = Color(0xFFD20070); // Color(0xFFD01972); +const Color softGrey = Color(0xFFB3B6B3); // not in new theme: blocked OpaqueThemeType GetCwtchTheme(String mode) { if (mode == mode_dark) { @@ -38,110 +38,192 @@ OpaqueThemeType GetCwtchTheme(String mode) { } class CwtchDark extends OpaqueThemeType { - static final Color background = darkGreyPurple; - static final Color header = darkGreyPurple; - static final Color userBubble = mauvePurple; - static final Color peerBubble = deepPurple; - static final Color font = whiteishPurple; - static final Color settings = whiteishPurple; - static final Color accent = hotPink; + static const Color background = darkGreyPurple; + static const Color header = darkGreyPurple; + static const Color userBubble = mauvePurple; + static const Color peerBubble = deepPurple; + static const Color font = whiteishPurple; + static const Color settings = whiteishPurple; + static const Color accent = hotPink; + @override get theme => cwtch_theme; + @override get mode => mode_dark; + @override get backgroundHilightElementColor => deepPurple; + @override get backgroundMainColor => background; // darkGreyPurple; + @override get backgroundPaneColor => header; //darkGreyPurple; + @override get defaultButtonColor => accent; //hotPink; + @override get defaultButtonDisabledColor => lightGrey; get defaultButtonDisabledTextColor => darkGreyPurple; + @override get defaultButtonTextColor => whiteishPurple; + @override get dropShadowColor => mauvePurple; + @override get hilightElementColor => purple; + @override get mainTextColor => font; //whiteishPurple; + @override get messageFromMeBackgroundColor => userBubble; // mauvePurple; + @override get messageFromMeTextColor => font; //whiteishPurple; + @override get messageFromOtherBackgroundColor => peerBubble; //deepPurple; + @override get messageFromOtherTextColor => font; //whiteishPurple; + @override get messageSelectionColor => accent; + @override get portraitBackgroundColor => deepPurple; + @override get portraitBlockedBorderColor => lightGrey; + @override get portraitBlockedTextColor => lightGrey; + @override get portraitContactBadgeColor => hotPink; + @override get portraitContactBadgeTextColor => whiteishPurple; + @override get portraitOfflineBorderColor => purple; + @override get portraitOnlineBorderColor => whiteishPurple; - get portraitOnlineAwayColor => Color(0xFFFFF59D); - get portraitOnlineBusyColor => Color(0xFFEF9A9A); + @override + get portraitOnlineAwayColor => const Color(0xFFFFF59D); + @override + get portraitOnlineBusyColor => const Color(0xFFEF9A9A); + @override get portraitProfileBadgeColor => hotPink; + @override get portraitProfileBadgeTextColor => whiteishPurple; + @override get scrollbarDefaultColor => purple; + @override get sendHintTextColor => mauvePurple; + @override get chatReactionIconColor => mauvePurple; + @override get textfieldBackgroundColor => deepPurple; + @override get textfieldBorderColor => deepPurple; + @override get textfieldErrorColor => hotPink; + @override get textfieldHintColor => mainTextColor; + @override get textfieldSelectionColor => accent; + @override get toolbarIconColor => settings; //whiteishPurple; + @override get topbarColor => header; //darkGreyPurple; + @override get menuBackgroundColor => accent; get menuTextColor => darkGreyPurple; + @override get snackbarBackgroundColor => accent; + @override get snackbarTextColor => whitePurple; + @override get chatImageColor => purple; } class CwtchLight extends OpaqueThemeType { - static final Color background = whitePurple; - static final Color header = softPurple; - static final Color userBubble = purple; - static final Color peerBubble = softPurple; - static final Color font = darkPurple; - static final Color settings = darkPurple; - static final Color accent = hotPink; + static const Color background = whitePurple; + static const Color header = softPurple; + static const Color userBubble = purple; + static const Color peerBubble = softPurple; + static const Color font = darkPurple; + static const Color settings = darkPurple; + static const Color accent = hotPink; + @override get theme => cwtch_theme; + @override get mode => mode_light; + @override get backgroundHilightElementColor => softPurple; + @override get backgroundMainColor => background; //whitePurple; + @override get backgroundPaneColor => background; //whitePurple; + @override get defaultButtonColor => accent; // hotPink; + @override get defaultButtonDisabledColor => softGrey; + @override get defaultButtonTextColor => whitePurple; // ? + @override get dropShadowColor => purple; + @override get hilightElementColor => purple; + @override get mainTextColor => settings; + @override get messageFromMeBackgroundColor => userBubble; //brightPurple; + @override get messageFromMeTextColor => font; //mainTextColor; + @override get messageFromOtherBackgroundColor => peerBubble; //purple; + @override get messageFromOtherTextColor => font; //darkPurple; + @override get messageSelectionColor => accent; + @override get portraitBackgroundColor => softPurple; + @override get portraitBlockedBorderColor => softGrey; + @override get portraitBlockedTextColor => softGrey; + @override get portraitContactBadgeColor => accent; + @override get portraitContactBadgeTextColor => whitePurple; + @override get portraitOfflineBorderColor => greyPurple; + @override get portraitOnlineBorderColor => greyPurple; - get portraitOnlineAwayColor => Color(0xFFFFF59D); - get portraitOnlineBusyColor => Color(0xFFEF9A9A); + @override + get portraitOnlineAwayColor => const Color(0xFFFFF59D); + @override + get portraitOnlineBusyColor => const Color(0xFFEF9A9A); + @override get portraitProfileBadgeColor => accent; + @override get portraitProfileBadgeTextColor => whitePurple; + @override get scrollbarDefaultColor => accent; + @override get sendHintTextColor => purple; + @override get chatReactionIconColor => purple; + @override get textfieldBackgroundColor => purple; + @override get textfieldBorderColor => purple; + @override get textfieldErrorColor => hotPink; + @override get textfieldHintColor => font; + @override get textfieldSelectionColor => accent; + @override get toolbarIconColor => settings; //darkPurple; + @override get topbarColor => header; //softPurple; + @override get menuBackgroundColor => accent; get menuTextColor => whitePurple; + @override get snackbarBackgroundColor => accent; + @override get snackbarTextColor => whitePurple; + @override get chatImageColor => purple; } diff --git a/lib/themes/opaque.dart b/lib/themes/opaque.dart index bab22160..a3733ba1 100644 --- a/lib/themes/opaque.dart +++ b/lib/themes/opaque.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'dart:ui'; import 'dart:core'; import 'package:cwtch/themes/cwtch.dart'; @@ -14,14 +13,14 @@ const custom_themes_subdir = "themes"; const mode_light = "light"; const mode_dark = "dark"; -final TextStyle defaultSmallTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.normal, fontSize: 10); +const TextStyle defaultSmallTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.normal, fontSize: 10); final TextStyle defaultMessageTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w400, fontSize: 13, fontFamilyFallback: [Platform.isWindows ? 'Segoe UI Emoji' : "Noto Color Emoji"]); -final TextStyle defaultFormLabelTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: 20); -final TextStyle defaultTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w500, fontSize: 12); +const TextStyle defaultFormLabelTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: 20); +const TextStyle defaultTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w500, fontSize: 12); final TextStyle defaultTextButtonStyle = defaultTextStyle.copyWith(fontWeight: FontWeight.bold); -final TextStyle defaultDropDownMenuItemTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: 16); +const TextStyle defaultDropDownMenuItemTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: 16); -Map> themes = Map(); +Map> themes = {}; LoadThemes(String cwtchDir) async { themes = await loadBuiltinThemes(); @@ -68,7 +67,7 @@ Color darken(Color color, [double amount = 0.15]) { } abstract class OpaqueThemeType { - static final Color red = Color(0xFFFF0000); + static const Color red = Color(0xFFFF0000); get theme => "dummy"; get mode => mode_light; @@ -111,8 +110,8 @@ abstract class OpaqueThemeType { get portraitProfileBadgeColor => red; get portraitProfileBadgeTextColor => red; - get portraitOnlineAwayColor => Color(0xFFFFF59D); - get portraitOnlineBusyColor => Color(0xFFEF9A9A); + get portraitOnlineAwayColor => const Color(0xFFFFF59D); + get portraitOnlineBusyColor => const Color(0xFFEF9A9A); // dropshaddpow // todo: probably should not be reply icon color in messagerow @@ -137,7 +136,7 @@ abstract class OpaqueThemeType { get chatImage => null; ImageProvider loadImage(String key, {BuildContext? context}) { - return AssetImage(""); + return const AssetImage(""); } // Sizes @@ -172,13 +171,11 @@ ThemeData mkThemeData(Settings opaque) { return ThemeData( hoverColor: opaque.current().backgroundHilightElementColor.withOpacity(0.5), visualDensity: VisualDensity.adaptivePlatformDensity, - primarySwatch: getMaterialColor(opaque.current().topbarColor), primaryIconTheme: IconThemeData( color: opaque.current().mainTextColor, ), primaryColor: opaque.current().mainTextColor, canvasColor: opaque.current().backgroundMainColor, - backgroundColor: opaque.current().backgroundMainColor, highlightColor: opaque.current().hilightElementColor, iconTheme: IconThemeData( color: opaque.current().toolbarIconColor, @@ -207,7 +204,7 @@ ThemeData mkThemeData(Settings opaque) { backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor), foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor), overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor), - padding: MaterialStateProperty.all(EdgeInsets.all(20))), + padding: MaterialStateProperty.all(const EdgeInsets.all(20))), ), hintColor: opaque.current().textfieldHintColor, elevatedButtonTheme: ElevatedButtonThemeData( @@ -221,7 +218,7 @@ ThemeData mkThemeData(Settings opaque) { : null), enableFeedback: true, textStyle: MaterialStateProperty.all(opaque.scaleFonts(defaultTextButtonStyle)), - padding: MaterialStateProperty.all(EdgeInsets.all(20)), + padding: MaterialStateProperty.all(const EdgeInsets.all(20)), shape: MaterialStateProperty.all(RoundedRectangleBorder( borderRadius: BorderRadius.circular(6.0), )), diff --git a/lib/themes/yamltheme.dart b/lib/themes/yamltheme.dart index 7e32b529..4838422c 100644 --- a/lib/themes/yamltheme.dart +++ b/lib/themes/yamltheme.dart @@ -1,13 +1,10 @@ import 'dart:convert'; import 'dart:io'; -import 'dart:ui'; -import 'package:cwtch/config.dart'; import 'package:cwtch/themes/cwtch.dart'; import 'package:cwtch/themes/opaque.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:glob/list_local_fs.dart'; import 'package:provider/provider.dart'; import 'package:yaml/yaml.dart'; import 'package:path/path.dart' as path; @@ -18,7 +15,7 @@ Future>> loadBuiltinThemes() async { final manifestJson = await rootBundle.loadString('AssetManifest.json'); final themesList = json.decode(manifestJson).keys.where((String key) => key.startsWith('assets/themes')); - Map> themes = Map(); + Map> themes = {}; for (String themefile in themesList) { if (themefile.substring(themefile.length - 4) != ".yml") { @@ -41,7 +38,7 @@ Future>> loadBuiltinThemes() async { } Future>> loadCustomThemes(String themesDirPath) async { - Map> themes = Map(); + Map> themes = {}; Directory themesDir = Directory(themesDirPath); if (!themesDir.existsSync()) { @@ -97,7 +94,7 @@ Future?> loadFileYamlTheme(String themefile, String } Future?> loadYamlTheme(YamlMap yml, [String? assetsDir]) async { - Map subthemes = Map(); + Map subthemes = {}; if ((yml["themes"] as YamlMap).containsKey(mode_dark)) { subthemes[mode_dark] = YmlTheme(yml, yml["themes"]["name"], mode_dark, assetsDir); } @@ -109,16 +106,14 @@ Future?> loadYamlTheme(YamlMap yml, [String? assets class YmlTheme extends OpaqueThemeType { late YamlMap yml; + @override late String mode; + @override late String theme; late String? assetsDir; late OpaqueThemeType fallbackTheme; - YmlTheme(YamlMap yml, theme, mode, assetsDir) { - this.yml = yml; - this.theme = theme; - this.mode = mode; - this.assetsDir = assetsDir; + YmlTheme(this.yml, this.theme, this.mode, this.assetsDir) { if (mode == mode_dark) { fallbackTheme = CwtchDark(); } else { @@ -128,16 +123,16 @@ class YmlTheme extends OpaqueThemeType { Color? getColor(String name) { var val = yml["themes"][mode]["theme"][name]; - if (!(val is int)) { + if ((val is! int)) { val = yml["themes"][mode]["theme"][val] ?? val; } - if (!(val is int)) { + if ((val is! int)) { val = yml["themes"][mode]?["colors"][val] ?? val; } - if (!(val is int)) { + if ((val is! int)) { val = yml["colors"]?[val]; } - if (!(val is int)) { + if ((val is! int)) { return null; } @@ -160,51 +155,93 @@ class YmlTheme extends OpaqueThemeType { return null; } + @override get backgroundMainColor => getColor("backgroundMainColor") ?? fallbackTheme.backgroundMainColor; + @override get backgroundPaneColor => getColor("backgroundPaneColor") ?? fallbackTheme.backgroundPaneColor; + @override get topbarColor => getColor("topbarColor") ?? fallbackTheme.topbarColor; + @override get mainTextColor => getColor("mainTextColor") ?? fallbackTheme.mainTextColor; + @override get hilightElementColor => getColor("hilightElementColor") ?? fallbackTheme.hilightElementColor; + @override get backgroundHilightElementColor => getColor("backgroundHilightElementColor") ?? fallbackTheme.backgroundHilightElementColor; + @override get sendHintTextColor => getColor("sendHintTextColor") ?? fallbackTheme.sendHintTextColor; + @override get defaultButtonColor => getColor("defaultButtonColor") ?? fallbackTheme.defaultButtonColor; + @override get defaultButtonActiveColor => /*mode == mode_light ? darken(defaultButtonColor) :*/ lighten(getColor("defaultButtonColor") ?? fallbackTheme.defaultButtonColor); + @override get defaultButtonTextColor => getColor("defaultButtonTextColor") ?? fallbackTheme.defaultButtonTextColor; + @override get defaultButtonDisabledColor => getColor("defaultButtonDisabledColor") ?? fallbackTheme.defaultButtonDisabledColor; + @override get textfieldBackgroundColor => getColor("textfieldBackgroundColor") ?? fallbackTheme.textfieldBackgroundColor; + @override get textfieldBorderColor => getColor("textfieldBorderColor") ?? fallbackTheme.textfieldBorderColor; + @override get textfieldHintColor => getColor("textfieldHintColor") ?? fallbackTheme.textfieldHintColor; + @override get textfieldErrorColor => getColor("textfieldErrorColor") ?? fallbackTheme.textfieldErrorColor; + @override get textfieldSelectionColor => getColor("textfieldSelectionColor") ?? fallbackTheme.textfieldSelectionColor; + @override get scrollbarDefaultColor => getColor("scrollbarDefaultColor") ?? fallbackTheme.scrollbarDefaultColor; + @override get portraitBackgroundColor => getColor("portraitBackgroundColor") ?? fallbackTheme.portraitBackgroundColor; + @override get portraitOnlineBorderColor => getColor("portraitOnlineBorderColor") ?? fallbackTheme.portraitOnlineBorderColor; + @override get portraitOfflineBorderColor => getColor("portraitOfflineBorderColor") ?? fallbackTheme.portraitOfflineBorderColor; + @override get portraitBlockedBorderColor => getColor("portraitBlockedBorderColor") ?? fallbackTheme.portraitBlockedBorderColor; + @override get portraitBlockedTextColor => getColor("portraitBlockedTextColor") ?? fallbackTheme.portraitBlockedTextColor; + @override get portraitContactBadgeColor => getColor("portraitContactBadgeColor") ?? fallbackTheme.portraitContactBadgeColor; + @override get portraitContactBadgeTextColor => getColor("portraitContactBadgeTextColor") ?? fallbackTheme.portraitContactBadgeTextColor; + @override get portraitProfileBadgeColor => getColor("portraitProfileBadgeColor") ?? fallbackTheme.portraitProfileBadgeColor; + @override get portraitProfileBadgeTextColor => getColor("portraitProfileBadgeTextColor") ?? fallbackTheme.portraitProfileBadgeTextColor; + @override get portraitOnlineAwayColor => getColor("portraitOnlineAwayColor") ?? fallbackTheme.portraitOnlineAwayColor; + @override get portraitOnlineBusyColor => getColor("portraitOnlineBusyColor") ?? fallbackTheme.portraitOnlineBusyColor; + @override get dropShadowColor => getColor("dropShadowColor") ?? fallbackTheme.dropShadowColor; + @override get toolbarIconColor => getColor("toolbarIconColor") ?? fallbackTheme.toolbarIconColor; + @override get chatReactionIconColor => getColor("chatReactionIconColor") ?? fallbackTheme.chatReactionIconColor; + @override get messageFromMeBackgroundColor => getColor("messageFromMeBackgroundColor") ?? fallbackTheme.messageFromMeBackgroundColor; + @override get messageFromMeTextColor => getColor("messageFromMeTextColor") ?? fallbackTheme.messageFromMeTextColor; + @override get messageFromOtherBackgroundColor => getColor("messageFromOtherBackgroundColor") ?? fallbackTheme.messageFromOtherBackgroundColor; + @override get messageFromOtherTextColor => getColor("messageFromOtherTextColor") ?? fallbackTheme.messageFromOtherTextColor; + @override get messageSelectionColor => getColor("messageSelectionColor") ?? fallbackTheme.messageSelectionColor; + @override get menuBackgroundColor => getColor("menuBackgroundColor") ?? fallbackTheme.menuBackgroundColor; + @override get snackbarBackgroundColor => getColor("snackbarBackgroundColor") ?? fallbackTheme.snackbarBackgroundColor; + @override get snackbarTextColor => getColor("snackbarTextColor") ?? fallbackTheme.snackbarTextColor; // Images + @override get chatImageColor => getColor("chatImageColor") ?? fallbackTheme.chatImageColor; + @override get chatImage => getImage("chatImage") ?? fallbackTheme.chatImage; + @override ImageProvider loadImage(String key, {BuildContext? context}) { File f = File(key); if (f.existsSync()) { diff --git a/lib/third_party/base32/base32.dart b/lib/third_party/base32/base32.dart index e62f0d32..32137331 100644 --- a/lib/third_party/base32/base32.dart +++ b/lib/third_party/base32/base32.dart @@ -109,7 +109,7 @@ class base32 { base32 = _pad(base32, encoding: encoding); if (!_isValid(base32, encoding: encoding)) { - throw FormatException('Invalid Base32 characters'); + throw const FormatException('Invalid Base32 characters'); } if (encoding == Encoding.crockford) { diff --git a/lib/third_party/base85/LICENSE b/lib/third_party/base85/LICENSE new file mode 100644 index 00000000..a55f9c44 --- /dev/null +++ b/lib/third_party/base85/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 litaoh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/third_party/base85/base_85_codec.dart b/lib/third_party/base85/base_85_codec.dart new file mode 100644 index 00000000..10027363 --- /dev/null +++ b/lib/third_party/base85/base_85_codec.dart @@ -0,0 +1,32 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'base_85_decoder.dart'; +import 'base_85_encoder.dart'; + +enum AlgoType { ascii85 } + +//This is not any official base85 alphabet, because we need one where the json encoder won't have an issue with +// the characters being encoded. For dart that means no angle brackets, and no double quotes +const String ascii85 = '{}&()*+,-./0123456789:;=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[~]^_`abcdefghijklmnopqrstuvwxyz'; + +class Base85Codec extends Codec { + final String alphabet; + Converter? _encoder; + Converter? _decoder; + AlgoType algo; + + Base85Codec([this.alphabet = ascii85, this.algo = AlgoType.ascii85]); + + @override + Converter get encoder { + _encoder ??= Base85Encoder(alphabet, algo); + return _encoder!; + } + + @override + Converter get decoder { + _decoder ??= Base85Decoder(alphabet, algo); + return _decoder!; + } +} diff --git a/lib/third_party/base85/base_85_decoder.dart b/lib/third_party/base85/base_85_decoder.dart new file mode 100644 index 00000000..616eae38 --- /dev/null +++ b/lib/third_party/base85/base_85_decoder.dart @@ -0,0 +1,101 @@ +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; + +import 'base_85_codec.dart'; + +/// Characters to allow (and ignore) in an encoded buffer +const List _IGNORE_CHARS = [0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x20]; + +/// num max value +var NUM_MAX_VALUE = pow(2, 32) - 1; + +/// quad 85 +var QUAD85 = pow(85, 4); + +/// trio 85 +var TRIO85 = pow(85, 3); + +/// duo 85 +var DUO85 = pow(85, 2); + +/// sign 85 +var SING85 = 85; + +class Base85Decoder extends Converter { + String alphabet; + AlgoType algo; + late Uint8List _baseMap; + + Base85Decoder(this.alphabet, this.algo) { + _baseMap = Uint8List(256); + _baseMap.fillRange(0, _baseMap.length, 255); + for (var i = 0; i < alphabet.length; i++) { + var xc = alphabet.codeUnitAt(i); + if (_baseMap[xc] != 255) { + throw FormatException('${alphabet[i]} is ambiguous'); + } + _baseMap[xc] = i; + } + } + + @override + Uint8List convert(String input) { + if (input.isEmpty) { + return Uint8List(0); + } + var bytes = Uint8List.fromList(input.codeUnits); + var dataLength = bytes.length; + + var padding = (dataLength % 5 == 0) ? 0 : 5 - dataLength % 5; + + var bufferStart = 0; + var bufferEnd = bufferStart + dataLength; + + var result = Uint8List(4 * ((bufferEnd - bufferStart) / 5).ceil()); + + nextValidByte(index) { + if (index < bufferEnd) { + while (_IGNORE_CHARS.contains(bytes[index])) { + padding = (padding + 1) % 5; + index++; // skip newline character + } + } + return index; + } + + var writeIndex = 0; + for (var i = bufferStart; i < bufferEnd;) { + var num = 0; + var starti = i; + + i = nextValidByte(i); + num = (_baseMap[bytes[i]]) * (QUAD85 as int); + + i = nextValidByte(i + 1); + num += (i >= bufferEnd ? 84 : _baseMap[bytes[i]]) * (TRIO85 as int); + + i = nextValidByte(i + 1); + num += (i >= bufferEnd ? 84 : _baseMap[bytes[i]]) * (DUO85 as int); + + i = nextValidByte(i + 1); + num += (i >= bufferEnd ? 84 : _baseMap[bytes[i]]) * SING85; + + i = nextValidByte(i + 1); + num += (i >= bufferEnd ? 84 : _baseMap[bytes[i]]); + + i = nextValidByte(i + 1); + + if (num > NUM_MAX_VALUE || num < 0) { + throw const FormatException('Bogus data'); + } + result[writeIndex] = (num >> 24); + result[writeIndex + 1] = (num >> 16); + result[writeIndex + 2] = (num >> 8); + result[writeIndex + 3] = (num & 0xff); + writeIndex += 4; + } + + return result.sublist(0, writeIndex - padding); + } +} diff --git a/lib/third_party/base85/base_85_encoder.dart b/lib/third_party/base85/base_85_encoder.dart new file mode 100644 index 00000000..acdc7617 --- /dev/null +++ b/lib/third_party/base85/base_85_encoder.dart @@ -0,0 +1,46 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'base_85_codec.dart'; + +class Base85Encoder extends Converter { + final String alphabet; + final AlgoType algo; + + Base85Encoder(this.alphabet, this.algo); + + @override + + /// Encodes the specified data. If algo is [AlgoType.ascii85], + /// the encoded data will be prepended with <~ and appended with ~>. + /// The [bytes] to encode, may be a [Uint8List] + /// return A String with the encoded [bytes]. + String convert(Uint8List bytes) { + var padding = (bytes.length % 4 == 0) ? 0 : 4 - bytes.length % 4; + + var result = ''; + for (var i = 0; i < bytes.length; i += 4) { + /// 32-bit number of the current 4 bytes (padded with 0 as necessary) + var num = ((bytes[i] << 24).toUnsigned(32)) + + (((i + 1 >= bytes.length ? 0 : bytes[i + 1]) << 16).toUnsigned(32)) + + (((i + 2 >= bytes.length ? 0 : bytes[i + 2]) << 8).toUnsigned(32)) + + (((i + 3 >= bytes.length ? 0 : bytes[i + 3]) << 0).toUnsigned(32)); + + /// Create 5 characters from '!' to 'u' alphabet + var block = []; + for (var j = 0; j < 5; ++j) { + block.insert(0, alphabet[num % 85]); + num = num ~/ 85; + } + + var r = block.join(''); + if (r == '!!!!!' && algo == AlgoType.ascii85) { + r = 'z'; + } + + /// And append them to the result + result += r; + } + + return result.substring(0, result.length - padding); + } +} diff --git a/lib/third_party/linkify/flutter_linkify.dart b/lib/third_party/linkify/flutter_linkify.dart index e6595dbc..02dc9d07 100644 --- a/lib/third_party/linkify/flutter_linkify.dart +++ b/lib/third_party/linkify/flutter_linkify.dart @@ -24,11 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import 'dart:ui'; - import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'linkify.dart'; @@ -132,7 +129,7 @@ class SelectableLinkify extends StatelessWidget { final BoxConstraints constraints; const SelectableLinkify({ - Key? key, + super.key, required this.text, this.linkifiers = defaultLinkifiers, this.onOpen, @@ -165,7 +162,7 @@ class SelectableLinkify extends StatelessWidget { this.selectionControls, this.onSelectionChanged, required this.constraints, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -304,7 +301,7 @@ TextSpan buildTextSpan( semanticsLabel: element.text); } else if (element is CodeElement) { return TextSpan( - text: element.text.replaceAll("\`", ""), + text: element.text.replaceAll("`", ""), // monospace fonts at the same size as regular text makes them appear // slightly larger, so we compensate by making them slightly smaller... style: codeStyle?.copyWith(fontFamily: "RobotoMono", fontSize: codeStyle.fontSize! - 1.5), diff --git a/lib/third_party/linkify/linkify.dart b/lib/third_party/linkify/linkify.dart index eaa5ddef..64526032 100644 --- a/lib/third_party/linkify/linkify.dart +++ b/lib/third_party/linkify/linkify.dart @@ -38,27 +38,27 @@ abstract class LinkifyElement { } class BoldElement extends LinkifyElement { - BoldElement(String text) : super(text); + BoldElement(super.text); } class ItalicElement extends LinkifyElement { - ItalicElement(String text) : super(text); + ItalicElement(super.text); } class SuperElement extends LinkifyElement { - SuperElement(String text) : super(text); + SuperElement(super.text); } class SubElement extends LinkifyElement { - SubElement(String text) : super(text); + SubElement(super.text); } class StrikeElement extends LinkifyElement { - StrikeElement(String text) : super(text); + StrikeElement(super.text); } class CodeElement extends LinkifyElement { - CodeElement(String text) : super(text); + CodeElement(super.text); } class LinkableElement extends LinkifyElement { @@ -75,7 +75,7 @@ class LinkableElement extends LinkifyElement { /// Represents an element containing text class TextElement extends LinkifyElement { - TextElement(String text) : super(text); + TextElement(super.text); @override String toString() { @@ -136,9 +136,9 @@ List linkify( return list; } - linkifiers.forEach((linkifier) { + for (var linkifier in linkifiers) { list = linkifier.parse(list, options); - }); + } return list; } diff --git a/lib/third_party/linkify/uri.dart b/lib/third_party/linkify/uri.dart index ccc3866e..5bd60579 100644 --- a/lib/third_party/linkify/uri.dart +++ b/lib/third_party/linkify/uri.dart @@ -148,7 +148,7 @@ class UrlLinkifier extends Linkifier { List parse(elements, options) { var list = []; - elements.forEach((element) { + for (var element in elements) { if (element is TextElement) { if (options.parseLinks == false && options.messageFormatting == false) { list.add(element); @@ -188,7 +188,7 @@ class UrlLinkifier extends Linkifier { // If protocol has not been specified then append a protocol // to the start of the URL so that it can be opened... if (!url.startsWith("https://") && !url.startsWith("http://")) { - url = "https://" + url; + url = "https://$url"; } list.add(UrlElement(url, originalUrl)); @@ -211,7 +211,7 @@ class UrlLinkifier extends Linkifier { EnvironmentConfig.debugLog("'unreachable' code path in formatting has been triggered. this is very likely a bug - please report $options"); } } - }); + } return list; } diff --git a/lib/third_party/nm/src/network_manager_client.dart b/lib/third_party/nm/src/network_manager_client.dart index e5c5d680..46bb4ef1 100644 --- a/lib/third_party/nm/src/network_manager_client.dart +++ b/lib/third_party/nm/src/network_manager_client.dart @@ -252,7 +252,7 @@ class _NetworkManagerObject extends DBusRemoteObject { return (value as DBusArray).children.map((value) => convertData(value)).toList(); } - _NetworkManagerObject(DBusClient client, DBusObjectPath path, Map> interfacesAndProperties) : super(client, name: 'org.freedesktop.NetworkManager', path: path) { + _NetworkManagerObject(super.client, DBusObjectPath path, Map> interfacesAndProperties) : super(name: 'org.freedesktop.NetworkManager', path: path) { updateInterfaces(interfacesAndProperties); } } @@ -284,7 +284,7 @@ class NetworkManagerClient { } /// Stream of property names as their values change. - Stream> get propertiesChanged => _manager?.interfaces[_managerInterfaceName]?.propertiesChangedStreamController.stream ?? Stream>.empty(); + Stream> get propertiesChanged => _manager?.interfaces[_managerInterfaceName]?.propertiesChangedStreamController.stream ?? const Stream>.empty(); /// Connects to the NetworkManager D-Bus objects. /// Must be called before accessing methods and properties. diff --git a/lib/torstatus.dart b/lib/torstatus.dart index af94b78d..0341219e 100644 --- a/lib/torstatus.dart +++ b/lib/torstatus.dart @@ -9,24 +9,24 @@ class TorStatus extends ChangeNotifier { TorStatus({this.connected = false, this.progress = 0, this.status = "", this.version = ""}); /// Called by the event bus. - handleUpdate(int new_progress, String new_status) { + handleUpdate(int newProgress, String newStatus) { if (progress == 100) { connected = true; } else { connected = false; } - progress = new_progress; - status = new_status; - if (new_progress != 100) { - status = "$new_progress% - $new_status"; + progress = newProgress; + status = newStatus; + if (newProgress != 100) { + status = "$newProgress% - $newStatus"; } notifyListeners(); } - updateVersion(String new_version) { - version = new_version; + updateVersion(String newVersion) { + version = newVersion; notifyListeners(); } } diff --git a/lib/views/addcontactview.dart b/lib/views/addcontactview.dart index 56b28a1a..87c6849e 100644 --- a/lib/views/addcontactview.dart +++ b/lib/views/addcontactview.dart @@ -1,12 +1,9 @@ -import 'dart:convert'; - import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/profile.dart'; import 'package:cwtch/models/remoteserver.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:cwtch/errorHandler.dart'; -import 'package:cwtch/models/profileservers.dart'; import 'package:cwtch/settings.dart'; import 'package:cwtch/widgets/buttontextfield.dart'; import 'package:cwtch/widgets/cwtchlabel.dart'; @@ -23,7 +20,7 @@ import '../main.dart'; class AddContactView extends StatefulWidget { final newGroup; - const AddContactView({Key? key, this.newGroup}) : super(key: key); + const AddContactView({super.key, this.newGroup}); @override _AddContactViewState createState() => _AddContactViewState(); @@ -80,7 +77,7 @@ class _AddContactViewState extends State { } void _copyOnion() { - Clipboard.setData(new ClipboardData(text: Provider.of(context, listen: false).onion)); + Clipboard.setData(ClipboardData(text: Provider.of(context, listen: false).onion)); final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification)); ScaffoldMessenger.of(context).showSnackBar(snackBar); } @@ -90,7 +87,7 @@ class _AddContactViewState extends State { return TabBar( tabs: [ Tab( - icon: Icon(CwtchIcons.add_peer), + icon: const Icon(CwtchIcons.add_peer), text: AppLocalizations.of(context)!.addPeer, ), ], @@ -102,11 +99,11 @@ class _AddContactViewState extends State { return TabBar( tabs: [ Tab( - icon: Icon(CwtchIcons.add_peer), + icon: const Icon(CwtchIcons.add_peer), text: AppLocalizations.of(context)!.tooltipAddContact, ), //Tab(icon: Icon(Icons.backup), text: AppLocalizations.of(context)!.titleManageServers), - Tab(icon: Icon(CwtchIcons.add_group), text: AppLocalizations.of(context)!.createGroup), + Tab(icon: const Icon(CwtchIcons.add_group), text: AppLocalizations.of(context)!.createGroup), ], ); } @@ -121,35 +118,35 @@ class _AddContactViewState extends State { clipBehavior: Clip.antiAlias, controller: controller, child: Container( - margin: EdgeInsets.all(30), - padding: EdgeInsets.all(20), + margin: const EdgeInsets.all(30), + padding: const EdgeInsets.all(20), child: Form( autovalidateMode: AutovalidateMode.always, key: _formKey, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ CwtchLabel(label: AppLocalizations.of(context)!.profileOnionLabel), - SizedBox( + const SizedBox( height: 20, ), CwtchButtonTextField( controller: ctrlrOnion, onPressed: _copyOnion, readonly: true, - icon: Icon( + icon: const Icon( CwtchIcons.address_copy, size: 32, ), tooltip: AppLocalizations.of(context)!.copyBtn, ), - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.pasteAddressToAddContact), - SizedBox( + const SizedBox( height: 20, ), CwtchTextField( - key: Key("txtAddP2P"), + key: const Key("txtAddP2P"), controller: ctrlrContact, validator: (value) { if (value == "") { @@ -196,8 +193,8 @@ class _AddContactViewState extends State { clipBehavior: Clip.antiAlias, controller: controller, child: Container( - margin: EdgeInsets.all(30), - padding: EdgeInsets.all(20), + margin: const EdgeInsets.all(30), + padding: const EdgeInsets.all(20), child: Form( autovalidateMode: AutovalidateMode.always, key: _createGroupFormKey, @@ -206,7 +203,7 @@ class _AddContactViewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ CwtchLabel(label: AppLocalizations.of(context)!.server), - SizedBox( + const SizedBox( height: 20, ), DropdownButton( @@ -226,20 +223,22 @@ class _AddContactViewState extends State { ), ); }).toList()), - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(bcontext)!.groupNameLabel), - SizedBox( + const SizedBox( height: 20, ), CwtchTextField( controller: ctrlrGroupName, hintText: AppLocalizations.of(bcontext)!.groupNameLabel, onChanged: (newValue) {}, - validator: (value) {}, + validator: (value) { + return null; + }, ), - SizedBox( + const SizedBox( height: 20, ), ElevatedButton( @@ -247,7 +246,7 @@ class _AddContactViewState extends State { var profileOnion = Provider.of(bcontext, listen: false).onion; Provider.of(bcontext, listen: false).cwtch.CreateGroup(profileOnion, server, ctrlrGroupName.text); Future.delayed(const Duration(milliseconds: 500), () { - final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.successfullAddedContact + " " + ctrlrGroupName.text)); + final snackBar = SnackBar(content: Text("${AppLocalizations.of(context)!.successfullAddedContact} ${ctrlrGroupName.text}")); ScaffoldMessenger.of(bcontext).showSnackBar(snackBar); Navigator.pop(bcontext); }); diff --git a/lib/views/addeditprofileview.dart b/lib/views/addeditprofileview.dart index c6db4414..ef7569d6 100644 --- a/lib/views/addeditprofileview.dart +++ b/lib/views/addeditprofileview.dart @@ -20,11 +20,10 @@ import '../constants.dart'; import '../cwtch_icons_icons.dart'; import '../errorHandler.dart'; import '../main.dart'; -import '../themes/opaque.dart'; import '../settings.dart'; class AddEditProfileView extends StatefulWidget { - const AddEditProfileView({Key? key}) : super(key: key); + const AddEditProfileView({super.key}); @override _AddEditProfileViewState createState() => _AddEditProfileViewState(); @@ -93,8 +92,8 @@ class _AddEditProfileViewState extends State { child: Form( key: _formKey, child: Container( - margin: EdgeInsets.all(30), - padding: EdgeInsets.all(20), + margin: const EdgeInsets.all(30), + padding: const EdgeInsets.all(20), child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ Visibility( visible: Provider.of(context).onion.isNotEmpty, @@ -121,7 +120,7 @@ class _AddEditProfileViewState extends State { }, () { final snackBar = SnackBar( content: Text(AppLocalizations.of(context)!.msgFileTooBig), - duration: Duration(seconds: 4), + duration: const Duration(seconds: 4), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); }, () {}); @@ -145,7 +144,7 @@ class _AddEditProfileViewState extends State { child: Column( children: [ Padding( - padding: EdgeInsets.all(5.0), + padding: const EdgeInsets.all(5.0), child: CwtchTextField( controller: ctrlrAttribute1, multiLine: false, @@ -154,9 +153,9 @@ class _AddEditProfileViewState extends State { Provider.of(context, listen: false).cwtch.SetProfileAttribute(onion, "profile.profile-attribute-1", profileAttribute1); Provider.of(context, listen: false).attributes[0] = profileAttribute1; }, - hintText: Provider.of(context).attributes[0] ?? AppLocalizations.of(context)!.profileInfoHint!)), + hintText: Provider.of(context).attributes[0] ?? AppLocalizations.of(context)!.profileInfoHint)), Padding( - padding: EdgeInsets.all(5.0), + padding: const EdgeInsets.all(5.0), child: CwtchTextField( controller: ctrlrAttribute2, multiLine: false, @@ -165,9 +164,9 @@ class _AddEditProfileViewState extends State { Provider.of(context, listen: false).cwtch.SetProfileAttribute(onion, "profile.profile-attribute-2", profileAttribute2); Provider.of(context, listen: false).attributes[1] = profileAttribute2; }, - hintText: Provider.of(context).attributes[1] ?? AppLocalizations.of(context)!.profileInfoHint2!)), + hintText: Provider.of(context).attributes[1] ?? AppLocalizations.of(context)!.profileInfoHint2)), Padding( - padding: EdgeInsets.all(5.0), + padding: const EdgeInsets.all(5.0), child: CwtchTextField( controller: ctrlrAttribute3, multiLine: false, @@ -176,21 +175,21 @@ class _AddEditProfileViewState extends State { Provider.of(context, listen: false).cwtch.SetProfileAttribute(onion, "profile.profile-attribute-3", profileAttribute3); Provider.of(context, listen: false).attributes[2] = profileAttribute3; }, - hintText: Provider.of(context).attributes[2] ?? AppLocalizations.of(context)!.profileInfoHint3!)), + hintText: Provider.of(context).attributes[2] ?? AppLocalizations.of(context)!.profileInfoHint3)), ], )) ], )), Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.displayNameLabel), - SizedBox( + const SizedBox( height: 20, ), CwtchTextField( - key: Key("displayNameFormElement"), + key: const Key("displayNameFormElement"), controller: ctrlrNick, autofocus: false, hintText: AppLocalizations.of(context)!.yourDisplayName, @@ -205,18 +204,18 @@ class _AddEditProfileViewState extends State { Visibility( visible: Provider.of(context).onion.isNotEmpty, child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.addressLabel), - SizedBox( + const SizedBox( height: 20, ), CwtchButtonTextField( controller: ctrlrOnion, onPressed: _copyOnion, readonly: true, - icon: Icon( + icon: const Icon( CwtchIcons.address_copy, size: 32, ), @@ -258,7 +257,7 @@ class _AddEditProfileViewState extends State { onChanged: (bool value) { Provider.of(context, listen: false).autostart = value; - if (!Provider.of(context, listen: false).onion.isEmpty) { + if (Provider.of(context, listen: false).onion.isNotEmpty) { Provider.of(context, listen: false) .cwtch .SetProfileAttribute(Provider.of(context, listen: false).onion, "profile.autostart", value ? "true" : "false"); @@ -280,7 +279,7 @@ class _AddEditProfileViewState extends State { onChanged: (bool value) { Provider.of(context, listen: false).appearOfflineAtStartup = value; var onion = Provider.of(context, listen: false).onion; - if (!onion.isEmpty) { + if (onion.isNotEmpty) { Provider.of(context, listen: false).cwtch.SetProfileAttribute(onion, "profile.appear-offline", value ? "true" : "false"); } }, @@ -291,14 +290,14 @@ class _AddEditProfileViewState extends State { Visibility( visible: Provider.of(context).onion.isEmpty, - child: SizedBox( + child: const SizedBox( height: 20, )), Visibility( visible: Provider.of(context).onion.isEmpty, child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Checkbox( - key: Key("passwordCheckBox"), + key: const Key("passwordCheckBox"), value: usePassword, fillColor: MaterialStateProperty.all(theme.current().defaultButtonColor), activeColor: theme.current().defaultButtonActiveColor, @@ -308,17 +307,17 @@ class _AddEditProfileViewState extends State { AppLocalizations.of(context)!.radioUsePassword, style: TextStyle(color: theme.current().mainTextColor), ), - SizedBox( + const SizedBox( height: 20, ), Padding( - padding: EdgeInsets.symmetric(horizontal: 24), + padding: const EdgeInsets.symmetric(horizontal: 24), child: Text( usePassword ? AppLocalizations.of(context)!.encryptedProfileDescription : AppLocalizations.of(context)!.plainProfileDescription, textAlign: TextAlign.center, )) ])), - SizedBox( + const SizedBox( height: 20, ), Visibility( @@ -328,13 +327,13 @@ class _AddEditProfileViewState extends State { visible: Provider.of(context, listen: false).onion.isNotEmpty && Provider.of(context).isEncrypted, child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ CwtchLabel(label: AppLocalizations.of(context)!.currentPasswordLabel), - SizedBox( + const SizedBox( height: 20, ), CwtchPasswordField( - key: Key("currentPasswordFormElement"), + key: const Key("currentPasswordFormElement"), controller: ctrlrOldPass, - autoFillHints: [AutofillHints.newPassword], + autoFillHints: const [AutofillHints.newPassword], validator: (value) { // Password field can be empty when just updating the profile, not on creation if (Provider.of(context, listen: false).isEncrypted && @@ -349,16 +348,16 @@ class _AddEditProfileViewState extends State { return null; }, ), - SizedBox( + const SizedBox( height: 20, ), ])), CwtchLabel(label: AppLocalizations.of(context)!.newPassword), - SizedBox( + const SizedBox( height: 20, ), CwtchPasswordField( - key: Key("passwordFormElement"), + key: const Key("passwordFormElement"), controller: ctrlrPass, validator: (value) { // Password field can be empty when just updating the profile, not on creation @@ -371,15 +370,15 @@ class _AddEditProfileViewState extends State { return null; }, ), - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.password2Label), - SizedBox( + const SizedBox( height: 20, ), CwtchPasswordField( - key: Key("confirmPasswordFormElement"), + key: const Key("confirmPasswordFormElement"), controller: ctrlrPass2, validator: (value) { // Password field can be empty when just updating the profile, not on creation @@ -393,22 +392,22 @@ class _AddEditProfileViewState extends State { }), ]), ), - SizedBox( + const SizedBox( height: 20, ), ElevatedButton( onPressed: _createPressed, style: ElevatedButton.styleFrom( - minimumSize: Size(400, 75), - maximumSize: Size(800, 75), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), + minimumSize: const Size(400, 75), + maximumSize: const Size(800, 75), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), ), child: Text( Provider.of(context).onion.isEmpty ? AppLocalizations.of(context)!.addNewProfileBtn : AppLocalizations.of(context)!.saveProfileBtn, textAlign: TextAlign.center, ), ), - SizedBox( + const SizedBox( height: 20, ), Visibility( @@ -417,29 +416,29 @@ class _AddEditProfileViewState extends State { message: AppLocalizations.of(context)!.exportProfileTooltip, child: ElevatedButton.icon( style: ElevatedButton.styleFrom( - minimumSize: Size(400, 75), - maximumSize: Size(800, 75), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), + minimumSize: const Size(400, 75), + maximumSize: const Size(800, 75), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), ), onPressed: () { if (Platform.isAndroid) { - Provider.of(context, listen: false).cwtch.ExportProfile(ctrlrOnion.value.text, ctrlrOnion.value.text + ".tar.gz"); - final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.fileSavedTo + " " + ctrlrOnion.value.text + ".tar.gz")); + Provider.of(context, listen: false).cwtch.ExportProfile(ctrlrOnion.value.text, "${ctrlrOnion.value.text}.tar.gz"); + final snackBar = SnackBar(content: Text("${AppLocalizations.of(context)!.fileSavedTo} ${ctrlrOnion.value.text}.tar.gz")); ScaffoldMessenger.of(context).showSnackBar(snackBar); } else { showCreateFilePicker(context).then((name) { if (name != null) { Provider.of(context, listen: false).cwtch.ExportProfile(ctrlrOnion.value.text, name); - final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.fileSavedTo + " " + name)); + final snackBar = SnackBar(content: Text("${AppLocalizations.of(context)!.fileSavedTo} $name")); ScaffoldMessenger.of(context).showSnackBar(snackBar); } }); } }, - icon: Icon(Icons.import_export), + icon: const Icon(Icons.import_export), label: Text(AppLocalizations.of(context)!.exportProfile), ))), - SizedBox( + const SizedBox( height: 20, ), Visibility( @@ -448,17 +447,17 @@ class _AddEditProfileViewState extends State { message: AppLocalizations.of(context)!.enterCurrentPasswordForDelete, child: ElevatedButton.icon( style: ElevatedButton.styleFrom( - minimumSize: Size(400, 75), - maximumSize: Size(800, 75), + minimumSize: const Size(400, 75), + backgroundColor: Provider.of(context).theme.backgroundMainColor, + maximumSize: const Size(800, 75), shape: RoundedRectangleBorder( side: BorderSide(color: Provider.of(context).theme.defaultButtonActiveColor, width: 2.0), - borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), - primary: Provider.of(context).theme.backgroundMainColor, + borderRadius: const BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), ), onPressed: () { showAlertDialog(context); }, - icon: Icon(Icons.delete_forever), + icon: const Icon(Icons.delete_forever), label: Text(AppLocalizations.of(context)!.deleteBtn, style: TextStyle(color: Provider.of(context).theme.defaultButtonActiveColor)), ))) ])))))); @@ -467,7 +466,7 @@ class _AddEditProfileViewState extends State { } void _copyOnion() { - Clipboard.setData(new ClipboardData(text: Provider.of(context, listen: false).onion)); + Clipboard.setData(ClipboardData(text: Provider.of(context, listen: false).onion)); final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification)); ScaffoldMessenger.of(context).showSnackBar(snackBar); } @@ -547,7 +546,7 @@ class _AddEditProfileViewState extends State { const Duration(milliseconds: 500), () { if (globalErrorHandler.deleteProfileSuccess) { - final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.deleteProfileSuccess + ":" + onion)); + final snackBar = SnackBar(content: Text("${AppLocalizations.of(context)!.deleteProfileSuccess}:$onion")); ScaffoldMessenger.of(context).showSnackBar(snackBar); Navigator.of(context).popUntil((route) => route.isFirst); // dismiss dialog } else { diff --git a/lib/views/addeditservers.dart b/lib/views/addeditservers.dart index cba4fffd..f088b31c 100644 --- a/lib/views/addeditservers.dart +++ b/lib/views/addeditservers.dart @@ -1,11 +1,9 @@ -import 'dart:convert'; import 'package:cwtch/cwtch/cwtch.dart'; import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/servers.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'; @@ -13,11 +11,10 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../errorHandler.dart'; import '../main.dart'; -import '../config.dart'; /// Pane to add or edit a server class AddEditServerView extends StatefulWidget { - const AddEditServerView(); + const AddEditServerView({super.key}); @override _AddEditServerViewState createState() => _AddEditServerViewState(); @@ -83,8 +80,8 @@ class _AddEditServerViewState extends State { child: Form( key: _formKey, child: Container( - margin: EdgeInsets.fromLTRB(30, 5, 30, 10), - padding: EdgeInsets.fromLTRB(20, 5, 20, 10), + margin: const EdgeInsets.fromLTRB(30, 5, 30, 10), + padding: const EdgeInsets.fromLTRB(20, 5, 20, 10), child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Onion Visibility( @@ -96,12 +93,12 @@ class _AddEditServerViewState extends State { // Description Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.serverDescriptionLabel), Text(AppLocalizations.of(context)!.serverDescriptionDescription), - SizedBox( + const SizedBox( height: 20, ), CwtchTextField( @@ -111,7 +108,7 @@ class _AddEditServerViewState extends State { ) ]), - SizedBox( + const SizedBox( height: 20, ), @@ -143,7 +140,7 @@ class _AddEditServerViewState extends State { onChanged: (bool value) { serverInfoState.setAutostart(value); - if (!serverInfoState.onion.isEmpty) { + if (serverInfoState.onion.isNotEmpty) { Provider.of(context, listen: false).cwtch.SetServerAttribute(serverInfoState.onion, "autostart", value ? "true" : "false"); } }, @@ -156,7 +153,7 @@ class _AddEditServerViewState extends State { Visibility( visible: serverInfoState.onion.isNotEmpty && serverInfoState.running, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 20, ), Text( @@ -182,7 +179,7 @@ class _AddEditServerViewState extends State { Visibility( visible: serverInfoState.onion.isEmpty, child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox( + const SizedBox( height: 20, ), Checkbox( @@ -195,16 +192,16 @@ class _AddEditServerViewState extends State { AppLocalizations.of(context)!.radioUsePassword, style: TextStyle(color: settings.current().mainTextColor), ), - SizedBox( + const SizedBox( height: 20, ), Padding( - padding: EdgeInsets.symmetric(horizontal: 24), + padding: const EdgeInsets.symmetric(horizontal: 24), child: Text( usePassword ? AppLocalizations.of(context)!.encryptedServerDescription : AppLocalizations.of(context)!.plainServerDescription, textAlign: TextAlign.center, )), - SizedBox( + const SizedBox( height: 20, ), ])), @@ -214,12 +211,12 @@ class _AddEditServerViewState extends State { visible: serverInfoState.onion.isNotEmpty && serverInfoState.isEncrypted, child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ CwtchLabel(label: AppLocalizations.of(context)!.currentPasswordLabel), - SizedBox( + const SizedBox( height: 20, ), CwtchPasswordField( controller: ctrlrOldPass, - autoFillHints: [AutofillHints.newPassword], + autoFillHints: const [AutofillHints.newPassword], validator: (value) { // Password field can be empty when just updating the profile, not on creation if (serverInfoState.isEncrypted && serverInfoState.onion.isEmpty && value.isEmpty && usePassword) { @@ -231,7 +228,7 @@ class _AddEditServerViewState extends State { return null; }, ), - SizedBox( + const SizedBox( height: 20, ), ])), @@ -242,7 +239,7 @@ class _AddEditServerViewState extends State { visible: serverInfoState.onion.isEmpty && usePassword, child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ CwtchLabel(label: AppLocalizations.of(context)!.newPassword), - SizedBox( + const SizedBox( height: 20, ), CwtchPasswordField( @@ -258,11 +255,11 @@ class _AddEditServerViewState extends State { return null; }, ), - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.password2Label), - SizedBox( + const SizedBox( height: 20, ), CwtchPasswordField( @@ -280,7 +277,7 @@ class _AddEditServerViewState extends State { ]), ), - SizedBox( + const SizedBox( height: 20, ), @@ -301,7 +298,7 @@ class _AddEditServerViewState extends State { Visibility( visible: serverInfoState.onion.isNotEmpty, child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ - SizedBox( + const SizedBox( height: 20, ), Tooltip( @@ -310,7 +307,7 @@ class _AddEditServerViewState extends State { onPressed: () { showAlertDialog(context); }, - icon: Icon(Icons.delete_forever), + icon: const Icon(Icons.delete_forever), label: Text(AppLocalizations.of(context)!.deleteBtn), )) ])) @@ -364,7 +361,7 @@ class _AddEditServerViewState extends State { const Duration(milliseconds: 500), () { if (globalErrorHandler.deletedServerSuccess) { - final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.deleteServerSuccess + ":" + onion)); + final snackBar = SnackBar(content: Text("${AppLocalizations.of(context)!.deleteServerSuccess}:$onion")); ScaffoldMessenger.of(context).showSnackBar(snackBar); Navigator.of(context).popUntil((route) => route.settings.name == "servers"); // dismiss dialog } else { diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index 250eb502..d53f650e 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -19,7 +19,6 @@ import 'package:provider/provider.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../config.dart'; import '../main.dart'; -import '../models/message.dart'; import '../models/redaction.dart'; import '../settings.dart'; import '../themes/opaque.dart'; @@ -35,7 +34,7 @@ enum ShareMenu { copyCode, qrcode } enum ProfileStatusMenu { available, away, busy, appearOnline, appearOffline, allowUnknownContacts, blockUnknownContacts, enableProfile, disableProfile, editProfile } class ContactsView extends StatefulWidget { - const ContactsView({Key? key}) : super(key: key); + const ContactsView({super.key}); @override _ContactsViewState createState() => _ContactsViewState(); @@ -43,18 +42,18 @@ class ContactsView extends StatefulWidget { // selectConversation can be called from anywhere to set the active conversation void selectConversation(BuildContext context, int handle, int? messageIndex) { - int? index = null; + int? index; if (messageIndex != null) { // this message is loaded Provider.of(context, listen: false).selectedSearchMessage = messageIndex; - Provider.of(context, listen: false).initialScrollIndex = messageIndex!; + Provider.of(context, listen: false).initialScrollIndex = messageIndex; EnvironmentConfig.debugLog("Looked up index $messageIndex"); } if (handle == Provider.of(context, listen: false).selectedConversation) { if (messageIndex != null) { - Provider.of(context, listen: false).messageScrollController.scrollTo(index: messageIndex, duration: Duration(milliseconds: 100)); + Provider.of(context, listen: false).messageScrollController.scrollTo(index: messageIndex, duration: const Duration(milliseconds: 100)); } return; } @@ -89,7 +88,7 @@ void _pushMessageView(BuildContext context, int handle) { Navigator.of(context).push( PageRouteBuilder( - settings: RouteSettings(name: "messages"), + settings: const RouteSettings(name: "messages"), pageBuilder: (builderContext, a1, a2) { var profile = Provider.of(builderContext).profs.getProfile(profileOnion)!; return MultiProvider( @@ -97,11 +96,11 @@ void _pushMessageView(BuildContext context, int handle) { ChangeNotifierProvider.value(value: profile), ChangeNotifierProvider.value(value: profile.contactList.getContact(handle)!), ], - builder: (context, child) => MessageView(), + builder: (context, child) => const MessageView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } @@ -114,7 +113,7 @@ class _ContactsViewState extends State { @override void initState() { super.initState(); - ctrlrFilter = new TextEditingController(text: Provider.of(context, listen: false).filter); + ctrlrFilter = TextEditingController(text: Provider.of(context, listen: false).filter); } @override @@ -132,7 +131,7 @@ class _ContactsViewState extends State { Align( alignment: Alignment.center, child: IconButton( - icon: Icon(Icons.arrow_back), + icon: const Icon(Icons.arrow_back), tooltip: MaterialLocalizations.of(context).backButtonTooltip, onPressed: () { Provider.of(context, listen: false).recountUnread(); @@ -198,11 +197,11 @@ class _ContactsViewState extends State { providers: [ ChangeNotifierProvider.value(value: Provider.of(context, listen: false)), ], - builder: (context, widget) => AddEditProfileView(key: Key('addprofile')), + builder: (context, widget) => const AddEditProfileView(key: Key('addprofile')), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); break; @@ -238,12 +237,12 @@ class _ContactsViewState extends State { value: ProfileStatusMenu.available, enabled: enabled, child: Row(children: [ - Icon( + const Icon( CwtchIcons.account_circle_24px, color: Colors.white, ), Expanded( - child: Text(AppLocalizations.of(context)!.availabilityStatusAvailable!, + child: Text(AppLocalizations.of(context)!.availabilityStatusAvailable, textAlign: TextAlign.right, style: Provider.of(context, listen: false).scaleFonts(defaultTextButtonStyle))) ]), ), @@ -251,12 +250,12 @@ class _ContactsViewState extends State { value: ProfileStatusMenu.away, enabled: enabled, child: Row(children: [ - Icon( + const Icon( CwtchIcons.account_circle_24px, color: Colors.yellowAccent, ), Expanded( - child: Text(AppLocalizations.of(context)!.availabilityStatusAway!, + child: Text(AppLocalizations.of(context)!.availabilityStatusAway, textAlign: TextAlign.right, style: Provider.of(context, listen: false).scaleFonts(defaultTextButtonStyle))) ]), ), @@ -264,23 +263,23 @@ class _ContactsViewState extends State { value: ProfileStatusMenu.busy, enabled: enabled, child: Row(children: [ - Icon( + const Icon( CwtchIcons.account_circle_24px, color: Colors.redAccent, ), Expanded( - child: Text(AppLocalizations.of(context)!.availabilityStatusBusy!, + child: Text(AppLocalizations.of(context)!.availabilityStatusBusy, textAlign: TextAlign.right, style: Provider.of(context, listen: false).scaleFonts(defaultTextButtonStyle))) ]), ), - PopupMenuDivider(), + const PopupMenuDivider(), PopupMenuItem( value: ProfileStatusMenu.appearOffline, enabled: enabled && !appearOffline, child: Row(children: [ - Icon(CwtchIcons.disconnect_from_contact), + const Icon(CwtchIcons.disconnect_from_contact), Expanded( - child: Text(AppLocalizations.of(context)!.profileAppearOffline!, + child: Text(AppLocalizations.of(context)!.profileAppearOffline, textAlign: TextAlign.right, style: Provider.of(context, listen: false).scaleFonts(defaultTextButtonStyle))) ]), ), @@ -288,13 +287,13 @@ class _ContactsViewState extends State { value: ProfileStatusMenu.appearOnline, enabled: enabled && appearOffline, child: Row(children: [ - Icon(CwtchIcons.disconnect_from_contact), + const Icon(CwtchIcons.disconnect_from_contact), Expanded( - child: Text(AppLocalizations.of(context)!.profileAppearOnline!, + child: Text(AppLocalizations.of(context)!.profileAppearOnline, textAlign: TextAlign.right, style: Provider.of(context, listen: false).scaleFonts(defaultTextButtonStyle))) ]), ), - PopupMenuDivider(), + const PopupMenuDivider(), PopupMenuItem( value: !settings.blockUnknownConnections ? ProfileStatusMenu.blockUnknownContacts : ProfileStatusMenu.allowUnknownContacts, child: Row(children: [ @@ -303,17 +302,17 @@ class _ContactsViewState extends State { color: settings.theme.mainTextColor, ), Expanded( - child: Text((settings.blockUnknownConnections ? AppLocalizations.of(context)!.profileAllowUnknownContacts! : AppLocalizations.of(context)!.profileBlockUnknownContacts!), + child: Text((settings.blockUnknownConnections ? AppLocalizations.of(context)!.profileAllowUnknownContacts : AppLocalizations.of(context)!.profileBlockUnknownContacts), textAlign: TextAlign.right, style: Provider.of(context, listen: false).scaleFonts(defaultTextButtonStyle))) ]), ), - PopupMenuDivider(), + const PopupMenuDivider(), PopupMenuItem( value: enabled ? ProfileStatusMenu.disableProfile : ProfileStatusMenu.enableProfile, child: Row(children: [ Icon(CwtchIcons.favorite_24dp, color: settings.theme.mainTextColor), Expanded( - child: Text((enabled ? AppLocalizations.of(context)!.profileDisableProfile! : AppLocalizations.of(context)!.profileEnableProfile!), + child: Text((enabled ? AppLocalizations.of(context)!.profileDisableProfile : AppLocalizations.of(context)!.profileEnableProfile), textAlign: TextAlign.right, style: Provider.of(context, listen: false).scaleFonts(defaultTextButtonStyle))) ]), ), @@ -326,13 +325,12 @@ class _ContactsViewState extends State { color: settings.theme.mainTextColor, ), Expanded( - child: - Text(AppLocalizations.of(context)!.editProfile!, textAlign: TextAlign.right, style: Provider.of(context, listen: false).scaleFonts(defaultTextButtonStyle))) + child: Text(AppLocalizations.of(context)!.editProfile, textAlign: TextAlign.right, style: Provider.of(context, listen: false).scaleFonts(defaultTextButtonStyle))) ]), ), ], ), - SizedBox( + const SizedBox( width: 10, ), Expanded( @@ -363,26 +361,26 @@ class _ContactsViewState extends State { List getActions(context) { var actions = List.empty(growable: true); if (Provider.of(context).blockUnknownConnections) { - actions.add(Tooltip(message: AppLocalizations.of(context)!.blockUnknownConnectionsEnabledDescription, child: Icon(CwtchIcons.block_unknown))); + actions.add(Tooltip(message: AppLocalizations.of(context)!.blockUnknownConnectionsEnabledDescription, child: const Icon(CwtchIcons.block_unknown))); } if (Provider.of(context, listen: false).isExperimentEnabled(QRCodeExperiment)) { actions.add(PopupMenuButton( - icon: Icon(CwtchIcons.address_copy), + icon: const Icon(CwtchIcons.address_copy), tooltip: AppLocalizations.of(context)!.shareProfileMenuTooltop, splashRadius: Material.defaultSplashRadius / 2, onSelected: (ShareMenu item) { switch (item) { case ShareMenu.copyCode: { - Clipboard.setData(new ClipboardData(text: Provider.of(context, listen: false).onion)); + Clipboard.setData(ClipboardData(text: Provider.of(context, listen: false).onion)); final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification)); scaffoldKey.currentState?.showSnackBar(snackBar); } break; case ShareMenu.qrcode: { - _showQRCode("cwtch:" + Provider.of(context, listen: false).onion); + _showQRCode("cwtch:${Provider.of(context, listen: false).onion}"); } break; } @@ -400,11 +398,11 @@ class _ContactsViewState extends State { )); } else { actions.add(IconButton( - icon: Icon(CwtchIcons.address_copy), + icon: const Icon(CwtchIcons.address_copy), tooltip: AppLocalizations.of(context)!.copyAddress, splashRadius: Material.defaultSplashRadius / 2, onPressed: () { - Clipboard.setData(new ClipboardData(text: Provider.of(context, listen: false).onion)); + Clipboard.setData(ClipboardData(text: Provider.of(context, listen: false).onion)); final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification)); scaffoldKey.currentState?.showSnackBar(snackBar); })); @@ -413,7 +411,7 @@ class _ContactsViewState extends State { // Manage known Servers if (Provider.of(context, listen: false).isExperimentEnabled(TapirGroupsExperiment) || Provider.of(context, listen: false).isExperimentEnabled(ServerManagementExperiment)) { actions.add(IconButton( - icon: Icon(CwtchIcons.dns_24px), + icon: const Icon(CwtchIcons.dns_24px), tooltip: AppLocalizations.of(context)!.manageKnownServersButton, splashRadius: Material.defaultSplashRadius / 2, onPressed: () { @@ -447,7 +445,7 @@ class _ContactsViewState extends State { Provider.of(context, listen: false).filter = newVal; }, ); - return Column(children: [Padding(padding: EdgeInsets.all(8), child: txtfield), Expanded(child: _buildContactList())]); + return Column(children: [Padding(padding: const EdgeInsets.all(8), child: txtfield), Expanded(child: _buildContactList())]); } Widget _buildContactList() { @@ -485,7 +483,7 @@ class _ContactsViewState extends State { itemCount: tilesSearchResult.length, initialScrollIndex: initialScroll, shrinkWrap: true, - physics: BouncingScrollPhysics(), + physics: const BouncingScrollPhysics(), semanticChildCount: tilesSearchResult.length, itemBuilder: (context, index) { if (tilesSearchResult.length > index) { @@ -494,7 +492,7 @@ class _ContactsViewState extends State { return Container(); }, separatorBuilder: (BuildContext context, int index) { - return Divider(height: 1); + return const Divider(height: 1); }, ); @@ -507,7 +505,7 @@ class _ContactsViewState extends State { Navigator.of(context).push( PageRouteBuilder( - settings: RouteSettings(name: "addcontact"), + settings: const RouteSettings(name: "addcontact"), pageBuilder: (builderContext, a1, a2) { return MultiProvider( providers: [ @@ -517,7 +515,7 @@ class _ContactsViewState extends State { ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } @@ -527,15 +525,15 @@ class _ContactsViewState extends State { Navigator.of(context).push( PageRouteBuilder( - settings: RouteSettings(name: "profileremoteservers"), + settings: const RouteSettings(name: "profileremoteservers"), pageBuilder: (bcontext, a1, a2) { return MultiProvider( providers: [ChangeNotifierProvider.value(value: profileInfoState), Provider.value(value: Provider.of(context))], - child: ProfileServersView(), + child: const ProfileServersView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } @@ -550,17 +548,17 @@ class _ContactsViewState extends State { return Padding( padding: MediaQuery.of(context).viewInsets, child: RepaintBoundary( - child: Container( + child: SizedBox( height: Platform.isAndroid ? 250 : 200, // bespoke value courtesy of the [TextField] docs child: Center( child: Padding( - padding: EdgeInsets.all(2.0), + padding: const EdgeInsets.all(2.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ - SizedBox( + const SizedBox( height: 20, ), Expanded( @@ -568,9 +566,9 @@ class _ContactsViewState extends State { message: AppLocalizations.of(context)!.tooltipAddContact, child: ElevatedButton( style: ElevatedButton.styleFrom( - minimumSize: Size.fromWidth(399), - maximumSize: Size.fromWidth(400), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), + minimumSize: const Size.fromWidth(399), + maximumSize: const Size.fromWidth(400), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), ), child: Text( AppLocalizations.of(context)!.addContact, @@ -582,7 +580,7 @@ class _ContactsViewState extends State { _pushAddContact(false); }, ))), - SizedBox( + const SizedBox( height: 20, ), Expanded( @@ -590,24 +588,24 @@ class _ContactsViewState extends State { message: groupsEnabled ? AppLocalizations.of(context)!.addServerTooltip : AppLocalizations.of(context)!.thisFeatureRequiresGroupExpermientsToBeEnabled, child: ElevatedButton( style: ElevatedButton.styleFrom( - minimumSize: Size.fromWidth(399), - maximumSize: Size.fromWidth(400), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), - ), - child: Text( - AppLocalizations.of(context)!.addServerTitle, - semanticsLabel: AppLocalizations.of(context)!.addServerTitle, - textAlign: TextAlign.center, - style: TextStyle(fontFamily: "Inter", fontSize: 10.0 * Provider.of(context).fontScaling, fontWeight: FontWeight.bold), + minimumSize: const Size.fromWidth(399), + maximumSize: const Size.fromWidth(400), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), ), onPressed: groupsEnabled ? () { _pushAddContact(false); } : null, + child: Text( + AppLocalizations.of(context)!.addServerTitle, + semanticsLabel: AppLocalizations.of(context)!.addServerTitle, + textAlign: TextAlign.center, + style: TextStyle(fontFamily: "Inter", fontSize: 10.0 * Provider.of(context).fontScaling, fontWeight: FontWeight.bold), + ), )), ), - SizedBox( + const SizedBox( height: 20, ), Expanded( @@ -615,23 +613,23 @@ class _ContactsViewState extends State { message: groupsEnabled ? AppLocalizations.of(context)!.createGroupTitle : AppLocalizations.of(context)!.thisFeatureRequiresGroupExpermientsToBeEnabled, child: ElevatedButton( style: ElevatedButton.styleFrom( - minimumSize: Size.fromWidth(399), - maximumSize: Size.fromWidth(400), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), - ), - child: Text( - AppLocalizations.of(context)!.createGroupTitle, - semanticsLabel: AppLocalizations.of(context)!.createGroupTitle, - textAlign: TextAlign.center, - style: TextStyle(fontFamily: "Inter", fontSize: 10.0 * Provider.of(context).fontScaling, fontWeight: FontWeight.bold), + minimumSize: const Size.fromWidth(399), + maximumSize: const Size.fromWidth(400), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), ), onPressed: groupsEnabled ? () { _pushAddContact(true); } : null, + child: Text( + AppLocalizations.of(context)!.createGroupTitle, + semanticsLabel: AppLocalizations.of(context)!.createGroupTitle, + textAlign: TextAlign.center, + style: TextStyle(fontFamily: "Inter", fontSize: 10.0 * Provider.of(context).fontScaling, fontWeight: FontWeight.bold), + ), ))), - SizedBox( + const SizedBox( height: 20, ), ], @@ -640,14 +638,14 @@ class _ContactsViewState extends State { }); } - void _showQRCode(String profile_code) { + void _showQRCode(String profileCode) { showModalBottomSheet( context: context, builder: (BuildContext context) { return Wrap(children: [ Center( child: QrImageView( - data: profile_code, + data: profileCode, version: QrVersions.auto, size: 400.0, backgroundColor: Provider.of(context).theme.backgroundPaneColor, diff --git a/lib/views/doublecolview.dart b/lib/views/doublecolview.dart index 3d9ae778..ef2601e1 100644 --- a/lib/views/doublecolview.dart +++ b/lib/views/doublecolview.dart @@ -4,12 +4,13 @@ import 'package:cwtch/models/profile.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import '../main.dart'; import '../settings.dart'; import 'contactsview.dart'; import 'messageview.dart'; class DoubleColumnView extends StatefulWidget { + const DoubleColumnView({super.key}); + @override _DoubleColumnViewState createState() => _DoubleColumnViewState(); } @@ -35,8 +36,8 @@ class _DoubleColumnViewState extends State { ? Container( color: Provider.of(context).theme.backgroundMainColor, child: Card( - margin: EdgeInsets.all(0.0), - shape: new RoundedRectangleBorder(side: new BorderSide(color: Provider.of(context).theme.defaultButtonColor, width: 4.0), borderRadius: BorderRadius.circular(4.0)), + margin: const EdgeInsets.all(0.0), + shape: RoundedRectangleBorder(side: BorderSide(color: Provider.of(context).theme.defaultButtonColor, width: 4.0), borderRadius: BorderRadius.circular(4.0)), child: Center(child: Text(AppLocalizations.of(context)!.addContactFirst)))) : //dev MultiProvider(providers: [ @@ -44,7 +45,7 @@ class _DoubleColumnViewState extends State { // there is a potential timing issue here where selectConversation is changes as we move profiles, this will result // in getContact being null, in that case we replace with an empty Contact Info State ChangeNotifierProvider.value(value: Provider.of(context).contactList.getContact(selectedConversation) ?? ContactInfoState("", -1, "")), - ], child: Container(key: Key(selectedConversation.toString()), child: MessageView())), + ], child: Container(key: Key(selectedConversation.toString()), child: const MessageView())), ), ], ); diff --git a/lib/views/filesharingview.dart b/lib/views/filesharingview.dart index f368d3bc..9ceeed46 100644 --- a/lib/views/filesharingview.dart +++ b/lib/views/filesharingview.dart @@ -12,6 +12,8 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../cwtch_icons_icons.dart'; class FileSharingView extends StatefulWidget { + const FileSharingView({super.key}); + @override _FileSharingViewState createState() => _FileSharingViewState(); } @@ -28,7 +30,7 @@ class _FileSharingViewState extends State { return Scaffold( appBar: AppBar( - title: Text(handle + " » " + AppLocalizations.of(context)!.manageSharedFiles), + title: Text("$handle » ${AppLocalizations.of(context)!.manageSharedFiles}"), ), body: FutureBuilder( future: Provider.of(context, listen: false).cwtch.GetSharedFiles(profileHandle, Provider.of(context).identifier), @@ -44,7 +46,7 @@ class _FileSharingViewState extends State { itemCount: sharedFiles.length, shrinkWrap: true, reverse: true, - physics: BouncingScrollPhysics(), + physics: const BouncingScrollPhysics(), semanticChildCount: sharedFiles.length, itemBuilder: (context, index) { String filekey = sharedFiles[index]["FileKey"]; @@ -69,7 +71,7 @@ class _FileSharingViewState extends State { }); }, separatorBuilder: (BuildContext context, int index) { - return Divider(height: 1); + return const Divider(height: 1); }, ); return fileList; diff --git a/lib/views/globalsettingsaboutview.dart b/lib/views/globalsettingsaboutview.dart index ccf8919c..e963ae68 100644 --- a/lib/views/globalsettingsaboutview.dart +++ b/lib/views/globalsettingsaboutview.dart @@ -10,10 +10,11 @@ import '../config.dart'; import '../cwtch_icons_icons.dart'; import '../main.dart'; import '../settings.dart'; -import '../themes/opaque.dart'; import 'globalsettingsview.dart'; class GlobalSettingsAboutView extends StatefulWidget { + const GlobalSettingsAboutView({super.key}); + @override _GlobalSettingsAboutViewState createState() => _GlobalSettingsAboutViewState(); } @@ -21,18 +22,19 @@ class GlobalSettingsAboutView extends StatefulWidget { class _GlobalSettingsAboutViewState extends State { ScrollController settingsListScrollController = ScrollController(); + @override Widget build(BuildContext context) { return Consumer(builder: (ccontext, settings, child) { return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { var appIcon = Icon(Icons.info, color: settings.current().mainTextColor); return Scrollbar( - key: Key("AboutSettingsView"), + key: const Key("AboutSettingsView"), trackVisibility: true, controller: settingsListScrollController, child: SingleChildScrollView( clipBehavior: Clip.antiAlias, controller: settingsListScrollController, - padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20), + padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 20), child: ConstrainedBox( constraints: BoxConstraints( minHeight: viewportConstraints.maxHeight, @@ -40,7 +42,7 @@ class _GlobalSettingsAboutViewState extends State { child: Column(children: [ AboutListTile( icon: appIcon, - applicationIcon: Padding(padding: EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)), + applicationIcon: const Padding(padding: EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)), applicationName: "Cwtch UI", applicationLegalese: '\u{a9} 2021-2023 Open Privacy Research Society', aboutBoxChildren: [ @@ -52,8 +54,8 @@ class _GlobalSettingsAboutViewState extends State { ]), SwitchListTile( // TODO: Translate, Remove, OR Hide Prior to Release - title: Text("Show Performance Overlay"), - subtitle: Text("Display an overlay graph of render time."), + title: const Text("Show Performance Overlay"), + subtitle: const Text("Display an overlay graph of render time."), value: settings.profileMode, onChanged: (bool value) { setState(() { @@ -71,8 +73,8 @@ class _GlobalSettingsAboutViewState extends State { Visibility( visible: EnvironmentConfig.BUILD_VER == dev_version && !Platform.isAndroid, child: SwitchListTile( - title: Text("Show Semantic Debugger"), - subtitle: Text("Show Accessibility Debugging View"), + title: const Text("Show Semantic Debugger"), + subtitle: const Text("Show Accessibility Debugging View"), value: settings.useSemanticDebugger, onChanged: (bool value) { if (value) { @@ -93,10 +95,7 @@ class _GlobalSettingsAboutViewState extends State { builder: (context, snapshot) { if (snapshot.hasData) { return Column( - children: [ - Text("libCwtch Debug Info: " + snapshot.data.toString()), - Text("Message Cache Size (Mb): " + (Provider.of(context).profs.cacheMemUsage() / (1024 * 1024)).toString()) - ], + children: [Text("libCwtch Debug Info: ${snapshot.data}"), Text("Message Cache Size (Mb): ${Provider.of(context).profs.cacheMemUsage() / (1024 * 1024)}")], ); } else { return Container(); @@ -124,13 +123,13 @@ class _GlobalSettingsAboutViewState extends State { var sortedKeys = platformChannelInfo.keys.toList(); sortedKeys.sort(); var widgets = List.empty(growable: true); - sortedKeys.forEach((element) { + for (var element in sortedKeys) { widgets.add(ListTile( leading: Icon(Icons.android, color: settings.current().mainTextColor), title: Text(element), subtitle: Text(platformChannelInfo[element]!), )); - }); + } return Column( children: widgets, ); @@ -142,6 +141,6 @@ class _GlobalSettingsAboutViewState extends State { if (pinfo == null) { return ""; } - return pinfo.version + "." + pinfo.buildNumber; + return "${pinfo.version}.${pinfo.buildNumber}"; } } diff --git a/lib/views/globalsettingsappearanceview.dart b/lib/views/globalsettingsappearanceview.dart index cf31d9e0..1daef9f0 100644 --- a/lib/views/globalsettingsappearanceview.dart +++ b/lib/views/globalsettingsappearanceview.dart @@ -14,10 +14,11 @@ import '../settings.dart'; import '../themes/cwtch.dart'; import '../themes/opaque.dart'; import '../themes/yamltheme.dart'; -import '../widgets/folderpicker.dart'; import 'globalsettingsview.dart'; class GlobalSettingsAppearanceView extends StatefulWidget { + const GlobalSettingsAppearanceView({super.key}); + @override _GlobalSettingsAppearanceViewState createState() => _GlobalSettingsAppearanceViewState(); } @@ -25,17 +26,18 @@ class GlobalSettingsAppearanceView extends StatefulWidget { class _GlobalSettingsAppearanceViewState extends State { ScrollController settingsListScrollController = ScrollController(); + @override Widget build(BuildContext context) { return Consumer(builder: (ccontext, settings, child) { return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { return Scrollbar( - key: Key("AppearanceSettingsView"), + key: const Key("AppearanceSettingsView"), trackVisibility: true, controller: settingsListScrollController, child: SingleChildScrollView( clipBehavior: Clip.antiAlias, controller: settingsListScrollController, - padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20), + padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 20), child: ConstrainedBox( constraints: BoxConstraints( minHeight: viewportConstraints.maxHeight, @@ -44,10 +46,10 @@ class _GlobalSettingsAppearanceViewState extends State(context).locale.toString(), onChanged: (String? newValue) { @@ -61,7 +63,7 @@ class _GlobalSettingsAppearanceViewState extends State( value: value.toString(), child: Text( - key: Key("dropdownLanguage" + value.languageCode), + key: Key("dropdownLanguage${value.languageCode}"), getLanguageFull(context, value.languageCode, value.countryCode), style: settings.scaleFonts(defaultDropDownMenuItemTextStyle), overflow: TextOverflow.ellipsis, @@ -87,10 +89,10 @@ class _GlobalSettingsAppearanceViewState extends State( - key: Key("DropdownTheme"), + key: const Key("DropdownTheme"), isExpanded: true, value: Provider.of(context).theme.theme, onChanged: (String? newValue) { @@ -117,7 +119,7 @@ class _GlobalSettingsAppearanceViewState extends State[ Text(AppLocalizations.of(context)!.settingThemeOverwriteQuestion.replaceAll("\$themeName", themeName)), - SizedBox( + const SizedBox( height: 20, ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Spacer(), + const Spacer(), Expanded( child: ElevatedButton( child: Text(AppLocalizations.of(context)!.settingThemeOverwriteConfirm, semanticsLabel: AppLocalizations.of(context)!.settingThemeOverwriteConfirm), @@ -432,7 +434,7 @@ class _GlobalSettingsAppearanceViewState extends State _GlobalSettingsBehaviourViewState(); } class _GlobalSettingsBehaviourViewState extends State { - static const androidSettingsChannel = const MethodChannel('androidSettings'); - static const androidSettingsChangeChannel = const MethodChannel('androidSettingsChanged'); + static const androidSettingsChannel = MethodChannel('androidSettings'); + static const androidSettingsChangeChannel = MethodChannel('androidSettingsChanged'); ScrollController settingsListScrollController = ScrollController(); bool powerExempt = false; @@ -60,17 +61,18 @@ class _GlobalSettingsBehaviourViewState extends State(builder: (ccontext, settings, child) { return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { return Scrollbar( - key: Key("BehaviourSettingsView"), + key: const Key("BehaviourSettingsView"), trackVisibility: true, controller: settingsListScrollController, child: SingleChildScrollView( clipBehavior: Clip.antiAlias, controller: settingsListScrollController, - padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20), + padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 20), child: ConstrainedBox( constraints: BoxConstraints( minHeight: viewportConstraints.maxHeight, @@ -98,7 +100,7 @@ class _GlobalSettingsBehaviourViewState extends State _GlobalSettingsExperimentsViewState(); } @@ -21,17 +22,18 @@ class GlobalSettingsExperimentsView extends StatefulWidget { class _GlobalSettingsExperimentsViewState extends State { ScrollController settingsListScrollController = ScrollController(); + @override Widget build(BuildContext context) { return Consumer(builder: (ccontext, settings, child) { return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { return Scrollbar( - key: Key("ExperimentsSettingsView"), + key: const Key("ExperimentsSettingsView"), trackVisibility: true, controller: settingsListScrollController, child: SingleChildScrollView( clipBehavior: Clip.antiAlias, controller: settingsListScrollController, - padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20), + padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 20), child: ConstrainedBox( constraints: BoxConstraints( minHeight: viewportConstraints.maxHeight, @@ -81,7 +83,7 @@ class _GlobalSettingsExperimentsViewState extends State(context, listen: false).cwtch.IsServersCompiled() ? Text(AppLocalizations.of(context)!.settingServersDescription) - : Text("This version of Cwtch has been compiled without support for the server hosting experiment."), + : const Text("This version of Cwtch has been compiled without support for the server hosting experiment."), value: Provider.of(context, listen: false).cwtch.IsServersCompiled() && settings.isExperimentEnabled(ServerManagementExperiment), onChanged: Provider.of(context, listen: false).cwtch.IsServersCompiled() ? (bool value) { @@ -98,7 +100,7 @@ class _GlobalSettingsExperimentsViewState extends State(context, listen: false).cwtch.IsBlodeuweddSupported() && settings.isExperimentEnabled(BlodeuweddExperiment), child: CwtchFolderPicker( - testKey: Key("DownloadFolderPicker"), + testKey: const Key("DownloadFolderPicker"), label: AppLocalizations.of(context)!.settingDownloadFolder, initialValue: settings.blodeuweddPath, description: AppLocalizations.of(context)!.blodeuweddPath, diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index eaf78bb2..8b67d70b 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -1,27 +1,20 @@ -import 'dart:collection'; import 'dart:convert'; -import 'dart:io'; import 'package:cwtch/cwtch_icons_icons.dart'; -import 'package:cwtch/models/servers.dart'; import 'package:cwtch/views/globalsettingsaboutview.dart'; import 'package:cwtch/views/globalsettingsappearanceview.dart'; import 'package:cwtch/views/globalsettingsbehaviourview.dart'; import 'package:cwtch/views/globalsettingsexperimentsview.dart'; -import 'package:cwtch/widgets/folderpicker.dart'; -import 'package:cwtch/themes/cwtch.dart'; -import 'package:cwtch/themes/opaque.dart'; -import 'package:flutter/services.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 '../main.dart'; -import '../config.dart'; /// Global Settings View provides access to modify all the Globally Relevant Settings including Locale, Theme and Experiments. class GlobalSettingsView extends StatefulWidget { + const GlobalSettingsView({super.key}); + @override _GlobalSettingsViewState createState() => _GlobalSettingsViewState(); } @@ -44,10 +37,10 @@ class _GlobalSettingsViewState extends State { bottom: TabBar( isScrollable: true, tabs: [ - Tab(key: Key("OpenSettingsAppearance"), icon: Icon(Icons.palette), text: AppLocalizations.of(context)!.settingsGroupAppearance), - Tab(key: Key("OpenSettingsBehaviour"), icon: Icon(Icons.settings), text: AppLocalizations.of(context)!.settingGroupBehaviour), - Tab(key: Key("OpenSettingsExperiments"), icon: Icon(CwtchIcons.enable_experiments), text: AppLocalizations.of(context)!.settingsGroupExperiments), - Tab(icon: Icon(Icons.info), text: AppLocalizations.of(context)!.settingsGroupAbout), + Tab(key: const Key("OpenSettingsAppearance"), icon: const Icon(Icons.palette), text: AppLocalizations.of(context)!.settingsGroupAppearance), + Tab(key: const Key("OpenSettingsBehaviour"), icon: const Icon(Icons.settings), text: AppLocalizations.of(context)!.settingGroupBehaviour), + Tab(key: const Key("OpenSettingsExperiments"), icon: const Icon(CwtchIcons.enable_experiments), text: AppLocalizations.of(context)!.settingsGroupExperiments), + Tab(icon: const Icon(Icons.info), text: AppLocalizations.of(context)!.settingsGroupAbout), ], )), body: _buildSettingsList(), @@ -57,7 +50,7 @@ class _GlobalSettingsViewState extends State { Widget _buildSettingsList() { return Consumer(builder: (ccontext, settings, child) { return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { - return TabBarView(children: [ + return const TabBarView(children: [ GlobalSettingsAppearanceView(), GlobalSettingsBehaviourView(), GlobalSettingsExperimentsView(), diff --git a/lib/views/groupsettingsview.dart b/lib/views/groupsettingsview.dart index 1334ee41..a41dc75b 100644 --- a/lib/views/groupsettingsview.dart +++ b/lib/views/groupsettingsview.dart @@ -16,6 +16,8 @@ import '../main.dart'; /// Group Settings View Provides way to Configure group settings class GroupSettingsView extends StatefulWidget { + const GroupSettingsView({super.key}); + @override _GroupSettingsViewState createState() => _GroupSettingsViewState(); } @@ -50,8 +52,8 @@ class _GroupSettingsViewState extends State { title: Container( height: Provider.of(context).fontScaling * 24.0, clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(), - child: Text(Provider.of(context).nickname + " " + AppLocalizations.of(context)!.conversationSettings)), + decoration: const BoxDecoration(), + child: Text("${Provider.of(context).nickname} ${AppLocalizations.of(context)!.conversationSettings}")), ), body: _buildSettingsList(), ); @@ -71,16 +73,16 @@ class _GroupSettingsViewState extends State { minHeight: viewportConstraints.maxHeight, ), child: Container( - margin: EdgeInsets.all(10), - padding: EdgeInsets.all(2), + margin: const EdgeInsets.all(10), + padding: const EdgeInsets.all(2), child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Nickname Save Button Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.displayNameLabel), - SizedBox( + const SizedBox( height: 20, ), CwtchButtonTextField( @@ -92,34 +94,36 @@ class _GroupSettingsViewState extends State { Provider.of(context, listen: false).nickname = ctrlrNick.text; Provider.of(context, listen: false).cwtch.SetConversationAttribute(profileOnion, handle, "profile.name", ctrlrNick.text); // todo translations - final snackBar = SnackBar(content: Text("Group Nickname changed successfully")); + const snackBar = SnackBar(content: Text("Group Nickname changed successfully")); ScaffoldMessenger.of(context).showSnackBar(snackBar); }, - icon: Icon(Icons.save), + icon: const Icon(Icons.save), tooltip: AppLocalizations.of(context)!.saveBtn, ) ]), Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.server), - SizedBox( + const SizedBox( height: 20, ), CwtchTextField( controller: TextEditingController(text: Provider.of(context, listen: false).server), - validator: (value) {}, + validator: (value) { + return null; + }, hintText: '', ) ]), Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.conversationSettings), - SizedBox( + const SizedBox( height: 20, ), ListTile( @@ -145,7 +149,7 @@ class _GroupSettingsViewState extends State { ]), Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ - SizedBox( + const SizedBox( height: 20, ), Tooltip( @@ -157,15 +161,15 @@ class _GroupSettingsViewState extends State { // locally update cache... Provider.of(context, listen: false).isArchived = true; Provider.of(context, listen: false).cwtch.ArchiveConversation(profileOnion, handle); - Future.delayed(Duration(milliseconds: 500), () { + Future.delayed(const Duration(milliseconds: 500), () { Provider.of(context, listen: false).selectedConversation = null; Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog }); }, - icon: Icon(CwtchIcons.leave_chat), + icon: const Icon(CwtchIcons.leave_chat), label: Text(AppLocalizations.of(context)!.archiveConversation), )), - SizedBox( + const SizedBox( height: 20, ), Row(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -178,7 +182,7 @@ class _GroupSettingsViewState extends State { style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.backgroundPaneColor), foregroundColor: MaterialStateProperty.all(Provider.of(context).theme.mainTextColor)), - icon: Icon(CwtchIcons.leave_group), + icon: const Icon(CwtchIcons.leave_group), label: Text( AppLocalizations.of(context)!.leaveConversation, style: settings.scaleFonts(defaultTextButtonStyle.copyWith(decoration: TextDecoration.underline)), @@ -192,7 +196,7 @@ class _GroupSettingsViewState extends State { } void _copyOnion() { - Clipboard.setData(new ClipboardData(text: Provider.of(context, listen: false).onion)); + Clipboard.setData(ClipboardData(text: Provider.of(context, listen: false).onion)); final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification)); ScaffoldMessenger.of(context).showSnackBar(snackBar); } @@ -206,7 +210,7 @@ class _GroupSettingsViewState extends State { }, ); Widget continueButton = ElevatedButton( - style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))), + style: ButtonStyle(padding: MaterialStateProperty.all(const EdgeInsets.all(20))), child: Text(AppLocalizations.of(context)!.yesLeave), onPressed: () { var profileOnion = Provider.of(context, listen: false).profileOnion; @@ -215,7 +219,7 @@ class _GroupSettingsViewState extends State { Provider.of(context, listen: false).isArchived = true; Provider.of(context, listen: false).contactList.removeContact(identifier); Provider.of(context, listen: false).cwtch.DeleteContact(profileOnion, identifier); - Future.delayed(Duration(milliseconds: 500), () { + Future.delayed(const Duration(milliseconds: 500), () { Provider.of(context, listen: false).selectedConversation = null; Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog }); diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index 4de4508d..18c1a8ee 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; -import 'dart:ui'; import 'package:crypto/crypto.dart'; import 'package:cwtch/cwtch/cwtch.dart'; import 'package:cwtch/cwtch_icons_icons.dart'; @@ -37,6 +36,8 @@ import 'filesharingview.dart'; import 'groupsettingsview.dart'; class MessageView extends StatefulWidget { + const MessageView({super.key}); + @override _MessageViewState createState() => _MessageViewState(); } @@ -52,14 +53,14 @@ class _MessageViewState extends State { @override void initState() { scrollListener.itemPositions.addListener(() { - if (scrollListener.itemPositions.value.length != 0 && + if (scrollListener.itemPositions.value.isNotEmpty && Provider.of(context, listen: false).unreadMessagesBelow == true && scrollListener.itemPositions.value.any((element) => element.index == 0)) { Provider.of(context, listen: false).initialScrollIndex = 0; Provider.of(context, listen: false).unreadMessagesBelow = false; } - if (scrollListener.itemPositions.value.length != 0 && !scrollListener.itemPositions.value.any((element) => element.index == 0)) { + if (scrollListener.itemPositions.value.isNotEmpty && !scrollListener.itemPositions.value.any((element) => element.index == 0)) { showDown = true; } else { showDown = false; @@ -100,13 +101,13 @@ class _MessageViewState extends State { if (showFileSharing) { appBarButtons.add(IconButton( - splashRadius: Material.defaultSplashRadius / 2, icon: Icon(CwtchIcons.manage_files), tooltip: AppLocalizations.of(context)!.manageSharedFiles, onPressed: _pushFileSharingSettings)); + splashRadius: Material.defaultSplashRadius / 2, icon: const Icon(CwtchIcons.manage_files), tooltip: AppLocalizations.of(context)!.manageSharedFiles, onPressed: _pushFileSharingSettings)); } if (Provider.of(context, listen: false).isOnline()) { appBarButtons.add(IconButton( splashRadius: Material.defaultSplashRadius / 2, - icon: Icon(CwtchIcons.disconnect_from_contact), + icon: const Icon(CwtchIcons.disconnect_from_contact), tooltip: AppLocalizations.of(context)!.contactDisconnect, onPressed: () { if (Provider.of(context, listen: false).isGroup) { @@ -119,7 +120,7 @@ class _MessageViewState extends State { .DisconnectFromPeer(Provider.of(context, listen: false).onion, Provider.of(context, listen: false).onion); } // reset the disconnect button to allow for immediate connection... - Provider.of(context, listen: false).lastRetryTime = DateTime.now().subtract(Duration(minutes: 2)); + Provider.of(context, listen: false).lastRetryTime = DateTime.now().subtract(const Duration(minutes: 2)); Provider.of(context, listen: false).contactEvents.add(ContactEvent("Disconnect from Peer")); })); } @@ -130,7 +131,7 @@ class _MessageViewState extends State { if (Provider.of(context, listen: false).cwtch.IsBlodeuweddSupported() && Provider.of(context).isExperimentEnabled(BlodeuweddExperiment)) { appBarButtons.add(IconButton( splashRadius: Material.defaultSplashRadius / 2, - icon: Icon(Icons.summarize), + icon: const Icon(Icons.summarize), tooltip: AppLocalizations.of(context)!.blodeuweddSummarize, onPressed: () async { Provider.of(context, listen: false).summary = ""; @@ -155,7 +156,7 @@ class _MessageViewState extends State { }, () { final snackBar = SnackBar( content: Text(AppLocalizations.of(context)!.msgFileTooBig), - duration: Duration(seconds: 4), + duration: const Duration(seconds: 4), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); }, () {}); @@ -165,7 +166,7 @@ class _MessageViewState extends State { appBarButtons.add(IconButton( splashRadius: Material.defaultSplashRadius / 2, - icon: Icon(CwtchIcons.send_invite, size: 24), + icon: const Icon(CwtchIcons.send_invite, size: 24), tooltip: AppLocalizations.of(context)!.sendInvite, onPressed: () { _modalSendInvitation(context); @@ -173,7 +174,7 @@ class _MessageViewState extends State { } appBarButtons.add(IconButton( splashRadius: Material.defaultSplashRadius / 2, - icon: Provider.of(context, listen: false).isGroup == true ? Icon(CwtchIcons.group_settings_24px) : Icon(CwtchIcons.peer_settings_24px), + icon: Provider.of(context, listen: false).isGroup == true ? const Icon(CwtchIcons.group_settings_24px) : const Icon(CwtchIcons.peer_settings_24px), tooltip: AppLocalizations.of(context)!.conversationSettings, onPressed: _pushContactSettings)); @@ -185,12 +186,12 @@ class _MessageViewState extends State { floatingActionButton: showDown ? FloatingActionButton( // heroTags need to be unique per screen (important when we pop up and down)... - heroTag: "popDown" + Provider.of(context, listen: false).onion, + heroTag: "popDown${Provider.of(context, listen: false).onion}", child: Icon(Icons.arrow_downward, color: Provider.of(context).current().defaultButtonTextColor), onPressed: () { Provider.of(context, listen: false).initialScrollIndex = 0; Provider.of(context, listen: false).unreadMessagesBelow = false; - Provider.of(context, listen: false).messageScrollController.scrollTo(index: 0, duration: Duration(milliseconds: 600)); + Provider.of(context, listen: false).messageScrollController.scrollTo(index: 0, duration: const Duration(milliseconds: 600)); }) : null, appBar: AppBar( @@ -239,14 +240,14 @@ class _MessageViewState extends State { size: 14.0, ))) : null), - SizedBox( + const SizedBox( width: 10, ), Expanded( child: Container( height: 42, clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), child: Align( alignment: Alignment.centerLeft, child: Text( @@ -259,7 +260,7 @@ class _MessageViewState extends State { actions: appBarButtons, ), body: Padding( - padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 182.0), + padding: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 182.0), child: MessageList( scrollListener, )), @@ -287,11 +288,11 @@ class _MessageViewState extends State { pageBuilder: (builderContext, a1, a2) { return MultiProvider( providers: [ChangeNotifierProvider.value(value: profileInfoState), ChangeNotifierProvider.value(value: contactInfoState)], - child: FileSharingView(), + child: const FileSharingView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } @@ -306,11 +307,11 @@ class _MessageViewState extends State { pageBuilder: (builderContext, a1, a2) { return MultiProvider( providers: [ChangeNotifierProvider.value(value: profileInfoState), ChangeNotifierProvider.value(value: contactInfoState)], - child: GroupSettingsView(), + child: const GroupSettingsView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } else { @@ -319,11 +320,11 @@ class _MessageViewState extends State { pageBuilder: (builderContext, a1, a2) { return MultiProvider( providers: [ChangeNotifierProvider.value(value: profileInfoState), ChangeNotifierProvider.value(value: contactInfoState)], - child: PeerSettingsView(), + child: const PeerSettingsView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } @@ -345,7 +346,7 @@ class _MessageViewState extends State { var attachedInvite = Provider.of(context, listen: false).messageDraft.getInviteHandle(); if (attachedInvite != null) { - this._sendInvitation(attachedInvite); + _sendInvitation(attachedInvite); } // Trim message @@ -368,18 +369,18 @@ class _MessageViewState extends State { var digest1 = sha256.convert(bytes1); var contentHash = base64Encode(digest1.bytes); var quotedMessage = jsonEncode(QuotedMessageStructure(contentHash, messageWithoutNewLine)); - ChatMessage cm = new ChatMessage(o: QuotedMessageOverlay, d: quotedMessage); + ChatMessage cm = ChatMessage(o: QuotedMessageOverlay, d: quotedMessage); Provider.of(context, listen: false) .cwtch .SendMessage(Provider.of(context, listen: false).profileOnion, Provider.of(context, listen: false).identifier, jsonEncode(cm)) .then(_sendMessageHandler); } catch (e) { - EnvironmentConfig.debugLog("Exception: reply to message could not be found: " + e.toString()); + EnvironmentConfig.debugLog("Exception: reply to message could not be found: $e"); } Provider.of(context, listen: false).messageDraft.clearQuotedReference(); }); } else { - ChatMessage cm = new ChatMessage(o: TextMessageOverlay, d: messageWithoutNewLine); + ChatMessage cm = ChatMessage(o: TextMessageOverlay, d: messageWithoutNewLine); Provider.of(context, listen: false) .cwtch .SendMessage(Provider.of(context, listen: false).profileOnion, Provider.of(context, listen: false).identifier, jsonEncode(cm)) @@ -442,14 +443,14 @@ class _MessageViewState extends State { return Wrap(children: [ SelectableText( - chrome + '\u202F', + '$chrome\u202F', style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of(context).theme.messageFromMeTextColor)), textAlign: TextAlign.left, maxLines: 2, textWidthBasis: TextWidthBasis.longestLine, ), SelectableText( - targetName + '\u202F', + '$targetName\u202F', style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of(context).theme.messageFromMeTextColor)), textAlign: TextAlign.left, maxLines: 2, @@ -462,11 +463,11 @@ class _MessageViewState extends State { var showClickableLinks = Provider.of(context).isExperimentEnabled(ClickableLinksExperiment); var wdgMessage = Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: SelectableLinkify( - text: Provider.of(context).messageDraft.messageText + '\n', + text: '${Provider.of(context).messageDraft.messageText}\n', options: LinkifyOptions(messageFormatting: true, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true), - linkifiers: [UrlLinkifier()], + linkifiers: const [UrlLinkifier()], onOpen: showClickableLinks ? null : null, style: TextStyle( color: Provider.of(context).theme.messageFromMeTextColor, @@ -488,14 +489,14 @@ class _MessageViewState extends State { backgroundColor: Provider.of(context).theme.messageFromOtherBackgroundColor), textAlign: TextAlign.left, textWidthBasis: TextWidthBasis.longestLine, - constraints: BoxConstraints.expand(), + constraints: const BoxConstraints.expand(), )); var showMessageFormattingPreview = Provider.of(context).isExperimentEnabled(FormattingExperiment); var preview = showMessageFormattingPreview ? IconButton( tooltip: AppLocalizations.of(context)!.tooltipBackToMessageEditing, - icon: Icon(Icons.text_fields), + icon: const Icon(Icons.text_fields), onPressed: () { setState(() { showPreview = false; @@ -505,8 +506,8 @@ class _MessageViewState extends State { var composeBox = Container( color: Provider.of(context).theme.backgroundMainColor, - padding: EdgeInsets.all(2), - margin: EdgeInsets.all(2), + padding: const EdgeInsets.all(2), + margin: const EdgeInsets.all(2), // 164 minimum height + 16px for every line of text so the entire message is displayed when previewed. height: 164 + ((Provider.of(context).messageDraft.messageText.split("\n").length - 1) * 16), @@ -534,7 +535,7 @@ class _MessageViewState extends State { var numberOfBytesMoreThanChar = (expectedLength - charLength); var bold = IconButton( - icon: Icon(Icons.format_bold), + icon: const Icon(Icons.format_bold), tooltip: AppLocalizations.of(context)!.tooltipBoldText, onPressed: () { setState(() { @@ -543,14 +544,14 @@ class _MessageViewState extends State { var selection = ctrlrCompose.selection; var start = ctrlrCompose.selection.start; var end = ctrlrCompose.selection.end; - ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "**" + selected + "**"); + ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "**$selected**"); ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 2, extentOffset: selection.start + 2); Provider.of(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose; }); }); var italic = IconButton( - icon: Icon(Icons.format_italic), + icon: const Icon(Icons.format_italic), tooltip: AppLocalizations.of(context)!.tooltipItalicize, onPressed: () { setState(() { @@ -559,14 +560,14 @@ class _MessageViewState extends State { var selection = ctrlrCompose.selection; var start = ctrlrCompose.selection.start; var end = ctrlrCompose.selection.end; - ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "*" + selected + "*"); + ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "*$selected*"); ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1); Provider.of(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose; }); }); var code = IconButton( - icon: Icon(Icons.code), + icon: const Icon(Icons.code), tooltip: AppLocalizations.of(context)!.tooltipCode, onPressed: () { setState(() { @@ -575,14 +576,14 @@ class _MessageViewState extends State { var selection = ctrlrCompose.selection; var start = ctrlrCompose.selection.start; var end = ctrlrCompose.selection.end; - ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "`" + selected + "`"); + ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "`$selected`"); ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1); Provider.of(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose; }); }); var superscript = IconButton( - icon: Icon(Icons.superscript), + icon: const Icon(Icons.superscript), tooltip: AppLocalizations.of(context)!.tooltipSuperscript, onPressed: () { setState(() { @@ -591,14 +592,14 @@ class _MessageViewState extends State { var selection = ctrlrCompose.selection; var start = ctrlrCompose.selection.start; var end = ctrlrCompose.selection.end; - ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "^" + selected + "^"); + ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "^$selected^"); ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1); Provider.of(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose; }); }); var strikethrough = IconButton( - icon: Icon(Icons.format_strikethrough), + icon: const Icon(Icons.format_strikethrough), tooltip: AppLocalizations.of(context)!.tooltipStrikethrough, onPressed: () { setState(() { @@ -607,14 +608,14 @@ class _MessageViewState extends State { var selection = ctrlrCompose.selection; var start = ctrlrCompose.selection.start; var end = ctrlrCompose.selection.end; - ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "~~" + selected + "~~"); + ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "~~$selected~~"); ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 2, extentOffset: selection.start + 2); Provider.of(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose; }); }); var preview = IconButton( - icon: Icon(Icons.text_format), + icon: const Icon(Icons.text_format), tooltip: AppLocalizations.of(context)!.tooltipPreviewFormatting, onPressed: () { setState(() { @@ -623,7 +624,7 @@ class _MessageViewState extends State { }); var vline = Padding( - padding: EdgeInsets.symmetric(vertical: 1, horizontal: 2), + padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 2), child: Container(height: 16, width: 1, decoration: BoxDecoration(color: Provider.of(context).theme.messageFromMeTextColor))); var formattingToolbar = Container( @@ -636,9 +637,9 @@ class _MessageViewState extends State { focusNode: FocusNode(), onKey: handleKeyPress, child: Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: TextFormField( - key: Key('txtCompose'), + key: const Key('txtCompose'), controller: Provider.of(context).messageDraft.ctrlCompose, focusNode: focusNode, autofocus: !Platform.isAndroid, @@ -673,8 +674,9 @@ class _MessageViewState extends State { focusedBorder: InputBorder.none, enabled: true, suffixIcon: ElevatedButton( - key: Key("btnSend"), - style: ElevatedButton.styleFrom(padding: EdgeInsets.all(0.0), shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(45.0))), + key: const Key("btnSend"), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.all(0.0), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(45.0))), + onPressed: cannotSend || (isGroup && Provider.of(context, listen: false).antispamTickets == 0) ? null : _sendMessage, child: Tooltip( message: cannotSend ? (isGroup ? AppLocalizations.of(context)!.serverNotSynced : AppLocalizations.of(context)!.peerOfflineMessage) @@ -682,21 +684,20 @@ class _MessageViewState extends State { ? AppLocalizations.of(context)!.acquiringTicketsFromServer : AppLocalizations.of(context)!.sendMessage, child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of(context).theme.defaultButtonTextColor)), - onPressed: cannotSend || (isGroup && Provider.of(context, listen: false).antispamTickets == 0) ? null : _sendMessage, ))), ))); - var textEditChildren; + List textEditChildren; if (showToolbar) { textEditChildren = [formattingToolbar, textField]; } else { textEditChildren = [textField]; } - var composeBox = - Container(color: Provider.of(context).theme.backgroundMainColor, padding: EdgeInsets.all(2), margin: EdgeInsets.all(2), height: 164, child: Column(children: textEditChildren)); + var composeBox = Container( + color: Provider.of(context).theme.backgroundMainColor, padding: const EdgeInsets.all(2), margin: const EdgeInsets.all(2), height: 164, child: Column(children: textEditChildren)); - var children; + List children; Widget invite = Container(); if (Provider.of(context).messageDraft.getInviteHandle() != null) { invite = FutureBuilder( @@ -706,31 +707,31 @@ class _MessageViewState extends State { var contactInvite = snapshot.data! as int; var contact = Provider.of(context, listen: false).contactList.getContact(contactInvite); return Container( - margin: EdgeInsets.all(5), - padding: EdgeInsets.all(5), + margin: const EdgeInsets.all(5), + padding: const EdgeInsets.all(5), color: Provider.of(context).theme.messageFromMeBackgroundColor, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack(children: [ Container( - margin: EdgeInsets.all(5), - padding: EdgeInsets.all(5), + margin: const EdgeInsets.all(5), + padding: const EdgeInsets.all(5), clipBehavior: Clip.antiAlias, decoration: BoxDecoration(color: Provider.of(context).theme.messageFromMeBackgroundColor), height: 75, child: Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, children: [ - Padding(padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0), child: Icon(CwtchIcons.send_invite, size: 32)), + const Padding(padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0), child: Icon(CwtchIcons.send_invite, size: 32)), Flexible( child: DefaultTextStyle( textWidthBasis: TextWidthBasis.parent, - child: senderInviteChrome("", contact!.nickname), style: Provider.of(context).scaleFonts(defaultTextStyle), overflow: TextOverflow.fade, + child: senderInviteChrome("", contact!.nickname), )) ])), Align( alignment: Alignment.topRight, child: IconButton( - icon: Icon(Icons.highlight_remove), + icon: const Icon(Icons.highlight_remove), splashRadius: Material.defaultSplashRadius / 2, tooltip: AppLocalizations.of(context)!.tooltipRemoveThisQuotedMessage, onPressed: () { @@ -756,16 +757,16 @@ class _MessageViewState extends State { ? Provider.of(context).theme.messageFromOtherTextColor : Provider.of(context).theme.messageFromMeTextColor; return Container( - margin: EdgeInsets.all(5), - padding: EdgeInsets.all(5), + margin: const EdgeInsets.all(5), + padding: const EdgeInsets.all(5), color: message.getMetadata().senderHandle != Provider.of(context).selectedProfile ? Provider.of(context).theme.messageFromOtherBackgroundColor : Provider.of(context).theme.messageFromMeBackgroundColor, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack(children: [ Container( - margin: EdgeInsets.all(5), - padding: EdgeInsets.all(5), + margin: const EdgeInsets.all(5), + padding: const EdgeInsets.all(5), clipBehavior: Clip.antiAlias, decoration: BoxDecoration( color: message.getMetadata().senderHandle != Provider.of(context).selectedProfile @@ -774,19 +775,19 @@ class _MessageViewState extends State { ), height: 75, child: Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, children: [ - Padding(padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0), child: Icon(Icons.reply, size: 32, color: qTextColor)), + Padding(padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0), child: Icon(Icons.reply, size: 32, color: qTextColor)), Flexible( child: DefaultTextStyle( textWidthBasis: TextWidthBasis.parent, - child: message.getPreviewWidget(context), style: TextStyle(color: qTextColor), overflow: TextOverflow.fade, + child: message.getPreviewWidget(context), )) ])), Align( alignment: Alignment.topRight, child: IconButton( - icon: Icon(Icons.highlight_remove), + icon: const Icon(Icons.highlight_remove), splashRadius: Material.defaultSplashRadius / 2, tooltip: AppLocalizations.of(context)!.tooltipRemoveThisQuotedMessage, onPressed: () { @@ -797,7 +798,7 @@ class _MessageViewState extends State { ]), ])); } else { - return MessageLoadingBubble(); + return const MessageLoadingBubble(); } }, ); @@ -832,18 +833,18 @@ class _MessageViewState extends State { showModalBottomSheet( context: ctx, builder: (BuildContext bcontext) { - return Container( + return SizedBox( height: 200, // bespoke value courtesy of the [TextField] docs child: Center( child: Padding( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text(AppLocalizations.of(bcontext)!.invitationLabel), - SizedBox( + const SizedBox( height: 20, ), ChangeNotifierProvider.value( @@ -852,17 +853,17 @@ class _MessageViewState extends State { return contact.onion != Provider.of(ctx).onion; }, onChanged: (newVal) { setState(() { - this.selectedContact = Provider.of(ctx, listen: false).contactList.findContact(newVal)!.identifier; + selectedContact = Provider.of(ctx, listen: false).contactList.findContact(newVal)!.identifier; }); })), - SizedBox( + const SizedBox( height: 20, ), ElevatedButton( child: Text(AppLocalizations.of(bcontext)!.inviteBtn, semanticsLabel: AppLocalizations.of(bcontext)!.inviteBtn), onPressed: () { - if (this.selectedContact != -1) { - Provider.of(context, listen: false).messageDraft.attachInvite(this.selectedContact); + if (selectedContact != -1) { + Provider.of(context, listen: false).messageDraft.attachInvite(selectedContact); } Navigator.pop(bcontext); setState(() {}); @@ -881,21 +882,19 @@ class _MessageViewState extends State { var showPreview = false; if (Provider.of(context, listen: false).shouldPreview(path)) { showPreview = true; - if (imagePreview == null) { - imagePreview = new File(path); - } + imagePreview ??= File(path); } - return Container( + return SizedBox( height: 300, // bespoke value courtesy of the [TextField] docs child: Center( child: Padding( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - Text(AppLocalizations.of(context)!.msgConfirmSend + " $path?"), - SizedBox( + Text("${AppLocalizations.of(context)!.msgConfirmSend} $path?"), + const SizedBox( height: 20, ), Visibility( @@ -910,13 +909,13 @@ class _MessageViewState extends State { height: 150, isAntiAlias: false, errorBuilder: (context, error, stackTrace) { - return MalformedBubble(); + return const MalformedBubble(); }, ) : Container()), Visibility( visible: showPreview, - child: SizedBox( + child: const SizedBox( height: 10, )), Row(mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -926,7 +925,7 @@ class _MessageViewState extends State { Navigator.pop(bcontext); }, ), - SizedBox( + const SizedBox( width: 20, ), ElevatedButton( @@ -951,7 +950,7 @@ void _summarizeConversation(BuildContext context, ProfileInfoState profile, Sett ) { return StatefulBuilder(builder: (BuildContext scontext, StateSetter setState /*You can rename this!*/) { if (scontext.mounted) { - new Timer.periodic(Duration(seconds: 1), (Timer t) { + Timer.periodic(const Duration(seconds: 1), (Timer t) { if (scontext.mounted) { setState(() {}); } @@ -966,13 +965,13 @@ void _summarizeConversation(BuildContext context, ProfileInfoState profile, Sett Provider.of(context).summary == "" ? Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ CircularProgressIndicator(color: settings.theme.defaultButtonActiveColor), - Padding(padding: EdgeInsets.all(5.0), child: Text(AppLocalizations.of(context)!.blodeuweddProcessing)) + Padding(padding: const EdgeInsets.all(5.0), child: Text(AppLocalizations.of(context)!.blodeuweddProcessing)) ]) : Flexible(child: Text(Provider.of(context).summary)) ])); var image = Padding( - padding: EdgeInsets.all(4.0), + padding: const EdgeInsets.all(4.0), child: ProfileImage( imagePath: "assets/blodeuwedd.png", diameter: 48.0, @@ -981,14 +980,14 @@ void _summarizeConversation(BuildContext context, ProfileInfoState profile, Sett badgeColor: Colors.red, )); - return Container( + return SizedBox( height: 300, // bespoke value courtesy of the [TextField] docs child: Container( alignment: Alignment.center, child: Padding( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Padding( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Row( mainAxisSize: MainAxisSize.min, children: [image, Flexible(child: bubble)], diff --git a/lib/views/peersettingsview.dart b/lib/views/peersettingsview.dart index 5920240c..f568cbfe 100644 --- a/lib/views/peersettingsview.dart +++ b/lib/views/peersettingsview.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; -import 'dart:ui'; import 'package:cwtch/config.dart'; import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/appstate.dart'; @@ -19,6 +17,8 @@ import '../themes/opaque.dart'; /// Peer Settings View Provides way to Configure . class PeerSettingsView extends StatefulWidget { + const PeerSettingsView({super.key}); + @override _PeerSettingsViewState createState() => _PeerSettingsViewState(); } @@ -52,8 +52,8 @@ class _PeerSettingsViewState extends State { title: Container( height: Provider.of(context).fontScaling * 24.0, clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(), - child: Text(handle + " " + AppLocalizations.of(context)!.conversationSettings)), + decoration: const BoxDecoration(), + child: Text("$handle ${AppLocalizations.of(context)!.conversationSettings}")), ), body: _buildSettingsList(), ); @@ -77,11 +77,11 @@ class _PeerSettingsViewState extends State { textAlign: TextAlign.left, text: TextSpan( text: country, - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 10, fontFamily: "RobotoMono"), - children: [TextSpan(text: " ($ip)", style: TextStyle(fontSize: 8, fontWeight: FontWeight.normal))])); + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 10, fontFamily: "RobotoMono"), + children: [TextSpan(text: " ($ip)", style: const TextStyle(fontSize: 8, fontWeight: FontWeight.normal))])); }).toList(growable: true); - paths.add(RichText(text: TextSpan(text: AppLocalizations.of(context)!.labelTorNetwork, style: TextStyle(fontWeight: FontWeight.normal, fontSize: 8, fontFamily: "monospace")))); + paths.add(RichText(text: TextSpan(text: AppLocalizations.of(context)!.labelTorNetwork, style: const TextStyle(fontWeight: FontWeight.normal, fontSize: 8, fontFamily: "monospace")))); path = Column( children: paths, @@ -107,8 +107,8 @@ class _PeerSettingsViewState extends State { minHeight: viewportConstraints.maxHeight, ), child: Container( - margin: EdgeInsets.all(10), - padding: EdgeInsets.all(2), + margin: const EdgeInsets.all(10), + padding: const EdgeInsets.all(2), child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ ProfileImage( @@ -125,21 +125,21 @@ class _PeerSettingsViewState extends State { width: MediaQuery.of(context).size.width / 2, child: Column(children: [ Padding( - padding: EdgeInsets.all(1), + padding: const EdgeInsets.all(1), child: SelectableText( Provider.of(context, listen: false).attributes[0] ?? "", textAlign: TextAlign.center, ), ), Padding( - padding: EdgeInsets.all(1), + padding: const EdgeInsets.all(1), child: SelectableText( Provider.of(context, listen: false).attributes[1] ?? "", textAlign: TextAlign.center, ), ), Padding( - padding: EdgeInsets.all(1), + padding: const EdgeInsets.all(1), child: SelectableText( Provider.of(context, listen: false).attributes[2] ?? "", textAlign: TextAlign.center, @@ -150,7 +150,7 @@ class _PeerSettingsViewState extends State { Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ CwtchLabel(label: AppLocalizations.of(context)!.displayNameLabel), - SizedBox( + const SizedBox( height: 20, ), CwtchButtonTextField( @@ -164,7 +164,7 @@ class _PeerSettingsViewState extends State { final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.nickChangeSuccess)); ScaffoldMessenger.of(context).showSnackBar(snackBar); }, - icon: Icon(Icons.save), + icon: const Icon(Icons.save), tooltip: AppLocalizations.of(context)!.saveBtn, ) ]), @@ -173,26 +173,26 @@ class _PeerSettingsViewState extends State { Visibility( visible: settings.streamerMode == false, child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.addressLabel), - SizedBox( + const SizedBox( height: 20, ), CwtchButtonTextField( controller: TextEditingController(text: Provider.of(context, listen: false).onion), onPressed: _copyOnion, - icon: Icon(CwtchIcons.address_copy), + icon: const Icon(CwtchIcons.address_copy), tooltip: AppLocalizations.of(context)!.copyBtn, ) ])), Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.conversationSettings), - SizedBox( + const SizedBox( height: 20, ), ListTile( @@ -202,7 +202,7 @@ class _PeerSettingsViewState extends State { subtitle: Text(AppLocalizations.of(context)!.descriptionACNCircuitInfo), trailing: path, ), - SizedBox( + const SizedBox( height: 20, ), SwitchListTile( @@ -290,7 +290,7 @@ class _PeerSettingsViewState extends State { )), ]), Column(mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ - SizedBox( + const SizedBox( height: 20, ), Row(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -303,16 +303,16 @@ class _PeerSettingsViewState extends State { // locally update cache... Provider.of(context, listen: false).isArchived = true; Provider.of(context, listen: false).cwtch.ArchiveConversation(profileOnion, handle); - Future.delayed(Duration(milliseconds: 500), () { + Future.delayed(const Duration(milliseconds: 500), () { Provider.of(context, listen: false).selectedConversation = null; Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog }); }, - icon: Icon(Icons.archive), + icon: const Icon(Icons.archive), label: Text(AppLocalizations.of(context)!.archiveConversation), )) ]), - SizedBox( + const SizedBox( height: 20, ), Row(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -325,7 +325,7 @@ class _PeerSettingsViewState extends State { style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.backgroundPaneColor), foregroundColor: MaterialStateProperty.all(Provider.of(context).theme.mainTextColor)), - icon: Icon(CwtchIcons.leave_group), + icon: const Icon(CwtchIcons.leave_group), label: Text( AppLocalizations.of(context)!.leaveConversation, style: settings.scaleFonts(defaultTextButtonStyle.copyWith(decoration: TextDecoration.underline)), @@ -345,7 +345,7 @@ class _PeerSettingsViewState extends State { } void _copyOnion() { - Clipboard.setData(new ClipboardData(text: Provider.of(context, listen: false).onion)); + Clipboard.setData(ClipboardData(text: Provider.of(context, listen: false).onion)); final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification)); ScaffoldMessenger.of(context).showSnackBar(snackBar); } @@ -353,14 +353,14 @@ class _PeerSettingsViewState extends State { showAlertDialog(BuildContext context) { // set up the buttons Widget cancelButton = ElevatedButton( - child: Text(AppLocalizations.of(context)!.cancel), - style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))), + style: ButtonStyle(padding: MaterialStateProperty.all(const EdgeInsets.all(20))), onPressed: () { Navigator.of(context).pop(); // dismiss dialog }, + child: Text(AppLocalizations.of(context)!.cancel), ); Widget continueButton = ElevatedButton( - style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))), + style: ButtonStyle(padding: MaterialStateProperty.all(const EdgeInsets.all(20))), child: Text(AppLocalizations.of(context)!.yesLeave), onPressed: () { var profileOnion = Provider.of(context, listen: false).profileOnion; @@ -368,7 +368,7 @@ class _PeerSettingsViewState extends State { // locally update cache... Provider.of(context, listen: false).contactList.removeContact(identifier); Provider.of(context, listen: false).cwtch.DeleteContact(profileOnion, identifier); - Future.delayed(Duration(milliseconds: 500), () { + Future.delayed(const Duration(milliseconds: 500), () { Provider.of(context, listen: false).selectedConversation = null; Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog }); diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart index 7adecc85..2a3e3a95 100644 --- a/lib/views/profilemgrview.dart +++ b/lib/views/profilemgrview.dart @@ -16,7 +16,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:cwtch/widgets/profilerow.dart'; import 'package:provider/provider.dart'; -import '../config.dart'; import '../main.dart'; import '../torstatus.dart'; import 'addeditprofileview.dart'; @@ -24,7 +23,7 @@ import 'globalsettingsview.dart'; import 'serversview.dart'; class ProfileMgrView extends StatefulWidget { - ProfileMgrView(); + const ProfileMgrView({super.key}); @override _ProfileMgrViewState createState() => _ProfileMgrViewState(); @@ -50,7 +49,7 @@ class _ProfileMgrViewState extends State { return Provider.of(context, listen: false).cwtchIsClosing; }, child: Scaffold( - key: Key("ProfileManagerView"), + key: const Key("ProfileManagerView"), backgroundColor: settings.theme.backgroundMainColor, appBar: AppBar( title: Row(children: [ @@ -59,7 +58,7 @@ class _ProfileMgrViewState extends State { size: 36, color: settings.theme.mainTextColor, ), - SizedBox( + const SizedBox( width: 10, ), Expanded( @@ -83,11 +82,11 @@ class _ProfileMgrViewState extends State { } List getActions() { - List actions = new List.empty(growable: true); + List actions = List.empty(growable: true); // Tor Status actions.add(IconButton( - icon: TorIcon(), + icon: const TorIcon(), onPressed: _pushTorStatus, splashRadius: Material.defaultSplashRadius / 2, tooltip: Provider.of(context).progress == 100 @@ -97,7 +96,7 @@ class _ProfileMgrViewState extends State { // Unlock Profiles actions.add(IconButton( - icon: Icon(CwtchIcons.lock_open_24px), + icon: const Icon(CwtchIcons.lock_open_24px), splashRadius: Material.defaultSplashRadius / 2, color: Provider.of(context).profiles.isEmpty ? Provider.of(context).theme.defaultButtonColor : Provider.of(context).theme.mainTextColor, tooltip: AppLocalizations.of(context)!.tooltipUnlockProfiles, @@ -109,26 +108,26 @@ class _ProfileMgrViewState extends State { Provider.of(context).isExperimentEnabled(ServerManagementExperiment) && !Platform.isAndroid && !Platform.isIOS) { - actions.add( - IconButton(icon: Icon(CwtchIcons.dns_black_24dp), splashRadius: Material.defaultSplashRadius / 2, tooltip: AppLocalizations.of(context)!.serversManagerTitleShort, onPressed: _pushServers)); + actions.add(IconButton( + icon: const Icon(CwtchIcons.dns_black_24dp), splashRadius: Material.defaultSplashRadius / 2, tooltip: AppLocalizations.of(context)!.serversManagerTitleShort, onPressed: _pushServers)); } // Global Settings actions.add(IconButton( - key: Key("OpenSettingsView"), - icon: Icon(Icons.settings), + key: const Key("OpenSettingsView"), + icon: const Icon(Icons.settings), tooltip: AppLocalizations.of(context)!.tooltipOpenSettings, splashRadius: Material.defaultSplashRadius / 2, onPressed: _pushGlobalSettings)); // shutdown cwtch - actions.add(IconButton(icon: Icon(Icons.close), tooltip: AppLocalizations.of(context)!.shutdownCwtchTooltip, splashRadius: Material.defaultSplashRadius / 2, onPressed: _modalShutdown)); + actions.add(IconButton(icon: const Icon(Icons.close), tooltip: AppLocalizations.of(context)!.shutdownCwtchTooltip, splashRadius: Material.defaultSplashRadius / 2, onPressed: _modalShutdown)); return actions; } void _modalShutdown() { - Provider.of(context, listen: false).modalShutdown(MethodCall("")); + Provider.of(context, listen: false).modalShutdown(const MethodCall("")); } void _pushGlobalSettings() { @@ -137,11 +136,11 @@ class _ProfileMgrViewState extends State { pageBuilder: (bcontext, a1, a2) { return Provider( create: (_) => Provider.of(bcontext, listen: false), - child: GlobalSettingsView(), + child: const GlobalSettingsView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } @@ -149,15 +148,15 @@ class _ProfileMgrViewState extends State { void _pushServers() { Navigator.of(context).push( PageRouteBuilder( - settings: RouteSettings(name: "servers"), + settings: const RouteSettings(name: "servers"), pageBuilder: (bcontext, a1, a2) { return MultiProvider( providers: [ChangeNotifierProvider.value(value: globalServersList), Provider.value(value: Provider.of(context))], - child: ServersView(), + child: const ServersView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } @@ -165,20 +164,20 @@ class _ProfileMgrViewState extends State { void _pushTorStatus() { Navigator.of(context).push( PageRouteBuilder( - settings: RouteSettings(name: "torconfig"), + settings: const RouteSettings(name: "torconfig"), pageBuilder: (bcontext, a1, a2) { return MultiProvider( providers: [Provider.value(value: Provider.of(context))], - child: TorStatusView(), + child: const TorStatusView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } - void _pushAddProfile(bcontext, {onion: ""}) { + void _pushAddProfile(bcontext, {onion = ""}) { Navigator.popUntil(bcontext, (route) => route.isFirst); Navigator.of(context).push( @@ -190,11 +189,11 @@ class _ProfileMgrViewState extends State { create: (_) => ProfileInfoState(onion: onion), ), ], - builder: (context, widget) => AddEditProfileView(key: Key('addprofile')), + builder: (context, widget) => const AddEditProfileView(key: Key('addprofile')), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } @@ -207,37 +206,37 @@ class _ProfileMgrViewState extends State { return Padding( padding: MediaQuery.of(context).viewInsets, child: RepaintBoundary( - child: Container( + child: SizedBox( height: Platform.isAndroid ? 250 : 200, // bespoke value courtesy of the [TextField] docs child: Center( child: Padding( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - SizedBox( + const SizedBox( height: 20, ), Expanded( child: ElevatedButton( style: ElevatedButton.styleFrom( - minimumSize: Size(399, 20), - maximumSize: Size(400, 20), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), + minimumSize: const Size(399, 20), + maximumSize: const Size(400, 20), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), ), child: Text( - key: Key("addNewProfileActual"), + key: const Key("addNewProfileActual"), AppLocalizations.of(context)!.addProfileTitle, semanticsLabel: AppLocalizations.of(context)!.addProfileTitle, - style: TextStyle(fontWeight: FontWeight.bold), + style: const TextStyle(fontWeight: FontWeight.bold), ), onPressed: () { _pushAddProfile(context); }, )), - SizedBox( + const SizedBox( height: 20, ), Expanded( @@ -245,12 +244,12 @@ class _ProfileMgrViewState extends State { message: AppLocalizations.of(context)!.importProfileTooltip, child: ElevatedButton( style: ElevatedButton.styleFrom( - minimumSize: Size(399, 20), - maximumSize: Size(400, 20), + minimumSize: const Size(399, 20), + backgroundColor: Provider.of(context).theme.backgroundMainColor, + maximumSize: const Size(400, 20), shape: RoundedRectangleBorder( side: BorderSide(color: Provider.of(context).theme.defaultButtonActiveColor, width: 2.0), - borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), - primary: Provider.of(context).theme.backgroundMainColor, + borderRadius: const BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), ), child: Text(AppLocalizations.of(context)!.importProfile, semanticsLabel: AppLocalizations.of(context)!.importProfile, @@ -273,7 +272,7 @@ class _ProfileMgrViewState extends State { }, () {}, () {}); }, ))), - SizedBox( + const SizedBox( height: 20, ), ], @@ -290,31 +289,33 @@ class _ProfileMgrViewState extends State { return Padding( padding: MediaQuery.of(context).viewInsets, child: RepaintBoundary( - child: Container( + child: SizedBox( height: Platform.isAndroid ? 250 : 200, // bespoke value courtesy of the [TextField] docs child: Center( child: Padding( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Text(AppLocalizations.of(context)!.enterProfilePassword), - SizedBox( + const SizedBox( height: 20, ), CwtchPasswordField( - key: Key("unlockPasswordProfileElement"), + key: const Key("unlockPasswordProfileElement"), autofocus: true, controller: ctrlrPassword, action: unlock, - validator: (value) {}, + validator: (value) { + return null; + }, ), - SizedBox( + const SizedBox( height: 20, ), Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Spacer(), + const Spacer(), Expanded( child: ElevatedButton( child: Text(AppLocalizations.of(context)!.unlock, semanticsLabel: AppLocalizations.of(context)!.unlock), @@ -322,7 +323,7 @@ class _ProfileMgrViewState extends State { unlock(ctrlrPassword.value.text); }, )), - Spacer() + const Spacer() ]), ], ))), @@ -343,7 +344,7 @@ class _ProfileMgrViewState extends State { (ProfileInfoState profile) { return ChangeNotifierProvider.value( value: profile, - builder: (context, child) => ProfileRow(), + builder: (context, child) => const ProfileRow(), ); }, ); @@ -353,7 +354,7 @@ class _ProfileMgrViewState extends State { value: ProfileInfoState(onion: ""), builder: (context, child) { return Container( - margin: EdgeInsets.only(top: 20), + margin: const EdgeInsets.only(top: 20), width: MediaQuery.of(context).size.width, child: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Tooltip( @@ -363,7 +364,7 @@ class _ProfileMgrViewState extends State { style: TextButton.styleFrom( minimumSize: Size(MediaQuery.of(context).size.width * 0.79, 80), maximumSize: Size(MediaQuery.of(context).size.width * 0.8, 80), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), ), label: Text( AppLocalizations.of(context)!.unlock, @@ -389,7 +390,7 @@ class _ProfileMgrViewState extends State { Text(AppLocalizations.of(context)!.unlockProfileTip, textAlign: TextAlign.center), Container( width: MediaQuery.of(context).size.width, - margin: EdgeInsets.only(top: 20), + margin: const EdgeInsets.only(top: 20), child: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Tooltip( message: AppLocalizations.of(context)!.addProfileTitle, @@ -398,12 +399,12 @@ class _ProfileMgrViewState extends State { style: TextButton.styleFrom( minimumSize: Size(MediaQuery.of(context).size.width * 0.79, 80), maximumSize: Size(MediaQuery.of(context).size.width * 0.8, 80), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))), ), label: Text( AppLocalizations.of(context)!.addProfileTitle, semanticsLabel: AppLocalizations.of(context)!.addProfileTitle, - style: TextStyle(fontWeight: FontWeight.bold), + style: const TextStyle(fontWeight: FontWeight.bold), ), onPressed: () { _modalAddImportProfiles(); diff --git a/lib/views/profileserversview.dart b/lib/views/profileserversview.dart index 8e115c31..35712737 100644 --- a/lib/views/profileserversview.dart +++ b/lib/views/profileserversview.dart @@ -12,6 +12,8 @@ import '../main.dart'; import '../settings.dart'; class ProfileServersView extends StatefulWidget { + const ProfileServersView({super.key}); + @override _ProfileServersView createState() => _ProfileServersView(); } @@ -25,7 +27,7 @@ class _ProfileServersView extends State { @override Widget build(BuildContext context) { var knownServers = Provider.of(context).serverList.servers.map((RemoteServerInfoState remoteServer) { - return remoteServer.onion + ".onion"; + return "${remoteServer.onion}.onion"; }).toSet(); var importServerList = Provider.of(context).servers.where((server) => !knownServers.contains(server.onion)).map>((ServerInfoState serverInfo) { return DropdownMenuItem( @@ -50,7 +52,7 @@ class _ProfileServersView extends State { (RemoteServerInfoState server) { return ChangeNotifierProvider.value( value: server, - builder: (context, child) => RemoteServerRow(), + builder: (context, child) => const RemoteServerRow(), ); }, ); @@ -84,8 +86,8 @@ class _ProfileServersView extends State { clipBehavior: Clip.antiAlias, controller: controller, child: Container( - margin: EdgeInsets.fromLTRB(5, 0, 5, 10), - padding: EdgeInsets.fromLTRB(5, 0, 5, 10), + margin: const EdgeInsets.fromLTRB(5, 0, 5, 10), + padding: const EdgeInsets.fromLTRB(5, 0, 5, 10), child: Column(children: [if (importServerList.length > 1) importCard, Column(children: divided)])))); }); diff --git a/lib/views/remoteserverview.dart b/lib/views/remoteserverview.dart index f4430c43..3652e027 100644 --- a/lib/views/remoteserverview.dart +++ b/lib/views/remoteserverview.dart @@ -1,31 +1,20 @@ -import 'dart:convert'; -import 'package:cwtch/cwtch/cwtch.dart'; import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/profile.dart'; -import 'package:cwtch/models/profileservers.dart'; import 'package:cwtch/models/remoteserver.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 '../models/appstate.dart'; import '../themes/opaque.dart'; /// Pane to add or edit a server class RemoteServerView extends StatefulWidget { - const RemoteServerView(); + const RemoteServerView({super.key}); @override _RemoteServerViewState createState() => _RemoteServerViewState(); @@ -57,25 +46,25 @@ class _RemoteServerViewState extends State { 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), + margin: const EdgeInsets.fromLTRB(30, 0, 30, 10), + padding: const EdgeInsets.fromLTRB(20, 0, 20, 10), child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.serverAddress), - SizedBox( + const SizedBox( height: 20, ), SelectableText(serverInfoState.onion), // Description - SizedBox( + const SizedBox( height: 20, ), CwtchLabel(label: AppLocalizations.of(context)!.serverDescriptionLabel), Text(AppLocalizations.of(context)!.serverDescriptionDescription), - SizedBox( + const SizedBox( height: 20, ), CwtchButtonTextField( @@ -83,14 +72,14 @@ class _RemoteServerViewState extends State { readonly: false, tooltip: AppLocalizations.of(context)!.saveBtn, labelText: AppLocalizations.of(context)!.fieldDescriptionLabel, - icon: Icon(Icons.save), + icon: const Icon(Icons.save), onPressed: () { Provider.of(context, listen: false).cwtch.SetConversationAttribute(profile.onion, serverInfoState.identifier, "server.description", ctrlrDesc.text); serverInfoState.updateDescription(ctrlrDesc.text); }, ), - SizedBox( + const SizedBox( height: 20, ), Row(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -102,7 +91,7 @@ class _RemoteServerViewState extends State { : () { showAlertDialog(context); }, - icon: Icon(CwtchIcons.leave_group), + icon: const Icon(CwtchIcons.leave_group), label: Text( AppLocalizations.of(context)!.deleteBtn, style: settings.scaleFonts(defaultTextButtonStyle), @@ -110,7 +99,7 @@ class _RemoteServerViewState extends State { )) ]), Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: Text(AppLocalizations.of(context)!.groupsOnThisServerLabel), ), Expanded(child: _buildGroupsList(serverInfoState)), @@ -136,7 +125,7 @@ class _RemoteServerViewState extends State { var size = MediaQuery.of(context).size; int cols = ((size.width - 50) / 500).ceil(); - final double itemHeight = 60; // magic arbitary + const 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); @@ -174,7 +163,7 @@ showAlertDialog(BuildContext context) { }, ); Widget continueButton = ElevatedButton( - style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))), + style: ButtonStyle(padding: MaterialStateProperty.all(const EdgeInsets.all(20))), child: Text(AppLocalizations.of(context)!.deleteServerConfirmBtn), onPressed: () { var profileOnion = Provider.of(context, listen: false).onion; diff --git a/lib/views/serversview.dart b/lib/views/serversview.dart index d96d6603..cf8b5f41 100644 --- a/lib/views/serversview.dart +++ b/lib/views/serversview.dart @@ -3,8 +3,6 @@ import 'package:cwtch/views/addeditservers.dart'; import 'package:cwtch/widgets/passwordfield.dart'; import 'package:cwtch/widgets/serverrow.dart'; import 'package:flutter/material.dart'; -import 'package:cwtch/torstatus.dart'; -import 'package:cwtch/widgets/tor_icon.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -14,6 +12,8 @@ import '../settings.dart'; /// class ServersView extends StatefulWidget { + const ServersView({super.key}); + @override _ServersView createState() => _ServersView(); } @@ -50,7 +50,7 @@ class _ServersView extends State { (ServerInfoState server) { return ChangeNotifierProvider.value( value: server, - builder: (context, child) => RepaintBoundary(child: ServerRow()), + builder: (context, child) => const RepaintBoundary(child: ServerRow()), ); }, ); @@ -74,11 +74,11 @@ class _ServersView extends State { } List getActions() { - List actions = new List.empty(growable: true); + List actions = List.empty(growable: true); // Unlock Profiles actions.add(IconButton( - icon: Icon(CwtchIcons.lock_open_24px), + icon: const Icon(CwtchIcons.lock_open_24px), color: Provider.of(context).servers.isEmpty ? Provider.of(context).theme.defaultButtonColor : Provider.of(context).theme.mainTextColor, tooltip: AppLocalizations.of(context)!.tooltipUnlockProfiles, onPressed: _modalUnlockServers, @@ -95,30 +95,32 @@ class _ServersView extends State { return Padding( padding: MediaQuery.of(context).viewInsets, child: RepaintBoundary( - child: Container( + child: SizedBox( height: 200, // bespoke value courtesy of the [TextField] docs child: Center( child: Padding( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Text(AppLocalizations.of(context)!.enterServerPassword), - SizedBox( + const SizedBox( height: 20, ), CwtchPasswordField( autofocus: true, controller: ctrlrPassword, action: unlock, - validator: (value) {}, + validator: (value) { + return null; + }, ), - SizedBox( + const SizedBox( height: 20, ), Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Spacer(), + const Spacer(), Expanded( child: ElevatedButton( child: Text(AppLocalizations.of(context)!.unlock, semanticsLabel: AppLocalizations.of(context)!.unlock), @@ -126,7 +128,7 @@ class _ServersView extends State { unlock(ctrlrPassword.value.text); }, )), - Spacer() + const Spacer() ]), ], ))), @@ -150,11 +152,11 @@ class _ServersView extends State { create: (_) => ServerInfoState(onion: "", serverBundle: "", description: "", autoStart: true, running: false, isEncrypted: true), ) ], - child: AddEditServerView(), + child: const AddEditServerView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } diff --git a/lib/views/splashView.dart b/lib/views/splashView.dart index 67ad2ce7..94ce2108 100644 --- a/lib/views/splashView.dart +++ b/lib/views/splashView.dart @@ -1,6 +1,5 @@ import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/themes/opaque.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -9,6 +8,8 @@ import '../main.dart'; import '../settings.dart'; class SplashView extends StatefulWidget { + const SplashView({super.key}); + @override _SplashViewState createState() => _SplashViewState(); } @@ -25,17 +26,17 @@ class _SplashViewState extends State { return Consumer( builder: (context, appState, child) => Scaffold( - key: Key("SplashView"), + key: const Key("SplashView"), body: Center( child: Column(mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image( + const Image( image: AssetImage("assets/core/knott-white.png"), filterQuality: FilterQuality.medium, isAntiAlias: true, width: 200, height: 200, ), - Image( + const Image( image: AssetImage("assets/cwtch_title.png"), filterQuality: FilterQuality.medium, isAntiAlias: true, @@ -44,7 +45,7 @@ class _SplashViewState extends State { padding: const EdgeInsets.all(20.0), child: Column(children: [ Padding( - padding: EdgeInsets.all(6.0), + padding: const EdgeInsets.all(6.0), child: Text( appState.appError != "" ? appState.appError @@ -61,7 +62,7 @@ class _SplashViewState extends State { color: Provider.of(context).theme.defaultButtonActiveColor, )) ])), - Image(image: AssetImage("assets/Open_Privacy_Logo_lightoutline.png")), + const Image(image: AssetImage("assets/Open_Privacy_Logo_lightoutline.png")), ])), )); } diff --git a/lib/views/torstatusview.dart b/lib/views/torstatusview.dart index beb2c601..5bf3dbe5 100644 --- a/lib/views/torstatusview.dart +++ b/lib/views/torstatusview.dart @@ -13,6 +13,8 @@ import 'globalsettingsview.dart'; /// Tor Status View provides all info on Tor network state and the (future) ability to configure the network in a variety /// of ways (restart, enable bridges, enable pluggable transports etc) class TorStatusView extends StatefulWidget { + const TorStatusView({super.key}); + @override _TorStatusView createState() => _TorStatusView(); } @@ -63,7 +65,7 @@ class _TorStatusView extends State { ), child: Column(children: [ ListTile( - leading: TorIcon(), + leading: const TorIcon(), title: Text(AppLocalizations.of(context)!.torStatus), subtitle: Text(torStatus.progress == 100 ? AppLocalizations.of(context)!.networkStatusOnline : torStatus.status), trailing: ElevatedButton( @@ -109,7 +111,7 @@ class _TorStatusView extends State { title: Text(AppLocalizations.of(context)!.torSettingsCustomSocksPort), subtitle: Text(AppLocalizations.of(context)!.torSettingsCustomSocksPortDescription), leading: Icon(CwtchIcons.swap_horiz_24px, color: settings.current().mainTextColor), - trailing: Container( + trailing: SizedBox( width: MediaQuery.of(context).size.width / 4, child: CwtchTextField( number: true, @@ -140,7 +142,7 @@ class _TorStatusView extends State { title: Text(AppLocalizations.of(context)!.torSettingsCustomControlPort), subtitle: Text(AppLocalizations.of(context)!.torSettingsCustomControlPortDescription), leading: Icon(CwtchIcons.swap_horiz_24px, color: settings.current().mainTextColor), - trailing: Container( + trailing: SizedBox( width: MediaQuery.of(context).size.width / 4, child: CwtchTextField( number: true, @@ -182,7 +184,7 @@ class _TorStatusView extends State { Visibility( visible: settings.useCustomTorConfig, child: Padding( - padding: EdgeInsets.all(5), + padding: const EdgeInsets.all(5), child: CwtchTextField( controller: torConfigController, multiLine: true, diff --git a/lib/widgets/DropdownContacts.dart b/lib/widgets/DropdownContacts.dart index aa373bc3..0f8b54b8 100644 --- a/lib/widgets/DropdownContacts.dart +++ b/lib/widgets/DropdownContacts.dart @@ -15,7 +15,8 @@ bool noFilter(ContactInfoState peer) { // Displays nicknames to UI but uses handles as values // Pass an onChanged handler to access value class DropdownContacts extends StatefulWidget { - DropdownContacts({ + const DropdownContacts({ + super.key, required this.onChanged, this.filter = noFilter, }); @@ -33,7 +34,7 @@ class _DropdownContactsState extends State { Widget build(BuildContext context) { return DropdownButton( isExpanded: true, // magic property - value: this.selected, + value: selected, items: Provider.of(context, listen: false).contactList.contacts.where(widget.filter).map>((ContactInfoState contact) { return DropdownMenuItem( value: contact.onion, @@ -44,7 +45,7 @@ class _DropdownContactsState extends State { }).toList(), onChanged: (String? newVal) { setState(() { - this.selected = newVal; + selected = newVal; }); widget.onChanged(newVal); }); diff --git a/lib/widgets/buttontextfield.dart b/lib/widgets/buttontextfield.dart index 3d4c6afd..737422e1 100644 --- a/lib/widgets/buttontextfield.dart +++ b/lib/widgets/buttontextfield.dart @@ -6,6 +6,7 @@ import 'package:provider/provider.dart'; // Callers must provide a text controller, label helper text and a validator. class CwtchButtonTextField extends StatefulWidget { CwtchButtonTextField({ + super.key, required this.controller, required this.onPressed, required this.icon, @@ -48,7 +49,7 @@ class _CwtchButtonTextFieldState extends State { return Consumer(builder: (context, theme, child) { return Container( clipBehavior: Clip.antiAlias, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ child: Theme( data: Theme.of(context).copyWith( @@ -65,7 +66,7 @@ class _CwtchButtonTextFieldState extends State { enableIMEPersonalizedLearning: false, onChanged: widget.onChanged, maxLines: 1, - style: widget.textStyle == null ? TextStyle(overflow: TextOverflow.clip) : widget.textStyle, + style: widget.textStyle ?? const TextStyle(overflow: TextOverflow.clip), decoration: InputDecoration( labelText: widget.labelText, labelStyle: TextStyle(color: theme.current().mainTextColor, backgroundColor: theme.current().textfieldBackgroundColor), @@ -73,7 +74,7 @@ class _CwtchButtonTextFieldState extends State { onPressed: widget.onPressed, icon: widget.icon, splashRadius: Material.defaultSplashRadius / 2, - padding: EdgeInsets.fromLTRB(0.0, 4.0, 2.0, 2.0), + padding: const EdgeInsets.fromLTRB(0.0, 4.0, 2.0, 2.0), tooltip: widget.tooltip, enableFeedback: true, color: theme.current().mainTextColor, @@ -91,7 +92,7 @@ class _CwtchButtonTextFieldState extends State { color: theme.current().textfieldErrorColor, fontWeight: FontWeight.bold, ), - contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), + contentPadding: const EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))), ))); }); diff --git a/lib/widgets/contactrow.dart b/lib/widgets/contactrow.dart index 1f4b85da..174458e9 100644 --- a/lib/widgets/contactrow.dart +++ b/lib/widgets/contactrow.dart @@ -14,28 +14,27 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../main.dart'; import '../models/message.dart'; import '../settings.dart'; -import 'package:intl/intl.dart'; class ContactRow extends StatefulWidget { int? messageIndex; - ContactRow({this.messageIndex}); + ContactRow({super.key, this.messageIndex}); @override _ContactRowState createState() => _ContactRowState(); } class _ContactRowState extends State { bool isHover = false; - Message? cachedMessage = null; + Message? cachedMessage; @override Widget build(BuildContext context) { var contact = Provider.of(context); - if (widget.messageIndex != null && this.cachedMessage == null) { + if (widget.messageIndex != null && cachedMessage == null) { messageHandler(context, Provider.of(context, listen: false).onion, contact.identifier, ByIndex(widget.messageIndex!)).then((value) { setState(() { - this.cachedMessage = value; + cachedMessage = value; }); }); } @@ -79,7 +78,7 @@ class _ContactRowState extends State { )), Expanded( child: Padding( - padding: EdgeInsets.all(6.0), + padding: const EdgeInsets.all(6.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -88,7 +87,7 @@ class _ContactRowState extends State { Container( clipBehavior: Clip.hardEdge, height: Provider.of(context).fontScaling * 14.0 + 5.0, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), child: Text( contact.augmentedNickname(context).trim() + (contact.messageDraft.isEmpty() ? "" : "*"), style: TextStyle( @@ -103,7 +102,7 @@ class _ContactRowState extends State { maxLines: 1, )), syncStatus ?? - SizedBox( + const SizedBox( width: 0, height: 0, ), @@ -111,20 +110,20 @@ class _ContactRowState extends State { IgnorePointer( ignoring: true, child: Visibility( - visible: this.cachedMessage != null, + visible: cachedMessage != null, maintainSize: false, maintainInteractivity: false, maintainSemantics: false, maintainState: false, - child: this.cachedMessage == null ? CircularProgressIndicator() : this.cachedMessage!.getPreviewWidget(context), + child: cachedMessage == null ? const CircularProgressIndicator() : cachedMessage!.getPreviewWidget(context), )), Container( - padding: EdgeInsets.all(0), + padding: const EdgeInsets.all(0), height: contact.isInvitation ? Provider.of(context).fontScaling * 14.0 + 35.0 : Provider.of(context).fontScaling * 14.0 + 5.0, child: contact.isInvitation == true ? Wrap(alignment: WrapAlignment.start, direction: Axis.vertical, children: [ Padding( - padding: EdgeInsets.all(2), + padding: const EdgeInsets.all(2), child: TextButton.icon( label: Text( AppLocalizations.of(context)!.tooltipAcceptContactRequest, @@ -138,7 +137,7 @@ class _ContactRowState extends State { onPressed: _btnApprove, )), Padding( - padding: EdgeInsets.all(2), + padding: const EdgeInsets.all(2), child: TextButton.icon( label: Text( AppLocalizations.of(context)!.tooltipRejectContactRequest, @@ -153,21 +152,21 @@ class _ContactRowState extends State { ]) : (contact.isBlocked ? IconButton( - padding: EdgeInsets.symmetric(vertical: 2.0, horizontal: 0.0), + padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 0.0), splashRadius: Material.defaultSplashRadius / 2, iconSize: 16, icon: Icon(Icons.block, color: Provider.of(context).theme.mainTextColor), onPressed: null, ) - : Text(prettyDateString(context, widget.messageIndex == null ? contact.lastMessageSentTime : (this.cachedMessage?.getMetadata().timestamp ?? DateTime.now())), - style: Provider.of(context).scaleFonts(TextStyle( + : Text(prettyDateString(context, widget.messageIndex == null ? contact.lastMessageSentTime : (cachedMessage?.getMetadata().timestamp ?? DateTime.now())), + style: Provider.of(context).scaleFonts(const TextStyle( fontSize: 12.0, fontFamily: "Inter", ))))), Visibility( visible: !Provider.of(context).streamerMode, child: Container( - padding: EdgeInsets.all(0), + padding: const EdgeInsets.all(0), height: Provider.of(context).fontScaling * 13.0 + 5.0, child: Text( contact.onion, diff --git a/lib/widgets/cwtchlabel.dart b/lib/widgets/cwtchlabel.dart index 9cfa8b11..5c64309f 100644 --- a/lib/widgets/cwtchlabel.dart +++ b/lib/widgets/cwtchlabel.dart @@ -6,7 +6,7 @@ import '../settings.dart'; // Provides a styled Label // Callers must provide a label text class CwtchLabel extends StatefulWidget { - CwtchLabel({required this.label}); + const CwtchLabel({super.key, required this.label}); final String label; @override diff --git a/lib/widgets/filebubble.dart b/lib/widgets/filebubble.dart index df27041d..de625c11 100644 --- a/lib/widgets/filebubble.dart +++ b/lib/widgets/filebubble.dart @@ -11,7 +11,6 @@ import 'package:cwtch/themes/opaque.dart'; import 'package:cwtch/widgets/malformedbubble.dart'; import 'package:cwtch/widgets/messageBubbleWidgetHelpers.dart'; import 'package:file_picker/file_picker.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../main.dart'; @@ -33,13 +32,13 @@ class FileBubble extends StatefulWidget { final bool isAuto; final bool isPreview; - FileBubble(this.nameSuggestion, this.rootHash, this.nonce, this.fileSize, {this.isAuto = false, this.interactive = true, this.isPreview = false}); + const FileBubble(this.nameSuggestion, this.rootHash, this.nonce, this.fileSize, {super.key, this.isAuto = false, this.interactive = true, this.isPreview = false}); @override FileBubbleState createState() => FileBubbleState(); String fileKey() { - return this.rootHash + "." + this.nonce; + return "$rootHash.$nonce"; } } @@ -64,7 +63,7 @@ class FileBubbleState extends State { alignment: Alignment.center, isAntiAlias: false, errorBuilder: (context, error, stackTrace) { - return MalformedBubble(); + return const MalformedBubble(); }, )); } @@ -101,7 +100,7 @@ class FileBubbleState extends State { if (isImagePreview) { if (myFile == null || myFile?.path != path) { setState(() { - myFile = new File(path!); + myFile = File(path!); // reset if (myFile?.existsSync() == false) { myFile = null; @@ -157,18 +156,21 @@ class FileBubbleState extends State { var wdgSender = Visibility( visible: widget.interactive, child: Container( - height: 14 * Provider.of(context).fontScaling, clipBehavior: Clip.hardEdge, decoration: BoxDecoration(), child: compileSenderWidget(context, null, fromMe, senderDisplayStr))); + height: 14 * Provider.of(context).fontScaling, + clipBehavior: Clip.hardEdge, + decoration: const BoxDecoration(), + child: compileSenderWidget(context, null, fromMe, senderDisplayStr))); var isPreview = false; var wdgMessage = !showFileSharing ? Text(AppLocalizations.of(context)!.messageEnableFileSharing, style: Provider.of(context).scaleFonts(defaultTextStyle)) : fromMe ? senderFileChrome(AppLocalizations.of(context)!.messageFileSent, widget.nameSuggestion, widget.rootHash) - : (fileChrome(AppLocalizations.of(context)!.messageFileOffered + ":", widget.nameSuggestion, widget.rootHash, widget.fileSize, + : (fileChrome("${AppLocalizations.of(context)!.messageFileOffered}:", widget.nameSuggestion, widget.rootHash, widget.fileSize, Provider.of(context).downloadSpeed(widget.fileKey()))); Widget wdgDecorations; if (!showFileSharing) { - wdgDecorations = Text('\u202F'); + wdgDecorations = const Text('\u202F'); } else if ((fromMe || downloadComplete) && path != null) { // in this case, whatever marked download.complete would have also set the path if (myFile != null && Provider.of(context).shouldPreview(path)) { @@ -178,21 +180,21 @@ class FileBubbleState extends State { child: MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( - child: Padding(padding: EdgeInsets.all(1.0), child: getPreview(context)), + child: Padding(padding: const EdgeInsets.all(1.0), child: getPreview(context)), onTap: () { pop(context, myFile!, widget.nameSuggestion); }, ))); } else if (fromMe) { - wdgDecorations = Text('\u202F'); + wdgDecorations = const Text('\u202F'); } else { wdgDecorations = Visibility( - visible: widget.interactive, child: SelectableText(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle))); + visible: widget.interactive, child: SelectableText('${AppLocalizations.of(context)!.fileSavedTo}: $path\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle))); } } else if (downloadActive) { if (!downloadGotManifest) { wdgDecorations = Visibility( - visible: widget.interactive, child: SelectableText(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle))); + visible: widget.interactive, child: SelectableText('${AppLocalizations.of(context)!.retrievingManifestMessage}\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle))); } else { wdgDecorations = Visibility( visible: widget.interactive, @@ -205,14 +207,14 @@ class FileBubbleState extends State { // in this case, the download was done in a previous application launch, // so we probably have to request an info lookup if (!downloadInterrupted) { - wdgDecorations = Text(AppLocalizations.of(context)!.fileCheckingStatus + '...' + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); + wdgDecorations = Text('${AppLocalizations.of(context)!.fileCheckingStatus}...\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); // We should have already requested this... } else { var path = Provider.of(context).downloadFinalPath(widget.fileKey()) ?? ""; wdgDecorations = Visibility( visible: widget.interactive, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)), + Text('${AppLocalizations.of(context)!.fileInterrupted}: $path\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)), ElevatedButton(onPressed: _btnResume, child: Text(AppLocalizations.of(context)!.verfiyResumeButton, style: Provider.of(context).scaleFonts(defaultTextButtonStyle))) ])); } @@ -227,9 +229,9 @@ class FileBubbleState extends State { widthFactor: 1, child: Wrap(children: [ Padding( - padding: EdgeInsets.all(5), + padding: const EdgeInsets.all(5), child: ElevatedButton( - child: Text(AppLocalizations.of(context)!.downloadFileButton + '\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnAccept)), + onPressed: _btnAccept, child: Text('${AppLocalizations.of(context)!.downloadFileButton}\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)))), ]))); } else { wdgDecorations = Container(); @@ -260,7 +262,7 @@ class FileBubbleState extends State { ), ), child: Padding( - padding: EdgeInsets.all(9.0), + padding: const EdgeInsets.all(9.0), child: Column( crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, @@ -292,7 +294,7 @@ class FileBubbleState extends State { Provider.of(context, listen: false).cwtch.SetMessageAttribute(profileOnion, conversation, 0, idx, "file-downloaded", "true"); ContactInfoState? contact = Provider.of(context, listen: false).contactList.findContact(Provider.of(context, listen: false).senderHandle); if (contact != null) { - var manifestPath = Provider.of(context, listen: false).downloadPath + "/" + widget.fileKey() + ".manifest"; + var manifestPath = "${Provider.of(context, listen: false).downloadPath}/${widget.fileKey()}.manifest"; Provider.of(context, listen: false).cwtch.CreateDownloadableFile(profileOnion, contact.identifier, widget.nameSuggestion, widget.fileKey(), manifestPath); } @@ -304,8 +306,8 @@ class FileBubbleState extends State { ); if (selectedFileName != null) { file = File(selectedFileName); - EnvironmentConfig.debugLog("saving to " + file.path); - var manifestPath = file.path + ".manifest"; + EnvironmentConfig.debugLog("saving to ${file.path}"); + var manifestPath = "${file.path}.manifest"; Provider.of(context, listen: false).downloadInit(widget.fileKey(), (widget.fileSize / 4096).ceil()); Provider.of(context, listen: false).cwtch.SetMessageAttribute(profileOnion, conversation, 0, idx, "file-downloaded", "true"); ContactInfoState? contact = Provider.of(context, listen: false).contactList.findContact(Provider.of(context, listen: false).senderHandle); @@ -331,9 +333,9 @@ class FileBubbleState extends State { var settings = Provider.of(context); return ListTile( visualDensity: VisualDensity.compact, - contentPadding: EdgeInsets.all(1.0), + contentPadding: const EdgeInsets.all(1.0), title: SelectableText( - fileName + '\u202F', + '$fileName\u202F', style: settings.scaleFonts(defaultMessageTextStyle.copyWith(overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold, color: Provider.of(context).theme.messageFromMeTextColor)), textAlign: TextAlign.left, @@ -341,7 +343,7 @@ class FileBubbleState extends State { maxLines: 2, ), subtitle: SelectableText( - prettyBytes(widget.fileSize) + '\u202F' + '\n' + 'sha512: ' + rootHash + '\u202F', + '${prettyBytes(widget.fileSize)}\u202F\nsha512: $rootHash\u202F', style: settings.scaleFonts(defaultSmallTextStyle.copyWith(fontFamily: "RobotoMono", color: Provider.of(context).theme.messageFromMeTextColor)), textAlign: TextAlign.left, maxLines: 4, @@ -355,9 +357,9 @@ class FileBubbleState extends State { var settings = Provider.of(context); return ListTile( visualDensity: VisualDensity.compact, - contentPadding: EdgeInsets.all(1.0), + contentPadding: const EdgeInsets.all(1.0), title: SelectableText( - fileName + '\u202F', + '$fileName\u202F', style: settings.scaleFonts(defaultMessageTextStyle.copyWith(overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold, color: Provider.of(context).theme.messageFromOtherTextColor)), textAlign: TextAlign.left, @@ -365,7 +367,7 @@ class FileBubbleState extends State { maxLines: 2, ), subtitle: SelectableText( - prettyBytes(widget.fileSize) + '\u202F' + '\n' + 'sha512: ' + rootHash + '\u202F', + '${prettyBytes(widget.fileSize)}\u202F\nsha512: $rootHash\u202F', style: settings.scaleFonts(defaultSmallTextStyle.copyWith(fontFamily: "RobotoMono", color: Provider.of(context).theme.messageFromOtherTextColor)), textAlign: TextAlign.left, maxLines: 4, @@ -376,7 +378,7 @@ class FileBubbleState extends State { trailing: speed == "0 B/s" ? null : SelectableText( - speed + '\u202F', + '$speed\u202F', style: settings.scaleFonts(defaultSmallTextStyle.copyWith(color: Provider.of(context).theme.messageFromOtherTextColor)), textAlign: TextAlign.left, maxLines: 1, @@ -393,20 +395,20 @@ class FileBubbleState extends State { child: SingleChildScrollView( controller: ScrollController(), child: Container( - padding: EdgeInsets.all(10), + padding: const EdgeInsets.all(10), child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ ListTile( - leading: Icon(CwtchIcons.attached_file_3), + leading: const Icon(CwtchIcons.attached_file_3), title: Text(meta), trailing: IconButton( - icon: Icon(Icons.close), + icon: const Icon(Icons.close), color: Provider.of(bcontext, listen: false).theme.mainTextColor, iconSize: 32, onPressed: () { Navigator.pop(bcontext, true); })), Padding( - padding: EdgeInsets.all(10), + padding: const EdgeInsets.all(10), child: Image.file( myFile, cacheWidth: (MediaQuery.of(bcontext).size.width * 0.6).floor(), @@ -419,9 +421,9 @@ class FileBubbleState extends State { visible: Platform.isAndroid, maintainSize: false, child: Padding( - padding: EdgeInsets.all(10), + padding: const EdgeInsets.all(10), child: ElevatedButton.icon( - icon: Icon(Icons.arrow_downward), + icon: const Icon(Icons.arrow_downward), onPressed: androidExport, label: Text( AppLocalizations.of(bcontext)!.saveBtn, diff --git a/lib/widgets/folderpicker.dart b/lib/widgets/folderpicker.dart index 8f16c839..1b40fc4e 100644 --- a/lib/widgets/folderpicker.dart +++ b/lib/widgets/folderpicker.dart @@ -6,7 +6,6 @@ import 'package:provider/provider.dart'; import '../settings.dart'; import 'buttontextfield.dart'; import 'package:path/path.dart' as path; -import 'cwtchlabel.dart'; class CwtchFolderPicker extends StatefulWidget { final String label; @@ -17,8 +16,7 @@ class CwtchFolderPicker extends StatefulWidget { final Key? testKey; final TextStyle? textStyle; final IconData icon; - const CwtchFolderPicker({Key? key, this.testKey, this.textStyle, this.label = "", this.tooltip = "", this.initialValue = "", this.onSave, this.description = "", this.icon = Icons.file_download}) - : super(key: key); + const CwtchFolderPicker({super.key, this.testKey, this.textStyle, this.label = "", this.tooltip = "", this.initialValue = "", this.onSave, this.description = "", this.icon = Icons.file_download}); @override _CwtchFolderPickerState createState() => _CwtchFolderPickerState(); @@ -39,7 +37,7 @@ class _CwtchFolderPickerState extends State { leading: Icon(widget.icon, color: Provider.of(context).theme.messageFromMeTextColor), title: Text(widget.label), subtitle: Text(widget.description), - trailing: Container( + trailing: SizedBox( width: MediaQuery.of(context).size.width / 4, child: CwtchButtonTextField( testKey: widget.testKey, @@ -66,7 +64,7 @@ class _CwtchFolderPickerState extends State { } }, onChanged: widget.onSave, - icon: Icon(Icons.folder), + icon: const Icon(Icons.folder), tooltip: widget.tooltip, ))); } diff --git a/lib/widgets/invitationbubble.dart b/lib/widgets/invitationbubble.dart index 5af90875..6ab86b4b 100644 --- a/lib/widgets/invitationbubble.dart +++ b/lib/widgets/invitationbubble.dart @@ -1,6 +1,3 @@ -import 'dart:convert'; -import 'dart:io'; - import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/message.dart'; @@ -10,7 +7,6 @@ import 'package:cwtch/widgets/malformedbubble.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../main.dart'; -import 'package:intl/intl.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../models/redaction.dart'; @@ -26,7 +22,7 @@ class InvitationBubble extends StatefulWidget { final String inviteNick; final String invite; - InvitationBubble(this.overlay, this.inviteTarget, this.inviteNick, this.invite); + const InvitationBubble(this.overlay, this.inviteTarget, this.inviteNick, this.invite, {super.key}); @override InvitationBubbleState createState() => InvitationBubbleState(); @@ -62,7 +58,7 @@ class InvitationBubbleState extends State { // some kind of malfeasance. var selfInvite = widget.inviteNick == Provider.of(context).onion; if (selfInvite) { - return MalformedBubble(); + return const MalformedBubble(); } var wdgMessage = isGroup && !showGroupInvite @@ -74,25 +70,25 @@ class InvitationBubbleState extends State { Widget wdgDecorations; if (isGroup && !showGroupInvite) { - wdgDecorations = Text('\u202F'); + wdgDecorations = const Text('\u202F'); } else if (fromMe) { wdgDecorations = MessageBubbleDecoration(ackd: Provider.of(context).ackd, errored: Provider.of(context).error, fromMe: fromMe, messageDate: messageDate); } else if (isAccepted) { - wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); - } else if (this.rejected) { - wdgDecorations = Text(AppLocalizations.of(context)!.rejected + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); + wdgDecorations = Text('${AppLocalizations.of(context)!.accepted}\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); + } else if (rejected) { + wdgDecorations = Text('${AppLocalizations.of(context)!.rejected}\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); } else { wdgDecorations = Center( widthFactor: 1, child: Wrap(children: [ Padding( - padding: EdgeInsets.all(5), + padding: const EdgeInsets.all(5), child: ElevatedButton( - child: Text(AppLocalizations.of(context)!.rejectGroupBtn + '\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnReject)), + onPressed: _btnReject, child: Text('${AppLocalizations.of(context)!.rejectGroupBtn}\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)))), Padding( - padding: EdgeInsets.all(5), + padding: const EdgeInsets.all(5), child: ElevatedButton( - child: Text(AppLocalizations.of(context)!.acceptGroupBtn + '\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnAccept)), + onPressed: _btnAccept, child: Text('${AppLocalizations.of(context)!.acceptGroupBtn}\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)))), ])); } @@ -126,11 +122,11 @@ class InvitationBubbleState extends State { child: Center( widthFactor: 1.0, child: Padding( - padding: EdgeInsets.all(9.0), + padding: const EdgeInsets.all(9.0), child: Wrap(runAlignment: WrapAlignment.spaceEvenly, alignment: WrapAlignment.spaceEvenly, runSpacing: 1.0, crossAxisAlignment: WrapCrossAlignment.center, children: [ Center( widthFactor: 1, - child: Padding(padding: EdgeInsets.all(10.0), child: Icon(isGroup && !showGroupInvite ? CwtchIcons.enable_experiments : CwtchIcons.send_invite, size: 32))), + child: Padding(padding: const EdgeInsets.all(10.0), child: Icon(isGroup && !showGroupInvite ? CwtchIcons.enable_experiments : CwtchIcons.send_invite, size: 32))), Center( widthFactor: 1.0, child: Column( @@ -167,14 +163,14 @@ class InvitationBubbleState extends State { return Wrap(children: [ SelectableText( - chrome + '\u202F', + '$chrome\u202F', style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of(context).theme.messageFromMeTextColor)), textAlign: TextAlign.left, maxLines: 2, textWidthBasis: TextWidthBasis.longestLine, ), SelectableText( - targetName + '\u202F', + '$targetName\u202F', style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of(context).theme.messageFromMeTextColor)), textAlign: TextAlign.left, maxLines: 2, @@ -189,14 +185,14 @@ class InvitationBubbleState extends State { return Wrap(children: [ SelectableText( - chrome + '\u202F', + '$chrome\u202F', style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of(context).theme.messageFromOtherTextColor)), textAlign: TextAlign.left, textWidthBasis: TextWidthBasis.longestLine, maxLines: 2, ), SelectableText( - targetName + '\u202F', + '$targetName\u202F', style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of(context).theme.messageFromOtherTextColor)), textAlign: TextAlign.left, maxLines: 2, diff --git a/lib/widgets/malformedbubble.dart b/lib/widgets/malformedbubble.dart index 0a6c1c27..023a8ec3 100644 --- a/lib/widgets/malformedbubble.dart +++ b/lib/widgets/malformedbubble.dart @@ -2,10 +2,12 @@ import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -final Color malformedColor = Color(0xFFE85DA1); +const Color malformedColor = Color(0xFFE85DA1); // MalformedBubble is displayed in the case of a malformed message class MalformedBubble extends StatefulWidget { + const MalformedBubble({super.key}); + @override MalformedBubbleState createState() => MalformedBubbleState(); } @@ -17,7 +19,7 @@ class MalformedBubbleState extends State { decoration: BoxDecoration( color: malformedColor, border: Border.all(color: malformedColor, width: 1), - borderRadius: BorderRadius.only( + borderRadius: const BorderRadius.only( topLeft: Radius.zero, topRight: Radius.zero, bottomLeft: Radius.zero, @@ -29,9 +31,9 @@ class MalformedBubbleState extends State { child: Center( widthFactor: 1.0, child: Padding( - padding: EdgeInsets.all(9.0), + padding: const EdgeInsets.all(9.0), child: Row(mainAxisSize: MainAxisSize.min, children: [ - Center( + const Center( widthFactor: 1, child: Padding( padding: EdgeInsets.all(4), diff --git a/lib/widgets/messageBubbleWidgetHelpers.dart b/lib/widgets/messageBubbleWidgetHelpers.dart index 8771ffcd..9a52ff68 100644 --- a/lib/widgets/messageBubbleWidgetHelpers.dart +++ b/lib/widgets/messageBubbleWidgetHelpers.dart @@ -9,7 +9,7 @@ Widget compileSenderWidget(BuildContext context, BoxConstraints? constraints, bo return Container( height: 14 * Provider.of(context).fontScaling, clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), child: SelectableText(senderDisplayStr, maxLines: 1, style: TextStyle( @@ -23,10 +23,10 @@ Widget compileSenderWidget(BuildContext context, BoxConstraints? constraints, bo Widget compileMessageContentWidget(BuildContext context, BoxConstraints constraints, fromMe, String content, FocusNode focus, bool formatMessages, bool showClickableLinks) { return SelectableLinkify( - text: content + '\u202F', + text: '$content\u202F', // TODO: onOpen breaks the "selectable" functionality. Maybe something to do with gesture handler? options: LinkifyOptions(messageFormatting: formatMessages, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true), - linkifiers: [UrlLinkifier()], + linkifiers: const [UrlLinkifier()], onOpen: showClickableLinks ? (link) { modalOpenLink(context, link); diff --git a/lib/widgets/messagebubble.dart b/lib/widgets/messagebubble.dart index b8747371..37d43d6c 100644 --- a/lib/widgets/messagebubble.dart +++ b/lib/widgets/messagebubble.dart @@ -1,11 +1,6 @@ -import 'dart:io'; - -import 'package:cwtch/controllers/open_link_modal.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/message.dart'; import 'package:cwtch/models/redaction.dart'; -import 'package:cwtch/themes/opaque.dart'; -import 'package:cwtch/third_party/linkify/flutter_linkify.dart'; import 'package:cwtch/models/profile.dart'; import 'package:cwtch/widgets/malformedbubble.dart'; import 'package:flutter/material.dart'; @@ -18,14 +13,14 @@ import 'messagebubbledecorations.dart'; class MessageBubble extends StatefulWidget { final String content; - MessageBubble(this.content); + const MessageBubble(this.content, {super.key}); @override MessageBubbleState createState() => MessageBubbleState(); } class MessageBubbleState extends State { - FocusNode _focus = FocusNode(); + final FocusNode _focus = FocusNode(); @override Widget build(BuildContext context) { @@ -67,7 +62,7 @@ class MessageBubbleState extends State { ), ), child: Padding( - padding: EdgeInsets.all(9.0), + padding: const EdgeInsets.all(9.0), child: Theme( data: Theme.of(context).copyWith( textSelectionTheme: TextSelectionThemeData( diff --git a/lib/widgets/messagebubbledecorations.dart b/lib/widgets/messagebubbledecorations.dart index 104a41b0..01793e6a 100644 --- a/lib/widgets/messagebubbledecorations.dart +++ b/lib/widgets/messagebubbledecorations.dart @@ -1,6 +1,3 @@ -import 'dart:io'; -import 'package:flutter/cupertino.dart'; -import 'package:intl/intl.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../models/redaction.dart'; @@ -9,7 +6,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; // Provides message decorations (acks/errors/dates etc.) for generic message bubble overlays (chats, invites etc.) class MessageBubbleDecoration extends StatefulWidget { - MessageBubbleDecoration({required this.ackd, required this.errored, required this.messageDate, required this.fromMe}); + const MessageBubbleDecoration({super.key, required this.ackd, required this.errored, required this.messageDate, required this.fromMe}); final DateTime messageDate; final bool fromMe; final bool ackd; @@ -38,9 +35,9 @@ class _MessageBubbleDecoration extends State { color: widget.fromMe ? Provider.of(context).theme.messageFromMeTextColor : Provider.of(context).theme.messageFromOtherTextColor), textAlign: widget.fromMe ? TextAlign.right : TextAlign.left), !widget.fromMe - ? SizedBox(width: 1, height: 1) + ? const SizedBox(width: 1, height: 1) : Padding( - padding: EdgeInsets.all(1.0), + padding: const EdgeInsets.all(1.0), child: widget.ackd == true ? Tooltip( message: AppLocalizations.of(context)!.acknowledgedLabel, child: Icon(Icons.check_circle_outline, color: Provider.of(context).theme.messageFromMeTextColor, size: 16)) diff --git a/lib/widgets/messagelist.dart b/lib/widgets/messagelist.dart index 1c2a987d..25971e71 100644 --- a/lib/widgets/messagelist.dart +++ b/lib/widgets/messagelist.dart @@ -5,7 +5,6 @@ import 'package:cwtch/models/messagecache.dart'; import 'package:cwtch/models/profile.dart'; import 'package:cwtch/widgets/messageloadingbubble.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:provider/provider.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -14,7 +13,7 @@ import '../settings.dart'; class MessageList extends StatefulWidget { ItemPositionsListener scrollListener; - MessageList(this.scrollListener); + MessageList(this.scrollListener, {super.key}); @override _MessageListState createState() => _MessageListState(); @@ -46,17 +45,17 @@ class _MessageListState extends State { // OR if users have never clicked the button AND they appear offline, then they can click the button // NOTE: all these listeners are false...this is not ideal, but if they were true we would end up rebuilding the message view every tick (which would kill performance) // any significant changes in state e.g. peer offline or button clicks will trigger a rebuild anyway - bool canReconnect = DateTime.now().difference(Provider.of(context, listen: false).lastRetryTime).abs() > Duration(seconds: 30) || + bool canReconnect = DateTime.now().difference(Provider.of(context, listen: false).lastRetryTime).abs() > const Duration(seconds: 30) || (Provider.of(context, listen: false).appearOffline && (Provider.of(context, listen: false).lastRetryTime == Provider.of(context, listen: false).loaded)); var reconnectButton = Padding( - padding: EdgeInsets.all(2), + padding: const EdgeInsets.all(2), child: canReconnect ? Tooltip( message: AppLocalizations.of(context)!.retryConnectionTooltip, child: ElevatedButton( - style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))), + style: ButtonStyle(padding: MaterialStateProperty.all(const EdgeInsets.all(20))), child: Text(AppLocalizations.of(context)!.retryConnection), onPressed: () { if (Provider.of(context, listen: false).isGroup) { @@ -84,7 +83,7 @@ class _MessageListState extends State { Visibility( visible: showMessageWarning, child: Container( - padding: EdgeInsets.all(5.0), + padding: const EdgeInsets.all(5.0), color: Provider.of(context).theme.defaultButtonActiveColor, child: DefaultTextStyle( style: TextStyle(color: Provider.of(context).theme.defaultButtonTextColor), @@ -104,7 +103,7 @@ class _MessageListState extends State { ? Text(AppLocalizations.of(context)!.chatHistoryDefault, textAlign: TextAlign.center) : // We are not allowed to put null here, so put an empty text widget - Text("")), + const Text("")), ))), Expanded( child: Container( @@ -120,7 +119,7 @@ class _MessageListState extends State { : DecorationImage( fit: BoxFit.scaleDown, alignment: Alignment.center, - image: AssetImage("assets/core/negative_heart_512px.png"), + image: const AssetImage("assets/core/negative_heart_512px.png"), colorFilter: ColorFilter.mode(Provider.of(context).theme.hilightElementColor.withOpacity(0.15), BlendMode.srcIn))), // Don't load messages for syncing server... child: loadMessages @@ -146,7 +145,7 @@ class _MessageListState extends State { var key = Provider.of(itemBuilderContext, listen: false).getMessageKey(contactHandle, messageIndex); return message.getWidget(fbcontext, key, messageIndex); } else { - return MessageLoadingBubble(); + return const MessageLoadingBubble(); } }, ); diff --git a/lib/widgets/messageloadingbubble.dart b/lib/widgets/messageloadingbubble.dart index aeba1657..b2177e96 100644 --- a/lib/widgets/messageloadingbubble.dart +++ b/lib/widgets/messageloadingbubble.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; class MessageLoadingBubble extends StatefulWidget { + const MessageLoadingBubble({super.key}); + @override MessageLoadingBubbleState createState() => MessageLoadingBubbleState(); } @@ -8,6 +10,6 @@ class MessageLoadingBubble extends StatefulWidget { class MessageLoadingBubbleState extends State { @override Widget build(BuildContext context) { - return Center(child: Row(children: [SizedBox(width: 40, height: 100, child: Text(""))])); + return const Center(child: Row(children: [SizedBox(width: 40, height: 100, child: Text(""))])); } } diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index 300a05d0..7226ea66 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -25,7 +25,7 @@ class MessageRow extends StatefulWidget { final Widget child; final int index; - MessageRow(this.child, this.index, {Key? key}) : super(key: key); + const MessageRow(this.child, this.index, {super.key}); @override MessageRowState createState() => MessageRowState(); @@ -51,9 +51,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi @override void dispose() { - if (_controller != null) { - _controller.dispose(); - } + _controller.dispose(); super.dispose(); } @@ -79,7 +77,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi } Widget wdgReply = Platform.isAndroid - ? SizedBox.shrink() + ? const SizedBox.shrink() : Visibility( visible: EnvironmentConfig.TEST_MODE || Provider.of(context).hoveredIndex == Provider.of(context).messageID, maintainSize: true, @@ -104,7 +102,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi var cache = Provider.of(context).messageCache; Widget wdgSeeReplies = Platform.isAndroid - ? SizedBox.shrink() + ? const SizedBox.shrink() : Visibility( visible: EnvironmentConfig.TEST_MODE || Provider.of(context).hoveredIndex == Provider.of(context).messageID, maintainSize: true, @@ -124,7 +122,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi var message = Provider.of(context, listen: false); Widget wdgTranslateMessage = Platform.isAndroid - ? SizedBox.shrink() + ? const SizedBox.shrink() : Visibility( visible: Provider.of(context, listen: false).cwtch.IsBlodeuweddSupported() && Provider.of(context).isExperimentEnabled(BlodeuweddExperiment) && @@ -156,22 +154,22 @@ class MessageRowState extends State with SingleTickerProviderStateMi ]; } else if (isBlocked && !showBlockedMessage) { Color blockedMessageBackground = Provider.of(context).theme.messageFromOtherBackgroundColor; - Widget wdgPortrait = Padding(padding: EdgeInsets.all(4.0), child: Icon(CwtchIcons.account_blocked)); + Widget wdgPortrait = const Padding(padding: EdgeInsets.all(4.0), child: Icon(CwtchIcons.account_blocked)); widgetRow = [ wdgPortrait, Container( - padding: EdgeInsets.all(2.0), + padding: const EdgeInsets.all(2.0), decoration: BoxDecoration( color: blockedMessageBackground, border: Border.all(color: blockedMessageBackground, width: 2), - borderRadius: BorderRadius.only( + borderRadius: const BorderRadius.only( topLeft: Radius.circular(15.0), topRight: Radius.circular(15.0), bottomLeft: Radius.circular(15.0), bottomRight: Radius.circular(15.0), )), child: Padding( - padding: EdgeInsets.all(9.0), + padding: const EdgeInsets.all(9.0), child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ SelectableText( AppLocalizations.of(context)!.blockedMessageMessage, @@ -183,18 +181,18 @@ class MessageRowState extends State with SingleTickerProviderStateMi textWidthBasis: TextWidthBasis.longestLine, ), Padding( - padding: EdgeInsets.all(1.0), + padding: const EdgeInsets.all(1.0), child: TextButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(blockedMessageBackground), ), child: Text( - AppLocalizations.of(context)!.showMessageButton + '\u202F', - style: TextStyle(decoration: TextDecoration.underline), + '${AppLocalizations.of(context)!.showMessageButton}\u202F', + style: const TextStyle(decoration: TextDecoration.underline), ), onPressed: () { setState(() { - this.showBlockedMessage = true; + showBlockedMessage = true; }); })), ]))), @@ -217,7 +215,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi ? _btnGoto : _btnAdd, child: Padding( - padding: EdgeInsets.all(4.0), + padding: const EdgeInsets.all(4.0), child: ProfileImage( diameter: 48.0, // default to the contact image...otherwise use a derived sender image... @@ -278,7 +276,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi } }, child: Padding( - padding: EdgeInsets.all(2), + padding: const EdgeInsets.all(2), child: Align( widthFactor: 1, alignment: _dragAlignment, @@ -291,7 +289,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi if (Provider.of(context).newMarkerMsgIndex == widget.index) { return Column( crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, - children: [Align(alignment: Alignment.center, child: Padding(padding: EdgeInsets.all(5.0), child: _bubbleNew())), mr]); + children: [Align(alignment: Alignment.center, child: Padding(padding: const EdgeInsets.all(5.0), child: _bubbleNew())), mr]); } else { return mr; } @@ -302,14 +300,14 @@ class MessageRowState extends State with SingleTickerProviderStateMi decoration: BoxDecoration( color: Provider.of(context).theme.messageFromMeBackgroundColor, border: Border.all(color: Provider.of(context).theme.messageFromMeBackgroundColor, width: 1), - borderRadius: BorderRadius.only( + borderRadius: const BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8), bottomLeft: Radius.circular(8), bottomRight: Radius.circular(8), ), ), - child: Padding(padding: EdgeInsets.all(9.0), child: Text(AppLocalizations.of(context)!.newMessagesLabel, style: Provider.of(context).scaleFonts(defaultTextButtonStyle)))); + child: Padding(padding: const EdgeInsets.all(9.0), child: Text(AppLocalizations.of(context)!.newMessagesLabel, style: Provider.of(context).scaleFonts(defaultTextButtonStyle)))); } void _runAnimation(Offset pixelsPerSecond, Size size) { @@ -361,20 +359,20 @@ class MessageRowState extends State with SingleTickerProviderStateMi showAddContactConfirmAlertDialog(BuildContext context, String profileOnion, String senderOnion) { // set up the buttons Widget cancelButton = ElevatedButton( - child: Text(AppLocalizations.of(context)!.cancel), - style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))), + style: ButtonStyle(padding: MaterialStateProperty.all(const EdgeInsets.all(20))), onPressed: () { Navigator.of(context).pop(); // dismiss dialog }, + child: Text(AppLocalizations.of(context)!.cancel), ); Widget continueButton = ElevatedButton( - style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))), + style: ButtonStyle(padding: MaterialStateProperty.all(const EdgeInsets.all(20))), child: Text(AppLocalizations.of(context)!.addContact), onPressed: () { Provider.of(context, listen: false).cwtch.ImportBundle(profileOnion, senderOnion); final snackBar = SnackBar( content: Text(AppLocalizations.of(context)!.successfullAddedContact), - duration: Duration(seconds: 2), + duration: const Duration(seconds: 2), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); Navigator.of(context).pop(); // dismiss dialog @@ -431,7 +429,7 @@ void modalShowReplies( } var image = Padding( - padding: EdgeInsets.all(4.0), + padding: const EdgeInsets.all(4.0), child: ProfileImage( imagePath: imagePath, diameter: 48.0, @@ -441,7 +439,7 @@ void modalShowReplies( )); return Padding( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Row( mainAxisSize: MainAxisSize.min, children: [image, Flexible(child: bubble)], @@ -453,21 +451,21 @@ void modalShowReplies( var original = StaticMessageBubble(profile, settings, cache.cache[messageID]!.metadata, Row(children: [Flexible(child: compileOverlay(cache.cache[messageID]!).getPreviewWidget(context))])); - withHeader.insert(0, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: original))); + withHeader.insert(0, Padding(padding: const EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: original))); withHeader.insert( 1, Padding( - padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), + padding: const EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Divider( color: settings.theme.mainTextColor, ))); if (replies.isNotEmpty) { - withHeader.insert(2, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Text(replyHeader, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)))); + withHeader.insert(2, Padding(padding: const EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Text(replyHeader, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)))); } else { - withHeader.insert( - 2, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: Text(noRepliesText, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold))))); + withHeader.insert(2, + Padding(padding: const EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: Text(noRepliesText, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold))))); } return Scrollbar( @@ -480,7 +478,7 @@ void modalShowReplies( minHeight: viewportConstraints.maxHeight, ), child: Padding( - padding: EdgeInsets.symmetric(vertical: 0.0, horizontal: 20.0), + padding: const EdgeInsets.symmetric(vertical: 0.0, horizontal: 20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: withHeader, @@ -497,7 +495,7 @@ void modalShowTranslation(BuildContext context, ProfileInfoState profile, Settin ) { return StatefulBuilder(builder: (BuildContext scontext, StateSetter setState /*You can rename this!*/) { if (scontext.mounted) { - new Timer.periodic(Duration(seconds: 1), (Timer t) { + Timer.periodic(const Duration(seconds: 1), (Timer t) { if (scontext.mounted) { setState(() {}); } @@ -512,13 +510,13 @@ void modalShowTranslation(BuildContext context, ProfileInfoState profile, Settin Provider.of(context).translation == "" ? Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ CircularProgressIndicator(color: settings.theme.defaultButtonActiveColor), - Padding(padding: EdgeInsets.all(5.0), child: Text(AppLocalizations.of(context)!.blodeuweddProcessing)) + Padding(padding: const EdgeInsets.all(5.0), child: Text(AppLocalizations.of(context)!.blodeuweddProcessing)) ]) : Flexible(child: SelectableText(Provider.of(context).translation)) ])); var image = Padding( - padding: EdgeInsets.all(4.0), + padding: const EdgeInsets.all(4.0), child: ProfileImage( imagePath: "assets/blodeuwedd.png", diameter: 48.0, @@ -527,14 +525,14 @@ void modalShowTranslation(BuildContext context, ProfileInfoState profile, Settin badgeColor: Colors.red, )); - return Container( + return SizedBox( height: 300, // bespoke value courtesy of the [TextField] docs child: Container( alignment: Alignment.center, child: Padding( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Padding( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Row( mainAxisSize: MainAxisSize.min, children: [image, Flexible(child: bubble)], @@ -599,5 +597,5 @@ String RandomProfileImage(String onion) { "050-unicorn" ]; var encoding = base32.decode(onion.toUpperCase()); - return "assets/profiles/" + choices[encoding[33] % choices.length] + ".png"; + return "assets/profiles/${choices[encoding[33] % choices.length]}.png"; } diff --git a/lib/widgets/passwordfield.dart b/lib/widgets/passwordfield.dart index e22ca6fe..6e4942ea 100644 --- a/lib/widgets/passwordfield.dart +++ b/lib/widgets/passwordfield.dart @@ -9,13 +9,14 @@ const hints = [AutofillHints.password]; // Provides a styled Password Input Field for use in Form Widgets. // Callers must provide a text controller, label helper text and a validator. class CwtchPasswordField extends StatefulWidget { - CwtchPasswordField({required this.controller, required this.validator, this.action, this.autofocus = false, this.autoFillHints = hints, this.key}); + const CwtchPasswordField({super.key, required this.controller, required this.validator, this.action, this.autofocus = false, this.autoFillHints = hints, this.ikey}); final TextEditingController controller; final FormFieldValidator validator; final Function(String)? action; final bool autofocus; final Iterable autoFillHints; - final Key? key; + @override + final Key? ikey; @override _CwtchPasswordTextFieldState createState() => _CwtchPasswordTextFieldState(); diff --git a/lib/widgets/profileimage.dart b/lib/widgets/profileimage.dart index bd729a31..42c533ec 100644 --- a/lib/widgets/profileimage.dart +++ b/lib/widgets/profileimage.dart @@ -9,8 +9,9 @@ import 'package:provider/provider.dart'; import '../settings.dart'; class ProfileImage extends StatefulWidget { - ProfileImage( - {required this.imagePath, + const ProfileImage( + {super.key, + required this.imagePath, required this.diameter, required this.border, this.badgeCount = 0, @@ -20,7 +21,7 @@ class ProfileImage extends StatefulWidget { this.tooltip = "", this.disabled = false, this.badgeEdit = false, - this.badgeIcon = null}); + this.badgeIcon}); final double diameter; final String imagePath; final Color border; @@ -40,7 +41,7 @@ class ProfileImage extends StatefulWidget { class _ProfileImageState extends State { @override Widget build(BuildContext context) { - var file = new File(widget.imagePath); + var file = File(widget.imagePath); var image = Image.file( file, cacheWidth: (4 * widget.diameter.floor()), @@ -105,13 +106,13 @@ class _ProfileImageState extends State { Icons.edit, color: widget.badgeTextColor, ) - : (widget.badgeIcon != null ? widget.badgeIcon : Text(widget.badgeCount > 99 ? "99+" : widget.badgeCount.toString(), style: TextStyle(color: widget.badgeTextColor, fontSize: 8.0))), + : (widget.badgeIcon ?? Text(widget.badgeCount > 99 ? "99+" : widget.badgeCount.toString(), style: TextStyle(color: widget.badgeTextColor, fontSize: 8.0))), ), )), // disabled center icon Visibility( visible: widget.disabled, - child: Container( + child: SizedBox( width: widget.diameter, height: widget.diameter, child: Center( diff --git a/lib/widgets/profilerow.dart b/lib/widgets/profilerow.dart index 65fb34a1..ad1f159f 100644 --- a/lib/widgets/profilerow.dart +++ b/lib/widgets/profilerow.dart @@ -1,9 +1,7 @@ import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/contactlist.dart'; import 'package:cwtch/models/profile.dart'; -import 'package:cwtch/models/profilelist.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:cwtch/views/addeditprofileview.dart'; import 'package:cwtch/views/contactsview.dart'; import 'package:cwtch/views/doublecolview.dart'; @@ -11,11 +9,12 @@ import 'package:cwtch/widgets/profileimage.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import '../errorHandler.dart'; import '../main.dart'; import '../settings.dart'; class ProfileRow extends StatefulWidget { + const ProfileRow({super.key}); + @override _ProfileRowState createState() => _ProfileRowState(); } @@ -26,7 +25,7 @@ class _ProfileRowState extends State { var profile = Provider.of(context); return Card( clipBehavior: Clip.antiAlias, - margin: EdgeInsets.all(0.0), + margin: const EdgeInsets.all(0.0), child: InkWell( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -49,8 +48,8 @@ class _ProfileRowState extends State { Container( height: 18.0 * Provider.of(context).fontScaling + 10.0, clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(), - padding: EdgeInsets.all(5.0), + decoration: const BoxDecoration(), + padding: const EdgeInsets.all(5.0), child: Text( profile.nickname, semanticsLabel: profile.nickname, @@ -75,7 +74,7 @@ class _ProfileRowState extends State { IconButton( enableFeedback: true, splashRadius: Material.defaultSplashRadius / 2, - tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname, + tooltip: "${AppLocalizations.of(context)!.editProfile} ${profile.nickname}", icon: Icon(Icons.create, color: Provider.of(context).current().mainTextColor), onPressed: () { _pushEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath, encrypted: profile.isEncrypted); @@ -98,7 +97,7 @@ class _ProfileRowState extends State { void _pushContactList(ProfileInfoState profile, bool isLandscape) { Navigator.of(context).push( PageRouteBuilder( - settings: RouteSettings(name: "conversations"), + settings: const RouteSettings(name: "conversations"), pageBuilder: (c, a1, a2) { return OrientationBuilder(builder: (orientationBuilderContext, orientation) { return MultiProvider( @@ -106,17 +105,17 @@ class _ProfileRowState extends State { builder: (innercontext, widget) { var appState = Provider.of(context); var settings = Provider.of(context); - return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? DoubleColumnView() : ContactsView(); + return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? const DoubleColumnView() : const ContactsView(); }); }); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } - void _pushEditProfile({onion: "", displayName: "", profileImage: "", encrypted: true}) { + void _pushEditProfile({onion = "", displayName = "", profileImage = "", encrypted = true}) { Navigator.of(context).push( PageRouteBuilder( pageBuilder: (bcontext, a1, a2) { @@ -127,11 +126,11 @@ class _ProfileRowState extends State { value: profile, ), ], - builder: (context, widget) => AddEditProfileView(), + builder: (context, widget) => const AddEditProfileView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); } diff --git a/lib/widgets/quotedmessage.dart b/lib/widgets/quotedmessage.dart index 045a380d..83eec009 100644 --- a/lib/widgets/quotedmessage.dart +++ b/lib/widgets/quotedmessage.dart @@ -15,14 +15,14 @@ class QuotedMessageBubble extends StatefulWidget { final Future quotedMessage; final String body; - QuotedMessageBubble(this.body, this.quotedMessage); + const QuotedMessageBubble(this.body, this.quotedMessage, {super.key}); @override QuotedMessageBubbleState createState() => QuotedMessageBubbleState(); } class QuotedMessageBubbleState extends State { - FocusNode _focus = FocusNode(); + final FocusNode _focus = FocusNode(); @override Widget build(BuildContext context) { @@ -84,14 +84,12 @@ class QuotedMessageBubbleState extends State { var messageInfo = Provider.of(context, listen: false).messageCache.getByContentHash(qMessage.getMetadata().contenthash); if (messageInfo != null) { var index = Provider.of(context, listen: false).messageCache.findIndex(messageInfo.metadata.messageID); - if (index != null) { - Provider.of(context, listen: false).messageScrollController.scrollTo(index: index, duration: Duration(milliseconds: 100)); - } + Provider.of(context, listen: false).messageScrollController.scrollTo(index: index, duration: const Duration(milliseconds: 100)); } }, child: Container( - margin: EdgeInsets.all(5), - padding: EdgeInsets.all(5), + margin: const EdgeInsets.all(5), + padding: const EdgeInsets.all(5), clipBehavior: Clip.antiAliasWithSaveLayer, decoration: BoxDecoration( color: fromMe ? Provider.of(context).theme.messageFromOtherBackgroundColor : Provider.of(context).theme.messageFromMeBackgroundColor, @@ -101,18 +99,18 @@ class QuotedMessageBubbleState extends State { Align(alignment: Alignment.centerLeft, child: wdgReplyingTo), Flexible( child: Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding(padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0), child: Icon(Icons.reply, size: 32, color: qTextColor)), + Padding(padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0), child: Icon(Icons.reply, size: 32, color: qTextColor)), Flexible(child: IntrinsicWidth(child: qMessage.getPreviewWidget(context))), ])) ])), ), ); } catch (e) { - return MalformedBubble(); + return const MalformedBubble(); } } else { // This should be almost instantly resolved, any failure likely means an issue in decoding... - return MessageLoadingBubble(); + return const MessageLoadingBubble(); } }, ); @@ -135,7 +133,7 @@ class QuotedMessageBubbleState extends State { ), ), child: Padding( - padding: EdgeInsets.all(9.0), + padding: const EdgeInsets.all(9.0), child: Theme( data: Theme.of(context).copyWith( textSelectionTheme: TextSelectionThemeData( diff --git a/lib/widgets/remoteserverrow.dart b/lib/widgets/remoteserverrow.dart index 113ad060..d6aff3b2 100644 --- a/lib/widgets/remoteserverrow.dart +++ b/lib/widgets/remoteserverrow.dart @@ -1,22 +1,17 @@ import 'package:cwtch/main.dart'; import 'package:cwtch/models/profile.dart'; -import 'package:cwtch/models/profileservers.dart'; import 'package:cwtch/models/remoteserver.dart'; -import 'package:cwtch/models/servers.dart'; import 'package:cwtch/themes/opaque.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 '../settings.dart'; class RemoteServerRow extends StatefulWidget { + const RemoteServerRow({super.key}); + @override _RemoteServerRowState createState() => _RemoteServerRowState(); } @@ -30,7 +25,7 @@ class _RemoteServerRowState extends State { return Consumer(builder: (context, profile, child) { return Card( clipBehavior: Clip.antiAlias, - margin: EdgeInsets.all(0.0), + margin: const EdgeInsets.all(0.0), child: InkWell( child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( @@ -80,15 +75,15 @@ class _RemoteServerRowState extends State { onTap: () { Navigator.of(context).push( PageRouteBuilder( - settings: RouteSettings(name: "remoteserverview"), + settings: const RouteSettings(name: "remoteserverview"), pageBuilder: (bcontext, a1, a2) { return MultiProvider( providers: [ChangeNotifierProvider.value(value: profile), ChangeNotifierProvider.value(value: server), Provider.value(value: Provider.of(context))], - child: RemoteServerView(), + child: const RemoteServerView(), ); }, transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child), - transitionDuration: Duration(milliseconds: 200), + transitionDuration: const Duration(milliseconds: 200), ), ); })); diff --git a/lib/widgets/serverrow.dart b/lib/widgets/serverrow.dart index dd402d0a..8b52b001 100644 --- a/lib/widgets/serverrow.dart +++ b/lib/widgets/serverrow.dart @@ -11,6 +11,8 @@ import '../errorHandler.dart'; import '../settings.dart'; class ServerRow extends StatefulWidget { + const ServerRow({super.key}); + @override _ServerRowState createState() => _ServerRowState(); } @@ -21,7 +23,7 @@ class _ServerRowState extends State { var server = Provider.of(context); return Card( clipBehavior: Clip.antiAlias, - margin: EdgeInsets.all(0.0), + margin: const EdgeInsets.all(0.0), child: InkWell( child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( @@ -69,7 +71,7 @@ class _ServerRowState extends State { tooltip: AppLocalizations.of(context)!.copyServerKeys, icon: Icon(CwtchIcons.address_copy, color: Provider.of(context).current().mainTextColor), onPressed: () { - Clipboard.setData(new ClipboardData(text: server.serverBundle)); + Clipboard.setData(ClipboardData(text: server.serverBundle)); final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification)); ScaffoldMessenger.of(context).showSnackBar(snackBar); }, @@ -94,11 +96,11 @@ class _ServerRowState extends State { void _pushEditServer(ServerInfoState server) { Provider.of(context, listen: false).reset(); Navigator.of(context).push(MaterialPageRoute( - settings: RouteSettings(name: "serveraddedit"), + settings: const RouteSettings(name: "serveraddedit"), builder: (BuildContext context) { return MultiProvider( providers: [ChangeNotifierProvider.value(value: server)], - child: AddEditServerView(), + child: const AddEditServerView(), ); }, )); diff --git a/lib/widgets/staticmessagebubble.dart b/lib/widgets/staticmessagebubble.dart index 5bb22b92..46d045fb 100644 --- a/lib/widgets/staticmessagebubble.dart +++ b/lib/widgets/staticmessagebubble.dart @@ -3,7 +3,6 @@ import 'package:cwtch/models/message.dart'; import 'package:cwtch/models/profile.dart'; import 'package:cwtch/widgets/malformedbubble.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import '../models/redaction.dart'; import '../settings.dart'; @@ -16,7 +15,7 @@ class StaticMessageBubble extends StatefulWidget { final MessageMetadata metadata; final Widget child; - StaticMessageBubble(this.profile, this.settings, this.metadata, this.child); + const StaticMessageBubble(this.profile, this.settings, this.metadata, this.child, {super.key}); @override StaticMessageBubbleState createState() => StaticMessageBubbleState(); @@ -61,7 +60,7 @@ class StaticMessageBubbleState extends State { ), ), child: Padding( - padding: EdgeInsets.all(9.0), + padding: const EdgeInsets.all(9.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, diff --git a/lib/widgets/textfield.dart b/lib/widgets/textfield.dart index 38e194f2..dec8093a 100644 --- a/lib/widgets/textfield.dart +++ b/lib/widgets/textfield.dart @@ -1,5 +1,4 @@ import 'package:cwtch/themes/opaque.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; @@ -10,8 +9,17 @@ doNothing(String x) {} // Provides a styled Text Field for use in Form Widgets. // Callers must provide a text controller, label helper text and a validator. class CwtchTextField extends StatefulWidget { - CwtchTextField( - {required this.controller, this.hintText = "", this.validator, this.autofocus = false, this.onChanged = doNothing, this.number = false, this.multiLine = false, this.key, this.testKey}); + const CwtchTextField( + {super.key, + required this.controller, + this.hintText = "", + this.validator, + this.autofocus = false, + this.onChanged = doNothing, + this.number = false, + this.multiLine = false, + this.ikey, + this.testKey}); final TextEditingController controller; final String hintText; final FormFieldValidator? validator; @@ -19,7 +27,8 @@ class CwtchTextField extends StatefulWidget { final bool autofocus; final bool multiLine; final bool number; - final Key? key; + @override + final Key? ikey; final Key? testKey; @override @@ -46,7 +55,7 @@ class _CwtchTextFieldState extends State { return Consumer(builder: (context, theme, child) { return Container( clipBehavior: Clip.antiAlias, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ child: Theme( data: Theme.of(context).copyWith( @@ -84,7 +93,7 @@ class _CwtchTextFieldState extends State { errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), errorStyle: TextStyle(color: theme.current().textfieldErrorColor, fontWeight: FontWeight.bold, overflow: TextOverflow.visible), fillColor: theme.current().textfieldBackgroundColor, - contentPadding: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0), + contentPadding: const EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0), enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))), ))); }); diff --git a/lib/widgets/tor_icon.dart b/lib/widgets/tor_icon.dart index 5bdebfa8..0ff07991 100644 --- a/lib/widgets/tor_icon.dart +++ b/lib/widgets/tor_icon.dart @@ -7,7 +7,7 @@ import '../torstatus.dart'; /// A reusable Tor Icon Widget that displays the current status of the underlying Tor connections class TorIcon extends StatefulWidget { - TorIcon(); + const TorIcon({super.key}); @override State createState() => _TorIconState(); diff --git a/pubspec.lock b/pubspec.lock index 44fdb30e..2a563c8d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -961,7 +961,7 @@ packages: source: hosted version: "6.3.0" yaml: - dependency: transitive + dependency: "direct main" description: name: yaml sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" diff --git a/test/buttontextfield_test.dart b/test/buttontextfield_test.dart index 95c2d65a..f1905fcb 100644 --- a/test/buttontextfield_test.dart +++ b/test/buttontextfield_test.dart @@ -15,13 +15,13 @@ import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -var settingsEnglishDark = Settings(Locale("en", ''), CwtchDark()); -var settingsEnglishLight = Settings(Locale("en", ''), CwtchLight()); +var settingsEnglishDark = Settings(const Locale("en", ''), CwtchDark()); +var settingsEnglishLight = Settings(const Locale("en", ''), CwtchLight()); ChangeNotifierProvider getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark); void main() { testWidgets('CwtchButtonTextField widget test', (WidgetTester tester) async { - final String testingStr = "A wonderful label"; + const String testingStr = "A wonderful label"; final TextEditingController ctrlr1 = TextEditingController(text: testingStr); // await tester.pumpWidget(MultiProvider( @@ -38,7 +38,7 @@ void main() { title: 'Test', theme: mkThemeData(Provider.of(context)), home: Card(child: CwtchButtonTextField( - icon: Icon(Icons.bug_report_outlined), + icon: const Icon(Icons.bug_report_outlined), tooltip: testingStr, controller: ctrlr1, onPressed: () { }, )), diff --git a/test/cwtchlabel_test.dart b/test/cwtchlabel_test.dart index 63ad75b7..8e08c9b1 100644 --- a/test/cwtchlabel_test.dart +++ b/test/cwtchlabel_test.dart @@ -15,13 +15,13 @@ import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -var settingsEnglishDark = Settings(Locale("en", ''), CwtchDark()); -var settingsEnglishLight = Settings(Locale("en", ''), CwtchLight()); +var settingsEnglishDark = Settings(const Locale("en", ''), CwtchDark()); +var settingsEnglishLight = Settings(const Locale("en", ''), CwtchLight()); ChangeNotifierProvider getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark); void main() { testWidgets('CwtchLabel widget test', (WidgetTester tester) async { - final String testingStr = "A wonderful label"; + const String testingStr = "A wonderful label"; // await tester.pumpWidget(MultiProvider( // providers:[getSettingsEnglishDark()], @@ -36,7 +36,7 @@ void main() { supportedLocales: AppLocalizations.supportedLocales, title: 'Test', theme: mkThemeData(Provider.of(context)), - home: CwtchLabel(label: testingStr), + home: const CwtchLabel(label: testingStr), );} )); diff --git a/test/profileimage_test.dart b/test/profileimage_test.dart index 239aa5aa..8e05ef51 100644 --- a/test/profileimage_test.dart +++ b/test/profileimage_test.dart @@ -15,18 +15,18 @@ import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -var settingsEnglishDark = Settings(Locale("en", ''), CwtchDark()); -var settingsEnglishLight = Settings(Locale("en", ''), CwtchLight()); +var settingsEnglishDark = Settings(const Locale("en", ''), CwtchDark()); +var settingsEnglishLight = Settings(const Locale("en", ''), CwtchLight()); ChangeNotifierProvider getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark); String file(String slug) { - return "profileimage_" + slug + ".png"; + return "profileimage_$slug.png"; } void main() { testWidgets('ProfileImage widget test', (WidgetTester tester) async { - tester.binding.window.physicalSizeTestValue = Size(200, 200); + tester.binding.window.physicalSizeTestValue = const Size(200, 200); // await tester.pumpWidget(MultiProvider( // providers:[getSettingsEnglishDark()], // child: Directionality(textDirection: TextDirection.ltr, child: CwtchLabel(label: testingStr)) diff --git a/test/textfield_test.dart b/test/textfield_test.dart index 27ea0f3e..360fa5df 100644 --- a/test/textfield_test.dart +++ b/test/textfield_test.dart @@ -15,20 +15,22 @@ import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -var settingsEnglishDark = Settings(Locale("en", ''), CwtchDark()); -var settingsEnglishLight = Settings(Locale("en", ''), CwtchLight()); +var settingsEnglishDark = Settings(const Locale("en", ''), CwtchDark()); +var settingsEnglishLight = Settings(const Locale("en", ''), CwtchLight()); ChangeNotifierProvider getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark); String file(String slug) { - return "textfield_" + slug + ".png"; + return "textfield_$slug.png"; } void main() { testWidgets('Textfield widget test', (WidgetTester tester) async { - tester.binding.window.physicalSizeTestValue = Size(800, 300); + tester.binding.window.physicalSizeTestValue = const Size(800, 300); final TextEditingController ctrlr1 = TextEditingController(); - Widget testWidget = CwtchTextField(controller: ctrlr1, validator: (value) { }, hintText: '',); + Widget testWidget = CwtchTextField(controller: ctrlr1, validator: (value) { + return null; + }, hintText: '',); Widget testHarness = MultiProvider( providers:[getSettingsEnglishDark()], @@ -61,12 +63,12 @@ void main() { //=\\ testWidgets('Textfield form validation test', (WidgetTester tester) async { - tester.binding.window.physicalSizeTestValue = Size(800, 300); + tester.binding.window.physicalSizeTestValue = const Size(800, 300); final formKey = GlobalKey(); final TextEditingController ctrlr1 = TextEditingController(); - final String strLabel1 = "Age (*Required)"; - final String strFail1 = "Required field"; - final String strFail2 = "Please enter an integer"; + const String strLabel1 = "Age (*Required)"; + const String strFail1 = "Required field"; + const String strFail2 = "Please enter an integer"; Widget testWidget = CwtchTextField( controller: ctrlr1, diff --git a/test_driver/integration_test_driver.dart b/test_driver/integration_test_driver.dart index 9d60fb33..e8564ace 100644 --- a/test_driver/integration_test_driver.dart +++ b/test_driver/integration_test_driver.dart @@ -54,7 +54,7 @@ Future writeGherkinReports( final version = reports.length == 1 ? "" : "_v${i + 1}"; //The filename of the report - final fileName = + const fileName = // "${currentTime.day}-${currentTime.month}-${currentTime.year}T${currentTime.hour}_${currentTime.minute}$version.json"; "integration_response_data.json";