From 1c62c26f306196892774611e4aeb856e82a9bfbe Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Sat, 25 Dec 2021 10:20:18 +0100 Subject: [PATCH] feat: Settings for stories --- ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- lib/config/routes.dart | 6 ++ .../settings_security_view.dart | 69 ++++++------- .../settings_stories/settings_stories.dart | 97 +++++++++++++++++++ .../settings_stories_view.dart | 49 ++++++++++ 6 files changed, 184 insertions(+), 41 deletions(-) create mode 100644 lib/pages/settings_stories/settings_stories.dart create mode 100644 lib/pages/settings_stories/settings_stories_view.dart diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 12664c42..dfc4598c 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -229,7 +229,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1240; - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140cf..3db53b6e 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ 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), + ), ), }, ], diff --git a/lib/pages/settings_stories/settings_stories.dart b/lib/pages/settings_stories/settings_stories.dart new file mode 100644 index 00000000..bf2f25f2 --- /dev/null +++ b/lib/pages/settings_stories/settings_stories.dart @@ -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 { + final Map users = {}; + + Room? _storiesRoom; + + Future? loadUsers; + + bool noStoriesRoom = false; + + Future 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 _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); +} diff --git a/lib/pages/settings_stories/settings_stories_view.dart b/lib/pages/settings_stories/settings_stories_view.dart new file mode 100644 index 00000000..b77008ba --- /dev/null +++ b/lib/pages/settings_stories/settings_stories_view.dart @@ -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()), + ); + }, + ); + }, + ), + ); + } +}