Merge pull request 'fastercwtch' (#351) from fastercwtch into gherkin

Reviewed-on: #351
This commit is contained in:
Sarah Jamie Lewis 2022-02-02 22:13:41 +00:00
commit 8d81116f2d
23 changed files with 148 additions and 54 deletions

View File

@ -1,4 +0,0 @@
@env:aliceandbob1
Feature: Streamer mode setting hides onions
Scenario: Turning on streamer mode
Given

View File

@ -1,3 +1,4 @@
@env:persist
Feature: Basic Profile Management Feature: Basic Profile Management
Scenario: Error on Creating a Profile without a Display Name Scenario: Error on Creating a Profile without a Display Name
Given I wait until the widget with type 'ProfileMgrView' is present Given I wait until the widget with type 'ProfileMgrView' is present
@ -18,14 +19,18 @@ Feature: Basic Profile Management
Then I tap the "passwordCheckBox" widget Then I tap the "passwordCheckBox" widget
And I expect the text 'New Password' to be absent And I expect the text 'New Password' to be absent
And I take a screenshot And I take a screenshot
Then I fill the "displayNameFormElement" field with "Alice (<h1>hello</h1>)" Then I fill the "displayNameFormElement" field with "Alice (Unencrypted)"
Then I tap the "button" widget with label "Add new profile" Then I tap the "button" widget with label "Add new profile"
And I expect a "ProfileRow" widget with text "Alice (<h1>hello</h1>)" And I expect a "ProfileRow" widget with text "Alice (Unencrypted)"
And I take a screenshot And I take a screenshot
Then I tap the "ProfileRow" widget with label "Alice (<h1>hello</h1>)" Then I tap the "ProfileRow" widget with label "Alice (Unencrypted)"
And I expect the text 'Alice (<h1>hello</h1>) » Conversations' to be present And I expect the text "Alice (Unencrypted) » Conversations" to be present
And I take a screenshot And I take a screenshot
Scenario: Load Unencrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect a "ProfileRow" widget with text "Alice (Unencrypted)"
Scenario: Create Encrypted Profile Scenario: Create Encrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the button with tooltip "Add new profile" And I tap the button with tooltip "Add new profile"
@ -41,4 +46,45 @@ Feature: Basic Profile Management
And I take a screenshot And I take a screenshot
Then I tap the "ProfileRow" widget with label "Alice (Encrypted)" Then I tap the "ProfileRow" widget with label "Alice (Encrypted)"
And I expect the text 'Alice (Encrypted) » Conversations' to be present And I expect the text 'Alice (Encrypted) » Conversations' to be present
And I take a screenshot And I take a screenshot
Scenario: Load an Encrypted Profile by Unlocking it with a Password
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect the text 'Enter a password to view your profiles' to be absent
And I tap the button with tooltip "Unlock encrypted profiles by entering their password."
Then I expect the text 'Enter a password to view your profiles' to be present
When I fill the "unlockPasswordProfileElement" field with "password1"
And I tap the "button" widget with label "Unlock"
Then I expect a "ProfileRow" widget with text "Alice (Encrypted)"
Scenario: Load an Encrypted Profile by Unlocking it with a Password and Change the Name
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect the text 'Enter a password to view your profiles' to be absent
And I tap the button with tooltip "Unlock encrypted profiles by entering their password."
Then I expect the text 'Enter a password to view your profiles' to be present
When I fill the "unlockPasswordProfileElement" field with "password1"
And I tap the "button" widget with label "Unlock"
Then I expect a "ProfileRow" widget with text "Alice (Encrypted)"
When I tap the "IconButton" widget with tooltip "Edit Profile Alice (Encrypted)"
Then I expect the text 'Display Name' to be present
Then I fill the "displayNameFormElement" field with "Carol (Encrypted)"
And I tap the "button" widget with label "Save Profile"
And I wait until the widget with type 'ProfileMgrView' is present
Then I expect a "ProfileRow" widget with text "Carol (Encrypted)"
Scenario: Delete an Encrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect the text 'Enter a password to view your profiles' to be absent
And I tap the button with tooltip "Unlock encrypted profiles by entering their password."
Then I expect the text 'Enter a password to view your profiles' to be present
When I fill the "unlockPasswordProfileElement" field with "password1"
And I tap the "button" widget with label "Unlock"
Then I expect a "ProfileRow" widget with text "Carol (Encrypted)"
And I take a screenshot
When I tap the "IconButton" widget with tooltip "Edit Profile Carol (Encrypted)"
Then I expect the text 'Display Name' to be present
When I tap the button that contains the text "Delete"
Then I expect the text "Really Delete Profile" to be present
When I tap the "button" widget with label "Really Delete Profile"
And I wait until the widget with type 'ProfileMgrView' is present
Then I expect a "ProfileRow" widget with text "Carol (Encrypted)" to be absent

View File

@ -38,7 +38,9 @@ void main() {
// overrides // overrides
TapWidgetWithType(), TapWidgetWithType(),
TapWidgetWithLabel(), TapWidgetWithLabel(),
TapWidgetWithTooltip(),
ExpectWidgetWithText(), ExpectWidgetWithText(),
AbsentWidgetWithText(),
WaitUntilTypeExists(), WaitUntilTypeExists(),
ExpectTextToBePresent(), ExpectTextToBePresent(),
ExpectWidgetWithTextWithin(), ExpectWidgetWithTextWithin(),

View File

@ -37,6 +37,22 @@ StepDefinitionGeneric TapWidgetWithLabel() {
firstMatchOnly: true); firstMatchOnly: true);
//Text wdg = await context.world.appDriver.widget(finder, ExpectedWidgetResultType.first); //Text wdg = await context.world.appDriver.widget(finder, ExpectedWidgetResultType.first);
//print(wdg.debugDescribeChildren().first.) //print(wdg.debugDescribeChildren().first.)
await context.world.appDriver.scrollIntoView(finder);
await context.world.appDriver.tap(finder);
await context.world.appDriver.waitForAppToSettle();
},
);
}
StepDefinitionGeneric TapWidgetWithTooltip() {
return given2<String, String, FlutterWorld>(
RegExp(r'I tap the {string} widget with tooltip {string}$'),
(ofType, text, context) async {
final finder = context.world.appDriver.findByDescendant(
context.world.appDriver.findBy(widgetTypeByName(ofType), FindType.type),
context.world.appDriver.findBy(text, FindType.tooltip),
firstMatchOnly: true);
await context.world.appDriver.scrollIntoView(finder);
await context.world.appDriver.tap(finder); await context.world.appDriver.tap(finder);
await context.world.appDriver.waitForAppToSettle(); await context.world.appDriver.waitForAppToSettle();
}, },
@ -59,6 +75,23 @@ StepDefinitionGeneric ExpectWidgetWithText() {
); );
} }
StepDefinitionGeneric AbsentWidgetWithText() {
return given2<String, String, FlutterWorld>(
RegExp(r'I expect a {string} widget with text {string} to be absent$'),
(ofType, text, context) async {
final finder = context.world.appDriver.findByDescendant(
context.world.appDriver.findBy(widgetTypeByName(ofType), FindType.type),
context.world.appDriver.findBy(text, FindType.text),
firstMatchOnly: true);
//Text wdg = await context.world.appDriver.widget(finder, ExpectedWidgetResultType.first);
//print(wdg.debugDescribeChildren().first.)
await context.world.appDriver.isAbsent(finder);
await context.world.appDriver.waitForAppToSettle();
},
);
}
StepDefinitionGeneric TapButtonWithText() { StepDefinitionGeneric TapButtonWithText() {
return given1<String, FlutterWorld>( return given1<String, FlutterWorld>(
RegExp(r'I tap the {string} (?:button|element|label|icon|field|text|widget)$'), RegExp(r'I tap the {string} (?:button|element|label|icon|field|text|widget)$'),
@ -67,8 +100,7 @@ StepDefinitionGeneric TapButtonWithText() {
context.world.appDriver.findBy(Flwtch, FindType.type), context.world.appDriver.findBy(Flwtch, FindType.type),
context.world.appDriver.findBy(input1, FindType.key), context.world.appDriver.findBy(input1, FindType.key),
firstMatchOnly: true); firstMatchOnly: true);
//Text wdg = await context.world.appDriver.widget(finder, ExpectedWidgetResultType.first); await context.world.appDriver.scrollIntoView(finder);
//print(wdg.debugDescribeChildren().first.)
await context.world.appDriver.tap(finder); await context.world.appDriver.tap(finder);
await context.world.appDriver.waitForAppToSettle(); await context.world.appDriver.waitForAppToSettle();
}, },
@ -229,6 +261,8 @@ Type widgetTypeByName(String input1) {
return TorIcon; return TorIcon;
case "button": case "button":
return ElevatedButton; return ElevatedButton;
case "IconButton":
return IconButton;
case "ProfileRow": case "ProfileRow":
return ProfileRow; return ProfileRow;
default: default:

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "de", "@@locale": "de",
"@@last_modified": "2022-01-18T00:38:14+01:00", "@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network", "labelTorNetwork": "Tor Network",

View File

@ -1,6 +1,7 @@
{ {
"@@locale": "en", "@@locale": "en",
"@@last_modified": "2022-01-18T00:38:14+01:00", "@@last_modified": "2022-01-28T19:57:41+01:00",
"editProfile": "Edit Profile",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network", "labelTorNetwork": "Tor Network",
@ -239,7 +240,6 @@
"radioNoPassword": "Unencrypted (No password)", "radioNoPassword": "Unencrypted (No password)",
"radioUsePassword": "Password", "radioUsePassword": "Password",
"copiedToClipboardNotification": "Copied to Clipboard", "copiedToClipboardNotification": "Copied to Clipboard",
"editProfile": "Edit Profille",
"newProfile": "New Profile", "newProfile": "New Profile",
"defaultProfileName": "Alice", "defaultProfileName": "Alice",
"profileName": "Display name", "profileName": "Display name",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "es", "@@locale": "es",
"@@last_modified": "2022-01-18T00:38:14+01:00", "@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network", "labelTorNetwork": "Tor Network",

View File

@ -1,22 +1,24 @@
{ {
"@@locale": "fr", "@@locale": "fr",
"@@last_modified": "2022-01-18T00:38:14+01:00", "@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "editProfile": "Modifier le profil",
"torSettingsEnableCache": "Cache Tor Consensus", "settingTheme": "Utilisez des thèmes clairs",
"labelTorNetwork": "Tor Network", "torSettingsUseCustomTorServiceConfiguration": "Utiliser une configuration personnalisée du service Tor (torrc)",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.", "torSettingsUseCustomTorServiceConfigurastionDescription": "Remplacer la configuration par défaut de tor. Avertissement : Cela peut être dangereux. Ne l'activez que si vous savez ce que vous faites.",
"labelACNCircuitInfo": "ACN Circuit Info", "torSettingsErrorSettingPort": "Le numéro de port doit être compris entre 1 et 65535.",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.", "torSettingsEnableCache": "Cache du Consensus Tor",
"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.", "torSettingsEnabledCacheDescription": "Mettre en cache le consensus Tor actuellement téléchargé pour le réutiliser la prochaine fois que Cwtch est ouvert. Cela permettra à Tor de démarrer plus rapidement. Lorsqu'il est désactivé, Cwtch purgera les données mises en cache au démarrage.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535", "torSettingsEnabledAdvancedDescription": "Utilisez un service Tor existant sur votre système ou modifiez les paramètres du service Tor de Cwtch.",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Override the default tor configuration. Warning: This could be dangerous. Only turn this on if you know what you are doing.", "torSettingsEnabledAdvanced": "Activer la configuration avancée de Tor",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)", "torSettingsCustomSocksPortDescription": "Utiliser un port personnalisé pour les connexions de données au proxy Tor",
"torSettingsCustomControlPortDescription": "Use a custom port for control connections to the Tor proxy", "torSettingsCustomSocksPort": "Port SOCKS personnalisé",
"torSettingsCustomControlPort": "Custom Control Port", "torSettingsCustomControlPortDescription": "Utiliser un port personnalisé pour contrôler les connexions au proxy Tor",
"torSettingsCustomSocksPortDescription": "Use a custom port for data connections to the Tor proxy", "torSettingsCustomControlPort": "Port de contrôle personnalisé",
"torSettingsCustomSocksPort": "Custom SOCKS Port", "labelTorNetwork": "Réseau Tor",
"torSettingsEnabledAdvancedDescription": "Use an existing Tor service on your system, or change the parameters of the Cwtch Tor Service", "labelACNCircuitInfo": "Informations sur le circuit ACN",
"torSettingsEnabledAdvanced": "Enable Advanced Tor Configuration", "fileSharingSettingsDownloadFolderTooltip": "Parcourir pour sélectionner un autre dossier par défaut pour les fichiers téléchargés.",
"fileSharingSettingsDownloadFolderDescription": "Lorsque les fichiers sont téléchargés automatiquement (par exemple, les fichiers image, lorsque les aperçus d'image sont activés), un emplacement par défaut pour télécharger les fichiers est nécessaire.",
"descriptionACNCircuitInfo": "Informations détaillées sur le chemin que le réseau de communication anonyme utilise pour se connecter à cette conversation.",
"msgConfirmSend": "Êtes-vous sûr de vouloir envoyer", "msgConfirmSend": "Êtes-vous sûr de vouloir envoyer",
"acceptGroupInviteLabel": "Voulez-vous accepter l'invitation au groupe de", "acceptGroupInviteLabel": "Voulez-vous accepter l'invitation au groupe de",
"msgFileTooBig": "La taille du fichier ne peut pas dépasser 10 Go", "msgFileTooBig": "La taille du fichier ne peut pas dépasser 10 Go",
@ -187,7 +189,6 @@
"deleteConfirmLabel": "Tapez SUPPRIMER pour confirmer", "deleteConfirmLabel": "Tapez SUPPRIMER pour confirmer",
"addNewProfileBtn": "Ajouter un nouveau profil", "addNewProfileBtn": "Ajouter un nouveau profil",
"enterProfilePassword": "Entrez un mot de passe pour consulter vos profils", "enterProfilePassword": "Entrez un mot de passe pour consulter vos profils",
"editProfile": "Modifier le profil",
"radioUsePassword": "Mot de passe", "radioUsePassword": "Mot de passe",
"radioNoPassword": "Non chiffré (pas de mot de passe)", "radioNoPassword": "Non chiffré (pas de mot de passe)",
"saveProfileBtn": "Sauvegarder le profil", "saveProfileBtn": "Sauvegarder le profil",
@ -205,7 +206,6 @@
"localePt": "Portugais", "localePt": "Portugais",
"localeDe": "Allemand", "localeDe": "Allemand",
"settingInterfaceZoom": "Niveau de zoom", "settingInterfaceZoom": "Niveau de zoom",
"settingTheme": "Thème",
"themeLight": "Clair", "themeLight": "Clair",
"themeDark": "Sombre", "themeDark": "Sombre",
"experimentsEnabled": "Activer les expériences", "experimentsEnabled": "Activer les expériences",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "it", "@@locale": "it",
"@@last_modified": "2022-01-18T00:38:14+01:00", "@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network", "labelTorNetwork": "Tor Network",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "pl", "@@locale": "pl",
"@@last_modified": "2022-01-18T00:38:14+01:00", "@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network", "labelTorNetwork": "Tor Network",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "pt", "@@locale": "pt",
"@@last_modified": "2022-01-18T00:38:14+01:00", "@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network", "labelTorNetwork": "Tor Network",
@ -227,7 +227,7 @@
"radioNoPassword": "Unencrypted (No password)", "radioNoPassword": "Unencrypted (No password)",
"radioUsePassword": "Password", "radioUsePassword": "Password",
"copiedToClipboardNotification": "Copiado", "copiedToClipboardNotification": "Copiado",
"editProfile": "Edit Profille", "editProfile": "Edit Profile",
"newProfile": "New Profile", "newProfile": "New Profile",
"defaultProfileName": "Alice", "defaultProfileName": "Alice",
"profileName": "Display name", "profileName": "Display name",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "ru", "@@locale": "ru",
"@@last_modified": "2022-01-18T00:38:14+01:00", "@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network", "labelTorNetwork": "Tor Network",

View File

@ -37,7 +37,7 @@ Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
print("runApp()"); print("runApp()");
runApp(Flwtch()); runApp(Flwtch());
sleep(Duration(seconds:1)); sleep(Duration(seconds: 1));
} }
class Flwtch extends StatefulWidget { class Flwtch extends StatefulWidget {

View File

@ -34,7 +34,7 @@ class RemoteServerInfoState extends ChangeNotifier {
if (status == "Authenticated") { if (status == "Authenticated") {
// syncing, set lastPreSyncMessageTime // syncing, set lastPreSyncMessageTime
_groups.forEach((g) { _groups.forEach((g) {
if(g.lastMessageTime.isAfter(lastPreSyncMessagTime)) { if (g.lastMessageTime.isAfter(lastPreSyncMessagTime)) {
lastPreSyncMessagTime = g.lastMessageTime; lastPreSyncMessagTime = g.lastMessageTime;
} }
}); });
@ -52,4 +52,4 @@ class RemoteServerInfoState extends ChangeNotifier {
} }
List<ContactInfoState> get groups => _groups.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier List<ContactInfoState> get groups => _groups.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
} }

View File

@ -183,7 +183,11 @@ ThemeData mkThemeData(Settings opaque) {
scrollbarTheme: ScrollbarThemeData(isAlwaysShown: false, thumbColor: MaterialStateProperty.all(opaque.current().scrollbarDefaultColor)), scrollbarTheme: ScrollbarThemeData(isAlwaysShown: false, thumbColor: MaterialStateProperty.all(opaque.current().scrollbarDefaultColor)),
tabBarTheme: TabBarTheme(indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor))), tabBarTheme: TabBarTheme(indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor))),
dialogTheme: DialogTheme( dialogTheme: DialogTheme(
backgroundColor: opaque.current().backgroundPaneColor, titleTextStyle: TextStyle(color: opaque.current().mainTextColor), contentTextStyle: TextStyle(color: opaque.current().mainTextColor)), backgroundColor: opaque.current().backgroundPaneColor,
titleTextStyle: TextStyle(color: opaque.current().mainTextColor),
contentTextStyle: TextStyle(
color: opaque.current().mainTextColor,
)),
textTheme: TextTheme( textTheme: TextTheme(
headline1: TextStyle(color: opaque.current().mainTextColor), headline1: TextStyle(color: opaque.current().mainTextColor),
headline2: TextStyle(color: opaque.current().mainTextColor), headline2: TextStyle(color: opaque.current().mainTextColor),

View File

@ -186,13 +186,13 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
autoFillHints: [AutofillHints.newPassword], autoFillHints: [AutofillHints.newPassword],
validator: (value) { validator: (value) {
// Password field can be empty when just updating the profile, not on creation // Password field can be empty when just updating the profile, not on creation
if (Provider.of<ProfileInfoState>(context).isEncrypted && if (Provider.of<ProfileInfoState>(context, listen: false).isEncrypted &&
Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty &&
value.isEmpty && value.isEmpty &&
usePassword) { usePassword) {
return AppLocalizations.of(context)!.passwordErrorEmpty; return AppLocalizations.of(context)!.passwordErrorEmpty;
} }
if (Provider.of<ErrorHandler>(context).deleteProfileError == true) { if (Provider.of<ErrorHandler>(context, listen: false).deleteProfileError == true) {
return AppLocalizations.of(context)!.enterCurrentPasswordForDelete; return AppLocalizations.of(context)!.enterCurrentPasswordForDelete;
} }
return null; return null;

View File

@ -163,7 +163,7 @@ class _ContactsViewState extends State<ContactsView> {
builder: (context, child) => RepaintBoundary(child: ContactRow()), builder: (context, child) => RepaintBoundary(child: ContactRow()),
); );
}); });
final divided = ListTile.divideTiles( final divided = ListTile.divideTiles(
context: context, context: context,
tiles: tiles, tiles: tiles,

View File

@ -107,7 +107,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
items: themes.keys.map<DropdownMenuItem<String>>((String themeId) { items: themes.keys.map<DropdownMenuItem<String>>((String themeId) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: themeId, value: themeId,
child: Text("ddi_$themeId",key: Key("ddi_$themeId")),//getThemeName(context, themeId)), child: Text("ddi_$themeId", key: Key("ddi_$themeId")), //getThemeName(context, themeId)),
); );
}).toList()), }).toList()),
leading: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor), leading: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor),

View File

@ -108,7 +108,12 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
} }
// Global Settings // Global Settings
actions.add(IconButton(key: Key("OpenSettingsView"), icon: Icon(Icons.settings), tooltip: AppLocalizations.of(context)!.tooltipOpenSettings, splashRadius: Material.defaultSplashRadius / 2, onPressed: _pushGlobalSettings)); actions.add(IconButton(
key: Key("OpenSettingsView"),
icon: Icon(Icons.settings),
tooltip: AppLocalizations.of(context)!.tooltipOpenSettings,
splashRadius: Material.defaultSplashRadius / 2,
onPressed: _pushGlobalSettings));
// shutdown cwtch // shutdown cwtch
actions.add(IconButton(icon: Icon(Icons.close), tooltip: AppLocalizations.of(context)!.shutdownCwtchTooltip, splashRadius: Material.defaultSplashRadius / 2, onPressed: _modalShutdown)); actions.add(IconButton(icon: Icon(Icons.close), tooltip: AppLocalizations.of(context)!.shutdownCwtchTooltip, splashRadius: Material.defaultSplashRadius / 2, onPressed: _modalShutdown));
@ -191,6 +196,7 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
height: 20, height: 20,
), ),
CwtchPasswordField( CwtchPasswordField(
key: Key("unlockPasswordProfileElement"),
autofocus: true, autofocus: true,
controller: ctrlrPassword, controller: ctrlrPassword,
action: unlock, action: unlock,

View File

@ -5,7 +5,16 @@ import 'package:provider/provider.dart';
// Provides a styled Text Field for use in Form Widgets. // Provides a styled Text Field for use in Form Widgets.
// Callers must provide a text controller, label helper text and a validator. // Callers must provide a text controller, label helper text and a validator.
class CwtchButtonTextField extends StatefulWidget { class CwtchButtonTextField extends StatefulWidget {
CwtchButtonTextField({required this.controller, required this.onPressed, required this.icon, required this.tooltip, this.readonly = true, this.labelText, this.testKey, this.onChanged,}); CwtchButtonTextField({
required this.controller,
required this.onPressed,
required this.icon,
required this.tooltip,
this.readonly = true,
this.labelText,
this.testKey,
this.onChanged,
});
final TextEditingController controller; final TextEditingController controller;
final Function()? onPressed; final Function()? onPressed;
final Function(String)? onChanged; final Function(String)? onChanged;

View File

@ -80,7 +80,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
Widget wdgIcons = Platform.isAndroid Widget wdgIcons = Platform.isAndroid
? SizedBox.shrink() ? SizedBox.shrink()
: Visibility( : Visibility(
visible: true,//Provider.of<AppState>(context).hoveredIndex == Provider.of<MessageMetadata>(context).messageID, visible: true, //Provider.of<AppState>(context).hoveredIndex == Provider.of<MessageMetadata>(context).messageID,
maintainSize: true, maintainSize: true,
maintainAnimation: true, maintainAnimation: true,
maintainState: true, maintainState: true,

View File

@ -89,10 +89,7 @@ class _ProfileRowState extends State<ProfileRow> {
builder: (BuildContext buildcontext) { builder: (BuildContext buildcontext) {
return OrientationBuilder(builder: (orientationBuilderContext, orientation) { return OrientationBuilder(builder: (orientationBuilderContext, orientation) {
return MultiProvider( return MultiProvider(
providers: [ providers: [ChangeNotifierProvider<ProfileInfoState>.value(value: profile), ChangeNotifierProvider<ContactListState>.value(value: profile.contactList)],
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
ChangeNotifierProvider<ContactListState>.value(value: profile.contactList)
],
builder: (innercontext, widget) { builder: (innercontext, widget) {
var appState = Provider.of<AppState>(context); var appState = Provider.of<AppState>(context);
var settings = Provider.of<Settings>(context); var settings = Provider.of<Settings>(context);