refactor: Update to Dart 3.10 with . shorthands

This commit is contained in:
Christian Kußowski 2025-11-30 12:54:06 +01:00
commit 1ea649f01e
No known key found for this signature in database
GPG key ID: E067ECD60F1A0652
167 changed files with 3351 additions and 3912 deletions

View file

@ -7,9 +7,9 @@ class AccountBundles {
AccountBundles({this.prefix, this.bundles});
AccountBundles.fromJson(Map<String, dynamic> json)
: prefix = json.tryGet<String>('prefix'),
bundles = json['bundles'] is List
? json['bundles']
: prefix = json.tryGet<String>('prefix'),
bundles = json['bundles'] is List
? json['bundles']
.map((b) {
try {
return AccountBundle.fromJson(b);
@ -19,13 +19,12 @@ class AccountBundles {
})
.whereType<AccountBundle>()
.toList()
: null;
: null;
Map<String, dynamic> toJson() => {
if (prefix != null) 'prefix': prefix,
if (bundles != null)
'bundles': bundles!.map((v) => v.toJson()).toList(),
};
if (prefix != null) 'prefix': prefix,
if (bundles != null) 'bundles': bundles!.map((v) => v.toJson()).toList(),
};
}
class AccountBundle {
@ -35,13 +34,13 @@ class AccountBundle {
AccountBundle({this.name, this.priority});
AccountBundle.fromJson(Map<String, dynamic> json)
: name = json.tryGet<String>('name'),
priority = json.tryGet<int>('priority');
: name = json.tryGet<String>('name'),
priority = json.tryGet<int>('priority');
Map<String, dynamic> toJson() => <String, dynamic>{
if (name != null) 'name': name,
if (priority != null) 'priority': priority,
};
if (name != null) 'name': name,
if (priority != null) 'priority': priority,
};
}
const accountBundlesType = 'im.fluffychat.account_bundles';
@ -50,24 +49,21 @@ extension AccountBundlesExtension on Client {
List<AccountBundle> get accountBundles {
List<AccountBundle>? ret;
if (accountData.containsKey(accountBundlesType)) {
ret = AccountBundles.fromJson(accountData[accountBundlesType]!.content)
.bundles;
ret = AccountBundles.fromJson(
accountData[accountBundlesType]!.content,
).bundles;
}
ret ??= [];
if (ret.isEmpty) {
ret.add(
AccountBundle(
name: userID,
priority: 0,
),
);
ret.add(AccountBundle(name: userID, priority: 0));
}
return ret;
}
Future<void> setAccountBundle(String name, [int? priority]) async {
final data =
AccountBundles.fromJson(accountData[accountBundlesType]?.content ?? {});
final data = AccountBundles.fromJson(
accountData[accountBundlesType]?.content ?? {},
);
var foundBundle = false;
final bundles = data.bundles ??= [];
for (final bundle in bundles) {
@ -87,16 +83,18 @@ extension AccountBundlesExtension on Client {
if (!accountData.containsKey(accountBundlesType)) {
return; // nothing to do
}
final data =
AccountBundles.fromJson(accountData[accountBundlesType]!.content);
final data = AccountBundles.fromJson(
accountData[accountBundlesType]!.content,
);
if (data.bundles == null) return;
data.bundles!.removeWhere((b) => b.name == name);
await setAccountData(userID!, accountBundlesType, data.toJson());
}
String get sendPrefix {
final data =
AccountBundles.fromJson(accountData[accountBundlesType]?.content ?? {});
final data = AccountBundles.fromJson(
accountData[accountBundlesType]?.content ?? {},
);
return data.prefix!;
}
}

View file

@ -8,19 +8,11 @@ extension ApplicationAccountConfigExtension on Client {
accountData[accountDataKey]?.content ?? {},
);
Future<void> setApplicationAccountConfig(
ApplicationAccountConfig config,
) =>
setAccountData(
userID!,
accountDataKey,
config.toJson(),
);
Future<void> setApplicationAccountConfig(ApplicationAccountConfig config) =>
setAccountData(userID!, accountDataKey, config.toJson());
/// Only updates the specified values in ApplicationAccountConfig
Future<void> updateApplicationAccountConfig(
ApplicationAccountConfig config,
) {
Future<void> updateApplicationAccountConfig(ApplicationAccountConfig config) {
final currentConfig = applicationAccountConfig;
return setAccountData(
userID!,
@ -57,14 +49,15 @@ class ApplicationAccountConfig {
wallpaperUrl: json['wallpaper_url'] is String
? Uri.tryParse(json['wallpaper_url'])
: null,
wallpaperOpacity:
_sanitizedOpacity(json.tryGet<double>('wallpaper_opacity')),
wallpaperOpacity: _sanitizedOpacity(
json.tryGet<double>('wallpaper_opacity'),
),
wallpaperBlur: json.tryGet<double>('wallpaper_blur'),
);
Map<String, dynamic> toJson() => {
'wallpaper_url': wallpaperUrl?.toString(),
'wallpaper_opacity': wallpaperOpacity,
'wallpaper_blur': wallpaperBlur,
};
'wallpaper_url': wallpaperUrl?.toString(),
'wallpaper_opacity': wallpaperOpacity,
'wallpaper_blur': wallpaperBlur,
};
}

View file

@ -21,10 +21,7 @@ Future<T?> showAdaptiveBottomSheet<T>({
builder: (context) => Center(
child: Container(
margin: const EdgeInsets.all(16),
constraints: const BoxConstraints(
maxWidth: 480,
maxHeight: 720,
),
constraints: const BoxConstraints(maxWidth: 480, maxHeight: 720),
child: Material(
elevation: Theme.of(context).dialogTheme.elevation ?? 4,
shadowColor: Theme.of(context).dialogTheme.shadowColor,
@ -42,11 +39,9 @@ Future<T?> showAdaptiveBottomSheet<T>({
context: context,
builder: (context) => ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.viewInsetsOf(context).bottom +
min(
MediaQuery.sizeOf(context).height - 32,
600,
),
maxHeight:
MediaQuery.viewInsetsOf(context).bottom +
min(MediaQuery.sizeOf(context).height - 32, 600),
),
child: builder(context),
),

View file

@ -62,7 +62,8 @@ class BackgroundPush {
Future<void> loadLocale() async {
final context = matrix?.context;
// inspired by _lookupL10n in .dart_tool/flutter_gen/gen_l10n/l10n.dart
l10n ??= (context != null ? L10n.of(context) : null) ??
l10n ??=
(context != null ? L10n.of(context) : null) ??
(await L10n.delegate.load(PlatformDispatcher.instance.locale));
}
@ -78,8 +79,26 @@ class BackgroundPush {
void _init() async {
//<GOOGLE_SERVICES>firebaseEnabled = true;
try {
mainIsolateReceivePort?.listen(
(message) async {
mainIsolateReceivePort?.listen((message) async {
try {
await notificationTap(
NotificationResponseJson.fromJsonString(message),
client: client,
router: FluffyChatApp.router,
l10n: l10n,
);
} catch (e, s) {
Logs().wtf('Main Notification Tap crashed', e, s);
}
});
if (PlatformInfos.isAndroid) {
final port = ReceivePort();
IsolateNameServer.removePortNameMapping('background_tab_port');
IsolateNameServer.registerPortWithName(
port.sendPort,
'background_tab_port',
);
port.listen((message) async {
try {
await notificationTap(
NotificationResponseJson.fromJsonString(message),
@ -90,29 +109,7 @@ class BackgroundPush {
} catch (e, s) {
Logs().wtf('Main Notification Tap crashed', e, s);
}
},
);
if (PlatformInfos.isAndroid) {
final port = ReceivePort();
IsolateNameServer.removePortNameMapping('background_tab_port');
IsolateNameServer.registerPortWithName(
port.sendPort,
'background_tab_port',
);
port.listen(
(message) async {
try {
await notificationTap(
NotificationResponseJson.fromJsonString(message),
client: client,
router: FluffyChatApp.router,
l10n: l10n,
);
} catch (e, s) {
Logs().wtf('Main Notification Tap crashed', e, s);
}
},
);
});
}
await _flutterLocalNotificationsPlugin.initialize(
const InitializationSettings(
@ -201,12 +198,14 @@ class BackgroundPush {
if (PlatformInfos.isAndroid) {
_flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
AndroidFlutterLocalNotificationsPlugin
>()
?.requestNotificationsPermission();
}
final clientName = PlatformInfos.clientName;
oldTokens ??= <String>{};
final pushers = await (client.getPushers().catchError((e) {
final pushers =
await (client.getPushers().catchError((e) {
Logs().w('[Push] Unable to request pushers', e);
return <Pusher>[];
})) ??
@ -235,10 +234,9 @@ class BackgroundPush {
currentPushers.first.data.url.toString() == gatewayUrl &&
currentPushers.first.data.format ==
AppSettings.pushNotificationsPusherFormat.value &&
mapEquals(
currentPushers.single.data.additionalProperties,
{"data_message": pusherDataMessageFormat},
)) {
mapEquals(currentPushers.single.data.additionalProperties, {
"data_message": pusherDataMessageFormat,
})) {
Logs().i('[Push] Pusher already set');
} else {
Logs().i('Need to set new pusher');
@ -290,8 +288,8 @@ class BackgroundPush {
final pusherDataMessageFormat = Platform.isAndroid
? 'android'
: Platform.isIOS
? 'ios'
: null;
? 'ios'
: null;
static bool _wentToRoomOnStartup = false;
@ -315,9 +313,9 @@ class BackgroundPush {
}
// ignore: unawaited_futures
_flutterLocalNotificationsPlugin
.getNotificationAppLaunchDetails()
.then((details) {
_flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails().then((
details,
) {
if (details == null ||
!details.didNotificationLaunchApp ||
_wentToRoomOnStartup) {
@ -348,9 +346,7 @@ class BackgroundPush {
if (PlatformInfos.isAndroid) {
onFcmError?.call(
l10n!.noGoogleServicesWarning,
link: Uri.parse(
AppConfig.enablePushTutorial,
),
link: Uri.parse(AppConfig.enablePushTutorial),
);
return;
}
@ -397,15 +393,13 @@ class BackgroundPush {
'https://matrix.gateway.unifiedpush.org/_matrix/push/v1/notify';
try {
final url = Uri.parse(newEndpoint)
.replace(
path: '/_matrix/push/v1/notify',
query: '',
)
.replace(path: '/_matrix/push/v1/notify', query: '')
.toString()
.split('?')
.first;
final res =
json.decode(utf8.decode((await http.get(Uri.parse(url))).bodyBytes));
final res = json.decode(
utf8.decode((await http.get(Uri.parse(url))).bodyBytes),
);
if (res['gateway'] == 'matrix' ||
(res['unifiedpush'] is Map &&
res['unifiedpush']['gateway'] == 'matrix')) {
@ -436,14 +430,13 @@ class BackgroundPush {
upAction = true;
Logs().i('[Push] Removing UnifiedPush endpoint...');
final oldEndpoint = AppSettings.unifiedPushEndpoint.value;
await AppSettings.unifiedPushEndpoint
.setItem(AppSettings.unifiedPushEndpoint.defaultValue);
await AppSettings.unifiedPushEndpoint.setItem(
AppSettings.unifiedPushEndpoint.defaultValue,
);
await AppSettings.unifiedPushRegistered.setItem(false);
if (oldEndpoint.isNotEmpty) {
// remove the old pusher
await setupPusher(
oldTokens: {oldEndpoint},
);
await setupPusher(oldTokens: {oldEndpoint});
}
}

View file

@ -41,8 +41,9 @@ extension ClientDownloadContentExtension on Client {
final response = await httpClient.get(
httpUri,
headers:
accessToken == null ? null : {'authorization': 'Bearer $accessToken'},
headers: accessToken == null
? null
: {'authorization': 'Bearer $accessToken'},
);
if (response.statusCode != 200) {
throw Exception();

View file

@ -38,22 +38,27 @@ abstract class ClientManager {
clientNames.add(PlatformInfos.clientName);
await store.setStringList(clientNamespace, clientNames.toList());
}
final clients =
await Future.wait(clientNames.map((name) => createClient(name, store)));
final clients = await Future.wait(
clientNames.map((name) => createClient(name, store)),
);
if (initialize) {
await Future.wait(
clients.map(
(client) => client.initWithRestore(
onMigration: () async {
final l10n = await lookupL10n(PlatformDispatcher.instance.locale);
sendInitNotification(
l10n.databaseMigrationTitle,
l10n.databaseMigrationBody,
);
},
).catchError(
(e, s) => Logs().e('Unable to initialize client', e, s),
),
(client) => client
.initWithRestore(
onMigration: () async {
final l10n = await lookupL10n(
PlatformDispatcher.instance.locale,
);
sendInitNotification(
l10n.databaseMigrationTitle,
l10n.databaseMigrationBody,
);
},
)
.catchError(
(e, s) => Logs().e('Unable to initialize client', e, s),
),
),
);
}
@ -122,25 +127,26 @@ abstract class ClientManager {
AuthenticationTypes.sso,
},
nativeImplementations: nativeImplementations,
customImageResizer:
PlatformInfos.isMobile || kIsWeb ? customImageResizer : null,
customImageResizer: PlatformInfos.isMobile || kIsWeb
? customImageResizer
: null,
defaultNetworkRequestTimeout: const Duration(minutes: 30),
enableDehydratedDevices: true,
shareKeysWith: ShareKeysWith.values
.singleWhereOrNull((share) => share.name == shareKeysWith) ??
shareKeysWith:
ShareKeysWith.values.singleWhereOrNull(
(share) => share.name == shareKeysWith,
) ??
ShareKeysWith.all,
convertLinebreaksInFormatting: false,
onSoftLogout:
enableSoftLogout ? (client) => client.refreshAccessToken() : null,
onSoftLogout: enableSoftLogout
? (client) => client.refreshAccessToken()
: null,
);
}
static void sendInitNotification(String title, String body) async {
if (kIsWeb) {
html.Notification(
title,
body: body,
);
html.Notification(title, body: body);
return;
}
if (Platform.isLinux) {
@ -148,9 +154,7 @@ abstract class ClientManager {
title,
body: body,
appName: AppSettings.applicationName.value,
hints: [
NotificationHint.soundName('message-new-instant'),
],
hints: [NotificationHint.soundName('message-new-instant')],
);
return;
}

View file

@ -33,8 +33,8 @@ class CustomHttpClient {
}
static http.Client createHTTPClient() => retry.RetryClient(
PlatformInfos.isAndroid
? IOClient(customHttpClient(ISRG_X1))
: http.Client(),
);
PlatformInfos.isAndroid
? IOClient(customHttpClient(ISRG_X1))
: http.Client(),
);
}

View file

@ -55,7 +55,8 @@ Future<MatrixImageFileResizedResponse?> customImageResizer(
// scale down image for blurhashing to speed it up
final (blurW, blurH) = _scaleToBox(width, height, boxSize: 100);
final blurhashImg = nativeImg.resample(
blurW, blurH,
blurW,
blurH,
// nearest is unsupported...
native.Transform.bilinear,
);
@ -73,8 +74,11 @@ Future<MatrixImageFileResizedResponse?> customImageResizer(
if (width > max || height > max) {
(width, height) = _scaleToBox(width, height, boxSize: max);
final scaledImg =
nativeImg.resample(width, height, native.Transform.lanczos);
final scaledImg = nativeImg.resample(
width,
height,
native.Transform.lanczos,
);
nativeImg.free();
nativeImg = scaledImg;
}

View file

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
class CustomScrollBehavior extends MaterialScrollBehavior {
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.trackpad,
};
PointerDeviceKind.touch,
PointerDeviceKind.trackpad,
};
}

View file

@ -42,7 +42,8 @@ extension DateTimeExtension on DateTime {
final sameDay = sameYear && now.month == month && now.day == day;
final sameWeek = sameYear &&
final sameWeek =
sameYear &&
!sameDay &&
now.millisecondsSinceEpoch - millisecondsSinceEpoch <
1000 * 60 * 60 * 24 * 7;
@ -50,14 +51,17 @@ extension DateTimeExtension on DateTime {
if (sameDay) {
return localizedTimeOfDay(context);
} else if (sameWeek) {
return DateFormat.E(Localizations.localeOf(context).languageCode)
.format(this);
return DateFormat.E(
Localizations.localeOf(context).languageCode,
).format(this);
} else if (sameYear) {
return DateFormat.MMMd(Localizations.localeOf(context).languageCode)
.format(this);
return DateFormat.MMMd(
Localizations.localeOf(context).languageCode,
).format(this);
}
return DateFormat.yMMMd(Localizations.localeOf(context).languageCode)
.format(this);
return DateFormat.yMMMd(
Localizations.localeOf(context).languageCode,
).format(this);
}
/// If the DateTime is today, this returns [localizedTimeOfDay()], if not it also

View file

@ -40,10 +40,7 @@ class ErrorReporter {
child: SingleChildScrollView(
child: Text(
text,
style: const TextStyle(
fontSize: 14,
fontFamily: 'RobotoMono',
),
style: const TextStyle(fontSize: 14, fontFamily: 'RobotoMono'),
),
),
),
@ -53,9 +50,7 @@ class ErrorReporter {
child: Text(L10n.of(context).close),
),
AdaptiveDialogAction(
onPressed: () => Clipboard.setData(
ClipboardData(text: text),
),
onPressed: () => Clipboard.setData(ClipboardData(text: text)),
child: Text(L10n.of(context).copy),
),
AdaptiveDialogAction(

View file

@ -6,18 +6,17 @@ extension EventCheckboxRoomExtension on Room {
String eventId,
int checkboxId, {
String? txid,
}) =>
sendEvent(
{
'm.relates_to': {
'rel_type': relationshipType,
'event_id': eventId,
'checkbox_id': checkboxId,
},
},
type: EventTypes.Reaction,
txid: txid,
);
}) => sendEvent(
{
'm.relates_to': {
'rel_type': relationshipType,
'event_id': eventId,
'checkbox_id': checkboxId,
},
},
type: EventTypes.Reaction,
txid: txid,
);
}
extension EventCheckboxExtension on Event {

View file

@ -23,12 +23,10 @@ abstract class FluffyShare {
);
return;
}
await Clipboard.setData(
ClipboardData(text: text),
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).copiedToClipboard)),
);
await Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(L10n.of(context).copiedToClipboard)));
return;
}

View file

@ -31,22 +31,22 @@ class SessionBackup {
SessionBackup.fromJson(jsonDecode(json));
factory SessionBackup.fromJson(Map<String, dynamic> json) => SessionBackup(
olmAccount: json['olm_account'],
accessToken: json['access_token'],
userId: json['user_id'],
homeserver: json['homeserver'],
deviceId: json['device_id'],
deviceName: json['device_name'],
);
olmAccount: json['olm_account'],
accessToken: json['access_token'],
userId: json['user_id'],
homeserver: json['homeserver'],
deviceId: json['device_id'],
deviceName: json['device_name'],
);
Map<String, dynamic> toJson() => {
'olm_account': olmAccount,
'access_token': accessToken,
'user_id': userId,
'homeserver': homeserver,
'device_id': deviceId,
if (deviceName != null) 'device_name': deviceName,
};
'olm_account': olmAccount,
'access_token': accessToken,
'user_id': userId,
'homeserver': homeserver,
'device_id': deviceId,
if (deviceName != null) 'device_name': deviceName,
};
@override
String toString() => jsonEncode(toJson());
@ -82,7 +82,8 @@ extension InitWithRestoreExtension on Client {
final homeserver = this.homeserver?.toString();
final deviceId = deviceID;
final userId = userID;
final hasBackup = accessToken != null &&
final hasBackup =
accessToken != null &&
homeserver != null &&
deviceId != null &&
userId != null;

View file

@ -20,8 +20,8 @@ extension LocalizedExceptionExtension on Object {
final numString = round < 10
? num.toStringAsFixed(2)
: round < 100
? num.toStringAsFixed(1)
: round.toString();
? num.toStringAsFixed(1)
: round.toString();
return '$numString ${'kMGTPEZY'[i - 1]}B';
}
@ -31,9 +31,9 @@ extension LocalizedExceptionExtension on Object {
]) {
if (this is FileTooBigMatrixException) {
final exception = this as FileTooBigMatrixException;
return L10n.of(context).fileIsTooBigForServer(
_formatFileSize(exception.maxFileSize),
);
return L10n.of(
context,
).fileIsTooBigForServer(_formatFileSize(exception.maxFileSize));
}
if (this is OtherPartyCanNotReceiveMessages) {
return L10n.of(context).otherPartyNotLoggedIn;

View file

@ -64,8 +64,10 @@ Widget markdownContextBuilder(
if (start == -1) start = 0;
final end = selection.end;
final fullLineSelection =
TextSelection(baseOffset: start, extentOffset: end);
final fullLineSelection = TextSelection(
baseOffset: start,
extentOffset: end,
);
const checkBox = '- [ ]';
@ -78,8 +80,11 @@ Widget markdownContextBuilder(
: '$checkBox $line',
)
.join('\n');
controller.text =
controller.text.replaceRange(start, end, replacedRange);
controller.text = controller.text.replaceRange(
start,
end,
replacedRange,
);
ContextMenuController.removeAny();
},
),

View file

@ -22,8 +22,13 @@ IconData _getIconFromName(String displayname) {
}.any((s) => name.contains(s))) {
return Icons.web_outlined;
}
if ({'desktop', 'windows', 'macos', 'linux', 'ubuntu'}
.any((s) => name.contains(s))) {
if ({
'desktop',
'windows',
'macos',
'linux',
'ubuntu',
}.any((s) => name.contains(s))) {
return Icons.desktop_mac_outlined;
}
return Icons.device_unknown_outlined;

View file

@ -15,8 +15,9 @@ extension LocalizedBody on Event {
showFutureLoadingDialog(
context: context,
futureWithProgress: (onProgress) {
final fileSize =
infoMap['size'] is int ? infoMap['size'] as int : null;
final fileSize = infoMap['size'] is int
? infoMap['size'] as int
: null;
return downloadAndDecryptAttachment(
onDownloadProgress: fileSize == null
? null
@ -47,8 +48,11 @@ extension LocalizedBody on Event {
thumbnailInfoMap['size'] < room.client.database.maxFileSize;
bool get showThumbnail =>
[MessageTypes.Image, MessageTypes.Sticker, MessageTypes.Video]
.contains(messageType) &&
[
MessageTypes.Image,
MessageTypes.Sticker,
MessageTypes.Video,
].contains(messageType) &&
(kIsWeb ||
isAttachmentSmallEnough ||
isThumbnailSmallEnough ||

View file

@ -6,29 +6,28 @@ extension VisibleInGuiExtension on List<Event> {
List<Event> filterByVisibleInGui({
String? exceptionEventId,
String? threadId,
}) =>
where(
(event) {
if (threadId != null &&
event.relationshipType != RelationshipTypes.reaction) {
if ((event.relationshipType != RelationshipTypes.thread ||
event.relationshipEventId != threadId) &&
event.eventId != threadId) {
return false;
}
} else if (event.relationshipType == RelationshipTypes.thread) {
return false;
}
return event.isVisibleInGui || event.eventId == exceptionEventId;
},
).toList();
}) => where((event) {
if (threadId != null &&
event.relationshipType != RelationshipTypes.reaction) {
if ((event.relationshipType != RelationshipTypes.thread ||
event.relationshipEventId != threadId) &&
event.eventId != threadId) {
return false;
}
} else if (event.relationshipType == RelationshipTypes.thread) {
return false;
}
return event.isVisibleInGui || event.eventId == exceptionEventId;
}).toList();
}
extension IsStateExtension on Event {
bool get isVisibleInGui =>
// always filter out edit and reaction relationships
!{RelationshipTypes.edit, RelationshipTypes.reaction}
.contains(relationshipType) &&
!{
RelationshipTypes.edit,
RelationshipTypes.reaction,
}.contains(relationshipType) &&
// always filter out m.key.* and other known but unimportant events
!isKnownHiddenStates &&
// event types to hide: redaction and reaction events
@ -40,22 +39,20 @@ extension IsStateExtension on Event {
(!AppSettings.hideUnknownEvents.value || isEventTypeKnown);
bool get isState => !{
EventTypes.Message,
EventTypes.Sticker,
EventTypes.Encrypted,
}.contains(type);
EventTypes.Message,
EventTypes.Sticker,
EventTypes.Encrypted,
}.contains(type);
bool get isCollapsedState => !{
EventTypes.Message,
EventTypes.Sticker,
EventTypes.Encrypted,
EventTypes.RoomCreate,
EventTypes.RoomTombstone,
}.contains(type);
EventTypes.Message,
EventTypes.Sticker,
EventTypes.Encrypted,
EventTypes.RoomCreate,
EventTypes.RoomTombstone,
}.contains(type);
bool get isKnownHiddenStates =>
{
PollEventContent.responseType,
}.contains(type) ||
{PollEventContent.responseType}.contains(type) ||
type.startsWith('m.key.verification.');
}

View file

@ -28,22 +28,19 @@ Future<DatabaseApi> flutterMatrixSdkDatabaseBuilder(String clientName) async {
try {
// Send error notification:
final l10n = await lookupL10n(PlatformDispatcher.instance.locale);
ClientManager.sendInitNotification(
l10n.initAppError,
e.toString(),
);
ClientManager.sendInitNotification(l10n.initAppError, e.toString());
} catch (e, s) {
Logs().e('Unable to send error notification', e, s);
}
// Try to delete database so that it can created again on next init:
database?.delete().catchError(
(e, s) => Logs().wtf(
'Unable to delete database, after failed construction',
e,
s,
),
);
(e, s) => Logs().wtf(
'Unable to delete database, after failed construction',
e,
s,
),
);
// Delete database file:
if (!kIsWeb) {
@ -77,8 +74,9 @@ Future<MatrixSdkDatabase> _constructDatabase(String clientName) async {
// fix dlopen for old Android
await applyWorkaroundToOpenSqlCipherOnOldAndroidVersions();
// import the SQLite / SQLCipher shared objects / dynamic libraries
final factory =
createDatabaseFactoryFfi(ffiInit: SQfLiteEncryptionHelper.ffiInit);
final factory = createDatabaseFactoryFfi(
ffiInit: SQfLiteEncryptionHelper.ffiInit,
);
// required for [getDatabasesPath]
databaseFactory = factory;
@ -90,11 +88,7 @@ Future<MatrixSdkDatabase> _constructDatabase(String clientName) async {
// to manage SQLite encryption
final helper = cipher == null
? null
: SQfLiteEncryptionHelper(
factory: factory,
path: path,
cipher: cipher,
);
: SQfLiteEncryptionHelper(factory: factory, path: path, cipher: cipher);
// check whether the DB is already encrypted and otherwise do so
await helper?.ensureDatabaseFileEncrypted();

View file

@ -25,10 +25,7 @@ Future<String?> getDatabaseCipher() async {
final list = Uint8List(32);
list.setAll(0, Iterable.generate(list.length, (i) => rng.nextInt(256)));
final newPassword = base64UrlEncode(list);
await secureStorage.write(
key: _passwordStorageKey,
value: newPassword,
);
await secureStorage.write(key: _passwordStorageKey, value: newPassword);
}
// workaround for if we just wrote to the key and it still doesn't exist
password = await secureStorage.read(key: _passwordStorageKey);

View file

@ -39,8 +39,9 @@ extension MatrixFileExtension on MatrixFile {
await SharePlus.instance.share(
ShareParams(
files: [XFile.fromData(bytes, name: name, mimeType: mimeType)],
sharePositionOrigin:
box == null ? null : box.localToGlobal(Offset.zero) & box.size,
sharePositionOrigin: box == null
? null
: box.localToGlobal(Offset.zero) & box.size,
),
);
return;

View file

@ -353,13 +353,15 @@ class MatrixLocals extends MatrixLocalizations {
String get cancelledSend => l10n.sendCanceled;
@override
String voiceMessage(String senderName, Duration? duration) =>
l10n.sentVoiceMessage(
senderName,
duration == null
? ''
: '${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}',
);
String voiceMessage(
String senderName,
Duration? duration,
) => l10n.sentVoiceMessage(
senderName,
duration == null
? ''
: '${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}',
);
@override
String get refreshingLastEvent => l10n.loadingPleaseWait;

View file

@ -21,19 +21,20 @@ bool _vodInitialized = false;
extension NotificationResponseJson on NotificationResponse {
String toJsonString() => jsonEncode({
'type': notificationResponseType.name,
'id': id,
'actionId': actionId,
'input': input,
'payload': payload,
'data': data,
});
'type': notificationResponseType.name,
'id': id,
'actionId': actionId,
'input': input,
'payload': payload,
'data': data,
});
static NotificationResponse fromJsonString(String jsonString) {
final json = jsonDecode(jsonString) as Map<String, Object?>;
return NotificationResponse(
notificationResponseType: NotificationResponseType.values
.singleWhere((t) => t.name == json['type']),
notificationResponseType: NotificationResponseType.values.singleWhere(
(t) => t.name == json['type'],
),
id: json['id'] as int?,
actionId: json['actionId'] as String?,
input: json['input'] as String?,
@ -55,8 +56,9 @@ Future<void> waitForPushIsolateDone() async {
void notificationTapBackground(
NotificationResponse notificationResponse,
) async {
final sendPort =
IsolateNameServer.lookupPortByName(AppConfig.mainIsolatePortName);
final sendPort = IsolateNameServer.lookupPortByName(
AppConfig.mainIsolatePortName,
);
if (sendPort != null) {
sendPort.send(notificationResponse.toJsonString());
Logs().i('Notification tap sent to main isolate!');
@ -80,8 +82,7 @@ void notificationTapBackground(
final client = (await ClientManager.getClients(
initialize: false,
store: store,
))
.first;
)).first;
await client.abortSync();
await client.init(
waitForFirstSync: false,
@ -111,8 +112,9 @@ Future<void> notificationTap(
'Notification action handler started',
notificationResponse.notificationResponseType.name,
);
final payload =
FluffyChatPushPayload.fromString(notificationResponse.payload ?? '');
final payload = FluffyChatPushPayload.fromString(
notificationResponse.payload ?? '',
);
switch (notificationResponse.notificationResponseType) {
case NotificationResponseType.selectedNotification:
final roomId = payload.roomId;
@ -182,16 +184,16 @@ Future<void> notificationTap(
final avatarFile = avatar == null
? null
: await client
.downloadMxcCached(
avatar,
thumbnailMethod: ThumbnailMethod.crop,
width: notificationAvatarDimension,
height: notificationAvatarDimension,
animated: false,
isThumbnail: true,
rounded: true,
)
.timeout(const Duration(seconds: 3));
.downloadMxcCached(
avatar,
thumbnailMethod: ThumbnailMethod.crop,
width: notificationAvatarDimension,
height: notificationAvatarDimension,
animated: false,
isThumbnail: true,
rounded: true,
)
.timeout(const Duration(seconds: 3));
final messagingStyleInformation =
await AndroidFlutterLocalNotificationsPlugin()
.getActiveNotificationMessagingStyle(room.id.hashCode);

View file

@ -89,8 +89,7 @@ Future<void> _tryPushHelper(
client ??= (await ClientManager.getClients(
initialize: false,
store: await AppSettings.init(),
))
.first;
)).first;
final event = await client.getEventByPushNotification(
notification,
storeInDatabase: false,
@ -105,8 +104,8 @@ Future<void> _tryPushHelper(
// Make sure client is fully loaded and synced before dismiss notifications:
await client.roomsLoading;
await client.oneShotSync();
final activeNotifications =
await flutterLocalNotificationsPlugin.getActiveNotifications();
final activeNotifications = await flutterLocalNotificationsPlugin
.getActiveNotifications();
for (final activeNotification in activeNotifications) {
final room = client.rooms.singleWhereOrNull(
(room) => room.id.hashCode == activeNotification.id,
@ -168,16 +167,16 @@ Future<void> _tryPushHelper(
roomAvatarFile = avatar == null
? null
: await client
.downloadMxcCached(
avatar,
thumbnailMethod: ThumbnailMethod.crop,
width: notificationAvatarDimension,
height: notificationAvatarDimension,
animated: false,
isThumbnail: true,
rounded: true,
)
.timeout(const Duration(seconds: 3));
.downloadMxcCached(
avatar,
thumbnailMethod: ThumbnailMethod.crop,
width: notificationAvatarDimension,
height: notificationAvatarDimension,
animated: false,
isThumbnail: true,
rounded: true,
)
.timeout(const Duration(seconds: 3));
} catch (e, s) {
Logs().e('Unable to get avatar picture', e, s);
}
@ -185,18 +184,18 @@ Future<void> _tryPushHelper(
senderAvatarFile = event.room.isDirectChat
? roomAvatarFile
: senderAvatar == null
? null
: await client
.downloadMxcCached(
senderAvatar,
thumbnailMethod: ThumbnailMethod.crop,
width: notificationAvatarDimension,
height: notificationAvatarDimension,
animated: false,
isThumbnail: true,
rounded: true,
)
.timeout(const Duration(seconds: 3));
? null
: await client
.downloadMxcCached(
senderAvatar,
thumbnailMethod: ThumbnailMethod.crop,
width: notificationAvatarDimension,
height: notificationAvatarDimension,
animated: false,
isThumbnail: true,
rounded: true,
)
.timeout(const Duration(seconds: 3));
} catch (e, s) {
Logs().e('Unable to get avatar picture', e, s);
}
@ -221,14 +220,15 @@ Future<void> _tryPushHelper(
final messagingStyleInformation = PlatformInfos.isAndroid
? await AndroidFlutterLocalNotificationsPlugin()
.getActiveNotificationMessagingStyle(id)
.getActiveNotificationMessagingStyle(id)
: null;
messagingStyleInformation?.messages?.add(newMessage);
final roomName = event.room.getLocalizedDisplayname(MatrixLocals(l10n));
final notificationGroupId =
event.room.isDirectChat ? 'directChats' : 'groupChats';
final notificationGroupId = event.room.isDirectChat
? 'directChats'
: 'groupChats';
final groupName = event.room.isDirectChat ? l10n.directChats : l10n.groups;
final messageRooms = AndroidNotificationChannelGroup(
@ -243,11 +243,13 @@ Future<void> _tryPushHelper(
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
AndroidFlutterLocalNotificationsPlugin
>()
?.createNotificationChannelGroup(messageRooms);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
AndroidFlutterLocalNotificationsPlugin
>()
?.createNotificationChannel(roomsChannel);
final androidPlatformChannelSpecifics = AndroidNotificationDetails(
@ -256,7 +258,8 @@ Future<void> _tryPushHelper(
number: notification.counts?.unread,
category: AndroidNotificationCategory.message,
shortcutId: event.room.id,
styleInformation: messagingStyleInformation ??
styleInformation:
messagingStyleInformation ??
MessagingStyleInformation(
Person(
name: senderName,
@ -288,9 +291,7 @@ Future<void> _tryPushHelper(
FluffyChatNotificationActions.reply.name,
l10n.reply,
inputs: [
AndroidNotificationActionInput(
label: l10n.writeAMessage,
),
AndroidNotificationActionInput(label: l10n.writeAMessage),
],
cancelNotification: false,
allowGeneratedReplies: true,
@ -320,9 +321,11 @@ Future<void> _tryPushHelper(
title,
body,
platformChannelSpecifics,
payload:
FluffyChatPushPayload(client.clientName, event.room.id, event.eventId)
.toString(),
payload: FluffyChatPushPayload(
client.clientName,
event.room.id,
event.eventId,
).toString(),
);
Logs().v('Push helper has been completed!');
}

View file

@ -39,10 +39,7 @@ extension ResizeImage on XFile {
try {
final bytes = await VideoCompress.getByteThumbnail(path);
if (bytes == null) return null;
return MatrixImageFile(
bytes: bytes,
name: name,
);
return MatrixImageFile(bytes: bytes, name: name);
} catch (e, s) {
Logs().w('Error while compressing video', e, s);
}

View file

@ -19,8 +19,9 @@ extension RoomStatusExtension on Room {
} else if (typingUsers.length == 1) {
typingText = L10n.of(context).isTyping;
if (typingUsers.first.id != directChatMatrixID) {
typingText =
L10n.of(context).userIsTyping(typingUsers.first.calcDisplayname());
typingText = L10n.of(
context,
).userIsTyping(typingUsers.first.calcDisplayname());
}
} else if (typingUsers.length == 2) {
typingText = L10n.of(context).userAndUserAreTyping(

View file

@ -10,28 +10,25 @@ Future<T?> showScaffoldDialog<T>({
double maxWidth = 480,
double maxHeight = 720,
required Widget Function(BuildContext context) builder,
}) =>
showDialog<T>(
context: context,
useSafeArea: false,
builder: FluffyThemes.isColumnMode(context)
? (context) => Center(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
),
color: containerColor ??
Theme.of(context).scaffoldBackgroundColor,
),
clipBehavior: Clip.hardEdge,
margin: const EdgeInsets.all(16),
constraints: BoxConstraints(
maxWidth: maxWidth,
maxHeight: maxHeight,
),
child: builder(context),
),
)
: builder,
);
}) => showDialog<T>(
context: context,
useSafeArea: false,
builder: FluffyThemes.isColumnMode(context)
? (context) => Center(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
color:
containerColor ?? Theme.of(context).scaffoldBackgroundColor,
),
clipBehavior: Clip.hardEdge,
margin: const EdgeInsets.all(16),
constraints: BoxConstraints(
maxWidth: maxWidth,
maxHeight: maxHeight,
),
child: builder(context),
),
)
: builder,
);

View file

@ -12,8 +12,9 @@ extension SyncStatusLocalization on SyncStatusUpdate {
case SyncStatus.waitingForResponse:
return L10n.of(context).waitingForServer;
case SyncStatus.error:
return ((error?.exception ?? Object()) as Object)
.toLocalizedString(context);
return ((error?.exception ?? Object()) as Object).toLocalizedString(
context,
);
case SyncStatus.processing:
case SyncStatus.cleaningUp:
case SyncStatus.finished:

View file

@ -16,7 +16,7 @@ extension UiaRequestManager on MatrixState {
final l10n = L10n.of(context);
final navigatorContext =
FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ??
context;
context;
try {
if (uiaRequest.state != UiaRequestState.waitForUser ||
uiaRequest.nextStages.isEmpty) {
@ -27,7 +27,8 @@ extension UiaRequestManager on MatrixState {
Logs().d('Uia Request Stage: $stage');
switch (stage) {
case AuthenticationTypes.password:
final input = cachedPassword ??
final input =
cachedPassword ??
(await showTextInputDialog(
context: navigatorContext,
title: l10n.pleaseEnterYourPassword,
@ -87,9 +88,7 @@ extension UiaRequestManager on MatrixState {
?.tryGet<String>('url');
final fallbackUrl = client.homeserver!.replace(
path: '/_matrix/client/v3/auth/$stage/fallback/web',
queryParameters: {
'session': uiaRequest.session,
},
queryParameters: {'session': uiaRequest.session},
);
final url = stageUrl != null
? (Uri.tryParse(stageUrl) ?? fallbackUrl)
@ -106,8 +105,9 @@ extension UiaRequestManager on MatrixState {
launchUrl(url, mode: LaunchMode.inAppBrowserView);
final completer = Completer();
final listener =
AppLifecycleListener(onResume: () => completer.complete());
final listener = AppLifecycleListener(
onResume: () => completer.complete(),
);
await completer.future;
listener.dispose();

View file

@ -99,13 +99,16 @@ class UrlLauncher {
// okay, we have either an http or an https URI.
// As some platforms have issues with opening unicode URLs, we are going to help
// them out by punycode-encoding them for them ourself.
final newHost = uri.host.split('.').map((hostPartEncoded) {
final hostPart = Uri.decodeComponent(hostPartEncoded);
final hostPartPunycode = punycodeEncode(hostPart);
return hostPartPunycode != '$hostPart-'
? 'xn--$hostPartPunycode'
: hostPart;
}).join('.');
final newHost = uri.host
.split('.')
.map((hostPartEncoded) {
final hostPart = Uri.decodeComponent(hostPartEncoded);
final hostPartPunycode = punycodeEncode(hostPart);
return hostPartPunycode != '$hostPart-'
? 'xn--$hostPartPunycode'
: hostPart;
})
.join('.');
// Force LaunchMode.externalApplication, otherwise url_launcher will default
// to opening links in a webview on mobile platforms.
launchUrlString(
@ -117,17 +120,17 @@ class UrlLauncher {
void openMatrixToUrl() async {
final matrix = Matrix.of(context);
final url = this.url!.replaceFirst(
AppConfig.deepLinkPrefix,
AppConfig.inviteLinkPrefix,
);
AppConfig.deepLinkPrefix,
AppConfig.inviteLinkPrefix,
);
// The identifier might be a matrix.to url and needs escaping. Or, it might have multiple
// identifiers (room id & event id), or it might also have a query part.
// All this needs parsing.
final identityParts = url.parseIdentifierIntoParts() ??
final identityParts =
url.parseIdentifierIntoParts() ??
Uri.tryParse(url)?.host.parseIdentifierIntoParts() ??
Uri.tryParse(url)
?.pathSegments
Uri.tryParse(url)?.pathSegments
.lastWhereOrNull((_) => true)
?.parseIdentifierIntoParts();
if (identityParts == null) {
@ -138,7 +141,8 @@ class UrlLauncher {
// we got a room! Let's open that one
final roomIdOrAlias = identityParts.primaryIdentifier;
final event = identityParts.secondaryIdentifier;
var room = matrix.client.getRoomByAlias(roomIdOrAlias) ??
var room =
matrix.client.getRoomByAlias(roomIdOrAlias) ??
matrix.client.getRoomById(roomIdOrAlias);
var roomId = room?.id;
// we make the servers a set and later on convert to a list, so that we can easily
@ -168,10 +172,7 @@ class UrlLauncher {
// we have the room, so....just open it
if (event != null) {
context.go(
'/${Uri(
pathSegments: ['rooms', room.id],
queryParameters: {'event': event},
)}',
'/${Uri(pathSegments: ['rooms', room.id], queryParameters: {'event': event})}',
);
} else {
context.go('/rooms/${room.id}');
@ -180,9 +181,8 @@ class UrlLauncher {
} else {
await showAdaptiveDialog(
context: context,
builder: (c) => PublicRoomDialog(
roomAlias: identityParts.primaryIdentifier,
),
builder: (c) =>
PublicRoomDialog(roomAlias: identityParts.primaryIdentifier),
);
}
if (roomIdOrAlias.sigil == '!') {
@ -223,12 +223,11 @@ class UrlLauncher {
var noProfileWarning = false;
final profileResult = await showFutureLoadingDialog(
context: context,
future: () => matrix.client.getProfileFromUserId(userId).catchError(
(_) {
noProfileWarning = true;
return Profile(userId: userId);
},
),
future: () =>
matrix.client.getProfileFromUserId(userId).catchError((_) {
noProfileWarning = true;
return Profile(userId: userId);
}),
);
await UserDialog.show(
context: context,

View file

@ -45,8 +45,9 @@ class _VideoRendererState extends State<VideoRenderer> {
@override
void initState() {
_streamChangeSubscription =
widget.stream?.onStreamChanged.stream.listen((stream) {
_streamChangeSubscription = widget.stream?.onStreamChanged.stream.listen((
stream,
) {
setState(() {
_renderer?.srcObject = stream;
});

View file

@ -33,13 +33,15 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate {
@override
void didChangeAppLifecycleState(AppLifecycleState? state) {
background = (state == AppLifecycleState.detached ||
background =
(state == AppLifecycleState.detached ||
state == AppLifecycleState.paused);
}
void addCallingOverlay(String callId, CallSession call) {
final context =
kIsWeb ? ChatList.contextForVoip! : this.context; // web is weird
final context = kIsWeb
? ChatList.contextForVoip!
: this.context; // web is weird
if (overlayEntry != null) {
Logs().e('[VOIP] addCallingOverlay: The call session already exists?');
@ -85,8 +87,7 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate {
Future<RTCPeerConnection> createPeerConnection(
Map<String, dynamic> configuration, [
Map<String, dynamic> constraints = const {},
]) =>
webrtc_impl.createPeerConnection(configuration, constraints);
]) => webrtc_impl.createPeerConnection(configuration, constraints);
Future<bool> get hasCallingAccount async => false;