mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-27 23:09:35 +01:00
feat: Better verification design
This commit is contained in:
parent
7403ac784b
commit
9bcd6b2609
@ -8,6 +8,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||||||
import 'adaptive_flat_button.dart';
|
import 'adaptive_flat_button.dart';
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import '../../utils/string_color.dart';
|
import '../../utils/string_color.dart';
|
||||||
|
import '../../utils/beautify_string_extension.dart';
|
||||||
|
|
||||||
class KeyVerificationDialog extends StatefulWidget {
|
class KeyVerificationDialog extends StatefulWidget {
|
||||||
Future<void> show(BuildContext context) => PlatformInfos.isCupertinoStyle
|
Future<void> show(BuildContext context) => PlatformInfos.isCupertinoStyle
|
||||||
@ -168,6 +169,26 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
],
|
],
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
);
|
);
|
||||||
|
final key = widget.request.client.userDeviceKeys[widget.request.userId]
|
||||||
|
.deviceKeys[widget.request.deviceId];
|
||||||
|
if (key != null) {
|
||||||
|
buttons.add(AdaptiveFlatButton(
|
||||||
|
child: Text(widget.l10n.verifyManual),
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await showOkCancelAlertDialog(
|
||||||
|
context: context,
|
||||||
|
title: widget.l10n.verifyManual,
|
||||||
|
message: key.ed25519Key.beautified,
|
||||||
|
);
|
||||||
|
if (result == OkCancelResult.ok) {
|
||||||
|
await key.setVerified(true);
|
||||||
|
}
|
||||||
|
await widget.request.cancel();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case KeyVerificationState.askSas:
|
case KeyVerificationState.askSas:
|
||||||
TextSpan compareWidget;
|
TextSpan compareWidget;
|
||||||
|
33
lib/utils/device_extension.dart
Normal file
33
lib/utils/device_extension.dart
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
extension DeviceExtension on Device {
|
||||||
|
String get displayname =>
|
||||||
|
(displayName?.isNotEmpty ?? false) ? displayName : 'Unknown device';
|
||||||
|
|
||||||
|
IconData get icon => displayname.toLowerCase().contains('android')
|
||||||
|
? Icons.phone_android_outlined
|
||||||
|
: displayname.toLowerCase().contains('ios')
|
||||||
|
? Icons.phone_iphone_outlined
|
||||||
|
: displayname.toLowerCase().contains('web')
|
||||||
|
? Icons.web_outlined
|
||||||
|
: displayname.toLowerCase().contains('desktop')
|
||||||
|
? Icons.desktop_mac_outlined
|
||||||
|
: Icons.device_unknown_outlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DeviceKeysExtension on DeviceKeys {
|
||||||
|
String get displayname => (deviceDisplayName?.isNotEmpty ?? false)
|
||||||
|
? deviceDisplayName
|
||||||
|
: 'Unknown device';
|
||||||
|
|
||||||
|
IconData get icon => displayname.toLowerCase().contains('android')
|
||||||
|
? Icons.phone_android_outlined
|
||||||
|
: displayname.toLowerCase().contains('ios')
|
||||||
|
? Icons.phone_iphone_outlined
|
||||||
|
: displayname.toLowerCase().contains('web')
|
||||||
|
? Icons.web_outlined
|
||||||
|
: displayname.toLowerCase().contains('desktop')
|
||||||
|
? Icons.desktop_mac_outlined
|
||||||
|
: Icons.device_unknown_outlined;
|
||||||
|
}
|
@ -1,13 +1,12 @@
|
|||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
|
||||||
import 'package:famedlysdk/encryption.dart';
|
import 'package:famedlysdk/encryption.dart';
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/components/avatar.dart';
|
import 'package:fluffychat/components/avatar.dart';
|
||||||
import 'package:fluffychat/components/matrix.dart';
|
import 'package:fluffychat/components/matrix.dart';
|
||||||
import 'package:fluffychat/utils/beautify_string_extension.dart';
|
|
||||||
import 'package:flushbar/flushbar_helper.dart';
|
import 'package:flushbar/flushbar_helper.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import '../components/dialogs/key_verification_dialog.dart';
|
import '../components/dialogs/key_verification_dialog.dart';
|
||||||
|
import '../utils/device_extension.dart';
|
||||||
|
|
||||||
class ChatEncryptionSettings extends StatefulWidget {
|
class ChatEncryptionSettings extends StatefulWidget {
|
||||||
final String id;
|
final String id;
|
||||||
@ -41,20 +40,6 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
|||||||
l10n: L10n.of(context),
|
l10n: L10n.of(context),
|
||||||
).show(context);
|
).show(context);
|
||||||
break;
|
break;
|
||||||
case 'verify_manual':
|
|
||||||
if (await showOkCancelAlertDialog(
|
|
||||||
context: context,
|
|
||||||
title: L10n.of(context).isDeviceKeyCorrect,
|
|
||||||
message: key.ed25519Key.beautified,
|
|
||||||
okLabel: L10n.of(context).ok,
|
|
||||||
cancelLabel: L10n.of(context).cancel,
|
|
||||||
) ==
|
|
||||||
OkCancelResult.ok) {
|
|
||||||
await unblock();
|
|
||||||
await key.setVerified(true);
|
|
||||||
setState(() => null);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'verify_user':
|
case 'verify_user':
|
||||||
await unblock();
|
await unblock();
|
||||||
final req =
|
final req =
|
||||||
@ -170,20 +155,11 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
|||||||
var items = <PopupMenuEntry<String>>[];
|
var items = <PopupMenuEntry<String>>[];
|
||||||
if (deviceKeys[i].blocked ||
|
if (deviceKeys[i].blocked ||
|
||||||
!deviceKeys[i].verified) {
|
!deviceKeys[i].verified) {
|
||||||
if (deviceKeys[i].userId == room.client.userID) {
|
|
||||||
items.add(PopupMenuItem(
|
items.add(PopupMenuItem(
|
||||||
child: Text(L10n.of(context).verifyStart),
|
child: Text(L10n.of(context).verifyStart),
|
||||||
value: 'verify',
|
value: deviceKeys[i].userId == room.client.userID
|
||||||
));
|
? 'verify'
|
||||||
} else {
|
: 'verify_user',
|
||||||
items.add(PopupMenuItem(
|
|
||||||
child: Text(L10n.of(context).verifyUser),
|
|
||||||
value: 'verify_user',
|
|
||||||
));
|
|
||||||
}
|
|
||||||
items.add(PopupMenuItem(
|
|
||||||
child: Text(L10n.of(context).verifyManual),
|
|
||||||
value: 'verify_manual',
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if (deviceKeys[i].blocked) {
|
if (deviceKeys[i].blocked) {
|
||||||
@ -201,8 +177,15 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
|||||||
return items;
|
return items;
|
||||||
},
|
},
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
|
leading: CircleAvatar(
|
||||||
|
foregroundColor:
|
||||||
|
Theme.of(context).textTheme.bodyText1.color,
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).secondaryHeaderColor,
|
||||||
|
child: Icon(deviceKeys[i].icon),
|
||||||
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'${deviceKeys[i].deviceDisplayName ?? L10n.of(context).unknownDevice}',
|
deviceKeys[i].displayname,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: deviceKeys[i].blocked
|
color: deviceKeys[i].blocked
|
||||||
? Colors.red
|
? Colors.red
|
||||||
|
@ -8,6 +8,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||||||
|
|
||||||
import '../components/matrix.dart';
|
import '../components/matrix.dart';
|
||||||
import '../utils/date_time_extension.dart';
|
import '../utils/date_time_extension.dart';
|
||||||
|
import '../utils/device_extension.dart';
|
||||||
|
|
||||||
class DevicesSettings extends StatefulWidget {
|
class DevicesSettings extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -113,6 +114,15 @@ class DevicesSettingsState extends State<DevicesSettings> {
|
|||||||
setState(() => null);
|
setState(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _unblockDeviceAction(BuildContext context, Device device) async {
|
||||||
|
final key = Matrix.of(context)
|
||||||
|
.client
|
||||||
|
.userDeviceKeys[Matrix.of(context).client.userID]
|
||||||
|
.deviceKeys[device.deviceId];
|
||||||
|
await key.setBlocked(false);
|
||||||
|
setState(() => null);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -152,6 +162,7 @@ class DevicesSettingsState extends State<DevicesSettings> {
|
|||||||
remove: (d) => _removeDevicesAction(context, [d]),
|
remove: (d) => _removeDevicesAction(context, [d]),
|
||||||
verify: (d) => _verifyDeviceAction(context, d),
|
verify: (d) => _verifyDeviceAction(context, d),
|
||||||
block: (d) => _blockDeviceAction(context, d),
|
block: (d) => _blockDeviceAction(context, d),
|
||||||
|
unblock: (d) => _unblockDeviceAction(context, d),
|
||||||
),
|
),
|
||||||
Divider(height: 1),
|
Divider(height: 1),
|
||||||
if (devices.isNotEmpty)
|
if (devices.isNotEmpty)
|
||||||
@ -189,6 +200,7 @@ class DevicesSettingsState extends State<DevicesSettings> {
|
|||||||
remove: (d) => _removeDevicesAction(context, [d]),
|
remove: (d) => _removeDevicesAction(context, [d]),
|
||||||
verify: (d) => _verifyDeviceAction(context, d),
|
verify: (d) => _verifyDeviceAction(context, d),
|
||||||
block: (d) => _blockDeviceAction(context, d),
|
block: (d) => _blockDeviceAction(context, d),
|
||||||
|
unblock: (d) => _unblockDeviceAction(context, d),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -205,6 +217,7 @@ enum UserDeviceListItemAction {
|
|||||||
remove,
|
remove,
|
||||||
verify,
|
verify,
|
||||||
block,
|
block,
|
||||||
|
unblock,
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserDeviceListItem extends StatelessWidget {
|
class UserDeviceListItem extends StatelessWidget {
|
||||||
@ -213,6 +226,7 @@ class UserDeviceListItem extends StatelessWidget {
|
|||||||
final void Function(Device) rename;
|
final void Function(Device) rename;
|
||||||
final void Function(Device) verify;
|
final void Function(Device) verify;
|
||||||
final void Function(Device) block;
|
final void Function(Device) block;
|
||||||
|
final void Function(Device) unblock;
|
||||||
|
|
||||||
const UserDeviceListItem(
|
const UserDeviceListItem(
|
||||||
this.userDevice, {
|
this.userDevice, {
|
||||||
@ -220,6 +234,7 @@ class UserDeviceListItem extends StatelessWidget {
|
|||||||
@required this.rename,
|
@required this.rename,
|
||||||
@required this.verify,
|
@required this.verify,
|
||||||
@required this.block,
|
@required this.block,
|
||||||
|
@required this.unblock,
|
||||||
Key key,
|
Key key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -229,9 +244,6 @@ class UserDeviceListItem extends StatelessWidget {
|
|||||||
.client
|
.client
|
||||||
.userDeviceKeys[Matrix.of(context).client.userID]
|
.userDeviceKeys[Matrix.of(context).client.userID]
|
||||||
?.deviceKeys[userDevice.deviceId];
|
?.deviceKeys[userDevice.deviceId];
|
||||||
final displayname = (userDevice.displayName?.isNotEmpty ?? false)
|
|
||||||
? userDevice.displayName
|
|
||||||
: L10n.of(context).unknownDevice;
|
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
@ -244,16 +256,23 @@ class UserDeviceListItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
SheetAction(
|
SheetAction(
|
||||||
key: UserDeviceListItemAction.verify,
|
key: UserDeviceListItemAction.verify,
|
||||||
label: L10n.of(context).verify,
|
label: L10n.of(context).verifyStart,
|
||||||
),
|
),
|
||||||
if (keys != null) ...{
|
if (keys != null) ...{
|
||||||
|
if (!keys.blocked)
|
||||||
SheetAction(
|
SheetAction(
|
||||||
key: UserDeviceListItemAction.block,
|
key: UserDeviceListItemAction.block,
|
||||||
label: L10n.of(context).blockDevice,
|
label: L10n.of(context).blockDevice,
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
),
|
),
|
||||||
|
if (keys.blocked)
|
||||||
SheetAction(
|
SheetAction(
|
||||||
key: UserDeviceListItemAction.block,
|
key: UserDeviceListItemAction.unblock,
|
||||||
|
label: L10n.of(context).unblockDevice,
|
||||||
|
isDestructiveAction: true,
|
||||||
|
),
|
||||||
|
SheetAction(
|
||||||
|
key: UserDeviceListItemAction.remove,
|
||||||
label: L10n.of(context).delete,
|
label: L10n.of(context).delete,
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
),
|
),
|
||||||
@ -273,25 +292,20 @@ class UserDeviceListItem extends StatelessWidget {
|
|||||||
case UserDeviceListItemAction.block:
|
case UserDeviceListItemAction.block:
|
||||||
block(userDevice);
|
block(userDevice);
|
||||||
break;
|
break;
|
||||||
|
case UserDeviceListItemAction.unblock:
|
||||||
|
unblock(userDevice);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
foregroundColor: Theme.of(context).textTheme.bodyText1.color,
|
foregroundColor: Theme.of(context).textTheme.bodyText1.color,
|
||||||
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
||||||
child: Icon(displayname.toLowerCase().contains('android')
|
child: Icon(userDevice.icon),
|
||||||
? Icons.phone_android_outlined
|
|
||||||
: displayname.toLowerCase().contains('ios')
|
|
||||||
? Icons.phone_iphone_outlined
|
|
||||||
: displayname.toLowerCase().contains('web')
|
|
||||||
? Icons.web_outlined
|
|
||||||
: displayname.toLowerCase().contains('desktop')
|
|
||||||
? Icons.desktop_mac_outlined
|
|
||||||
: Icons.device_unknown_outlined),
|
|
||||||
),
|
),
|
||||||
title: Row(
|
title: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
displayname,
|
userDevice.displayname,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user