fluffychat merge
This commit is contained in:
commit
3514623e51
15 changed files with 134 additions and 90 deletions
2
.github/workflows/versions.env
vendored
2
.github/workflows/versions.env
vendored
|
|
@ -1,2 +1,2 @@
|
|||
FLUTTER_VERSION=3.38.1
|
||||
FLUTTER_VERSION=3.38.3
|
||||
JAVA_VERSION=17
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ analyzer:
|
|||
errors:
|
||||
todo: ignore
|
||||
use_build_context_synchronously: ignore
|
||||
deprecated_member_use: ignore
|
||||
exclude:
|
||||
- lib/generated_plugin_registrant.dart
|
||||
- lib/l10n/*.dart
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ abstract class AppConfig {
|
|||
'https://fluffy.chat/faq/#how_to_use_end_to_end_encryption';
|
||||
static const String startChatTutorial =
|
||||
'https://fluffy.chat/faq/#how_do_i_find_other_users';
|
||||
static const String howDoIGetStickersTutorial =
|
||||
'https://fluffy.chat/faq/#how_do_i_get_stickers';
|
||||
static const String appId = 'im.fluffychat.FluffyChat';
|
||||
// #Pangea
|
||||
// static const String appOpenUrlScheme = 'im.fluffychat';
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import 'package:fluffychat/config/app_config.dart';
|
|||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/archive/archive.dart';
|
||||
import 'package:fluffychat/pages/bootstrap/bootstrap_dialog.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_controller.dart';
|
||||
import 'package:fluffychat/pages/chat_details/chat_details.dart';
|
||||
|
|
@ -246,6 +247,17 @@ abstract class AppRoutes {
|
|||
),
|
||||
),
|
||||
// Pangea#
|
||||
GoRoute(
|
||||
path: '/backup',
|
||||
redirect: loggedOutRedirect,
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
BootstrapDialog(
|
||||
wipe: state.uri.queryParameters['wipe'] == 'true',
|
||||
),
|
||||
),
|
||||
),
|
||||
ShellRoute(
|
||||
// Never use a transition on the shell route. Changing the PageBuilder
|
||||
// here based on a MediaQuery causes the child to briefly be rendered
|
||||
|
|
|
|||
|
|
@ -502,7 +502,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"chatBackupDescription": "Your old messages are secured with a recovery key. Please make sure you don't lose it.",
|
||||
"chatBackupDescription": "Your messages are secured with a recovery key. Please make sure you don't lose it.",
|
||||
"@chatBackupDescription": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -3461,6 +3461,10 @@
|
|||
"newStickerPack": "New sticker pack",
|
||||
"stickerPackName": "Sticker pack name",
|
||||
"attribution": "Attribution",
|
||||
"skipChatBackup": "Skip chat backup",
|
||||
"skipChatBackupWarning": "Are you sure? Without enabling the chat backup you may lose access to your messages if you switch your device.",
|
||||
"loadingMessages": "Loading messages",
|
||||
"setupChatBackup": "Set up chat backup",
|
||||
"ignore": "Block",
|
||||
"ignoredUsers": "Blocked users",
|
||||
"writeAMessageLangCodes": "Type in {l1} or {l2}...",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/encryption.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
|
|
@ -10,26 +11,21 @@ import 'package:fluffychat/utils/error_reporter.dart';
|
|||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/utils/sync_status_localization.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import '../../utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/widgets/layouts/login_scaffold.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../key_verification/key_verification_dialog.dart';
|
||||
|
||||
class BootstrapDialog extends StatefulWidget {
|
||||
final bool wipe;
|
||||
final Client client;
|
||||
|
||||
const BootstrapDialog({
|
||||
super.key,
|
||||
this.wipe = false,
|
||||
required this.client,
|
||||
});
|
||||
|
||||
Future<bool?> show(BuildContext context) => showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => this,
|
||||
);
|
||||
|
||||
@override
|
||||
BootstrapDialogState createState() => BootstrapDialogState();
|
||||
}
|
||||
|
|
@ -38,7 +34,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
final TextEditingController _recoveryKeyTextEditingController =
|
||||
TextEditingController();
|
||||
|
||||
late Bootstrap bootstrap;
|
||||
Bootstrap? bootstrap;
|
||||
|
||||
String? _recoveryKeyInputError;
|
||||
|
||||
|
|
@ -54,7 +50,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
bool? _wipe;
|
||||
|
||||
String get _secureStorageKey =>
|
||||
'ssss_recovery_key_${bootstrap.client.userID}';
|
||||
'ssss_recovery_key_${bootstrap!.client.userID}';
|
||||
|
||||
bool get _supportsSecureStorage =>
|
||||
PlatformInfos.isMobile || PlatformInfos.isDesktop;
|
||||
|
|
@ -69,18 +65,42 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
return L10n.of(context).storeSecurlyOnThisDevice;
|
||||
}
|
||||
|
||||
late final Client client;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_createBootstrap(widget.wipe);
|
||||
super.initState();
|
||||
client = Matrix.of(context).client;
|
||||
_createBootstrap(widget.wipe);
|
||||
}
|
||||
|
||||
void _cancelAction() async {
|
||||
final consent = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).skipChatBackup,
|
||||
message: L10n.of(context).skipChatBackupWarning,
|
||||
okLabel: L10n.of(context).skip,
|
||||
isDestructive: true,
|
||||
);
|
||||
if (consent != OkCancelResult.ok) return;
|
||||
if (!mounted) return;
|
||||
_goBackAction(false);
|
||||
}
|
||||
|
||||
void _goBackAction(bool success) =>
|
||||
context.canPop() ? context.pop(success) : context.go('/rooms');
|
||||
|
||||
void _createBootstrap(bool wipe) async {
|
||||
await client.roomsLoading;
|
||||
await client.accountDataLoading;
|
||||
await client.userDeviceKeysLoading;
|
||||
while (client.prevBatch == null) {
|
||||
await client.onSync.stream.first;
|
||||
}
|
||||
_wipe = wipe;
|
||||
titleText = null;
|
||||
_recoveryKeyStored = false;
|
||||
bootstrap =
|
||||
widget.client.encryption!.bootstrap(onUpdate: (_) => setState(() {}));
|
||||
bootstrap = client.encryption!.bootstrap(onUpdate: (_) => setState(() {}));
|
||||
final key = await const FlutterSecureStorage().read(key: _secureStorageKey);
|
||||
if (key == null) return;
|
||||
_recoveryKeyTextEditingController.text = key;
|
||||
|
|
@ -89,22 +109,45 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final bootstrap = this.bootstrap;
|
||||
if (bootstrap == null) {
|
||||
return LoginScaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
leading: CloseButton(onPressed: _cancelAction),
|
||||
title: Text(L10n.of(context).loadingMessages),
|
||||
),
|
||||
body: Center(
|
||||
child: StreamBuilder(
|
||||
stream: client.onSyncStatus.stream,
|
||||
builder: (context, snapshot) {
|
||||
final status = snapshot.data;
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator.adaptive(value: status?.progress),
|
||||
if (status != null) Text(status.calcLocalizedString(context)),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_wipe ??= widget.wipe;
|
||||
final buttons = <Widget>[];
|
||||
Widget body = const CircularProgressIndicator.adaptive();
|
||||
Widget body = const Center(child: CircularProgressIndicator.adaptive());
|
||||
titleText = L10n.of(context).loadingPleaseWait;
|
||||
|
||||
if (bootstrap.newSsssKey?.recoveryKey != null &&
|
||||
_recoveryKeyStored == false) {
|
||||
final key = bootstrap.newSsssKey!.recoveryKey;
|
||||
titleText = L10n.of(context).recoveryKey;
|
||||
return Scaffold(
|
||||
return LoginScaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
leading: CloseButton(onPressed: _cancelAction),
|
||||
title: Text(L10n.of(context).recoveryKey),
|
||||
),
|
||||
body: Center(
|
||||
|
|
@ -220,14 +263,11 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
break;
|
||||
case BootstrapState.openExistingSsss:
|
||||
_recoveryKeyStored = true;
|
||||
return Scaffold(
|
||||
return LoginScaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
title: Text(L10n.of(context).chatBackup),
|
||||
leading: CloseButton(onPressed: _cancelAction),
|
||||
title: Text(L10n.of(context).setupChatBackup),
|
||||
),
|
||||
body: Center(
|
||||
child: ConstrainedBox(
|
||||
|
|
@ -373,16 +413,14 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
context: context,
|
||||
delay: false,
|
||||
future: () async {
|
||||
await widget.client.updateUserDeviceKeys();
|
||||
return widget.client
|
||||
.userDeviceKeys[widget.client.userID!]!
|
||||
await client.updateUserDeviceKeys();
|
||||
return client.userDeviceKeys[client.userID!]!
|
||||
.startVerification();
|
||||
},
|
||||
);
|
||||
if (req.error != null) return;
|
||||
await KeyVerificationDialog(request: req.result!)
|
||||
.show(context);
|
||||
Navigator.of(context, rootNavigator: false).pop();
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
|
@ -446,8 +484,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
body = const Icon(Icons.error_outline, color: Colors.red, size: 80);
|
||||
buttons.add(
|
||||
ElevatedButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: false).pop<bool>(false),
|
||||
onPressed: () => _goBackAction(false),
|
||||
child: Text(L10n.of(context).close),
|
||||
),
|
||||
);
|
||||
|
|
@ -472,8 +509,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
);
|
||||
buttons.add(
|
||||
ElevatedButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: false).pop<bool>(false),
|
||||
onPressed: () => _goBackAction(false),
|
||||
child: Text(L10n.of(context).close),
|
||||
),
|
||||
);
|
||||
|
|
@ -481,14 +517,9 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
return LoginScaffold(
|
||||
appBar: AppBar(
|
||||
leading: Center(
|
||||
child: CloseButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: false).pop<bool>(true),
|
||||
),
|
||||
),
|
||||
leading: CloseButton(onPressed: _cancelAction),
|
||||
title: Text(titleText ?? L10n.of(context).loadingPleaseWait),
|
||||
),
|
||||
body: Center(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
|
|
@ -108,26 +109,33 @@ class ChatEventList extends StatelessWidget {
|
|||
// if (i == events.length + 1) {
|
||||
if (i == events.length + 2) {
|
||||
// Pangea#
|
||||
if (timeline.isRequestingHistory) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
);
|
||||
}
|
||||
if (timeline.canRequestHistory &&
|
||||
controller.activeThreadId == null) {
|
||||
if (controller.activeThreadId == null) {
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
// #Pangea
|
||||
// WidgetsBinding.instance
|
||||
// .addPostFrameCallback(controller.requestHistory);
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => controller.requestHistory(),
|
||||
final visibleIndex = timeline.events.lastIndexWhere(
|
||||
(event) =>
|
||||
!event.isCollapsedState && event.isVisibleInGui,
|
||||
);
|
||||
// Pangea#
|
||||
if (visibleIndex > timeline.events.length - 50) {
|
||||
// #Pangea
|
||||
// WidgetsBinding.instance
|
||||
// .addPostFrameCallback(controller.requestHistory);
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => controller.requestHistory(),
|
||||
);
|
||||
// Pangea#
|
||||
}
|
||||
return Center(
|
||||
child: IconButton(
|
||||
onPressed: controller.requestHistory,
|
||||
icon: const Icon(Icons.refresh_outlined),
|
||||
child: AnimatedSwitcher(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
child: timeline.canRequestHistory
|
||||
? IconButton(
|
||||
onPressed: controller.requestHistory,
|
||||
icon: const Icon(Icons.refresh_outlined),
|
||||
)
|
||||
: const CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -82,9 +82,7 @@ class MessageContent extends StatelessWidget {
|
|||
// }
|
||||
// final client = Matrix.of(context).client;
|
||||
// if (client.isUnknownSession && client.encryption!.crossSigning.enabled) {
|
||||
// final success = await BootstrapDialog(
|
||||
// client: Matrix.of(context).client,
|
||||
// ).show(context);
|
||||
// final success = await context.push('/backup');
|
||||
// if (success != true) return;
|
||||
// }
|
||||
// event.requestKey();
|
||||
|
|
@ -159,7 +157,8 @@ class MessageContent extends StatelessWidget {
|
|||
case MessageTypes.Image:
|
||||
case MessageTypes.Sticker:
|
||||
if (event.redacted) continue textmessage;
|
||||
const maxSize = 256.0;
|
||||
final maxSize =
|
||||
event.messageType == MessageTypes.Sticker ? 128.0 : 256.0;
|
||||
final w = event.content
|
||||
.tryGetMap<String, Object?>('info')
|
||||
?.tryGet<int>('w');
|
||||
|
|
|
|||
|
|
@ -66,7 +66,9 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
|
|||
GridView.builder(
|
||||
itemCount: imageKeys.length,
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 128,
|
||||
maxCrossAxisExtent: 84,
|
||||
mainAxisSpacing: 8.0,
|
||||
crossAxisSpacing: 8.0,
|
||||
),
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
|
|
@ -142,7 +144,7 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
|
|||
// OutlinedButton.icon(
|
||||
// onPressed: () => UrlLauncher(
|
||||
// context,
|
||||
// 'https://matrix.to/#/#fluffychat-stickers:janian.de',
|
||||
// AppConfig.howDoIGetStickersTutorial,
|
||||
// ).launchUrl(),
|
||||
// icon: const Icon(Icons.explore_outlined),
|
||||
// label: Text(L10n.of(context).discover),
|
||||
|
|
|
|||
|
|
@ -1039,17 +1039,6 @@ class ChatListController extends State<ChatList>
|
|||
setState(() {
|
||||
waitForFirstSync = true;
|
||||
});
|
||||
|
||||
// Display first login bootstrap if enabled
|
||||
// #Pangea
|
||||
// if (client.encryption?.keyManager.enabled == true) {
|
||||
// if (await client.encryption?.keyManager.isCached() == false ||
|
||||
// await client.encryption?.crossSigning.isCached() == false ||
|
||||
// client.isUnknownSession && !mounted) {
|
||||
// await BootstrapDialog(client: client).show(context);
|
||||
// }
|
||||
// }
|
||||
// Pangea#
|
||||
}
|
||||
|
||||
// #Pangea
|
||||
|
|
|
|||
|
|
@ -204,9 +204,7 @@ class SettingsController extends State<Settings> {
|
|||
// );
|
||||
// return;
|
||||
// }
|
||||
// await BootstrapDialog(
|
||||
// client: Matrix.of(context).client,
|
||||
// ).show(context);
|
||||
// await context.push('/backup');
|
||||
// checkBootstrap();
|
||||
// Pangea#
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'
|
|||
import 'package:fluffychat/widgets/app_lock.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../bootstrap/bootstrap_dialog.dart';
|
||||
import 'settings_security_view.dart';
|
||||
|
||||
class SettingsSecurity extends StatefulWidget {
|
||||
|
|
@ -145,12 +144,6 @@ class SettingsSecurityController extends State<SettingsSecurity> {
|
|||
}
|
||||
}
|
||||
|
||||
void showBootstrapDialog(BuildContext context) async {
|
||||
await BootstrapDialog(
|
||||
client: Matrix.of(context).client,
|
||||
).show(context);
|
||||
}
|
||||
|
||||
Future<void> dehydrateAction() => Matrix.of(context).dehydrateAction(context);
|
||||
|
||||
void changeShareKeysWith(ShareKeysWith? shareKeysWith) async {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,16 @@ extension FileDescriptionExtension on Event {
|
|||
return null;
|
||||
}
|
||||
final formattedBody = content.tryGet<String>('formatted_body');
|
||||
if (formattedBody != null) return formattedBody;
|
||||
if (formattedBody != null && formattedBody.isNotEmpty) return formattedBody;
|
||||
|
||||
final filename = content.tryGet<String>('filename');
|
||||
final body = content.tryGet<String>('body');
|
||||
if (filename != body && body != null && filename != null) return body;
|
||||
if (filename != body &&
|
||||
body != null &&
|
||||
filename != null &&
|
||||
body.isNotEmpty) {
|
||||
return body;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
_registerSubs(_loginClientCandidate!.clientName);
|
||||
_loginClientCandidate = null;
|
||||
// #Pangea
|
||||
// FluffyChatApp.router.go('/rooms');
|
||||
// FluffyChatApp.router.go('/backup');
|
||||
final isL2Set = await pangeaController.userController.isUserL2Set;
|
||||
FluffyChatApp.router.go(
|
||||
isL2Set ? '/rooms' : '/registration/create',
|
||||
|
|
@ -443,7 +443,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
FluffyChatApp.router.go('/home');
|
||||
}
|
||||
// FluffyChatApp.router
|
||||
// .go(state == LoginState.loggedIn ? '/rooms' : '/home');
|
||||
// .go(state == LoginState.loggedIn ? '/backup' : '/home');
|
||||
// Pangea#
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ SPEC CHECKSUMS:
|
|||
dynamic_color: cb7c2a300ee67ed3bd96c3e852df3af0300bf610
|
||||
emoji_picker_flutter: 51ca408e289d84d1e460016b2a28721ec754fcf7
|
||||
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
||||
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
|
||||
file_selector_macos: 9e9e068e90ebee155097d00e89ae91edb2374db7
|
||||
flutter_local_notifications: 4bf37a31afde695b56091b4ae3e4d9c7a7e6cda0
|
||||
flutter_new_badger: 6fe9bf7e42793a164032c21f164c0ad9985cd0f2
|
||||
flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue