Delete Profile Stub

This commit is contained in:
Sarah Jamie Lewis 2021-03-04 16:15:18 -08:00
parent f753bfd1c1
commit 75a508fe74
2 changed files with 216 additions and 144 deletions

View File

@ -59,6 +59,9 @@ required - any new Cwtch work is beyond the scope of this initial spec.
- [ ] Error Message When Attempting to Update Password with Wrong Old Password
- [ ] Easy Transition from Unencrypted Profile -> Encrypted Profile
- [ ] Delete a Profile
- [ ] Dialog Acknowledgement
- [ ] Require Old Password Gate
- [ ] Async Checking of Password
- [X] Copy Profile Onion Address
## Profile Pane (formally Contacts Pane)

View File

@ -29,6 +29,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
final ctrlrPass2 = TextEditingController(text: "");
final ctrlrOnion = TextEditingController(text: "");
bool usePassword;
bool deleted;
@override
void initState() {
@ -63,170 +64,187 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
Widget _buildForm() {
return Consumer<OpaqueTheme>(builder: (context, theme, child) {
return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
clipBehavior: Clip.antiAliasWithSaveLayer,
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Form(
key: _formKey,
child: Container(
margin: EdgeInsets.all(30),
padding: EdgeInsets.all(20),
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Visibility(
visible: Provider.of<ProfileInfoState>(context).onion.isNotEmpty,
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
SizedBox(
width: 120,
height: 120,
child: ClipOval(
child: SizedBox(
width: 120,
height: 120,
child: Container(
color: Colors.white,
return Scrollbar(
isAlwaysShown: true,
child: SingleChildScrollView(
clipBehavior: Clip.antiAlias,
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Form(
key: _formKey,
child: Container(
margin: EdgeInsets.all(30),
padding: EdgeInsets.all(20),
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Visibility(
visible: Provider.of<ProfileInfoState>(context).onion.isNotEmpty,
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
SizedBox(
width: 120,
height: 120,
child: ClipOval(
child: SizedBox(
width: 120,
height: 120,
child: Image(
image: AssetImage("assets/" + Provider.of<ProfileInfoState>(context).imagePath),
width: 100,
height: 100,
))),
),
)
])),
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
CwtchLabel(label: AppLocalizations.of(context).displayNameLabel),
SizedBox(
height: 20,
),
CwtchTextField(
controller: ctrlrNick,
labelText: AppLocalizations.of(context).yourDisplayName,
validator: (value) {
if (value.isEmpty) {
return "Please enter a display name";
}
return null;
},
),
]),
Visibility(
visible: Provider.of<ProfileInfoState>(context).onion.isNotEmpty,
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
child: Container(
color: Colors.white,
width: 120,
height: 120,
child: Image(
image: AssetImage("assets/" + Provider.of<ProfileInfoState>(context).imagePath),
width: 100,
height: 100,
))),
),
)
])),
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
CwtchLabel(label: AppLocalizations.of(context).displayNameLabel),
SizedBox(
height: 20,
),
CwtchLabel(label: AppLocalizations.of(context).addressLabel),
SizedBox(
height: 20,
CwtchTextField(
controller: ctrlrNick,
labelText: AppLocalizations.of(context).yourDisplayName,
validator: (value) {
if (value.isEmpty) {
return "Please enter a display name";
}
return null;
},
),
CwtchButtonTextField(
controller: ctrlrOnion,
onPressed: _copyOnion,
icon: Icon(Icons.copy),
tooltip: AppLocalizations.of(context).copyBtn,
)
])),
// We only allow setting password types on profile creation
Visibility(
visible: Provider.of<ProfileInfoState>(context).onion.isEmpty,
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Radio(
value: false,
groupValue: usePassword,
onChanged: _handleSwitchPassword,
),
Text(
AppLocalizations.of(context).radioNoPassword,
style: TextStyle(color: theme.current().mainTextColor()),
),
Radio(
value: true,
groupValue: usePassword,
onChanged: _handleSwitchPassword,
),
Text(
AppLocalizations.of(context).radioUsePassword,
style: TextStyle(color: theme.current().mainTextColor()),
),
])),
SizedBox(
height: 20,
),
Visibility(
visible: usePassword,
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
]),
Visibility(
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
visible: Provider.of<ProfileInfoState>(context).onion.isNotEmpty,
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
CwtchLabel(label: AppLocalizations.of(context).currentPasswordLabel),
SizedBox(
height: 20,
),
CwtchPasswordField(
controller: ctrlrOldPass,
CwtchLabel(label: AppLocalizations.of(context).addressLabel),
SizedBox(
height: 20,
),
CwtchButtonTextField(
controller: ctrlrOnion,
onPressed: _copyOnion,
icon: Icon(Icons.copy),
tooltip: AppLocalizations.of(context).copyBtn,
)
])),
// We only allow setting password types on profile creation
Visibility(
visible: Provider.of<ProfileInfoState>(context).onion.isEmpty,
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Radio(
value: false,
groupValue: usePassword,
onChanged: _handleSwitchPassword,
),
Text(
AppLocalizations.of(context).radioNoPassword,
style: TextStyle(color: theme.current().mainTextColor()),
),
Radio(
value: true,
groupValue: usePassword,
onChanged: _handleSwitchPassword,
),
Text(
AppLocalizations.of(context).radioUsePassword,
style: TextStyle(color: theme.current().mainTextColor()),
),
])),
SizedBox(
height: 20,
),
Visibility(
visible: usePassword,
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Visibility(
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
CwtchLabel(label: AppLocalizations.of(context).currentPasswordLabel),
SizedBox(
height: 20,
),
CwtchPasswordField(
controller: ctrlrOldPass,
validator: (value) {
// Password field can be empty when just updating the profile, not on creation
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
return AppLocalizations.of(context).passwordErrorEmpty;
}
return null;
},
),
SizedBox(
height: 20,
),
])),
CwtchLabel(label: AppLocalizations.of(context).password1Label),
SizedBox(
height: 20,
),
CwtchPasswordField(
controller: ctrlrPass,
validator: (value) {
// Password field can be empty when just updating the profile, not on creation
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
return AppLocalizations.of(context).passwordErrorEmpty;
}
if (value != ctrlrPass2.value.text) {
return AppLocalizations.of(context).passwordErrorMatch;
}
return null;
},
),
SizedBox(
height: 20,
),
CwtchLabel(label: AppLocalizations.of(context).password2Label),
SizedBox(
height: 20,
),
CwtchPasswordField(
controller: ctrlrPass2,
validator: (value) {
// Password field can be empty when just updating the profile, not on creation
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
return AppLocalizations.of(context).passwordErrorEmpty;
}
if (value != ctrlrPass.value.text) {
return AppLocalizations.of(context).passwordErrorMatch;
}
return null;
},
),
}),
]),
),
SizedBox(
height: 20,
),
ElevatedButton(
onPressed: _createPressed,
style: ElevatedButton.styleFrom(primary: theme.current().defaultButtonColor()),
child: Text(Provider.of<ProfileInfoState>(context).onion.isEmpty ? AppLocalizations.of(context).addNewProfileBtn : AppLocalizations.of(context).saveProfileBtn),
),
Visibility(
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [
SizedBox(
height: 20,
),
])),
CwtchLabel(label: AppLocalizations.of(context).password1Label),
SizedBox(
height: 20,
),
CwtchPasswordField(
controller: ctrlrPass,
validator: (value) {
// Password field can be empty when just updating the profile, not on creation
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
return AppLocalizations.of(context).passwordErrorEmpty;
}
if (value != ctrlrPass2.value.text) {
return AppLocalizations.of(context).passwordErrorMatch;
}
return null;
},
),
SizedBox(
height: 20,
),
CwtchLabel(label: AppLocalizations.of(context).password2Label),
SizedBox(
height: 20,
),
CwtchPasswordField(
controller: ctrlrPass2,
validator: (value) {
// Password field can be empty when just updating the profile, not on creation
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
return AppLocalizations.of(context).passwordErrorEmpty;
}
if (value != ctrlrPass.value.text) {
return AppLocalizations.of(context).passwordErrorMatch;
}
return null;
}),
]),
),
SizedBox(
height: 20,
),
ElevatedButton(
onPressed: _createPressed,
style: ElevatedButton.styleFrom(primary: theme.current().defaultButtonColor()),
child: Text(Provider.of<ProfileInfoState>(context).onion.isEmpty ? AppLocalizations.of(context).addNewProfileBtn : AppLocalizations.of(context).saveProfileBtn),
)
])))));
ElevatedButton.icon(
onPressed: () {
showAlertDialog(context);
},
style: ElevatedButton.styleFrom(primary: theme.current().defaultButtonColor()),
icon: Icon(Icons.delete_forever),
label: Text(AppLocalizations.of(context).deleteBtn),
)
]))
]))))));
});
});
}
@ -286,3 +304,54 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
}
}
}
showAlertDialog(BuildContext context) {
// set up the buttons
Widget cancelButton = TextButton(
child: Text("Cancel"),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonColor()),
foregroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonTextColor()),
overlayColor: MaterialStateProperty.all(Opaque.current().defaultButtonActiveColor()),
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
},
);
Widget continueButton = TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonColor()),
foregroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonTextColor()),
overlayColor: MaterialStateProperty.all(Opaque.current().defaultButtonActiveColor()),
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
child: Text(AppLocalizations.of(context).deleteProfileConfirmBtn),
onPressed: () {
// TODO Actually Delete the Peer
Navigator.of(context).pop(); // dismiss dialog
},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
backgroundColor: Provider.of<OpaqueTheme>(context, listen: false).current().backgroundPaneColor(),
title: Text(AppLocalizations.of(context).deleteProfileConfirmBtn),
titleTextStyle: TextStyle(
color: Provider.of<OpaqueTheme>(context, listen: false).current().mainTextColor(),
),
contentTextStyle: TextStyle(
color: Provider.of<OpaqueTheme>(context, listen: false).current().mainTextColor(),
),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}