Skip to content

Commit

Permalink
feat: improve bootstrap and keyverification (#14)
Browse files Browse the repository at this point in the history
* fix: bt rework

* fix: bootstrap and keyverification

* remove bootstrap null

* osss
  • Loading branch information
Feichtmeier authored Jan 11, 2025
1 parent 6325a09 commit abd7f56
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 116 deletions.
2 changes: 1 addition & 1 deletion lib/chat/authentication/chat_login_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class _ChatLoginPageState extends State<ChatLoginPage> {
homeServer: _homeServerController.text.trim(),
username: _usernameController.text,
password: _passwordController.text,
onSuccess: () async => Navigator.of(context).pushAndRemoveUntil(
onSuccess: () => Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (_) => const ChatMasterDetailPage(),
),
Expand Down
17 changes: 6 additions & 11 deletions lib/chat/bootstrap/bootstrap_model.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:matrix/encryption.dart';
import 'package:matrix/matrix.dart';
Expand All @@ -18,11 +17,11 @@ class BootstrapModel extends SafeChangeNotifier {
final Client _client;
final FlutterSecureStorage _secureStorage;

Future<bool> isBootrapNeeded() async =>
_client.isUnknownSession && _client.encryption!.crossSigning.enabled;
Future<bool> checkBootstrap() async {
if (!_client.encryptionEnabled) {
return true;
}

Future<bool> isBootrapNeededFull() async {
if (!_client.encryptionEnabled) return true;
await _client.accountDataLoading;
await _client.userDeviceKeysLoading;
if (_client.prevBatch == null) {
Expand Down Expand Up @@ -112,12 +111,10 @@ class BootstrapModel extends SafeChangeNotifier {
_bootstrap =
_client.encryption?.bootstrap(onUpdate: (v) => _setBootsTrap(v));
final theKey = await _loadKeyFromSecureStorage();
if (key == null) {
notifyListeners();
return;
if (key != null) {
_key = theKey;
}

_key = theKey;
notifyListeners();
}

Expand All @@ -131,8 +128,6 @@ class BootstrapModel extends SafeChangeNotifier {
}
}

bool get supportsSecureStorage => !kIsWeb;

String getSecureStorageLocalizedName(AppLocalizations l10n) {
if (Platform.isAndroid) {
return l10n.storeInAndroidKeystore;
Expand Down
61 changes: 19 additions & 42 deletions lib/chat/bootstrap/view/bootstrap_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ import '../bootstrap_model.dart';
import 'key_verification_dialog.dart';

class BootstrapPage extends StatelessWidget with WatchItMixin {
const BootstrapPage({
super.key,
});
const BootstrapPage({super.key});

@override
Widget build(BuildContext context) {
Expand All @@ -46,7 +44,9 @@ class BootstrapPage extends StatelessWidget with WatchItMixin {

if (key != null && recoveryKeyStored == false) {
return Scaffold(
appBar: AppBar(
appBar: YaruWindowTitleBar(
border: BorderSide.none,
backgroundColor: Colors.transparent,
centerTitle: true,
leading: IconButton(
icon: const Icon(YaruIcons.window_close),
Expand Down Expand Up @@ -87,15 +87,14 @@ class BootstrapPage extends StatelessWidget with WatchItMixin {
),
),
const SizedBox(height: 16),
if (model.supportsSecureStorage)
YaruCheckboxListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
value: storeInSecureStorage,
onChanged: (v) => model.setStoreInSecureStorage(v ?? false),
title:
Text(model.getSecureStorageLocalizedName(context.l10n)),
subtitle: Text(l10n.storeInSecureStorageDescription),
),
YaruCheckboxListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
value: storeInSecureStorage,
onChanged: (v) => model.setStoreInSecureStorage(v ?? false),
title:
Text(model.getSecureStorageLocalizedName(context.l10n)),
subtitle: Text(l10n.storeInSecureStorageDescription),
),
const SizedBox(height: 16),
YaruCheckboxListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
Expand Down Expand Up @@ -213,8 +212,10 @@ class BootstrapPage extends StatelessWidget with WatchItMixin {
}

return Scaffold(
appBar: YaruDialogTitleBar(
appBar: YaruWindowTitleBar(
title: Text(titleText),
border: BorderSide.none,
backgroundColor: Colors.transparent,
),
body: Center(
child: Column(
Expand All @@ -230,34 +231,6 @@ class BootstrapPage extends StatelessWidget with WatchItMixin {
}
}

Future<T?> showAdaptiveBottomSheet<T>({
required BuildContext context,
required Widget Function(BuildContext) builder,
bool isDismissible = true,
bool isScrollControlled = true,
double maxHeight = 512,
bool useRootNavigator = true,
}) =>
showModalBottomSheet(
context: context,
builder: builder,
// this sadly is ugly on desktops but otherwise breaks `.of(context)` calls
useRootNavigator: useRootNavigator,
isDismissible: isDismissible,
isScrollControlled: isScrollControlled,
constraints: BoxConstraints(
maxHeight: maxHeight,
maxWidth: 400,
),
clipBehavior: Clip.hardEdge,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
),
);

class OpenExistingSSSSPage extends StatefulWidget
with WatchItStatefulWidgetMixin {
const OpenExistingSSSSPage({super.key});
Expand Down Expand Up @@ -289,6 +262,10 @@ class _OpenExistingSSSSPageState extends State<OpenExistingSSSSPage> {
watchPropertyValue((BootstrapModel m) => m.recoveryKeyInputError);

return Scaffold(
appBar: const YaruWindowTitleBar(
border: BorderSide.none,
backgroundColor: Colors.transparent,
),
body: Center(
child: SizedBox(
width: 400,
Expand Down
30 changes: 17 additions & 13 deletions lib/chat/bootstrap/view/key_verification_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import 'package:flutter/services.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/encryption.dart';
import 'package:matrix/matrix.dart';
import 'package:yaru/yaru.dart';

import '../../../common/view/build_context_x.dart';
import '../../../l10n/l10n.dart';
import '../../view/chat_avatar.dart';
import '../../view/chat_master/chat_master_detail_page.dart';

// TODO: code by fluffy-chat, replace
// Credit: this code has been initially copied from https://github.com/krille-chan/fluffychat
// Thank you @krille-chan
class KeyVerificationDialog extends StatefulWidget {
Future<void> show(BuildContext context) => showAdaptiveDialog(
context: context,
Expand All @@ -21,14 +24,10 @@ class KeyVerificationDialog extends StatefulWidget {
);

final KeyVerification request;
final Function()? onCancel;
final Function()? onDone;

const KeyVerificationDialog({
super.key,
required this.request,
this.onCancel,
this.onDone,
});

@override
Expand Down Expand Up @@ -188,7 +187,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
);
buttons.add(
TextButton.icon(
icon: const Icon(Icons.close),
icon: const Icon(YaruIcons.window_close),
style: TextButton.styleFrom(foregroundColor: Colors.red),
label: Text(l10n.reject),
onPressed: () => widget.request.rejectVerification().then((_) {
Expand All @@ -200,7 +199,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
);
buttons.add(
TextButton.icon(
icon: const Icon(Icons.check),
icon: const Icon(YaruIcons.checkmark),
label: Text(l10n.accept),
onPressed: () => widget.request.acceptVerification(),
),
Expand Down Expand Up @@ -233,7 +232,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
);
buttons.add(
TextButton.icon(
icon: const Icon(Icons.close),
icon: const Icon(YaruIcons.window_close),
label: Text(l10n.cancel),
onPressed: () => widget.request.cancel(),
),
Expand Down Expand Up @@ -273,7 +272,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
);
buttons.add(
TextButton.icon(
icon: const Icon(Icons.close),
icon: const Icon(YaruIcons.window_close),
style: TextButton.styleFrom(
foregroundColor: Colors.red,
),
Expand All @@ -283,7 +282,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
);
buttons.add(
TextButton.icon(
icon: const Icon(Icons.check_outlined),
icon: const Icon(YaruIcons.checkmark),
label: Text(l10n.theyMatch),
onPressed: () => widget.request.acceptSas(),
),
Expand All @@ -308,7 +307,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Icon(
Icons.check_circle_outlined,
YaruIcons.ok,
color: Colors.green,
size: 128.0,
),
Expand All @@ -325,12 +324,17 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
l10n.close,
),
onPressed: () {
widget.onDone?.call();
if (context.mounted) {
Navigator.of(
context,
rootNavigator: false,
).pop();
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (_) => const ChatMasterDetailPage(),
),
(route) => false,
);
}
},
),
Expand All @@ -340,7 +344,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
body = Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Icon(Icons.cancel, color: Colors.red, size: 128.0),
const Icon(YaruIcons.edit_clear, color: Colors.red, size: 128.0),
const SizedBox(height: 16),
Text(
'Error ${widget.request.canceledCode}: ${widget.request.canceledReason}',
Expand Down
5 changes: 2 additions & 3 deletions lib/chat/chat_download_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:matrix/matrix.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../app_config.dart';
import 'package:yaru/yaru.dart';

class ChatDownloadService {
ChatDownloadService({
Expand Down Expand Up @@ -34,7 +33,7 @@ class ChatDownloadService {
}
MatrixFile? file;
String? path;
if (isMobilePlatform) {
if (isMobile) {
file = await event.downloadAndDecryptAttachment();
path = await FilePicker.platform.saveFile(
fileName: file.name,
Expand Down
18 changes: 11 additions & 7 deletions lib/chat/draft_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import 'package:matrix/matrix.dart';
import 'package:mime/mime.dart';
import 'package:safe_change_notifier/safe_change_notifier.dart';
import 'package:video_compress/video_compress.dart';
import 'package:yaru/yaru.dart';

import '../app_config.dart';
import '../common/logging.dart';
import 'local_image_service.dart';

Expand Down Expand Up @@ -79,11 +79,12 @@ class DraftModel extends SafeChangeNotifier {
}
}

final draft = '${getDraft(room.id)}';
removeDraft(room.id);
if (draft.isNotEmpty) {
if (getDraft(room.id)?.isNotEmpty == true) {
final draft = '${getDraft(room.id)}';
removeDraft(room.id);
String? eventId;
try {
await room.sendTextEvent(
eventId = await room.sendTextEvent(
draft.trim(),
inReplyTo: replyEvent,
editEventId: _editEvents[room.id]?.eventId,
Expand All @@ -92,6 +93,9 @@ class DraftModel extends SafeChangeNotifier {
onFail(e.toString());
printMessageInDebugMode(e, s);
}
if (eventId == null) {
setDraft(roomId: room.id, draft: draft, notify: true);
}
}

_sending = false;
Expand Down Expand Up @@ -237,7 +241,7 @@ class DraftModel extends SafeChangeNotifier {
Future<MatrixVideoFile> resizeVideo(XFile xFile) async {
MediaInfo? mediaInfo;
try {
if (isMobilePlatform) {
if (isMobile) {
// will throw an error e.g. on Android SDK < 18
mediaInfo = await VideoCompress.compressVideo(xFile.path);
}
Expand All @@ -258,7 +262,7 @@ class DraftModel extends SafeChangeNotifier {
// final Map<MatrixVideoFile, XFile> _fileMap = {};

Future<MatrixImageFile?> getVideoThumbnail(XFile xFile) async {
if (!isMobilePlatform) return null;
if (!isMobile) return null;

try {
final bytes = await VideoCompress.getByteThumbnail(xFile.path);
Expand Down
33 changes: 17 additions & 16 deletions lib/chat/view/chat_master/chat_master_detail_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,25 @@ class _ChatMasterDetailPageState extends State<ChatMasterDetailPage> {
@override
void initState() {
super.initState();
di<BootstrapModel>().isBootrapNeeded().then(
(isNeeded) {
WidgetsBinding.instance.addPostFrameCallback((_) {
final bootstrapModel = di<BootstrapModel>();
bootstrapModel.checkBootstrap().then((isNeeded) {
if (isNeeded) {
di<BootstrapModel>().startBootstrap(wipe: false).then((value) {
if (mounted) {
showDialog(
context: context,
builder: (context) => const SizedBox(
height: 500,
width: 400,
child: BootstrapPage(),
),
);
}
});
bootstrapModel.startBootstrap(wipe: false).then(
(_) {
if (mounted) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (_) => const BootstrapPage(),
),
(route) => false,
);
}
},
);
}
},
);
});
});
}

@override
Expand Down
3 changes: 1 addition & 2 deletions lib/chat/view/events/chat_html_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import '../../../common/view/confirm.dart';
import '../../../l10n/l10n.dart';
import '../mxc_image.dart';

// Credit: this code has been copied and from https://github.com/krille-chan/fluffychat
// and then modified
// Credit: this code has been initially copied from https://github.com/krille-chan/fluffychat
// Thank you @krille-chan
class HtmlMessage extends StatelessWidget {
final String html;
Expand Down
Loading

0 comments on commit abd7f56

Please sign in to comment.