Fix Change Password for Unencrypted Profiles / Handle TokenUpdates with No Tokens / Format
continuous-integration/drone/pr Build is running Details

This commit is contained in:
Sarah Jamie Lewis 2022-12-06 12:11:55 -08:00
parent 7ee619f1a6
commit 27a729d09a
8 changed files with 118 additions and 96 deletions

View File

@ -72,7 +72,8 @@ class CwtchNotifier {
} }
EnvironmentConfig.debugLog("NewPeer $data"); EnvironmentConfig.debugLog("NewPeer $data");
// if tag != v1-defaultPassword then it is either encrypted OR it is an unencrypted account created during pre-beta... // if tag != v1-defaultPassword then it is either encrypted OR it is an unencrypted account created during pre-beta...
profileCN.add(data["Identity"], data["name"], data["picture"], data["defaultPicture"], data["ContactsJson"], data["ServerList"], data["Online"] == "true", data["autostart"] == "true", data["tag"] != "v1-defaultPassword"); profileCN.add(data["Identity"], data["name"], data["picture"], data["defaultPicture"], data["ContactsJson"], data["ServerList"], data["Online"] == "true", data["autostart"] == "true",
data["tag"] != "v1-defaultPassword");
break; break;
case "ContactCreated": case "ContactCreated":
EnvironmentConfig.debugLog("ContactCreated $data"); EnvironmentConfig.debugLog("ContactCreated $data");
@ -310,12 +311,16 @@ class CwtchNotifier {
profileCN.getProfile(data["ProfileOnion"])?.replaceServers(data["ServerList"]); profileCN.getProfile(data["ProfileOnion"])?.replaceServers(data["ServerList"]);
break; break;
case "TokenManagerInfo": case "TokenManagerInfo":
List<dynamic> associatedGroups = jsonDecode(data["Data"]); try {
int count = int.parse(data["ServerTokenCount"]); List<dynamic> associatedGroups = jsonDecode(data["Data"]);
associatedGroups.forEach((identifier) { int count = int.parse(data["ServerTokenCount"]);
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(int.parse(identifier.toString()))!.antispamTickets = count; associatedGroups.forEach((identifier) {
}); profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(int.parse(identifier.toString()))!.antispamTickets = count;
EnvironmentConfig.debugLog("update server token count for ${associatedGroups}, $count"); });
EnvironmentConfig.debugLog("update server token count for ${associatedGroups}, $count");
} catch (e) {
// No tokens in data...
}
break; break;
case "NewGroup": case "NewGroup":
String invite = data["GroupInvite"].toString(); String invite = data["GroupInvite"].toString();

View File

@ -124,6 +124,10 @@ class ProfileInfoState extends ChangeNotifier {
// Check encrypted status for profile info screen // Check encrypted status for profile info screen
bool get isEncrypted => this._encrypted; bool get isEncrypted => this._encrypted;
set isEncrypted(bool newValue) {
this._encrypted = newValue;
notifyListeners();
}
String get nickname => this._nickname; String get nickname => this._nickname;

View File

@ -13,7 +13,15 @@ class ProfileListState extends ChangeNotifier {
var idx = _profiles.indexWhere((element) => element.onion == onion); var idx = _profiles.indexWhere((element) => element.onion == onion);
if (idx == -1) { if (idx == -1) {
_profiles.add(ProfileInfoState( _profiles.add(ProfileInfoState(
onion: onion, nickname: name, imagePath: picture, defaultImagePath: defaultPicture, contactsJson: contactsJson, serversJson: serverJson, online: online, autostart: autostart, encrypted: encrypted)); onion: onion,
nickname: name,
imagePath: picture,
defaultImagePath: defaultPicture,
contactsJson: contactsJson,
serversJson: serverJson,
online: online,
autostart: autostart,
encrypted: encrypted));
} else { } else {
_profiles[idx].updateFrom(onion, name, picture, contactsJson, serverJson, online); _profiles[idx].updateFrom(onion, name, picture, contactsJson, serverJson, online);
} }

View File

@ -26,7 +26,7 @@ class GhostDark extends CwtchDark {
static final Color peerBubble = darkBlue; static final Color peerBubble = darkBlue;
static final Color font = Colors.white; static final Color font = Colors.white;
static final Color settings = Color(0xFFFDFFFD); static final Color settings = Color(0xFFFDFFFD);
static final Color accent = lightBlue;//Color(0xFFD20070); static final Color accent = lightBlue; //Color(0xFFD20070);
get theme => ghost_theme; get theme => ghost_theme;
get mode => mode_dark; get mode => mode_dark;

View File

@ -49,7 +49,7 @@ class MidnightDark extends CwtchDark {
} }
class MidnightLight extends CwtchLight { class MidnightLight extends CwtchLight {
static final Color background = Color(0xFFFBFBFB);//Color(0xFFFFFDFF); static final Color background = Color(0xFFFBFBFB); //Color(0xFFFFFDFF);
static final Color header = Color(0xFFE0E0E0); static final Color header = Color(0xFFE0E0E0);
static final Color userBubble = Color(0xFFE0E0E0); static final Color userBubble = Color(0xFFE0E0E0);
static final Color peerBubble = Color(0xFFBABDBE); static final Color peerBubble = Color(0xFFBABDBE);

View File

@ -199,7 +199,9 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
Provider.of<ProfileInfoState>(context).autostart = value; Provider.of<ProfileInfoState>(context).autostart = value;
if (!Provider.of<ProfileInfoState>(context).onion.isEmpty) { if (!Provider.of<ProfileInfoState>(context).onion.isEmpty) {
Provider.of<FlwtchState>(context, listen: false).cwtch.SetProfileAttribute(Provider.of<ProfileInfoState>(context).onion, "profile.autostart", value ? "true" : "false"); Provider.of<FlwtchState>(context, listen: false)
.cwtch
.SetProfileAttribute(Provider.of<ProfileInfoState>(context).onion, "profile.autostart", value ? "true" : "false");
} }
}, },
activeTrackColor: Provider.of<Settings>(context).theme.defaultButtonColor, activeTrackColor: Provider.of<Settings>(context).theme.defaultButtonColor,
@ -207,7 +209,6 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
secondary: Icon(CwtchIcons.favorite_24dp, color: Provider.of<Settings>(context).current().mainTextColor), secondary: Icon(CwtchIcons.favorite_24dp, color: Provider.of<Settings>(context).current().mainTextColor),
), ),
Visibility( Visibility(
visible: Provider.of<ProfileInfoState>(context).onion.isEmpty, visible: Provider.of<ProfileInfoState>(context).onion.isEmpty,
child: SizedBox( child: SizedBox(
@ -418,7 +419,9 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
var profile = Provider.of<ProfileInfoState>(context, listen: false).onion; var profile = Provider.of<ProfileInfoState>(context, listen: false).onion;
Provider.of<ProfileInfoState>(context, listen: false).nickname = ctrlrNick.value.text; Provider.of<ProfileInfoState>(context, listen: false).nickname = ctrlrNick.value.text;
Provider.of<FlwtchState>(context, listen: false).cwtch.SetProfileAttribute(profile, "profile.name", ctrlrNick.value.text); Provider.of<FlwtchState>(context, listen: false).cwtch.SetProfileAttribute(profile, "profile.name", ctrlrNick.value.text);
Provider.of<FlwtchState>(context, listen: false).cwtch.ChangePassword(profile, ctrlrOldPass.text, ctrlrPass.text, ctrlrPass2.text); // Use default password if the profile is unencrypted
var password = Provider.of<ProfileInfoState>(context, listen: false).isEncrypted ? ctrlrOldPass.text : DefaultPassword;
Provider.of<FlwtchState>(context, listen: false).cwtch.ChangePassword(profile, password, ctrlrPass.text, ctrlrPass2.text);
EnvironmentConfig.debugLog("waiting for change password response"); EnvironmentConfig.debugLog("waiting for change password response");
Future.delayed(const Duration(milliseconds: 500), () { Future.delayed(const Duration(milliseconds: 500), () {
@ -434,6 +437,8 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
} }
}).whenComplete(() { }).whenComplete(() {
if (globalErrorHandler.explicitChangePasswordSuccess) { if (globalErrorHandler.explicitChangePasswordSuccess) {
// we need to set the local encrypted status to display correct password forms on this run...
Provider.of<ProfileInfoState>(context, listen: false).isEncrypted = true;
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.newPassword)); final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.newPassword));
ScaffoldMessenger.of(context).showSnackBar(snackBar); ScaffoldMessenger.of(context).showSnackBar(snackBar);
Navigator.pop(context); Navigator.pop(context);

View File

@ -95,70 +95,73 @@ class _ContactsViewState extends State<ContactsView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ScaffoldMessenger(key: scaffoldKey, child: Scaffold( return ScaffoldMessenger(
endDrawerEnableOpenDragGesture: false, key: scaffoldKey,
drawerEnableOpenDragGesture: false, child: Scaffold(
appBar: AppBar( endDrawerEnableOpenDragGesture: false,
leading: Stack(children: [ drawerEnableOpenDragGesture: false,
Align( appBar: AppBar(
alignment: Alignment.center, leading: Stack(children: [
child: IconButton( Align(
icon: Icon(Icons.arrow_back), alignment: Alignment.center,
tooltip: MaterialLocalizations.of(context).backButtonTooltip, child: IconButton(
onPressed: () { icon: Icon(Icons.arrow_back),
Provider.of<ProfileInfoState>(context, listen: false).recountUnread(); tooltip: MaterialLocalizations.of(context).backButtonTooltip,
Provider.of<AppState>(context, listen: false).selectedProfile = ""; onPressed: () {
Navigator.of(context).pop(); Provider.of<ProfileInfoState>(context, listen: false).recountUnread();
}, Provider.of<AppState>(context, listen: false).selectedProfile = "";
)), Navigator.of(context).pop();
Positioned( },
bottom: 5.0, )),
right: 5.0, Positioned(
child: StreamBuilder<bool>( bottom: 5.0,
stream: Provider.of<AppState>(context).getUnreadProfileNotifyStream(), right: 5.0,
builder: (BuildContext context, AsyncSnapshot<bool> unreadCountSnapshot) { child: StreamBuilder<bool>(
int unreadCount = Provider.of<ProfileListState>(context).generateUnreadCount(Provider.of<AppState>(context).selectedProfile ?? ""); stream: Provider.of<AppState>(context).getUnreadProfileNotifyStream(),
builder: (BuildContext context, AsyncSnapshot<bool> unreadCountSnapshot) {
int unreadCount = Provider.of<ProfileListState>(context).generateUnreadCount(Provider.of<AppState>(context).selectedProfile ?? "");
return Visibility( return Visibility(
visible: unreadCount > 0, visible: unreadCount > 0,
child: CircleAvatar( child: CircleAvatar(
radius: 10.0, radius: 10.0,
backgroundColor: Provider.of<Settings>(context).theme.portraitProfileBadgeColor, backgroundColor: Provider.of<Settings>(context).theme.portraitProfileBadgeColor,
child: Text(unreadCount > 99 ? "99+" : unreadCount.toString(), style: TextStyle(color: Provider.of<Settings>(context).theme.portraitProfileBadgeTextColor, fontSize: 8.0)), child:
)); Text(unreadCount > 99 ? "99+" : unreadCount.toString(), style: TextStyle(color: Provider.of<Settings>(context).theme.portraitProfileBadgeTextColor, fontSize: 8.0)),
}), ));
) }),
]), )
title: Row(children: [ ]),
ProfileImage( title: Row(children: [
imagePath: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment) ProfileImage(
? Provider.of<ProfileInfoState>(context).imagePath imagePath: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment)
: Provider.of<ProfileInfoState>(context).defaultImagePath, ? Provider.of<ProfileInfoState>(context).imagePath
diameter: 42, : Provider.of<ProfileInfoState>(context).defaultImagePath,
border: Provider.of<ProfileInfoState>(context).isOnline diameter: 42,
? Provider.of<Settings>(context).current().portraitOnlineBorderColor border: Provider.of<ProfileInfoState>(context).isOnline
: Provider.of<Settings>(context).current().portraitOfflineBorderColor, ? Provider.of<Settings>(context).current().portraitOnlineBorderColor
badgeTextColor: Colors.red, : Provider.of<Settings>(context).current().portraitOfflineBorderColor,
badgeColor: Colors.red, badgeTextColor: Colors.red,
badgeColor: Colors.red,
),
SizedBox(
width: 10,
),
Expanded(
child: Text("%1 » %2".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname).replaceAll("%2", AppLocalizations.of(context)!.titleManageContacts),
overflow: TextOverflow.ellipsis, style: TextStyle(color: Provider.of<Settings>(context).current().mainTextColor))),
]),
actions: getActions(context),
), ),
SizedBox( floatingActionButton: FloatingActionButton(
width: 10, onPressed: _modalAddImportChoice,
tooltip: AppLocalizations.of(context)!.tooltipAddContact,
child: Icon(
CwtchIcons.person_add_alt_1_24px,
color: Provider.of<Settings>(context).theme.defaultButtonTextColor,
),
), ),
Expanded( body: showSearchBar || Provider.of<ContactListState>(context).isFiltered ? _buildFilterable() : _buildContactList()));
child: Text("%1 » %2".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname).replaceAll("%2", AppLocalizations.of(context)!.titleManageContacts),
overflow: TextOverflow.ellipsis, style: TextStyle(color: Provider.of<Settings>(context).current().mainTextColor))),
]),
actions: getActions(context),
),
floatingActionButton: FloatingActionButton(
onPressed: _modalAddImportChoice,
tooltip: AppLocalizations.of(context)!.tooltipAddContact,
child: Icon(
CwtchIcons.person_add_alt_1_24px,
color: Provider.of<Settings>(context).theme.defaultButtonTextColor,
),
),
body: showSearchBar || Provider.of<ContactListState>(context).isFiltered ? _buildFilterable() : _buildContactList()));
} }
List<Widget> getActions(context) { List<Widget> getActions(context) {

View File

@ -82,15 +82,17 @@ class _ProfileImageState extends State<ProfileImage> {
width: widget.diameter, width: widget.diameter,
height: widget.diameter, height: widget.diameter,
color: widget.border, color: widget.border,
foregroundDecoration: widget.disabled ? BoxDecoration( foregroundDecoration: widget.disabled
color: Provider.of<Settings>(context).theme.portraitBackgroundColor, //Colors.grey, ? BoxDecoration(
backgroundBlendMode: BlendMode.color, //saturation, color: Provider.of<Settings>(context).theme.portraitBackgroundColor, //Colors.grey,
) : null, backgroundBlendMode: BlendMode.color, //saturation,
)
: null,
child: Padding( child: Padding(
padding: const EdgeInsets.all(2.0), //border size padding: const EdgeInsets.all(2.0), //border size
child: ClipOval(clipBehavior: Clip.antiAlias, child: widget.tooltip == "" ? image : Tooltip(message: widget.tooltip, child: image))))), child: ClipOval(clipBehavior: Clip.antiAlias, child: widget.tooltip == "" ? image : Tooltip(message: widget.tooltip, child: image))))),
// badge // badge
Visibility( Visibility(
visible: widget.badgeIcon != null || widget.badgeEdit || widget.badgeCount > 0, visible: widget.badgeIcon != null || widget.badgeEdit || widget.badgeCount > 0,
child: Positioned( child: Positioned(
bottom: 0.0, bottom: 0.0,
@ -107,22 +109,17 @@ class _ProfileImageState extends State<ProfileImage> {
), ),
)), )),
// disabled center icon // disabled center icon
Visibility( Visibility(
visible: widget.disabled, visible: widget.disabled,
child: Container( child: Container(
width: widget.diameter, width: widget.diameter,
height: widget.diameter, height: widget.diameter,
child: child: Center(
Center( child: Icon(
CwtchIcons.negative_heart_24px,
size: widget.diameter / 1.5,
child: Icon( color: Provider.of<Settings>(context).theme.portraitOfflineBorderColor,
CwtchIcons.negative_heart_24px, )))),
size: widget.diameter / 1.5,
color: Provider.of<Settings>(context).theme.portraitOfflineBorderColor,
)
))),
])); ]));
} }
} }