Delete Profile Stub
This commit is contained in:
parent
f753bfd1c1
commit
75a508fe74
3
SPEC.md
3
SPEC.md
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue