// SPDX-FileCopyrightText: 2019-Present Christian Kußowski // SPDX-FileCopyrightText: 2019-Present Contributors to FluffyChat // // SPDX-License-Identifier: AGPL-3.0-or-later import 'package:tatchat/config/app_config.dart'; import 'package:tatchat/config/setting_keys.dart'; import 'package:tatchat/config/themes.dart'; import 'package:tatchat/l10n/l10n.dart'; import 'package:tatchat/utils/beautify_string_extension.dart'; import 'package:tatchat/utils/platform_infos.dart'; import 'package:tatchat/widgets/app_lock.dart'; import 'package:tatchat/widgets/layouts/max_width_body.dart'; import 'package:tatchat/widgets/matrix.dart'; import 'package:tatchat/widgets/settings_switch_list_tile.dart'; import 'package:go_router/go_router.dart'; import 'package:local_auth/local_auth.dart'; import 'package:material_ui/material_ui.dart'; import 'package:matrix/matrix.dart'; import 'settings_security.dart'; class SettingsSecurityView extends StatelessWidget { final SettingsSecurityController controller; const SettingsSecurityView(this.controller, {super.key}); @override Widget build(BuildContext context) { final theme = Theme.of(context); final client = Matrix.of(context).client; final publicMasterKey = client.userDeviceKeys[client.userID]?.masterKey?.publicKey; return Scaffold( appBar: AppBar( title: Text(L10n.of(context).security), automaticallyImplyLeading: !FluffyThemes.isColumnMode(context), centerTitle: FluffyThemes.isColumnMode(context), ), body: ListTileTheme( iconColor: theme.colorScheme.onSurface, child: MaxWidthBody( child: FutureBuilder( future: Matrix.of( context, ).client.getCapabilities().timeout(const Duration(seconds: 10)), builder: (context, snapshot) { final capabilities = snapshot.data; final error = snapshot.error; if (error == null && capabilities == null) { return const Center( child: Padding( padding: EdgeInsets.all(16.0), child: CircularProgressIndicator.adaptive(strokeWidth: 2), ), ); } return Column( children: [ ListTile( title: Text( L10n.of(context).privacy, style: TextStyle( color: theme.colorScheme.secondary, fontWeight: FontWeight.bold, ), ), ), SettingsSwitchListTile.adaptive( title: L10n.of(context).sendTypingNotifications, subtitle: L10n.of( context, ).sendTypingNotificationsDescription, setting: AppSettings.sendTypingNotifications, ), SettingsSwitchListTile.adaptive( title: L10n.of(context).sendReadReceipts, subtitle: L10n.of(context).sendReadReceiptsDescription, setting: AppSettings.sendPublicReadReceipts, ), SettingsSwitchListTile.adaptive( title: L10n.of(context).shareCrashReports, subtitle: L10n.of(context).shareCrashReportsDescription, setting: AppSettings.autoSendErrorReports, defaultValue: false, ), ListTile( trailing: const Icon(Icons.chevron_right_outlined), title: Text(L10n.of(context).blockedUsers), subtitle: Text( L10n.of(context).thereAreCountUsersBlocked( Matrix.of(context).client.ignoredUsers.length, ), ), onTap: () => context.go('/rooms/settings/security/ignorelist'), ), if (PlatformInfos.supportsAppLock) ...[ Divider(color: theme.dividerColor), ListTile( title: Text( L10n.of(context).appLock, style: TextStyle( color: theme.colorScheme.secondary, fontWeight: FontWeight.bold, ), ), subtitle: Text(L10n.of(context).appLockDescription), ), SwitchListTile.adaptive( value: AppLock.of(context).isActive, title: Text(L10n.of(context).useAppLock), onChanged: (active) => active ? controller.setAppLockAction() : controller.disableAppLockAction(), ), if (AppLock.of(context).isActive) ...[ FutureBuilder( future: LocalAuthentication().canCheckBiometrics, builder: (context, snapshot) { if (snapshot.data != true) return SizedBox.shrink(); return SwitchListTile.adaptive( value: AppLock.of(context).useBiometrics, onChanged: (_) => controller.toggleBiometrics(), title: Text(L10n.of(context).unlockWithBiometrics), ); }, ), ListTile( trailing: const Icon(Icons.lock_reset_outlined), title: Text(L10n.of(context).resetPin), onTap: controller.setAppLockAction, ), ], ], Divider(color: theme.dividerColor), ListTile( title: Text( L10n.of(context).shareKeysWith, style: TextStyle( color: theme.colorScheme.secondary, fontWeight: FontWeight.bold, ), ), subtitle: Text(L10n.of(context).shareKeysWithDescription), ), ListTile( title: Material( borderRadius: BorderRadius.circular( AppConfig.borderRadius / 2, ), color: theme.colorScheme.onInverseSurface, child: DropdownButton( isExpanded: true, padding: const EdgeInsets.symmetric(horizontal: 8.0), borderRadius: BorderRadius.circular( AppConfig.borderRadius / 2, ), underline: const SizedBox.shrink(), value: Matrix.of(context).client.shareKeysWith, items: ShareKeysWith.values .map( (share) => DropdownMenuItem( value: share, child: Text(share.localized(L10n.of(context))), ), ) .toList(), onChanged: controller.changeShareKeysWith, ), ), ), Divider(color: theme.dividerColor), ListTile( title: Text( L10n.of(context).account, style: TextStyle( color: theme.colorScheme.secondary, fontWeight: FontWeight.bold, ), ), ), if (publicMasterKey != null) ListTile( title: Text(L10n.of(context).yourPublicKey), leading: const Icon(Icons.verified_user_outlined), subtitle: SelectableText( publicMasterKey.beautified, style: const TextStyle(fontFamily: 'RobotoMono'), ), ), ListTile( title: Text(L10n.of(context).deviceIdentityKey), leading: const Icon(Icons.mobile_friendly_outlined), subtitle: SelectableText( Matrix.of(context).client.fingerprintKey.beautified, style: const TextStyle(fontFamily: 'RobotoMono'), ), ), if (capabilities?.mChangePassword?.enabled != false || error != null) ListTile( leading: const Icon(Icons.password_outlined), trailing: const Icon(Icons.chevron_right_outlined), title: Text(L10n.of(context).changePassword), onTap: () => context.go('/rooms/settings/security/password'), ), Divider(color: theme.dividerColor), ListTile( iconColor: Colors.orange, leading: const Icon(Icons.delete_sweep_outlined), title: Text( L10n.of(context).dehydrate, style: const TextStyle(color: Colors.orange), ), onTap: controller.dehydrateAction, ), ListTile( iconColor: Colors.red, leading: const Icon(Icons.delete_outlined), title: Text( L10n.of(context).deleteAccount, style: const TextStyle(color: Colors.red), ), onTap: controller.deleteAccountAction, ), ], ); }, ), ), ), ); } } extension on ShareKeysWith { String localized(L10n l10n) { switch (this) { case ShareKeysWith.all: return l10n.allDevices; case ShareKeysWith.crossVerifiedIfEnabled: return l10n.crossVerifiedDevicesIfEnabled; case ShareKeysWith.crossVerified: return l10n.crossVerifiedDevices; case ShareKeysWith.directlyVerifiedOnly: return l10n.verifiedDevicesOnly; } } }