mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-03 22:07:23 +01:00 
			
		
		
		
	Merge branch 'krille/stories-settings' into 'main'
feat: Settings for stories See merge request famedly/fluffychat!640
This commit is contained in:
		
						commit
						8789863906
					
				@ -229,7 +229,7 @@
 | 
			
		||||
			isa = PBXProject;
 | 
			
		||||
			attributes = {
 | 
			
		||||
				LastSwiftUpdateCheck = 1240;
 | 
			
		||||
				LastUpgradeCheck = 1020;
 | 
			
		||||
				LastUpgradeCheck = 1300;
 | 
			
		||||
				ORGANIZATIONNAME = "";
 | 
			
		||||
				TargetAttributes = {
 | 
			
		||||
					97C146ED1CF9000F007C117D = {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<Scheme
 | 
			
		||||
   LastUpgradeVersion = "1020"
 | 
			
		||||
   LastUpgradeVersion = "1300"
 | 
			
		||||
   version = "1.3">
 | 
			
		||||
   <BuildAction
 | 
			
		||||
      parallelizeBuildables = "YES"
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ import 'package:fluffychat/pages/settings_ignore_list/settings_ignore_list.dart'
 | 
			
		||||
import 'package:fluffychat/pages/settings_multiple_emotes/settings_multiple_emotes.dart';
 | 
			
		||||
import 'package:fluffychat/pages/settings_notifications/settings_notifications.dart';
 | 
			
		||||
import 'package:fluffychat/pages/settings_security/settings_security.dart';
 | 
			
		||||
import 'package:fluffychat/pages/settings_stories/settings_stories.dart';
 | 
			
		||||
import 'package:fluffychat/pages/settings_style/settings_style.dart';
 | 
			
		||||
import 'package:fluffychat/pages/sign_up/signup.dart';
 | 
			
		||||
import 'package:fluffychat/pages/story/story_page.dart';
 | 
			
		||||
@ -334,6 +335,11 @@ class AppRoutes {
 | 
			
		||||
          widget: const SettingsSecurity(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
          stackedRoutes: [
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: 'stories',
 | 
			
		||||
              widget: const SettingsStories(),
 | 
			
		||||
              buildTransition: _dynamicTransition,
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: 'ignorelist',
 | 
			
		||||
              widget: const SettingsIgnoreList(),
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,11 @@ class SettingsSecurityView extends StatelessWidget {
 | 
			
		||||
          withScrolling: true,
 | 
			
		||||
          child: Column(
 | 
			
		||||
            children: [
 | 
			
		||||
              ListTile(
 | 
			
		||||
                trailing: const Icon(Icons.panorama_fish_eye),
 | 
			
		||||
                title: Text(L10n.of(context).whoCanSeeMyStories),
 | 
			
		||||
                onTap: () => VRouter.of(context).to('stories'),
 | 
			
		||||
              ),
 | 
			
		||||
              ListTile(
 | 
			
		||||
                trailing: const Icon(Icons.close),
 | 
			
		||||
                title: Text(L10n.of(context).ignoredUsers),
 | 
			
		||||
@ -61,36 +66,24 @@ class SettingsSecurityView extends StatelessWidget {
 | 
			
		||||
                  ),
 | 
			
		||||
                  trailing: const Icon(Icons.vpn_key_outlined),
 | 
			
		||||
                ),
 | 
			
		||||
                ListTile(
 | 
			
		||||
                  title: Text(L10n.of(context).crossSigningEnabled),
 | 
			
		||||
                  trailing:
 | 
			
		||||
                      Matrix.of(context).client.encryption.crossSigning.enabled
 | 
			
		||||
                          ? const Icon(Icons.check, color: Colors.green)
 | 
			
		||||
                          : const Icon(Icons.error, color: Colors.red),
 | 
			
		||||
                  onTap:
 | 
			
		||||
                      Matrix.of(context).client.encryption.crossSigning.enabled
 | 
			
		||||
                          ? null
 | 
			
		||||
                          : () => controller.showBootstrapDialog(context),
 | 
			
		||||
                ),
 | 
			
		||||
                ListTile(
 | 
			
		||||
                  title: Text(L10n.of(context).onlineKeyBackupEnabled),
 | 
			
		||||
                  trailing:
 | 
			
		||||
                      Matrix.of(context).client.encryption.keyManager.enabled
 | 
			
		||||
                          ? const Icon(Icons.check, color: Colors.green)
 | 
			
		||||
                          : const Icon(Icons.error, color: Colors.red),
 | 
			
		||||
                  onTap: Matrix.of(context).client.encryption.keyManager.enabled
 | 
			
		||||
                      ? null
 | 
			
		||||
                      : () => controller.showBootstrapDialog(context),
 | 
			
		||||
                ),
 | 
			
		||||
                ListTile(
 | 
			
		||||
                  title: const Text('Session verified'),
 | 
			
		||||
                  trailing: !Matrix.of(context).client.isUnknownSession
 | 
			
		||||
                      ? const Icon(Icons.check, color: Colors.green)
 | 
			
		||||
                      : const Icon(Icons.error, color: Colors.red),
 | 
			
		||||
                  onTap: !Matrix.of(context).client.isUnknownSession
 | 
			
		||||
                      ? null
 | 
			
		||||
                      : () => controller.showBootstrapDialog(context),
 | 
			
		||||
                ),
 | 
			
		||||
                if (!Matrix.of(context).client.encryption.crossSigning.enabled)
 | 
			
		||||
                  ListTile(
 | 
			
		||||
                    title: Text(L10n.of(context).crossSigningEnabled),
 | 
			
		||||
                    trailing: const Icon(Icons.error, color: Colors.red),
 | 
			
		||||
                    onTap: () => controller.showBootstrapDialog(context),
 | 
			
		||||
                  ),
 | 
			
		||||
                if (!Matrix.of(context).client.encryption.keyManager.enabled)
 | 
			
		||||
                  ListTile(
 | 
			
		||||
                    title: Text(L10n.of(context).onlineKeyBackupEnabled),
 | 
			
		||||
                    trailing: const Icon(Icons.error, color: Colors.red),
 | 
			
		||||
                    onTap: () => controller.showBootstrapDialog(context),
 | 
			
		||||
                  ),
 | 
			
		||||
                if (Matrix.of(context).client.isUnknownSession)
 | 
			
		||||
                  ListTile(
 | 
			
		||||
                    title: const Text('Session verified'),
 | 
			
		||||
                    trailing: const Icon(Icons.error, color: Colors.red),
 | 
			
		||||
                    onTap: () => controller.showBootstrapDialog(context),
 | 
			
		||||
                  ),
 | 
			
		||||
                FutureBuilder(
 | 
			
		||||
                  future: () async {
 | 
			
		||||
                    return (await Matrix.of(context)
 | 
			
		||||
@ -104,15 +97,13 @@ class SettingsSecurityView extends StatelessWidget {
 | 
			
		||||
                            .crossSigning
 | 
			
		||||
                            .isCached());
 | 
			
		||||
                  }(),
 | 
			
		||||
                  builder: (context, snapshot) => ListTile(
 | 
			
		||||
                    title: Text(L10n.of(context).keysCached),
 | 
			
		||||
                    trailing: snapshot.data == true
 | 
			
		||||
                        ? const Icon(Icons.check, color: Colors.green)
 | 
			
		||||
                        : const Icon(Icons.error, color: Colors.red),
 | 
			
		||||
                    onTap: snapshot.data == true
 | 
			
		||||
                        ? null
 | 
			
		||||
                        : () => controller.showBootstrapDialog(context),
 | 
			
		||||
                  ),
 | 
			
		||||
                  builder: (context, snapshot) => snapshot.data == true
 | 
			
		||||
                      ? Container()
 | 
			
		||||
                      : ListTile(
 | 
			
		||||
                          title: Text(L10n.of(context).keysCached),
 | 
			
		||||
                          trailing: const Icon(Icons.error, color: Colors.red),
 | 
			
		||||
                          onTap: () => controller.showBootstrapDialog(context),
 | 
			
		||||
                        ),
 | 
			
		||||
                ),
 | 
			
		||||
              },
 | 
			
		||||
            ],
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										97
									
								
								lib/pages/settings_stories/settings_stories.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								lib/pages/settings_stories/settings_stories.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
			
		||||
//@dart=2.12
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
 | 
			
		||||
import 'package:matrix/matrix.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:fluffychat/pages/settings_stories/settings_stories_view.dart';
 | 
			
		||||
import 'package:fluffychat/widgets/matrix.dart';
 | 
			
		||||
import '../../utils/matrix_sdk_extensions.dart/client_stories_extension.dart';
 | 
			
		||||
 | 
			
		||||
class SettingsStories extends StatefulWidget {
 | 
			
		||||
  const SettingsStories({Key? key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  SettingsStoriesController createState() => SettingsStoriesController();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SettingsStoriesController extends State<SettingsStories> {
 | 
			
		||||
  final Map<User, bool> users = {};
 | 
			
		||||
 | 
			
		||||
  Room? _storiesRoom;
 | 
			
		||||
 | 
			
		||||
  Future<void>? loadUsers;
 | 
			
		||||
 | 
			
		||||
  bool noStoriesRoom = false;
 | 
			
		||||
 | 
			
		||||
  Future<void> toggleUser(User user) async {
 | 
			
		||||
    final room = _storiesRoom;
 | 
			
		||||
    if (room == null) return;
 | 
			
		||||
 | 
			
		||||
    if (users[user] ?? false) {
 | 
			
		||||
      // Kick user from stories room and add to block list
 | 
			
		||||
      final blockList = room.client.storiesBlockList;
 | 
			
		||||
      blockList.add(user.id);
 | 
			
		||||
      await showFutureLoadingDialog(
 | 
			
		||||
          context: context,
 | 
			
		||||
          future: () async {
 | 
			
		||||
            await user.kick();
 | 
			
		||||
            await room.client.setStoriesBlockList(blockList.toSet().toList());
 | 
			
		||||
            setState(() {
 | 
			
		||||
              users[user] = false;
 | 
			
		||||
            });
 | 
			
		||||
          });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Invite user to stories room and remove from block list
 | 
			
		||||
    final blockList = room.client.storiesBlockList;
 | 
			
		||||
    blockList.remove(user.id);
 | 
			
		||||
    await showFutureLoadingDialog(
 | 
			
		||||
        context: context,
 | 
			
		||||
        future: () async {
 | 
			
		||||
          await room.client.setStoriesBlockList(blockList);
 | 
			
		||||
          await room.invite(user.id);
 | 
			
		||||
          setState(() {
 | 
			
		||||
            users[user] = true;
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _loadUsers() async {
 | 
			
		||||
    final room =
 | 
			
		||||
        _storiesRoom = await Matrix.of(context).client.getStoriesRoom(context);
 | 
			
		||||
    if (room == null) {
 | 
			
		||||
      noStoriesRoom = true;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    final users = await room.requestParticipants();
 | 
			
		||||
    users.removeWhere((u) => u.id == room.client.userID);
 | 
			
		||||
    final contacts = Matrix.of(context)
 | 
			
		||||
        .client
 | 
			
		||||
        .contacts
 | 
			
		||||
        .where((contact) => !users.any((u) => u.id == contact.id));
 | 
			
		||||
    for (final user in contacts) {
 | 
			
		||||
      this.users[user] = false;
 | 
			
		||||
    }
 | 
			
		||||
    for (final user in users) {
 | 
			
		||||
      this.users[user] = true;
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    WidgetsBinding.instance?.addPostFrameCallback((_) {
 | 
			
		||||
      setState(() {
 | 
			
		||||
        loadUsers = _loadUsers();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) => SettingsStoriesView(this);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								lib/pages/settings_stories/settings_stories_view.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								lib/pages/settings_stories/settings_stories_view.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
			
		||||
//@dart=2.12
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:fluffychat/pages/settings_stories/settings_stories.dart';
 | 
			
		||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
 | 
			
		||||
import 'package:fluffychat/widgets/avatar.dart';
 | 
			
		||||
 | 
			
		||||
class SettingsStoriesView extends StatelessWidget {
 | 
			
		||||
  final SettingsStoriesController controller;
 | 
			
		||||
  const SettingsStoriesView(this.controller, {Key? key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(),
 | 
			
		||||
      body: FutureBuilder(
 | 
			
		||||
        future: controller.loadUsers,
 | 
			
		||||
        builder: (context, snapshot) {
 | 
			
		||||
          final error = snapshot.error;
 | 
			
		||||
          if (error != null) {
 | 
			
		||||
            return Center(child: Text(error.toLocalizedString(context)));
 | 
			
		||||
          }
 | 
			
		||||
          if (snapshot.connectionState != ConnectionState.done) {
 | 
			
		||||
            return const Center(
 | 
			
		||||
                child: CircularProgressIndicator.adaptive(
 | 
			
		||||
              strokeWidth: 2,
 | 
			
		||||
            ));
 | 
			
		||||
          }
 | 
			
		||||
          return ListView.builder(
 | 
			
		||||
            itemCount: controller.users.length,
 | 
			
		||||
            itemBuilder: (context, i) {
 | 
			
		||||
              final user = controller.users.keys.toList()[i];
 | 
			
		||||
              return SwitchListTile.adaptive(
 | 
			
		||||
                value: controller.users[user] ?? false,
 | 
			
		||||
                onChanged: (_) => controller.toggleUser(user),
 | 
			
		||||
                secondary: Avatar(
 | 
			
		||||
                  mxContent: user.avatarUrl,
 | 
			
		||||
                  name: user.calcDisplayname(),
 | 
			
		||||
                ),
 | 
			
		||||
                title: Text(user.calcDisplayname()),
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user