Accessibility, Tooltips and better Strings
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
Sarah Jamie Lewis 2021-04-06 13:33:44 -07:00
parent 9448529061
commit e1b9b0b3c1
12 changed files with 121 additions and 19 deletions

View File

@ -44,6 +44,9 @@
"deleteConfirmText": "LÖSCHEN",
"deleteProfileBtn": "Profil löschen",
"deleteProfileConfirmBtn": "Profil wirklich löschen",
"descriptionBlockUnknownConnections": "",
"descriptionExperiments": "",
"descriptionExperimentsGroups": "",
"displayNameLabel": "Angezeigter Name",
"dmTooltip": "Klicken, um DM zu senden",
"dontSavePeerHistory": "Peer-Verlauf löschen",
@ -122,8 +125,13 @@
"smallTextLabel": "Klein",
"themeDark": "Dunkel",
"themeLight": "Licht",
"titleManageContacts": "",
"titleManageProfiles": "",
"titlePlaceholder": "Titel...",
"todoPlaceholder": "noch zu erledigen",
"tooltipAddContact": "",
"tooltipOpenSettings": "",
"tooltipUnlockProfiles": "",
"unblockBtn": "Peer entblockieren",
"unlock": "Entsperren",
"update": "",

View File

@ -44,6 +44,9 @@
"deleteConfirmText": "DELETE",
"deleteProfileBtn": "Delete Profile",
"deleteProfileConfirmBtn": "Really Delete Profile",
"descriptionBlockUnknownConnections": "If turned on, this option will automatically close connections from Cwtch users that have not been added to your contact list.",
"descriptionExperiments": "Cwtch experiments are optional, opt-in features that add additional functionality to Cwtch that may have different privacy considerations than traditional 1:1 metadata resistant chat e.g. group chat, bot integration etc.",
"descriptionExperimentsGroups": "The group experiment allows Cwtch to connect with untrusted server infrastructure to facilitate communication with more than one contact.",
"displayNameLabel": "Display Name",
"dmTooltip": "Click to DM",
"dontSavePeerHistory": "Delete Peer History",
@ -89,7 +92,7 @@
"passwordChangeError": "Error changing password: Supplied password rejected",
"passwordErrorEmpty": "Password cannot be empty",
"passwordErrorMatch": "Passwords do not match",
"pasteAddressToAddContact": "... paste an address here to add a contact...",
"pasteAddressToAddContact": "Paste a cwtch address here to add a new contact.",
"peerAddress": "Address",
"peerBlockedMessage": "Peer is blocked",
"peerName": "Name",
@ -122,8 +125,13 @@
"smallTextLabel": "Small",
"themeDark": "Dark",
"themeLight": "Light",
"titleManageContacts": "Manage Contacts",
"titleManageProfiles": "Manage Cwtch Profiles",
"titlePlaceholder": "title...",
"todoPlaceholder": "Todo...",
"tooltipAddContact": "Add a new contact",
"tooltipOpenSettings": "Open the settings pane",
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
"unblockBtn": "Unblock Peer",
"unlock": "Unlock",
"update": "Update",

View File

@ -44,6 +44,9 @@
"deleteConfirmText": "ELIMINAR",
"deleteProfileBtn": "Eliminar Perfil",
"deleteProfileConfirmBtn": "Confirmar eliminar perfil",
"descriptionBlockUnknownConnections": "",
"descriptionExperiments": "",
"descriptionExperimentsGroups": "",
"displayNameLabel": "Nombre de Usuario",
"dmTooltip": "Haz clic para enviar mensaje directo",
"dontSavePeerHistory": "Eliminar historial de contacto",
@ -89,7 +92,7 @@
"passwordChangeError": "Hubo un error cambiando tu contraseña: la contraseña ingresada fue rechazada",
"passwordErrorEmpty": "El campo de contraseña no puede estar vacío",
"passwordErrorMatch": "Las contraseñas no coinciden",
"pasteAddressToAddContact": "...pegar una dirección aquí para añadir un contacto...",
"pasteAddressToAddContact": "...pegar una dirección aquí para añadir contacto...",
"peerAddress": "Dirección",
"peerBlockedMessage": "Contacto bloqueado",
"peerName": "Nombre",
@ -122,8 +125,13 @@
"smallTextLabel": "Pequeño",
"themeDark": "Oscuro",
"themeLight": "Claro",
"titleManageContacts": "",
"titleManageProfiles": "",
"titlePlaceholder": "título...",
"todoPlaceholder": "Por hacer...",
"tooltipAddContact": "",
"tooltipOpenSettings": "",
"tooltipUnlockProfiles": "",
"unblockBtn": "Desbloquear contacto",
"unlock": "Desbloquear",
"update": "Actualizar",

View File

@ -44,6 +44,9 @@
"deleteConfirmText": "",
"deleteProfileBtn": "",
"deleteProfileConfirmBtn": "",
"descriptionBlockUnknownConnections": "",
"descriptionExperiments": "",
"descriptionExperimentsGroups": "",
"displayNameLabel": "Pseudo",
"dmTooltip": "Envoyer un message privé",
"dontSavePeerHistory": "",
@ -122,8 +125,13 @@
"smallTextLabel": "Petit",
"themeDark": "",
"themeLight": "",
"titleManageContacts": "",
"titleManageProfiles": "",
"titlePlaceholder": "titre...",
"todoPlaceholder": "A faire...",
"tooltipAddContact": "",
"tooltipOpenSettings": "",
"tooltipUnlockProfiles": "",
"unblockBtn": "",
"unlock": "",
"update": "",

View File

@ -44,6 +44,9 @@
"deleteConfirmText": "ELIMINA",
"deleteProfileBtn": "Elimina profilo",
"deleteProfileConfirmBtn": "Elimina realmente il profilo",
"descriptionBlockUnknownConnections": "",
"descriptionExperiments": "",
"descriptionExperimentsGroups": "",
"displayNameLabel": "Nome visualizzato",
"dmTooltip": "Clicca per inviare un Messagio Diretto",
"dontSavePeerHistory": "Elimina cronologia dei peer",
@ -89,7 +92,7 @@
"passwordChangeError": "Errore durante la modifica della password: password fornita rifiutata",
"passwordErrorEmpty": "La password non può essere vuota",
"passwordErrorMatch": "Le password non corrispondono",
"pasteAddressToAddContact": "... incolla qui un indirizzo per aggiungere un contatto ...",
"pasteAddressToAddContact": "... incolla qui un indirizzo per aggiungere un contatto...",
"peerAddress": "Indirizzo",
"peerBlockedMessage": "Il peer è bloccato",
"peerName": "Nome",
@ -122,8 +125,13 @@
"smallTextLabel": "Piccolo",
"themeDark": "Scuro",
"themeLight": "Chiaro",
"titleManageContacts": "",
"titleManageProfiles": "",
"titlePlaceholder": "titolo...",
"todoPlaceholder": "Da fare...",
"tooltipAddContact": "",
"tooltipOpenSettings": "",
"tooltipUnlockProfiles": "",
"unblockBtn": "Sblocca il peer",
"unlock": "Sblocca",
"update": "Aggiornamento",

View File

@ -44,6 +44,9 @@
"deleteConfirmText": "",
"deleteProfileBtn": "",
"deleteProfileConfirmBtn": "",
"descriptionBlockUnknownConnections": "",
"descriptionExperiments": "",
"descriptionExperimentsGroups": "",
"displayNameLabel": "Nome de Exibição",
"dmTooltip": "Clique para DM",
"dontSavePeerHistory": "",
@ -122,8 +125,13 @@
"smallTextLabel": "Pequeno",
"themeDark": "",
"themeLight": "",
"titleManageContacts": "",
"titleManageProfiles": "",
"titlePlaceholder": "título…",
"todoPlaceholder": "Afazer…",
"tooltipAddContact": "",
"tooltipOpenSettings": "",
"tooltipUnlockProfiles": "",
"unblockBtn": "",
"unlock": "",
"update": "",

View File

@ -19,6 +19,8 @@ class Settings extends ChangeNotifier {
bool experimentsEnabled;
HashMap<String, bool> experiments = HashMap.identity();
bool blockUnknownConnections;
/// Set the dark theme.
void setDark() {
theme = Opaque.dark;
@ -49,6 +51,9 @@ class Settings extends ChangeNotifier {
// Set Locale and notify listeners
switchLocale(Locale(settings["Locale"]));
// Decide whether to enable Experiments
blockUnknownConnections = settings["BlockUnknownConnections"];
// Decide whether to enable Experiments
experimentsEnabled = settings["ExperimentsEnabled"];
@ -67,12 +72,28 @@ class Settings extends ChangeNotifier {
});
}
// Switch the Locale of the App
/// Switch the Locale of the App
switchLocale(Locale newLocale) {
locale = newLocale;
notifyListeners();
}
/// Block Unknown Connections will autoblock connections if they authenticate with public key not in our contacts list.
/// This is one of the best tools we have to combat abuse, while it isn't ideal it does allow a user to curate their contacts
/// list without being bothered by spurious requests (either permanently, or as a short term measure).
/// Note: This is not an *appear offline* setting which would explicitly close the listen port, rather than simply auto disconnecting unknown attempts.
forbidUnknownConnections() {
blockUnknownConnections = true;
notifyListeners();
}
/// Allow Unknown Connections will allow new contact requires from unknown public keys
/// See above for more information.
allowUnknownConnections() {
blockUnknownConnections = false;
notifyListeners();
}
/// Turn Experiments On, this will also have the side effect of enabling any
/// Experiments that have been previously activated.
enableExperiments() {
@ -113,6 +134,7 @@ class Settings extends ChangeNotifier {
"Locale": this.locale.languageCode,
"Theme": themeString,
"PreviousPid": -1,
"BlockUnknownConnections": blockUnknownConnections,
"ExperimentsEnabled": this.experimentsEnabled,
"Experiments": experiments,
"StateRootPane": 0,

View File

@ -32,7 +32,7 @@ class _AddContactViewState extends State<AddContactView> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).newConnectionPaneTitle),
title: Text(AppLocalizations.of(context).titleManageContacts),
),
body: _buildForm(),
);
@ -40,6 +40,7 @@ class _AddContactViewState extends State<AddContactView> {
Widget _buildForm() {
ctrlrOnion.text = Provider.of<ProfileInfoState>(context).onion;
/// We display a different number of tabs dependening on the experiment setup
bool groupsEnabled = Provider.of<Settings>(context).experimentsEnabled && Provider.of<Settings>(context).experiments[TapirGroupsExperiment];
return Consumer<ErrorHandler>(builder: (context, globalErrorHandler, child) {
@ -116,14 +117,14 @@ class _AddContactViewState extends State<AddContactView> {
CwtchTextField(
controller: ctrlrContact,
validator: (value) {
if (value == "") {
if (value == "") {
return null;
} if (globalErrorHandler.invalidImportStringError) {
}
if (globalErrorHandler.invalidImportStringError) {
return AppLocalizations.of(context).invalidImportString;
} else if (globalErrorHandler.contactAlreadyExistsError) {
return AppLocalizations.of(context).contactAlreadyExists;
} else if (globalErrorHandler.explicitAddContactSuccess) {
}
} else if (globalErrorHandler.explicitAddContactSuccess) {}
return null;
},
onChanged: (String peerAddr) async {

View File

@ -29,7 +29,7 @@ class _ContactsViewState extends State<ContactsView> {
),
floatingActionButton: FloatingActionButton(
onPressed: _pushAddContact,
tooltip: AppLocalizations.of(context).newConnectionPaneTitle,
tooltip: AppLocalizations.of(context).tooltipAddContact,
child: const Icon(Icons.person_add_sharp),
),
body: _buildContactList(),

View File

@ -74,8 +74,25 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
},
secondary: Icon(Icons.lightbulb_outline, color: settings.current().mainTextColor()),
),
SwitchListTile(
title: Text(AppLocalizations.of(context).blockUnknownLabel, style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text(AppLocalizations.of(context).descriptionBlockUnknownConnections),
value: settings.blockUnknownConnections,
onChanged: (bool value) {
if (value) {
settings.forbidUnknownConnections();
} else {
settings.allowUnknownConnections();
}
// Save Settings...
saveSettings(context);
},
secondary: Icon(Icons.app_blocking, color: settings.current().mainTextColor()),
),
SwitchListTile(
title: Text(AppLocalizations.of(context).experimentsEnabled, style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text(AppLocalizations.of(context).descriptionExperiments),
value: settings.experimentsEnabled,
onChanged: (bool value) {
if (value) {
@ -94,6 +111,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
children: [
SwitchListTile(
title: Text(AppLocalizations.of(context).enableGroups, style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text(AppLocalizations.of(context).descriptionExperimentsGroups),
value: settings.experiments.containsKey(TapirGroupsExperiment) && settings.experiments[TapirGroupsExperiment],
onChanged: (bool value) {
if (value) {

View File

@ -29,20 +29,24 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).profileName),
title: Text(AppLocalizations.of(context).titleManageProfiles),
actions: [
IconButton(icon: Icon(Icons.bug_report_outlined), onPressed: _setLoggingLevelDebug),
IconButton(
icon: Icon(Icons.lock_open),
tooltip: AppLocalizations.of(context).tooltipUnlockProfiles,
onPressed: _modalUnlockProfiles,
),
IconButton(icon: Icon(Icons.settings), onPressed: _pushGlobalSettings),
IconButton(icon: Icon(Icons.settings), tooltip: AppLocalizations.of(context).tooltipOpenSettings, onPressed: _pushGlobalSettings),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _pushAddEditProfile,
tooltip: AppLocalizations.of(context).addNewProfileBtn,
child: const Icon(Icons.add),
child: Icon(
Icons.add,
semanticLabel: AppLocalizations.of(context).addNewProfileBtn,
),
),
body: _buildProfileManager(), //_buildSuggestions(),
);
@ -105,7 +109,7 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
),
),
ElevatedButton(
child: Text(AppLocalizations.of(context).unlock),
child: Text(AppLocalizations.of(context).unlock, semanticsLabel: AppLocalizations.of(context).unlock),
onPressed: () {
Provider.of<FlwtchState>(context, listen: false).cwtch.LoadProfiles(ctrlrPassword.value.text);
Navigator.pop(context);

View File

@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_app/views/addeditprofileview.dart';
import 'package:flutter_app/views/contactsview.dart';
import 'package:flutter_app/views/doublecolview.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../model.dart';
@ -30,23 +32,30 @@ class _ProfileRowState extends State<ProfileRow> {
width: 60,
height: 60,
child: Image(
excludeFromSemantics: true,
image: AssetImage("assets/" + profile.imagePath),
width: 50,
height: 50,
))),
),
),
title: Text(
profile.nickname,
semanticsLabel: profile.nickname,
style: Provider.of<FlwtchState>(context).biggerFont,
),
subtitle: ExcludeSemantics(child: Text(profile.onion)),
trailing: IconButton(
enableFeedback: true,
tooltip: AppLocalizations.of(context).editProfile + " " + profile.nickname,
icon: Icon(Icons.create, color: Provider.of<Settings>(context).current().mainTextColor()),
onPressed: () {
_pushAddEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath);
},
), //(nb: Icons.create is a pencil and we use it for "edit", not create)
title: Text(
profile.nickname,
style: Provider.of<FlwtchState>(context).biggerFont,
),
subtitle: Text(profile.onion),
onTap: () {
setState(() {
var flwtch = Provider.of<FlwtchState>(context, listen: false);