mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-03 22:07:23 +01:00 
			
		
		
		
	fix: Permission chooser dialog on iOS
This commit is contained in:
		
							parent
							
								
									7d96fe7224
								
							
						
					
					
						commit
						f2865f6f09
					
				@ -2875,5 +2875,7 @@
 | 
			
		||||
    "placeholders": {
 | 
			
		||||
      "count": {}
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  },
 | 
			
		||||
  "user": "User",
 | 
			
		||||
  "custom": "Custom"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -30,9 +30,10 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
 | 
			
		||||
          SnackBar(content: Text(L10n.of(context)!.noPermission)));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    final newLevel =
 | 
			
		||||
        await PermissionSliderDialog(initialPermission: currentLevel)
 | 
			
		||||
            .show(context);
 | 
			
		||||
    final newLevel = await showPermissionChooser(
 | 
			
		||||
      context,
 | 
			
		||||
      currentLevel: currentLevel,
 | 
			
		||||
    );
 | 
			
		||||
    if (newLevel == null) return;
 | 
			
		||||
    final content = Map<String, dynamic>.from(
 | 
			
		||||
        room.getState(EventTypes.RoomPowerLevels)!.content);
 | 
			
		||||
 | 
			
		||||
@ -115,9 +115,10 @@ class UserBottomSheetController extends State<UserBottomSheet> {
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'permission':
 | 
			
		||||
        final newPermission = await PermissionSliderDialog(
 | 
			
		||||
                initialPermission: widget.user.powerLevel)
 | 
			
		||||
            .show(context);
 | 
			
		||||
        final newPermission = await showPermissionChooser(
 | 
			
		||||
          context,
 | 
			
		||||
          currentLevel: widget.user.powerLevel,
 | 
			
		||||
        );
 | 
			
		||||
        if (newPermission != null) {
 | 
			
		||||
          if (newPermission == 100 && await _askConfirmation() == false) break;
 | 
			
		||||
          await showFutureLoadingDialog(
 | 
			
		||||
 | 
			
		||||
@ -1,119 +0,0 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:matrix/matrix.dart';
 | 
			
		||||
import 'package:vrouter/vrouter.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:fluffychat/widgets/avatar.dart';
 | 
			
		||||
import 'package:fluffychat/widgets/matrix.dart';
 | 
			
		||||
import '../utils/matrix_sdk_extensions.dart/client_presence_extension.dart';
 | 
			
		||||
import '../utils/matrix_sdk_extensions.dart/presence_extension.dart';
 | 
			
		||||
 | 
			
		||||
class ContactsList extends StatefulWidget {
 | 
			
		||||
  final TextEditingController searchController;
 | 
			
		||||
 | 
			
		||||
  const ContactsList({
 | 
			
		||||
    Key? key,
 | 
			
		||||
    required this.searchController,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
  @override
 | 
			
		||||
  _ContactsState createState() => _ContactsState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ContactsState extends State<ContactsList> {
 | 
			
		||||
  StreamSubscription? _onSync;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    _onSync?.cancel();
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  DateTime _lastSetState = DateTime.now();
 | 
			
		||||
  Timer? _coolDown;
 | 
			
		||||
 | 
			
		||||
  void _updateView() {
 | 
			
		||||
    _lastSetState = DateTime.now();
 | 
			
		||||
    setState(() {});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final client = Matrix.of(context).client;
 | 
			
		||||
    _onSync ??= client.onSync.stream.listen((_) {
 | 
			
		||||
      if (DateTime.now().millisecondsSinceEpoch -
 | 
			
		||||
              _lastSetState.millisecondsSinceEpoch <
 | 
			
		||||
          1000) {
 | 
			
		||||
        _coolDown?.cancel();
 | 
			
		||||
        _coolDown = Timer(const Duration(seconds: 1), _updateView);
 | 
			
		||||
      } else {
 | 
			
		||||
        _updateView();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    final contactList = Matrix.of(context)
 | 
			
		||||
        .client
 | 
			
		||||
        .contactList
 | 
			
		||||
        .where((p) => p.userid
 | 
			
		||||
            .toLowerCase()
 | 
			
		||||
            .contains(widget.searchController.text.toLowerCase()))
 | 
			
		||||
        .toList();
 | 
			
		||||
    return ListView.builder(
 | 
			
		||||
      itemCount: contactList.length,
 | 
			
		||||
      itemBuilder: (_, i) => _ContactListTile(contact: contactList[i]),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ContactListTile extends StatelessWidget {
 | 
			
		||||
  final CachedPresence contact;
 | 
			
		||||
 | 
			
		||||
  const _ContactListTile({Key? key, required this.contact}) : super(key: key);
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return FutureBuilder<Profile>(
 | 
			
		||||
        future: Matrix.of(context).client.getProfileFromUserId(contact.userid),
 | 
			
		||||
        builder: (context, snapshot) {
 | 
			
		||||
          final displayname = snapshot.data?.displayName ??
 | 
			
		||||
              contact.userid.localpart ??
 | 
			
		||||
              'No valid MXID';
 | 
			
		||||
          final avatarUrl = snapshot.data?.avatarUrl;
 | 
			
		||||
          return ListTile(
 | 
			
		||||
            leading: SizedBox(
 | 
			
		||||
              width: Avatar.defaultSize,
 | 
			
		||||
              height: Avatar.defaultSize,
 | 
			
		||||
              child: Stack(
 | 
			
		||||
                children: [
 | 
			
		||||
                  Center(
 | 
			
		||||
                    child: Avatar(
 | 
			
		||||
                      mxContent: avatarUrl,
 | 
			
		||||
                      name: displayname,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  Align(
 | 
			
		||||
                    alignment: Alignment.bottomRight,
 | 
			
		||||
                    child: Icon(
 | 
			
		||||
                      Icons.circle,
 | 
			
		||||
                      color: contact.color,
 | 
			
		||||
                      size: 12,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            title: Text(displayname),
 | 
			
		||||
            subtitle: Text(contact.getLocalizedStatusMessage(context),
 | 
			
		||||
                style: contact.statusMsg?.isNotEmpty ?? false
 | 
			
		||||
                    ? TextStyle(
 | 
			
		||||
                        color: Theme.of(context).colorScheme.secondary,
 | 
			
		||||
                        fontWeight: FontWeight.bold,
 | 
			
		||||
                      )
 | 
			
		||||
                    : null),
 | 
			
		||||
            onTap: () => VRouter.of(context).toSegments([
 | 
			
		||||
              'rooms',
 | 
			
		||||
              Matrix.of(context).client.getDirectChatFromUserId(contact.userid)!
 | 
			
		||||
            ]),
 | 
			
		||||
          );
 | 
			
		||||
        });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,91 +1,68 @@
 | 
			
		||||
import 'package:flutter/cupertino.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
 | 
			
		||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:fluffychat/utils/platform_infos.dart';
 | 
			
		||||
import 'package:fluffychat/widgets/adaptive_flat_button.dart';
 | 
			
		||||
 | 
			
		||||
class PermissionSliderDialog extends StatefulWidget {
 | 
			
		||||
  const PermissionSliderDialog({
 | 
			
		||||
    Key? key,
 | 
			
		||||
    this.initialPermission = 0,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  Future<int?> show(BuildContext context) => PlatformInfos.isCupertinoStyle
 | 
			
		||||
      ? showCupertinoDialog<int>(
 | 
			
		||||
          context: context,
 | 
			
		||||
          builder: (context) => this,
 | 
			
		||||
          useRootNavigator: false,
 | 
			
		||||
        )
 | 
			
		||||
      : showDialog<int>(
 | 
			
		||||
          context: context,
 | 
			
		||||
          builder: (context) => this,
 | 
			
		||||
          useRootNavigator: false,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
  final int initialPermission;
 | 
			
		||||
  @override
 | 
			
		||||
  _PermissionSliderDialogState createState() => _PermissionSliderDialogState();
 | 
			
		||||
enum PermissionLevel {
 | 
			
		||||
  user,
 | 
			
		||||
  moderator,
 | 
			
		||||
  admin,
 | 
			
		||||
  custom,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _PermissionSliderDialogState extends State<PermissionSliderDialog> {
 | 
			
		||||
  late int _permission;
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    _permission = widget.initialPermission;
 | 
			
		||||
    super.initState();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final title = Text(
 | 
			
		||||
      L10n.of(context)!.setPermissionsLevel,
 | 
			
		||||
      textAlign: TextAlign.center,
 | 
			
		||||
    );
 | 
			
		||||
    final content = Column(
 | 
			
		||||
      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
      children: [
 | 
			
		||||
        Text('Level: ' +
 | 
			
		||||
            (_permission == 100
 | 
			
		||||
                ? '$_permission (${L10n.of(context)!.admin})'
 | 
			
		||||
                : _permission >= 50
 | 
			
		||||
                    ? '$_permission (${L10n.of(context)!.moderator})'
 | 
			
		||||
                    : _permission.toString())),
 | 
			
		||||
        SizedBox(
 | 
			
		||||
          height: 56,
 | 
			
		||||
          child: Slider.adaptive(
 | 
			
		||||
            value: _permission.toDouble(),
 | 
			
		||||
            onChanged: (d) => setState(() => _permission = d.round()),
 | 
			
		||||
            max: 100.0,
 | 
			
		||||
            min: 0.0,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
    final buttons = [
 | 
			
		||||
      AdaptiveFlatButton(
 | 
			
		||||
        label: L10n.of(context)!.cancel,
 | 
			
		||||
        onPressed: () =>
 | 
			
		||||
            Navigator.of(context, rootNavigator: false).pop<int>(null),
 | 
			
		||||
      ),
 | 
			
		||||
      AdaptiveFlatButton(
 | 
			
		||||
        label: L10n.of(context)!.confirm,
 | 
			
		||||
        onPressed: () =>
 | 
			
		||||
            Navigator.of(context, rootNavigator: false).pop<int>(_permission),
 | 
			
		||||
      ),
 | 
			
		||||
    ];
 | 
			
		||||
    if (PlatformInfos.isCupertinoStyle) {
 | 
			
		||||
      return CupertinoAlertDialog(
 | 
			
		||||
        title: title,
 | 
			
		||||
        content: content,
 | 
			
		||||
        actions: buttons,
 | 
			
		||||
      );
 | 
			
		||||
extension on PermissionLevel {
 | 
			
		||||
  String toLocalizedString(BuildContext context) {
 | 
			
		||||
    switch (this) {
 | 
			
		||||
      case PermissionLevel.user:
 | 
			
		||||
        return L10n.of(context)!.user;
 | 
			
		||||
      case PermissionLevel.moderator:
 | 
			
		||||
        return L10n.of(context)!.moderator;
 | 
			
		||||
      case PermissionLevel.admin:
 | 
			
		||||
        return L10n.of(context)!.admin;
 | 
			
		||||
      case PermissionLevel.custom:
 | 
			
		||||
      default:
 | 
			
		||||
        return L10n.of(context)!.custom;
 | 
			
		||||
    }
 | 
			
		||||
    return AlertDialog(
 | 
			
		||||
      title: title,
 | 
			
		||||
      content: content,
 | 
			
		||||
      actions: buttons,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Future<int?> showPermissionChooser(BuildContext context,
 | 
			
		||||
    {int currentLevel = 0}) async {
 | 
			
		||||
  final permissionLevel = await showModalActionSheet(
 | 
			
		||||
    context: context,
 | 
			
		||||
    title: L10n.of(context)!.setPermissionsLevel,
 | 
			
		||||
    actions: PermissionLevel.values
 | 
			
		||||
        .map(
 | 
			
		||||
          (level) => SheetAction(
 | 
			
		||||
            key: level,
 | 
			
		||||
            label: level.toLocalizedString(context),
 | 
			
		||||
          ),
 | 
			
		||||
        )
 | 
			
		||||
        .toList(),
 | 
			
		||||
  );
 | 
			
		||||
  if (permissionLevel == null) return null;
 | 
			
		||||
 | 
			
		||||
  switch (permissionLevel) {
 | 
			
		||||
    case PermissionLevel.user:
 | 
			
		||||
      return 0;
 | 
			
		||||
    case PermissionLevel.moderator:
 | 
			
		||||
      return 50;
 | 
			
		||||
    case PermissionLevel.admin:
 | 
			
		||||
      return 100;
 | 
			
		||||
    case PermissionLevel.custom:
 | 
			
		||||
      final customLevel = await showTextInputDialog(
 | 
			
		||||
        context: context,
 | 
			
		||||
        title: L10n.of(context)!.setPermissionsLevel,
 | 
			
		||||
        textFields: [
 | 
			
		||||
          DialogTextField(
 | 
			
		||||
            initialText: currentLevel.toString(),
 | 
			
		||||
            keyboardType: TextInputType.number,
 | 
			
		||||
            autocorrect: false,
 | 
			
		||||
          )
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
      if (customLevel == null) return null;
 | 
			
		||||
      return int.tryParse(customLevel.first);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user