Merge pull request 'Tor Circuit Info Display for P2P + Many Minor UI Fixups.' (#304) from getinfo into trunk
continuous-integration/drone/push Build is passing Details

Reviewed-on: #304
Reviewed-by: erinn <erinn@openprivacy.ca>
This commit is contained in:
erinn 2022-01-17 23:05:11 +00:00
commit 9d10b9ea8d
32 changed files with 199 additions and 79 deletions

View File

@ -1 +1 @@
2022-01-12-17-26-v1.5.4-3-gaf47036
2022-01-17-17-19-v1.5.4-5-g4cf95d6

View File

@ -1 +1 @@
2022-01-12-22-26-v1.5.4-3-gaf47036
2022-01-17-22-19-v1.5.4-5-g4cf95d6

View File

@ -351,6 +351,13 @@ class CwtchNotifier {
case "DoneStorageMigration":
appState.SetModalState(ModalState.none);
break;
case "ACNInfo":
var key = data["Key"];
var handle = data["Handle"];
if (key == "circuit") {
profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(handle)?.acnCircuit = data["Data"];
}
break;
default:
EnvironmentConfig.debugLog("unhandled event: $type");
}

View File

@ -1,6 +1,11 @@
{
"@@locale": "de",
"@@last_modified": "2022-01-12T23:54:51+01:00",
"@@last_modified": "2022-01-17T21:20:54+01:00",
"labelTorNetwork": "Tor Network",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
"labelACNCircuitInfo": "ACN Circuit Info",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Override the default tor configuration. Warning: This could be dangerous. Only turn this on if you know what you are doing.",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",

View File

@ -1,6 +1,11 @@
{
"@@locale": "en",
"@@last_modified": "2022-01-12T23:54:51+01:00",
"@@last_modified": "2022-01-17T21:20:54+01:00",
"labelTorNetwork": "Tor Network",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
"labelACNCircuitInfo": "ACN Circuit Info",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
"settingTheme": "Use Light Themes",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",

View File

@ -1,6 +1,11 @@
{
"@@locale": "es",
"@@last_modified": "2022-01-12T23:54:51+01:00",
"@@last_modified": "2022-01-17T21:20:54+01:00",
"labelTorNetwork": "Tor Network",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
"labelACNCircuitInfo": "ACN Circuit Info",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Override the default tor configuration. Warning: This could be dangerous. Only turn this on if you know what you are doing.",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",

View File

@ -1,6 +1,11 @@
{
"@@locale": "fr",
"@@last_modified": "2022-01-12T23:54:51+01:00",
"@@last_modified": "2022-01-17T21:20:54+01:00",
"labelTorNetwork": "Tor Network",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
"labelACNCircuitInfo": "ACN Circuit Info",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Override the default tor configuration. Warning: This could be dangerous. Only turn this on if you know what you are doing.",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",

View File

@ -1,6 +1,11 @@
{
"@@locale": "it",
"@@last_modified": "2022-01-12T23:54:51+01:00",
"@@last_modified": "2022-01-17T21:20:54+01:00",
"labelTorNetwork": "Tor Network",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
"labelACNCircuitInfo": "ACN Circuit Info",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Override the default tor configuration. Warning: This could be dangerous. Only turn this on if you know what you are doing.",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",

View File

@ -1,6 +1,11 @@
{
"@@locale": "pl",
"@@last_modified": "2022-01-12T23:54:51+01:00",
"@@last_modified": "2022-01-17T21:20:54+01:00",
"labelTorNetwork": "Tor Network",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
"labelACNCircuitInfo": "ACN Circuit Info",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Override the default tor configuration. Warning: This could be dangerous. Only turn this on if you know what you are doing.",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",

View File

@ -1,6 +1,11 @@
{
"@@locale": "pt",
"@@last_modified": "2022-01-12T23:54:51+01:00",
"@@last_modified": "2022-01-17T21:20:54+01:00",
"labelTorNetwork": "Tor Network",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
"labelACNCircuitInfo": "ACN Circuit Info",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Override the default tor configuration. Warning: This could be dangerous. Only turn this on if you know what you are doing.",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",

View File

@ -1,6 +1,11 @@
{
"@@locale": "ru",
"@@last_modified": "2022-01-12T23:54:51+01:00",
"@@last_modified": "2022-01-17T21:20:54+01:00",
"labelTorNetwork": "Tor Network",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
"labelACNCircuitInfo": "ACN Circuit Info",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
"msgAddToAccept": "Добавьте учетную запись в контакты, чтобы принять этот файл.",
"btnSendFile": "Отправить файл",

View File

@ -565,6 +565,8 @@ class ContactInfoState extends ChangeNotifier {
String? _server;
late bool _archived;
String? _acnCircuit;
ContactInfoState(this.profileOnion, this.identifier, this.onion,
{nickname = "",
isGroup = false,
@ -598,6 +600,12 @@ class ContactInfoState extends ChangeNotifier {
String get savePeerHistory => this._savePeerHistory;
String? get acnCircuit => this._acnCircuit;
set acnCircuit(String? acnCircuit) {
this._acnCircuit = acnCircuit;
notifyListeners();
}
// Indicated whether the conversation is archived, in which case it will
// be moved to the very bottom of the active conversations list until
// new messages appear

View File

@ -176,7 +176,7 @@ ThemeData mkThemeData(Settings opaque) {
splashFactory: InkRipple.splashFactory,
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
borderRadius: BorderRadius.circular(6.0),
)),
),
),

View File

@ -109,6 +109,7 @@ class _ContactsViewState extends State<ContactsView> {
actions.add(IconButton(
icon: Icon(CwtchIcons.address_copy_2),
tooltip: AppLocalizations.of(context)!.copyAddress,
splashRadius: Material.defaultSplashRadius / 2,
onPressed: () {
Clipboard.setData(new ClipboardData(text: Provider.of<ProfileInfoState>(context, listen: false).onion));
}));
@ -118,6 +119,7 @@ class _ContactsViewState extends State<ContactsView> {
actions.add(IconButton(
icon: Icon(CwtchIcons.dns_24px),
tooltip: AppLocalizations.of(context)!.manageKnownServersButton,
splashRadius: Material.defaultSplashRadius / 2,
onPressed: () {
_pushServers();
}));
@ -127,6 +129,7 @@ class _ContactsViewState extends State<ContactsView> {
actions.add(IconButton(
// need both conditions for displaying initial empty textfield and also allowing filters to be cleared if this widget gets lost/reset
icon: Icon(showSearchBar || Provider.of<ContactListState>(context).isFiltered ? Icons.search_off : Icons.search),
splashRadius: Material.defaultSplashRadius / 2,
onPressed: () {
Provider.of<ContactListState>(context, listen: false).filter = "";
setState(() {

View File

@ -280,32 +280,36 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
child: CwtchFolderPicker(
label: AppLocalizations.of(context)!.settingDownloadFolder,
initialValue: settings.downloadPath,
description: AppLocalizations.of(context)!.fileSharingSettingsDownloadFolderDescription,
tooltip: AppLocalizations.of(context)!.fileSharingSettingsDownloadFolderTooltip,
onSave: (newVal) {
settings.downloadPath = newVal;
saveSettings(context);
},
),
),
SwitchListTile(
title: Text(AppLocalizations.of(context)!.enableExperimentClickableLinks, style: TextStyle(color: settings.current().mainTextColor)),
subtitle: Text(AppLocalizations.of(context)!.experimentClickableLinksDescription),
value: settings.isExperimentEnabled(ClickableLinksExperiment),
onChanged: (bool value) {
if (value) {
settings.enableExperiment(ClickableLinksExperiment);
} else {
settings.disableExperiment(ClickableLinksExperiment);
}
saveSettings(context);
},
activeTrackColor: settings.theme.defaultButtonActiveColor,
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
secondary: Icon(Icons.link, color: settings.current().mainTextColor),
),
]),
),
],
)),
Visibility(
visible: settings.experimentsEnabled,
child: SwitchListTile(
title: Text(AppLocalizations.of(context)!.enableExperimentClickableLinks, style: TextStyle(color: settings.current().mainTextColor)),
subtitle: Text(AppLocalizations.of(context)!.experimentClickableLinksDescription),
value: settings.isExperimentEnabled(ClickableLinksExperiment),
onChanged: (bool value) {
if (value) {
settings.enableExperiment(ClickableLinksExperiment);
} else {
settings.disableExperiment(ClickableLinksExperiment);
}
saveSettings(context);
},
activeTrackColor: settings.theme.defaultButtonActiveColor,
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
secondary: Icon(Icons.link, color: settings.current().mainTextColor),
)),
AboutListTile(
icon: appIcon,
applicationIcon: Padding(padding: EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)),

View File

@ -85,6 +85,7 @@ class _MessageViewState extends State<MessageView> {
if (Provider.of<ContactInfoState>(context).isOnline()) {
if (showFileSharing) {
appBarButtons.add(IconButton(
splashRadius: Material.defaultSplashRadius / 2,
icon: Icon(Icons.attach_file, size: 24),
tooltip: AppLocalizations.of(context)!.tooltipSendFile,
onPressed: () {
@ -93,6 +94,7 @@ class _MessageViewState extends State<MessageView> {
));
}
appBarButtons.add(IconButton(
splashRadius: Material.defaultSplashRadius / 2,
icon: Icon(CwtchIcons.send_invite, size: 24),
tooltip: AppLocalizations.of(context)!.sendInvite,
onPressed: () {
@ -100,6 +102,7 @@ class _MessageViewState extends State<MessageView> {
}));
}
appBarButtons.add(IconButton(
splashRadius: Material.defaultSplashRadius / 2,
icon: Provider.of<ContactInfoState>(context, listen: false).isGroup == true ? Icon(CwtchIcons.group_settings_24px) : Icon(CwtchIcons.peer_settings_24px),
tooltip: AppLocalizations.of(context)!.conversationSettings,
onPressed: _pushContactSettings));
@ -263,6 +266,7 @@ class _MessageViewState extends State<MessageView> {
focusedBorder: InputBorder.none,
enabled: true,
suffixIcon: ElevatedButton(
style: ElevatedButton.styleFrom(padding: EdgeInsets.all(0.0), shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(45.0))),
child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.defaultButtonTextColor),
onPressed: isOffline ? null : _sendMessage,
))),
@ -291,6 +295,7 @@ class _MessageViewState extends State<MessageView> {
alignment: Alignment.topRight,
child: IconButton(
icon: Icon(Icons.highlight_remove),
splashRadius: Material.defaultSplashRadius / 2,
tooltip: AppLocalizations.of(context)!.tooltipRemoveThisQuotedMessage,
onPressed: () {
Provider.of<AppState>(context, listen: false).selectedIndex = null;

View File

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:ui';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/services.dart';
import 'package:cwtch/model.dart';
@ -47,6 +48,33 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
Widget _buildSettingsList() {
return Consumer<Settings>(builder: (context, settings, child) {
return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
String? acnCircuit = Provider.of<ContactInfoState>(context).acnCircuit;
Widget path = Text(Provider.of<ContactInfoState>(context, listen: false).status);
if (acnCircuit != null) {
var hops = acnCircuit.split(",");
if (hops.length == 3) {
List<Widget> paths = hops.map((String countryCodeAndIp) {
var parts = countryCodeAndIp.split(":");
var country = parts[0];
var ip = parts[1];
return RichText(
textAlign: TextAlign.left,
text: TextSpan(
text: country,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 10, fontFamily: "monospace"),
children: [TextSpan(text: " ($ip)", style: 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"))));
path = Column(
children: paths,
);
}
}
return Scrollbar(
isAlwaysShown: true,
child: SingleChildScrollView(
@ -104,6 +132,16 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
SizedBox(
height: 20,
),
ListTile(
leading: Icon(CwtchIcons.onion_on, color: settings.current().mainTextColor),
isThreeLine: true,
title: Text(AppLocalizations.of(context)!.labelACNCircuitInfo),
subtitle: Text(AppLocalizations.of(context)!.descriptionACNCircuitInfo),
trailing: path,
),
SizedBox(
height: 20,
),
SwitchListTile(
title: Text(AppLocalizations.of(context)!.blockBtn, style: TextStyle(color: settings.current().mainTextColor)),
value: Provider.of<ContactInfoState>(context).isBlocked,

View File

@ -83,6 +83,7 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
actions.add(IconButton(
icon: TorIcon(),
onPressed: _pushTorStatus,
splashRadius: Material.defaultSplashRadius / 2,
tooltip: Provider.of<TorStatus>(context).progress == 100
? AppLocalizations.of(context)!.networkStatusOnline
: (Provider.of<TorStatus>(context).progress == 0 ? AppLocalizations.of(context)!.networkStatusDisconnected : AppLocalizations.of(context)!.networkStatusAttemptingTor),
@ -93,6 +94,7 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
// Unlock Profiles
actions.add(IconButton(
icon: Icon(CwtchIcons.lock_open_24px),
splashRadius: Material.defaultSplashRadius / 2,
color: Provider.of<ProfileListState>(context).profiles.isEmpty ? Provider.of<Settings>(context).theme.defaultButtonColor : Provider.of<Settings>(context).theme.mainTextColor,
tooltip: AppLocalizations.of(context)!.tooltipUnlockProfiles,
onPressed: _modalUnlockProfiles,
@ -100,14 +102,15 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
// Servers
if (Provider.of<Settings>(context).isExperimentEnabled(ServerManagementExperiment) && !Platform.isAndroid && !Platform.isIOS) {
actions.add(IconButton(icon: Icon(CwtchIcons.dns_black_24dp), tooltip: AppLocalizations.of(context)!.serversManagerTitleShort, onPressed: _pushServers));
actions.add(
IconButton(icon: Icon(CwtchIcons.dns_black_24dp), splashRadius: Material.defaultSplashRadius / 2, tooltip: AppLocalizations.of(context)!.serversManagerTitleShort, onPressed: _pushServers));
}
// Global Settings
actions.add(IconButton(icon: Icon(Icons.settings), tooltip: AppLocalizations.of(context)!.tooltipOpenSettings, onPressed: _pushGlobalSettings));
actions.add(IconButton(icon: 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, onPressed: _modalShutdown));
actions.add(IconButton(icon: Icon(Icons.close), tooltip: AppLocalizations.of(context)!.shutdownCwtchTooltip, splashRadius: Material.defaultSplashRadius / 2, onPressed: _modalShutdown));
return actions;
}

View File

@ -45,6 +45,7 @@ class _CwtchButtonTextFieldState extends State<CwtchButtonTextField> {
suffixIcon: IconButton(
onPressed: widget.onPressed,
icon: widget.icon,
splashRadius: Material.defaultSplashRadius / 2,
padding: EdgeInsets.fromLTRB(0.0, 4.0, 2.0, 2.0),
tooltip: widget.tooltip,
enableFeedback: true,
@ -56,15 +57,15 @@ class _CwtchButtonTextFieldState extends State<CwtchButtonTextField> {
floatingLabelBehavior: FloatingLabelBehavior.never,
filled: true,
fillColor: theme.current().textfieldBackgroundColor,
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 3.0)),
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 3.0)),
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 3.0)),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)),
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
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,
),
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 3.0))),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))),
);
});
}

View File

@ -78,6 +78,7 @@ class _ContactRowState extends State<ContactRow> {
? Wrap(direction: Axis.vertical, children: <Widget>[
IconButton(
padding: EdgeInsets.zero,
splashRadius: Material.defaultSplashRadius / 2,
iconSize: 16,
icon: Icon(
Icons.favorite,
@ -88,6 +89,7 @@ class _ContactRowState extends State<ContactRow> {
),
IconButton(
padding: EdgeInsets.zero,
splashRadius: Material.defaultSplashRadius / 2,
iconSize: 16,
icon: Icon(Icons.delete, color: Provider.of<Settings>(context).theme.mainTextColor),
tooltip: AppLocalizations.of(context)!.tooltipRejectContactRequest,
@ -97,6 +99,7 @@ class _ContactRowState extends State<ContactRow> {
: (contact.isBlocked != null && contact.isBlocked
? IconButton(
padding: EdgeInsets.zero,
splashRadius: Material.defaultSplashRadius / 2,
iconSize: 16,
icon: Icon(Icons.block, color: Provider.of<Settings>(context).theme.mainTextColor),
onPressed: () {},

View File

@ -1,16 +1,21 @@
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'dart:io';
import 'package:file_picker_desktop/file_picker_desktop.dart';
import 'package:provider/provider.dart';
import '../settings.dart';
import 'buttontextfield.dart';
import 'cwtchlabel.dart';
class CwtchFolderPicker extends StatefulWidget {
final String label;
final String initialValue;
final String tooltip;
final String description;
final Function(String)? onSave;
const CwtchFolderPicker({Key? key, this.label = "", this.initialValue = "", this.onSave}) : super(key: key);
const CwtchFolderPicker({Key? key, this.label = "", this.tooltip = "", this.initialValue = "", this.onSave, this.description = ""}) : super(key: key);
@override
_CwtchFolderPickerState createState() => _CwtchFolderPickerState();
@ -27,41 +32,38 @@ class _CwtchFolderPickerState extends State<CwtchFolderPicker> {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(2),
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
CwtchLabel(label: widget.label),
SizedBox(
height: 20,
),
CwtchButtonTextField(
controller: ctrlrVal,
readonly: Platform.isAndroid,
onPressed: () async {
if (Platform.isAndroid) {
return;
}
try {
var selectedDirectory = await getDirectoryPath();
if (selectedDirectory != null) {
//File directory = File(selectedDirectory);
selectedDirectory += "/";
ctrlrVal.text = selectedDirectory;
if (widget.onSave != null) {
widget.onSave!(selectedDirectory);
}
} else {
// User canceled the picker
return ListTile(
leading: Icon(Icons.file_download, color: Provider.of<Settings>(context).theme.messageFromMeTextColor, size: 16),
title: Text(widget.label),
subtitle: Text(widget.description),
trailing: Container(
width: 200,
child: CwtchButtonTextField(
controller: ctrlrVal,
readonly: Platform.isAndroid,
onPressed: () async {
if (Platform.isAndroid) {
return;
}
} catch (e) {
print(e);
}
},
icon: Icon(Icons.folder),
tooltip: "Browse", //todo: l18n
)
]));
try {
var selectedDirectory = await getDirectoryPath();
if (selectedDirectory != null) {
//File directory = File(selectedDirectory);
selectedDirectory += "/";
ctrlrVal.text = selectedDirectory;
if (widget.onSave != null) {
widget.onSave!(selectedDirectory);
}
} else {
// User canceled the picker
}
} catch (e) {
print(e);
}
},
icon: Icon(Icons.folder),
tooltip: widget.tooltip,
)));
}
}

View File

@ -85,6 +85,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
maintainInteractivity: false,
child: IconButton(
tooltip: AppLocalizations.of(context)!.tooltipReplyToThisMessage,
splashRadius: Material.defaultSplashRadius / 2,
onPressed: () {
Provider.of<AppState>(context, listen: false).selectedIndex = Provider.of<MessageMetadata>(context, listen: false).messageID;
},

View File

@ -37,6 +37,7 @@ class _CwtchTextFieldState extends State<CwtchPasswordField> {
controller: widget.controller,
validator: widget.validator,
obscureText: obscureText,
obscuringCharacter: '*',
enableIMEPersonalizedLearning: false,
autofillHints: widget.autoFillHints,
autovalidateMode: AutovalidateMode.always,
@ -57,17 +58,18 @@ class _CwtchTextFieldState extends State<CwtchPasswordField> {
highlightColor: theme.current().defaultButtonColor,
focusColor: theme.current().defaultButtonActiveColor,
splashColor: theme.current().defaultButtonActiveColor,
splashRadius: Material.defaultSplashRadius / 2,
),
errorStyle: TextStyle(
color: theme.current().textfieldErrorColor,
fontWeight: FontWeight.bold,
),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 3.0)),
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 3.0)),
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 3.0)),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)),
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
filled: true,
fillColor: theme.current().textfieldBackgroundColor,
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 3.0)),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)),
),
);
});

View File

@ -59,6 +59,7 @@ class _ProfileRowState extends State<ProfileRow> {
)),
IconButton(
enableFeedback: true,
splashRadius: Material.defaultSplashRadius / 2,
tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname,
icon: Icon(Icons.create, color: Provider.of<Settings>(context).current().mainTextColor),
onPressed: () {

View File

@ -57,6 +57,7 @@ class _ServerRowState extends State<ServerRow> {
// Copy server button
IconButton(
enableFeedback: true,
splashRadius: Material.defaultSplashRadius / 2,
tooltip: AppLocalizations.of(context)!.copyServerKeys,
icon: Icon(CwtchIcons.address_copy_2, color: Provider.of<Settings>(context).current().mainTextColor),
onPressed: () {
@ -67,6 +68,7 @@ class _ServerRowState extends State<ServerRow> {
// Edit button
IconButton(
enableFeedback: true,
splashRadius: Material.defaultSplashRadius / 2,
tooltip: AppLocalizations.of(context)!.editServerTitle,
icon: Icon(Icons.create, color: Provider.of<Settings>(context).current().mainTextColor),
onPressed: () {

View File

@ -61,13 +61,13 @@ class _CwtchTextFieldState extends State<CwtchTextField> {
hintText: widget.hintText,
floatingLabelBehavior: FloatingLabelBehavior.never,
filled: true,
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 3.0)),
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 3.0)),
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 3.0)),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)),
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
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(20.0, 10.0, 20.0, 10.0),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 3.0))),
contentPadding: 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))),
);
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB