mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-03 22:07:23 +01:00 
			
		
		
		
	feat: Verify and block devices in devices list
This commit is contained in:
		
							parent
							
								
									4c22fdf010
								
							
						
					
					
						commit
						8ebacfeae6
					
				@ -448,6 +448,16 @@
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "verified": "Verified",
 | 
			
		||||
  "@verified": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "blocked": "Blocked",
 | 
			
		||||
  "@blocked": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "zoomIn": "Zoom in",
 | 
			
		||||
  "@zoomIn": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
 | 
			
		||||
import 'package:famedlysdk/encryption/utils/key_verification.dart';
 | 
			
		||||
import 'package:famedlysdk/famedlysdk.dart';
 | 
			
		||||
import 'package:fluffychat/components/dialogs/key_verification_dialog.dart';
 | 
			
		||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
@ -81,6 +83,36 @@ class DevicesSettingsState extends State<DevicesSettings> {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _verifyDeviceAction(BuildContext context, Device device) async {
 | 
			
		||||
    final req = Matrix.of(context)
 | 
			
		||||
        .client
 | 
			
		||||
        .userDeviceKeys[Matrix.of(context).client.userID]
 | 
			
		||||
        .deviceKeys[device.deviceId]
 | 
			
		||||
        .startVerification();
 | 
			
		||||
    req.onUpdate = () {
 | 
			
		||||
      if ({KeyVerificationState.error, KeyVerificationState.done}
 | 
			
		||||
          .contains(req.state)) {
 | 
			
		||||
        setState(() => null);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    await KeyVerificationDialog(
 | 
			
		||||
      request: req,
 | 
			
		||||
      l10n: L10n.of(context),
 | 
			
		||||
    ).show(context);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _blockDeviceAction(BuildContext context, Device device) async {
 | 
			
		||||
    final key = Matrix.of(context)
 | 
			
		||||
        .client
 | 
			
		||||
        .userDeviceKeys[Matrix.of(context).client.userID]
 | 
			
		||||
        .deviceKeys[device.deviceId];
 | 
			
		||||
    if (key.directVerified) {
 | 
			
		||||
      await key.setVerified(false);
 | 
			
		||||
    }
 | 
			
		||||
    await key.setBlocked(true);
 | 
			
		||||
    setState(() => null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
@ -118,6 +150,8 @@ class DevicesSettingsState extends State<DevicesSettings> {
 | 
			
		||||
                  thisDevice,
 | 
			
		||||
                  rename: (d) => _renameDeviceAction(context, d),
 | 
			
		||||
                  remove: (d) => _removeDevicesAction(context, [d]),
 | 
			
		||||
                  verify: (d) => _verifyDeviceAction(context, d),
 | 
			
		||||
                  block: (d) => _blockDeviceAction(context, d),
 | 
			
		||||
                ),
 | 
			
		||||
              Divider(height: 1),
 | 
			
		||||
              if (devices.isNotEmpty)
 | 
			
		||||
@ -153,6 +187,8 @@ class DevicesSettingsState extends State<DevicesSettings> {
 | 
			
		||||
                          devices[i],
 | 
			
		||||
                          rename: (d) => _renameDeviceAction(context, d),
 | 
			
		||||
                          remove: (d) => _removeDevicesAction(context, [d]),
 | 
			
		||||
                          verify: (d) => _verifyDeviceAction(context, d),
 | 
			
		||||
                          block: (d) => _blockDeviceAction(context, d),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
              ),
 | 
			
		||||
@ -164,62 +200,125 @@ class DevicesSettingsState extends State<DevicesSettings> {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum UserDeviceListItemAction {
 | 
			
		||||
  rename,
 | 
			
		||||
  remove,
 | 
			
		||||
  verify,
 | 
			
		||||
  block,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class UserDeviceListItem extends StatelessWidget {
 | 
			
		||||
  final Device userDevice;
 | 
			
		||||
  final Function remove;
 | 
			
		||||
  final Function rename;
 | 
			
		||||
  final void Function(Device) remove;
 | 
			
		||||
  final void Function(Device) rename;
 | 
			
		||||
  final void Function(Device) verify;
 | 
			
		||||
  final void Function(Device) block;
 | 
			
		||||
 | 
			
		||||
  const UserDeviceListItem(this.userDevice, {this.remove, this.rename, Key key})
 | 
			
		||||
      : super(key: key);
 | 
			
		||||
  const UserDeviceListItem(
 | 
			
		||||
    this.userDevice, {
 | 
			
		||||
    @required this.remove,
 | 
			
		||||
    @required this.rename,
 | 
			
		||||
    @required this.verify,
 | 
			
		||||
    @required this.block,
 | 
			
		||||
    Key key,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return PopupMenuButton(
 | 
			
		||||
      onSelected: (String action) {
 | 
			
		||||
    final keys = Matrix.of(context)
 | 
			
		||||
        .client
 | 
			
		||||
        .userDeviceKeys[Matrix.of(context).client.userID]
 | 
			
		||||
        ?.deviceKeys[userDevice.deviceId];
 | 
			
		||||
    final displayname = (userDevice.displayName?.isNotEmpty ?? false)
 | 
			
		||||
        ? userDevice.displayName
 | 
			
		||||
        : L10n.of(context).unknownDevice;
 | 
			
		||||
 | 
			
		||||
    return ListTile(
 | 
			
		||||
      onTap: () async {
 | 
			
		||||
        final action = await showModalActionSheet<UserDeviceListItemAction>(
 | 
			
		||||
          context: context,
 | 
			
		||||
          actions: [
 | 
			
		||||
            SheetAction(
 | 
			
		||||
              key: UserDeviceListItemAction.rename,
 | 
			
		||||
              label: L10n.of(context).changeDeviceName,
 | 
			
		||||
            ),
 | 
			
		||||
            SheetAction(
 | 
			
		||||
              key: UserDeviceListItemAction.verify,
 | 
			
		||||
              label: L10n.of(context).verify,
 | 
			
		||||
            ),
 | 
			
		||||
            if (keys != null) ...{
 | 
			
		||||
              SheetAction(
 | 
			
		||||
                key: UserDeviceListItemAction.block,
 | 
			
		||||
                label: L10n.of(context).blockDevice,
 | 
			
		||||
                isDestructiveAction: true,
 | 
			
		||||
              ),
 | 
			
		||||
              SheetAction(
 | 
			
		||||
                key: UserDeviceListItemAction.block,
 | 
			
		||||
                label: L10n.of(context).delete,
 | 
			
		||||
                isDestructiveAction: true,
 | 
			
		||||
              ),
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
        );
 | 
			
		||||
        switch (action) {
 | 
			
		||||
          case 'remove':
 | 
			
		||||
            if (remove != null) remove(userDevice);
 | 
			
		||||
          case UserDeviceListItemAction.rename:
 | 
			
		||||
            rename(userDevice);
 | 
			
		||||
            break;
 | 
			
		||||
          case UserDeviceListItemAction.remove:
 | 
			
		||||
            remove(userDevice);
 | 
			
		||||
            break;
 | 
			
		||||
          case UserDeviceListItemAction.verify:
 | 
			
		||||
            verify(userDevice);
 | 
			
		||||
            break;
 | 
			
		||||
          case UserDeviceListItemAction.block:
 | 
			
		||||
            block(userDevice);
 | 
			
		||||
            break;
 | 
			
		||||
          case 'rename':
 | 
			
		||||
            if (rename != null) rename(userDevice);
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      itemBuilder: (BuildContext context) => [
 | 
			
		||||
        PopupMenuItem<String>(
 | 
			
		||||
          value: 'rename',
 | 
			
		||||
          child: Text(L10n.of(context).changeDeviceName),
 | 
			
		||||
        ),
 | 
			
		||||
        PopupMenuItem<String>(
 | 
			
		||||
          value: 'remove',
 | 
			
		||||
          child: Text(
 | 
			
		||||
            L10n.of(context).removeDevice,
 | 
			
		||||
            style: TextStyle(color: Colors.red),
 | 
			
		||||
      leading: CircleAvatar(
 | 
			
		||||
        foregroundColor: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
        backgroundColor: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
        child: 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),
 | 
			
		||||
      ),
 | 
			
		||||
      title: Row(
 | 
			
		||||
        children: <Widget>[
 | 
			
		||||
          Text(
 | 
			
		||||
            displayname,
 | 
			
		||||
            maxLines: 1,
 | 
			
		||||
            overflow: TextOverflow.ellipsis,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
      child: ListTile(
 | 
			
		||||
        contentPadding: EdgeInsets.all(16.0),
 | 
			
		||||
        title: Row(
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: Text(
 | 
			
		||||
                (userDevice.displayName?.isNotEmpty ?? false)
 | 
			
		||||
                    ? userDevice.displayName
 | 
			
		||||
                    : L10n.of(context).unknownDevice,
 | 
			
		||||
                maxLines: 1,
 | 
			
		||||
                overflow: TextOverflow.ellipsis,
 | 
			
		||||
          Spacer(),
 | 
			
		||||
          Text(userDevice.lastSeenTs.localizedTimeShort(context)),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      subtitle: Row(
 | 
			
		||||
        children: <Widget>[
 | 
			
		||||
          Text(userDevice.deviceId),
 | 
			
		||||
          Spacer(),
 | 
			
		||||
          if (keys != null)
 | 
			
		||||
            Text(
 | 
			
		||||
              keys.blocked
 | 
			
		||||
                  ? L10n.of(context).blocked
 | 
			
		||||
                  : keys.verified
 | 
			
		||||
                      ? L10n.of(context).verified
 | 
			
		||||
                      : L10n.of(context).unknownDevice,
 | 
			
		||||
              style: TextStyle(
 | 
			
		||||
                color: keys.blocked
 | 
			
		||||
                    ? Colors.red
 | 
			
		||||
                    : keys.verified
 | 
			
		||||
                        ? Colors.green
 | 
			
		||||
                        : Colors.orange,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Text(userDevice.lastSeenTs.localizedTimeShort(context)),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        subtitle: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            Text('${L10n.of(context).id}: ${userDevice.deviceId}'),
 | 
			
		||||
            Text('${L10n.of(context).lastSeenIp}: ${userDevice.lastSeenIp}'),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user