diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 941ca7b2e..00325b0ca 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2793,5 +2793,6 @@ "welcomeText": "Hey Hey 👋 This is FluffyChat. You can sign in to any homeserver, which is compatible with https://matrix.org. And then chat with anyone. It's a huge decentralized messaging network!", "blur": "Blur:", "opacity": "Opacity:", - "setWallpaper": "Set wallpaper" + "setWallpaper": "Set wallpaper", + "manageAccount": "Manage account" } diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index 679b60206..d0535d77c 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -160,6 +160,7 @@ class HomeserverPickerController extends State { final result = await FlutterWebAuth2.authenticate( url: url.toString(), callbackUrlScheme: urlScheme, + options: FlutterWebAuth2Options(useWebview: !isDefaultPlatform), ); final token = Uri.parse(result).queryParameters['loginToken']; if (token?.isEmpty ?? false) return; diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index e1b713655..51929f357 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -201,6 +201,13 @@ class SettingsController extends State { checkBootstrap(); } + Future getOidcAccountManageUrl() async { + final wellKnown = await Matrix.of(context).client.getWellknown(); + return wellKnown.additionalProperties + .tryGetMap('org.matrix.msc2965.authentication') + ?.tryGet('account'); + } + @override Widget build(BuildContext context) { final client = Matrix.of(context).client; diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index f6fbcbed2..40934653f 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -32,154 +32,172 @@ class SettingsView extends StatelessWidget { ), body: ListTileTheme( iconColor: theme.colorScheme.onSurface, - child: ListView( - key: const Key('SettingsListViewContent'), - children: [ - FutureBuilder( - future: controller.profileFuture, - builder: (context, snapshot) { - final profile = snapshot.data; - final mxid = - Matrix.of(context).client.userID ?? L10n.of(context).user; - final displayname = - profile?.displayName ?? mxid.localpart ?? mxid; - return Row( - children: [ - Padding( - padding: const EdgeInsets.all(32.0), - child: Stack( - children: [ - Avatar( - mxContent: profile?.avatarUrl, - name: displayname, - size: Avatar.defaultSize * 2.5, + child: FutureBuilder( + future: controller.getOidcAccountManageUrl(), + builder: (context, snapshot) { + final accountManageUrl = snapshot.data; + return ListView( + key: const Key('SettingsListViewContent'), + children: [ + FutureBuilder( + future: controller.profileFuture, + builder: (context, snapshot) { + final profile = snapshot.data; + final mxid = Matrix.of(context).client.userID ?? + L10n.of(context).user; + final displayname = + profile?.displayName ?? mxid.localpart ?? mxid; + return Row( + children: [ + Padding( + padding: const EdgeInsets.all(32.0), + child: Stack( + children: [ + Avatar( + mxContent: profile?.avatarUrl, + name: displayname, + size: Avatar.defaultSize * 2.5, + ), + if (profile != null) + Positioned( + bottom: 0, + right: 0, + child: FloatingActionButton.small( + elevation: 2, + onPressed: controller.setAvatarAction, + heroTag: null, + child: + const Icon(Icons.camera_alt_outlined), + ), + ), + ], ), - if (profile != null) - Positioned( - bottom: 0, - right: 0, - child: FloatingActionButton.small( - elevation: 2, - onPressed: controller.setAvatarAction, - heroTag: null, - child: const Icon(Icons.camera_alt_outlined), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextButton.icon( + onPressed: controller.setDisplaynameAction, + icon: const Icon( + Icons.edit_outlined, + size: 16, + ), + style: TextButton.styleFrom( + foregroundColor: theme.colorScheme.onSurface, + ), + label: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 18, + ), + ), ), - ), - ], - ), - ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextButton.icon( - onPressed: controller.setDisplaynameAction, - icon: const Icon( - Icons.edit_outlined, - size: 16, - ), - style: TextButton.styleFrom( - foregroundColor: theme.colorScheme.onSurface, - ), - label: Text( - displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 18, + TextButton.icon( + onPressed: () => + FluffyShare.share(mxid, context), + icon: const Icon( + Icons.copy_outlined, + size: 14, + ), + style: TextButton.styleFrom( + foregroundColor: theme.colorScheme.secondary, + ), + label: Text( + mxid, + maxLines: 1, + overflow: TextOverflow.ellipsis, + // style: const TextStyle(fontSize: 12), + ), ), - ), - ), - TextButton.icon( - onPressed: () => FluffyShare.share(mxid, context), - icon: const Icon( - Icons.copy_outlined, - size: 14, - ), - style: TextButton.styleFrom( - foregroundColor: theme.colorScheme.secondary, - ), - label: Text( - mxid, - maxLines: 1, - overflow: TextOverflow.ellipsis, - // style: const TextStyle(fontSize: 12), - ), + ], ), - ], - ), + ), + ], + ); + }, + ), + if (accountManageUrl != null) + ListTile( + leading: const Icon(Icons.account_circle_outlined), + title: Text(L10n.of(context).manageAccount), + trailing: const Icon(Icons.open_in_new_outlined), + onTap: () => launchUrlString( + accountManageUrl, + mode: LaunchMode.inAppBrowserView, ), - ], - ); - }, - ), - Divider(color: theme.dividerColor), - if (showChatBackupBanner == null) - ListTile( - leading: const Icon(Icons.backup_outlined), - title: Text(L10n.of(context).chatBackup), - trailing: const CircularProgressIndicator.adaptive(), - ) - else - SwitchListTile.adaptive( - controlAffinity: ListTileControlAffinity.trailing, - value: controller.showChatBackupBanner == false, - secondary: const Icon(Icons.backup_outlined), - title: Text(L10n.of(context).chatBackup), - onChanged: controller.firstRunBootstrapAction, - ), - Divider( - color: theme.dividerColor, - ), - ListTile( - leading: const Icon(Icons.format_paint_outlined), - title: Text(L10n.of(context).changeTheme), - onTap: () => context.go('/rooms/settings/style'), - ), - ListTile( - leading: const Icon(Icons.notifications_outlined), - title: Text(L10n.of(context).notifications), - onTap: () => context.go('/rooms/settings/notifications'), - ), - ListTile( - leading: const Icon(Icons.devices_outlined), - title: Text(L10n.of(context).devices), - onTap: () => context.go('/rooms/settings/devices'), - ), - ListTile( - leading: const Icon(Icons.forum_outlined), - title: Text(L10n.of(context).chat), - onTap: () => context.go('/rooms/settings/chat'), - ), - ListTile( - leading: const Icon(Icons.shield_outlined), - title: Text(L10n.of(context).security), - onTap: () => context.go('/rooms/settings/security'), - ), - Divider(color: theme.dividerColor), - ListTile( - leading: const Icon(Icons.help_outline_outlined), - title: Text(L10n.of(context).help), - onTap: () => launchUrlString(AppConfig.supportUrl), - ), - ListTile( - leading: const Icon(Icons.shield_sharp), - title: Text(L10n.of(context).privacy), - onTap: () => launchUrlString(AppConfig.privacyUrl), - ), - ListTile( - leading: const Icon(Icons.info_outline_rounded), - title: Text(L10n.of(context).about), - onTap: () => PlatformInfos.showDialog(context), - ), - Divider(color: theme.dividerColor), - ListTile( - leading: const Icon(Icons.logout_outlined), - title: Text(L10n.of(context).logout), - onTap: controller.logoutAction, - ), - ], + ), + Divider(color: theme.dividerColor), + if (showChatBackupBanner == null) + ListTile( + leading: const Icon(Icons.backup_outlined), + title: Text(L10n.of(context).chatBackup), + trailing: const CircularProgressIndicator.adaptive(), + ) + else + SwitchListTile.adaptive( + controlAffinity: ListTileControlAffinity.trailing, + value: controller.showChatBackupBanner == false, + secondary: const Icon(Icons.backup_outlined), + title: Text(L10n.of(context).chatBackup), + onChanged: controller.firstRunBootstrapAction, + ), + Divider( + color: theme.dividerColor, + ), + ListTile( + leading: const Icon(Icons.format_paint_outlined), + title: Text(L10n.of(context).changeTheme), + onTap: () => context.go('/rooms/settings/style'), + ), + ListTile( + leading: const Icon(Icons.notifications_outlined), + title: Text(L10n.of(context).notifications), + onTap: () => context.go('/rooms/settings/notifications'), + ), + ListTile( + leading: const Icon(Icons.devices_outlined), + title: Text(L10n.of(context).devices), + onTap: () => context.go('/rooms/settings/devices'), + ), + ListTile( + leading: const Icon(Icons.forum_outlined), + title: Text(L10n.of(context).chat), + onTap: () => context.go('/rooms/settings/chat'), + ), + ListTile( + leading: const Icon(Icons.shield_outlined), + title: Text(L10n.of(context).security), + onTap: () => context.go('/rooms/settings/security'), + ), + Divider(color: theme.dividerColor), + ListTile( + leading: const Icon(Icons.help_outline_outlined), + title: Text(L10n.of(context).help), + onTap: () => launchUrlString(AppConfig.supportUrl), + ), + ListTile( + leading: const Icon(Icons.shield_sharp), + title: Text(L10n.of(context).privacy), + onTap: () => launchUrlString(AppConfig.privacyUrl), + ), + ListTile( + leading: const Icon(Icons.info_outline_rounded), + title: Text(L10n.of(context).about), + onTap: () => PlatformInfos.showDialog(context), + ), + Divider(color: theme.dividerColor), + ListTile( + leading: const Icon(Icons.logout_outlined), + title: Text(L10n.of(context).logout), + onTap: controller.logoutAction, + ), + ], + ); + }, ), ), );