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

@ -49,7 +49,8 @@ class AdaptiveDialogAction extends StatelessWidget {
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: borderRadius ??
borderRadius:
borderRadius ??
BorderRadius.circular(AppConfig.borderRadius),
),
backgroundColor: autofocus

View file

@ -86,10 +86,7 @@ class DialogTextField extends StatelessWidget {
if (errorText != null)
Text(
errorText,
style: TextStyle(
fontSize: 11,
color: theme.colorScheme.error,
),
style: TextStyle(fontSize: 11, color: theme.colorScheme.error),
textAlign: TextAlign.left,
),
],

View file

@ -37,10 +37,7 @@ class PublicRoomDialog extends StatelessWidget {
}
final roomId = chunk != null && knock
? await client.knockRoom(chunk.roomId, via: via)
: await client.joinRoom(
roomAlias ?? chunk!.roomId,
via: via,
);
: await client.joinRoom(roomAlias ?? chunk!.roomId, via: via);
if (!knock && client.getRoomById(roomId) == null) {
await client.waitForRoomInSync(roomId);
@ -78,11 +75,9 @@ class PublicRoomDialog extends StatelessWidget {
final chunk = this.chunk;
if (chunk != null) return chunk;
final query = await Matrix.of(context).client.queryPublicRooms(
server: roomAlias!.domain,
filter: PublicRoomQueryFilter(
genericSearchTerm: roomAlias,
),
);
server: roomAlias!.domain,
filter: PublicRoomQueryFilter(genericSearchTerm: roomAlias),
);
if (!query.chunk.any(_testRoom)) {
throw (L10n.of(context).noRoomsFound);
}
@ -115,8 +110,8 @@ class PublicRoomDialog extends StatelessWidget {
return SingleChildScrollView(
child: Column(
spacing: 8,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: .min,
crossAxisAlignment: .stretch,
children: [
if (roomLink != null)
HoverBuilder(
@ -125,9 +120,7 @@ class PublicRoomDialog extends StatelessWidget {
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
Clipboard.setData(
ClipboardData(text: roomLink),
);
Clipboard.setData(ClipboardData(text: roomLink));
setState(() {
copied = true;
});
@ -137,8 +130,9 @@ class PublicRoomDialog extends StatelessWidget {
children: [
WidgetSpan(
child: Padding(
padding:
const EdgeInsets.only(right: 4.0),
padding: const EdgeInsets.only(
right: 4.0,
),
child: AnimatedScale(
duration:
FluffyThemes.animationDuration,
@ -146,8 +140,8 @@ class PublicRoomDialog extends StatelessWidget {
scale: hovered
? 1.33
: copied
? 1.25
: 1.0,
? 1.25
: 1.0,
child: Icon(
copied
? Icons.check_circle
@ -160,8 +154,9 @@ class PublicRoomDialog extends StatelessWidget {
),
TextSpan(text: roomLink),
],
style: theme.textTheme.bodyMedium
?.copyWith(fontSize: 10),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 10,
),
),
textAlign: TextAlign.center,
),
@ -176,25 +171,26 @@ class PublicRoomDialog extends StatelessWidget {
size: Avatar.defaultSize * 2,
onTap: avatar != null
? () => showDialog(
context: context,
builder: (_) => MxcImageViewer(avatar),
)
context: context,
builder: (_) => MxcImageViewer(avatar),
)
: null,
),
),
if (profile?.numJoinedMembers != null)
Text(
L10n.of(context).countParticipants(
profile?.numJoinedMembers ?? 0,
),
L10n.of(
context,
).countParticipants(profile?.numJoinedMembers ?? 0),
style: const TextStyle(fontSize: 10),
textAlign: TextAlign.center,
),
if (topic != null && topic.isNotEmpty)
SelectableLinkify(
text: topic,
textScaleFactor:
MediaQuery.textScalerOf(context).scale(1),
textScaleFactor: MediaQuery.textScalerOf(
context,
).scale(1),
textAlign: TextAlign.center,
options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle(
@ -221,8 +217,8 @@ class PublicRoomDialog extends StatelessWidget {
Matrix.of(context).client.getRoomById(chunk!.roomId) == null
? L10n.of(context).knock
: chunk?.roomType == 'm.space'
? L10n.of(context).joinSpace
: L10n.of(context).joinRoom,
? L10n.of(context).joinSpace
: L10n.of(context).joinRoom,
),
),
AdaptiveDialogAction(

View file

@ -32,10 +32,7 @@ Future<T?> showModalActionPopup<T>({
ListTile(
title: title == null
? null
: Text(
title,
style: theme.textTheme.labelSmall,
),
: Text(title, style: theme.textTheme.labelSmall),
subtitle: message == null ? null : Text(message),
),
const Divider(height: 1),
@ -49,8 +46,9 @@ Future<T?> showModalActionPopup<T>({
style: action.isDestructive
? TextStyle(
color: theme.colorScheme.error,
fontWeight:
action.isDefaultAction ? FontWeight.bold : null,
fontWeight: action.isDefaultAction
? FontWeight.bold
: null,
)
: null,
),

View file

@ -16,50 +16,49 @@ Future<OkCancelResult?> showOkCancelAlertDialog({
String? cancelLabel,
bool isDestructive = false,
bool useRootNavigator = true,
}) =>
showAdaptiveDialog<OkCancelResult>(
context: context,
useRootNavigator: useRootNavigator,
builder: (context) => AlertDialog.adaptive(
title: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: Text(title),
),
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: message == null
? null
: SelectableLinkify(
text: message,
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
linkStyle: TextStyle(
color: Theme.of(context).colorScheme.primary,
decorationColor: Theme.of(context).colorScheme.primary,
),
options: const LinkifyOptions(humanize: false),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
),
),
actions: [
AdaptiveDialogAction(
onPressed: () => Navigator.of(context)
.pop<OkCancelResult>(OkCancelResult.cancel),
child: Text(cancelLabel ?? L10n.of(context).cancel),
),
AdaptiveDialogAction(
onPressed: () =>
Navigator.of(context).pop<OkCancelResult>(OkCancelResult.ok),
autofocus: true,
child: Text(
okLabel ?? L10n.of(context).ok,
style: isDestructive
? TextStyle(color: Theme.of(context).colorScheme.error)
: null,
}) => showAdaptiveDialog<OkCancelResult>(
context: context,
useRootNavigator: useRootNavigator,
builder: (context) => AlertDialog.adaptive(
title: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: Text(title),
),
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: message == null
? null
: SelectableLinkify(
text: message,
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
linkStyle: TextStyle(
color: Theme.of(context).colorScheme.primary,
decorationColor: Theme.of(context).colorScheme.primary,
),
options: const LinkifyOptions(humanize: false),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
),
),
],
),
actions: [
AdaptiveDialogAction(
onPressed: () =>
Navigator.of(context).pop<OkCancelResult>(OkCancelResult.cancel),
child: Text(cancelLabel ?? L10n.of(context).cancel),
),
);
AdaptiveDialogAction(
onPressed: () =>
Navigator.of(context).pop<OkCancelResult>(OkCancelResult.ok),
autofocus: true,
child: Text(
okLabel ?? L10n.of(context).ok,
style: isDestructive
? TextStyle(color: Theme.of(context).colorScheme.error)
: null,
),
),
],
),
);
Future<OkCancelResult?> showOkAlertDialog({
required BuildContext context,
@ -67,37 +66,36 @@ Future<OkCancelResult?> showOkAlertDialog({
String? message,
String? okLabel,
bool useRootNavigator = true,
}) =>
showAdaptiveDialog<OkCancelResult>(
context: context,
useRootNavigator: useRootNavigator,
builder: (context) => AlertDialog.adaptive(
title: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: Text(title),
),
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: message == null
? null
: SelectableLinkify(
text: message,
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
linkStyle: TextStyle(
color: Theme.of(context).colorScheme.primary,
decorationColor: Theme.of(context).colorScheme.primary,
),
options: const LinkifyOptions(humanize: false),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
),
),
actions: [
AdaptiveDialogAction(
onPressed: () =>
Navigator.of(context).pop<OkCancelResult>(OkCancelResult.ok),
autofocus: true,
child: Text(okLabel ?? L10n.of(context).close),
),
],
}) => showAdaptiveDialog<OkCancelResult>(
context: context,
useRootNavigator: useRootNavigator,
builder: (context) => AlertDialog.adaptive(
title: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: Text(title),
),
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: message == null
? null
: SelectableLinkify(
text: message,
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
linkStyle: TextStyle(
color: Theme.of(context).colorScheme.primary,
decorationColor: Theme.of(context).colorScheme.primary,
),
options: const LinkifyOptions(humanize: false),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
),
),
actions: [
AdaptiveDialogAction(
onPressed: () =>
Navigator.of(context).pop<OkCancelResult>(OkCancelResult.ok),
autofocus: true,
child: Text(okLabel ?? L10n.of(context).close),
),
);
],
),
);

View file

@ -42,7 +42,7 @@ Future<String?> showTextInputDialog({
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisSize: .min,
children: [
if (message != null)
SelectableLinkify(

View file

@ -22,15 +22,12 @@ class UserDialog extends StatelessWidget {
required BuildContext context,
required Profile profile,
bool noProfileWarning = false,
}) =>
showAdaptiveDialog(
context: context,
barrierDismissible: true,
builder: (context) => UserDialog(
profile,
noProfileWarning: noProfileWarning,
),
);
}) => showAdaptiveDialog(
context: context,
barrierDismissible: true,
builder: (context) =>
UserDialog(profile, noProfileWarning: noProfileWarning),
);
final Profile profile;
final bool noProfileWarning;
@ -41,7 +38,8 @@ class UserDialog extends StatelessWidget {
Widget build(BuildContext context) {
final client = Matrix.of(context).client;
final dmRoomId = client.getDirectChatFromUserId(profile.userId);
final displayname = profile.displayName ??
final displayname =
profile.displayName ??
profile.userId.localpart ??
L10n.of(context).user;
var copied = false;
@ -64,15 +62,15 @@ class UserDialog extends StatelessWidget {
final presenceText = presence.currentlyActive == true
? L10n.of(context).currentlyActive
: lastActiveTimestamp != null
? L10n.of(context).lastActiveAgo(
lastActiveTimestamp.localizedTimeShort(context),
)
: null;
? L10n.of(context).lastActiveAgo(
lastActiveTimestamp.localizedTimeShort(context),
)
: null;
return SingleChildScrollView(
child: Column(
spacing: 8,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: .min,
crossAxisAlignment: .stretch,
children: [
Center(
child: Avatar(
@ -81,9 +79,9 @@ class UserDialog extends StatelessWidget {
size: Avatar.defaultSize * 2,
onTap: avatar != null
? () => showDialog(
context: context,
builder: (_) => MxcImageViewer(avatar),
)
context: context,
builder: (_) => MxcImageViewer(avatar),
)
: null,
),
),
@ -112,8 +110,8 @@ class UserDialog extends StatelessWidget {
scale: hovered
? 1.33
: copied
? 1.25
: 1.0,
? 1.25
: 1.0,
child: Icon(
copied
? Icons.check_circle
@ -126,8 +124,9 @@ class UserDialog extends StatelessWidget {
),
TextSpan(text: profile.userId),
],
style: theme.textTheme.bodyMedium
?.copyWith(fontSize: 10),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 10,
),
),
textAlign: TextAlign.center,
),
@ -144,8 +143,9 @@ class UserDialog extends StatelessWidget {
if (statusMsg != null)
SelectableLinkify(
text: statusMsg,
textScaleFactor:
MediaQuery.textScalerOf(context).scale(1),
textScaleFactor: MediaQuery.textScalerOf(
context,
).scale(1),
textAlign: TextAlign.center,
options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle(

View file

@ -82,8 +82,8 @@ class AppLock extends State<AppLockWidget> with WidgetsBindingObserver {
}
void showLockScreen() => setState(() {
_isLocked = true;
});
_isLocked = true;
});
Future<T> pauseWhile<T>(Future<T> future) async {
_paused = true;
@ -94,20 +94,15 @@ class AppLock extends State<AppLockWidget> with WidgetsBindingObserver {
}
}
static AppLock of(BuildContext context) => Provider.of<AppLock>(
context,
listen: false,
);
static AppLock of(BuildContext context) =>
Provider.of<AppLock>(context, listen: false);
@override
Widget build(BuildContext context) => Provider<AppLock>(
create: (_) => this,
child: Stack(
fit: StackFit.expand,
children: [
widget.child,
if (isLocked) const LockScreen(),
],
),
);
create: (_) => this,
child: Stack(
fit: StackFit.expand,
children: [widget.child, if (isLocked) const LockScreen()],
),
);
}

View file

@ -42,10 +42,12 @@ class Avatar extends StatelessWidget {
final theme = Theme.of(context);
final name = this.name;
final fallbackLetters =
name == null || name.isEmpty ? '@' : name.substring(0, 1);
final fallbackLetters = name == null || name.isEmpty
? '@'
: name.substring(0, 1);
final noPic = mxContent == null ||
final noPic =
mxContent == null ||
mxContent.toString().isEmpty ||
mxContent.toString() == 'null';
final borderRadius = this.borderRadius ?? BorderRadius.circular(size / 2);
@ -113,8 +115,8 @@ class Avatar extends StatelessWidget {
final dotColor = presence.presence.isOnline
? Colors.green
: presence.presence.isUnavailable
? Colors.orange
: Colors.grey;
? Colors.orange
: Colors.grey;
return Positioned(
bottom: -3,
right: -3,
@ -147,10 +149,7 @@ class Avatar extends StatelessWidget {
if (onTap == null) return container;
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: onTap,
child: container,
),
child: GestureDetector(onTap: onTap, child: container),
);
}
}

View file

@ -25,8 +25,8 @@ class AvatarPageHeader extends StatelessWidget {
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: FluffyThemes.columnWidth),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: .min,
crossAxisAlignment: .center,
spacing: 8.0,
children: [
Stack(
@ -58,7 +58,7 @@ class AvatarPageHeader extends StatelessWidget {
child: LayoutBuilder(
builder: (context, constraints) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: .center,
children: [
ConstrainedBox(
constraints: BoxConstraints(
@ -87,7 +87,7 @@ class AvatarPageHeader extends StatelessWidget {
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisAlignment: .spaceEvenly,
children: iconButtons,
),
),

View file

@ -25,9 +25,7 @@ class BlurHash extends StatefulWidget {
class _BlurHashState extends State<BlurHash> {
Uint8List? _data;
static Future<Uint8List> getBlurhashData(
BlurhashData blurhashData,
) async {
static Future<Uint8List> getBlurhashData(BlurhashData blurhashData) async {
final blurhash = b.BlurHash.decode(blurhashData.hsh);
final img = blurhash.toImage(blurhashData.w, blurhashData.h);
return Uint8List.fromList(image.encodePng(img));
@ -46,11 +44,7 @@ class _BlurHashState extends State<BlurHash> {
return _data ??= await compute(
getBlurhashData,
BlurhashData(
hsh: widget.blurhash,
w: width,
h: height,
),
BlurhashData(hsh: widget.blurhash, w: width, h: height),
);
}
@ -84,21 +78,10 @@ class BlurhashData {
final int w;
final int h;
const BlurhashData({
required this.hsh,
required this.w,
required this.h,
});
const BlurhashData({required this.hsh, required this.w, required this.h});
factory BlurhashData.fromJson(Map<String, dynamic> json) => BlurhashData(
hsh: json['hsh'],
w: json['w'],
h: json['h'],
);
factory BlurhashData.fromJson(Map<String, dynamic> json) =>
BlurhashData(hsh: json['hsh'], w: json['w'], h: json['h']);
Map<String, dynamic> toJson() => {
'hsh': hsh,
'w': w,
'h': h,
};
Map<String, dynamic> toJson() => {'hsh': hsh, 'w': w, 'h': h};
}

View file

@ -36,10 +36,7 @@ class ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
@override
Widget build(BuildContext context) {
notificationChangeSub ??= Matrix.of(context)
.client
.onSync
.stream
notificationChangeSub ??= Matrix.of(context).client.onSync.stream
.where(
(syncUpdate) =>
syncUpdate.accountData?.any(
@ -47,9 +44,7 @@ class ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
) ??
false,
)
.listen(
(u) => setState(() {}),
);
.listen((u) => setState(() {}));
return Stack(
alignment: Alignment.center,
children: [

View file

@ -53,9 +53,7 @@ class _ConfigViewerState extends State<ConfigViewer> {
return Scaffold(
appBar: AppBar(
title: const Text('Advanced configurations'),
leading: BackButton(
onPressed: () => context.go('/'),
),
leading: BackButton(onPressed: () => context.go('/')),
),
body: Column(
children: [
@ -65,9 +63,7 @@ class _ConfigViewerState extends State<ConfigViewer> {
color: theme.colorScheme.errorContainer,
child: Text(
'Changing configs by hand is untested! Use without any warranty!',
style: TextStyle(
color: theme.colorScheme.onErrorContainer,
),
style: TextStyle(color: theme.colorScheme.onErrorContainer),
),
),
Expanded(

View file

@ -21,10 +21,10 @@ class _FluffyChatErrorWidgetState extends State<FluffyChatErrorWidget> {
}
knownExceptions.add(widget.details.exception.toString());
WidgetsBinding.instance.addPostFrameCallback((_) {
ErrorReporter(context, 'Error Widget').onErrorCallback(
widget.details.exception,
widget.details.stack,
);
ErrorReporter(
context,
'Error Widget',
).onErrorCallback(widget.details.exception, widget.details.stack);
});
}

View file

@ -46,8 +46,11 @@ class FluffyChatApp extends StatelessWidget {
title: AppSettings.applicationName.value,
themeMode: themeMode,
theme: FluffyThemes.buildTheme(context, Brightness.light, primaryColor),
darkTheme:
FluffyThemes.buildTheme(context, Brightness.dark, primaryColor),
darkTheme: FluffyThemes.buildTheme(
context,
Brightness.dark,
primaryColor,
),
scrollBehavior: CustomScrollBehavior(),
localizationsDelegates: L10n.localizationsDelegates,
supportedLocales: L10n.supportedLocales,

View file

@ -54,10 +54,7 @@ Future<Result<T>> showFutureLoadingDialog<T>({
),
);
return result ??
Result.error(
Exception('FutureDialog canceled'),
StackTrace.current,
);
Result.error(Exception('FutureDialog canceled'), StackTrace.current);
}
class LoadingDialog<T> extends StatefulWidget {
@ -114,15 +111,13 @@ class LoadingDialogState<T> extends State<LoadingDialog> {
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
crossAxisAlignment: .center,
children: [
if (exception == null) ...[
StreamBuilder(
stream: widget.onProgressStream,
builder: (context, snapshot) =>
CircularProgressIndicator.adaptive(
value: snapshot.data,
),
CircularProgressIndicator.adaptive(value: snapshot.data),
),
const SizedBox(width: 20),
],
@ -141,12 +136,9 @@ class LoadingDialogState<T> extends State<LoadingDialog> {
? null
: [
AdaptiveDialogAction(
onPressed: () => Navigator.of(context).pop<Result<T>>(
Result.error(
exception,
stackTrace,
),
),
onPressed: () => Navigator.of(
context,
).pop<Result<T>>(Result.error(exception, stackTrace)),
child: Text(widget.backLabel ?? L10n.of(context).close),
),
],

View file

@ -71,7 +71,7 @@ class LoginScaffold extends StatelessWidget {
),
),
),
const _PrivacyButtons(mainAxisAlignment: MainAxisAlignment.center),
const _PrivacyButtons(mainAxisAlignment: .center),
],
),
);
@ -95,31 +95,19 @@ class _PrivacyButtons extends StatelessWidget {
children: [
TextButton(
onPressed: () => launchUrlString(AppConfig.website),
child: Text(
L10n.of(context).website,
style: shadowTextStyle,
),
child: Text(L10n.of(context).website, style: shadowTextStyle),
),
TextButton(
onPressed: () => launchUrlString(AppConfig.supportUrl),
child: Text(
L10n.of(context).help,
style: shadowTextStyle,
),
child: Text(L10n.of(context).help, style: shadowTextStyle),
),
TextButton(
onPressed: () => launchUrl(AppConfig.privacyUrl),
child: Text(
L10n.of(context).privacy,
style: shadowTextStyle,
),
child: Text(L10n.of(context).privacy, style: shadowTextStyle),
),
TextButton(
onPressed: () => PlatformInfos.showDialog(context),
child: Text(
L10n.of(context).about,
style: shadowTextStyle,
),
child: Text(L10n.of(context).about, style: shadowTextStyle),
),
],
),

View file

@ -35,11 +35,10 @@ class MaxWidthBody extends StatelessWidget {
),
child: Material(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
side: BorderSide(
color: theme.dividerColor,
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
),
side: BorderSide(color: theme.dividerColor),
),
clipBehavior: Clip.hardEdge,
child: Padding(

View file

@ -25,15 +25,8 @@ class TwoColumnLayout extends StatelessWidget {
width: FluffyThemes.columnWidth + FluffyThemes.navRailWidth,
child: mainView,
),
Container(
width: 1.0,
color: theme.dividerColor,
),
Expanded(
child: ClipRRect(
child: sideView,
),
),
Container(width: 1.0, color: theme.dividerColor),
Expanded(child: ClipRRect(child: sideView)),
],
),
),

View file

@ -33,11 +33,13 @@ extension LocalNotificationsExtension on MatrixState {
}
}
final title =
event.room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)));
final title = event.room.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)),
);
final body = await event.calcLocalizedBody(
MatrixLocals(L10n.of(context)),
withSenderNamePrefix: !event.room.isDirectChat ||
withSenderNamePrefix:
!event.room.isDirectChat ||
event.room.lastEvent?.senderId == client.userID,
plaintextBody: true,
hideReply: true,
@ -66,13 +68,13 @@ extension LocalNotificationsExtension on MatrixState {
Logs().d('Unable to pre-download avatar for web notification', e, s);
}
thumbnailUri =
await event.senderFromMemoryOrFallback.avatarUrl?.getThumbnailUri(
client,
width: size,
height: size,
method: thumbnailMethod,
);
thumbnailUri = await event.senderFromMemoryOrFallback.avatarUrl
?.getThumbnailUri(
client,
width: size,
height: size,
method: thumbnailMethod,
);
}
_audioPlayer.play();
@ -133,8 +135,9 @@ extension LocalNotificationsExtension on MatrixState {
hints: hints,
);
notification.action.then((actionStr) {
var action = DesktopNotificationActions.values
.singleWhereOrNull((a) => a.name == actionStr);
var action = DesktopNotificationActions.values.singleWhereOrNull(
(a) => a.name == actionStr,
);
if (action == null && actionStr == "default") {
action = DesktopNotificationActions.openChat;
}

View file

@ -79,10 +79,7 @@ class _LockScreenState extends State<LockScreen> {
shrinkWrap: true,
children: [
Center(
child: Image.asset(
'assets/info-logo.png',
width: 256,
),
child: Image.asset('assets/info-logo.png', width: 256),
),
TextField(
controller: _textEditingController,
@ -95,9 +92,7 @@ class _LockScreenState extends State<LockScreen> {
onChanged: tryUnlock,
onSubmitted: tryUnlock,
style: const TextStyle(fontSize: 40),
inputFormatters: [
LengthLimitingTextInputFormatter(4),
],
inputFormatters: [LengthLimitingTextInputFormatter(4)],
decoration: InputDecoration(
errorText: _errorText,
hintText: '****',

View file

@ -15,17 +15,14 @@ class LogViewerState extends State<LogViewer> {
double fontSize = 14;
@override
Widget build(BuildContext context) {
final outputEvents = Logs()
.outputEvents
final outputEvents = Logs().outputEvents
.where((e) => e.level.index <= logLevel.index)
.toList();
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: Text(logLevel.toString()),
leading: BackButton(
onPressed: () => context.go('/'),
),
leading: BackButton(onPressed: () => context.go('/')),
actions: [
IconButton(
icon: const Icon(Icons.zoom_in_outlined),
@ -55,9 +52,7 @@ class LogViewerState extends State<LogViewer> {
scrollDirection: Axis.horizontal,
child: SelectableText(
outputEvents[i].toDisplayString(),
style: TextStyle(
color: outputEvents[i].color,
),
style: TextStyle(color: outputEvents[i].color),
),
),
),

View file

@ -120,10 +120,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
}
resBundles[bundle.name] ??= [];
resBundles[bundle.name]!.add(
_AccountBundleWithClient(
client: widget.clients[i],
bundle: bundle,
),
_AccountBundleWithClient(client: widget.clients[i], bundle: bundle),
);
}
}
@ -132,12 +129,13 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
(a, b) => a.bundle!.priority == null
? 1
: b.bundle!.priority == null
? -1
: a.bundle!.priority!.compareTo(b.bundle!.priority!),
? -1
: a.bundle!.priority!.compareTo(b.bundle!.priority!),
);
}
return resBundles
.map((k, v) => MapEntry(k, v.map((vv) => vv.client).toList()));
return resBundles.map(
(k, v) => MapEntry(k, v.map((vv) => vv.client).toList()),
);
}
bool get hasComplexBundles => accountBundles.values.any((v) => v.length > 1);
@ -151,27 +149,26 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
if (widget.clients.isNotEmpty && !client.isLogged()) {
return client;
}
final candidate =
_loginClientCandidate ??= await ClientManager.createClient(
'${AppSettings.applicationName.value}-${DateTime.now().millisecondsSinceEpoch}',
store,
)
..onLoginStateChanged
.stream
final candidate = _loginClientCandidate ??=
await ClientManager.createClient(
'${AppSettings.applicationName.value}-${DateTime.now().millisecondsSinceEpoch}',
store,
)
..onLoginStateChanged.stream
.where((l) => l == LoginState.loggedIn)
.first
.then((_) {
if (!widget.clients.contains(_loginClientCandidate)) {
widget.clients.add(_loginClientCandidate!);
}
ClientManager.addClientNameToStore(
_loginClientCandidate!.clientName,
store,
);
_registerSubs(_loginClientCandidate!.clientName);
_loginClientCandidate = null;
FluffyChatApp.router.go('/backup');
});
if (!widget.clients.contains(_loginClientCandidate)) {
widget.clients.add(_loginClientCandidate!);
}
ClientManager.addClientNameToStore(
_loginClientCandidate!.clientName,
store,
);
_registerSubs(_loginClientCandidate!.clientName);
_loginClientCandidate = null;
FluffyChatApp.router.go('/backup');
});
if (widget.clients.isEmpty) widget.clients.add(candidate);
return candidate;
}
@ -210,8 +207,9 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
return route.split('/')[2];
}
final linuxNotifications =
PlatformInfos.isLinux ? NotificationsClient() : null;
final linuxNotifications = PlatformInfos.isLinux
? NotificationsClient()
: null;
final Map<String, int> linuxNotificationIds = {};
@override
@ -229,8 +227,9 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
);
return;
}
onRoomKeyRequestSub[name] ??=
c.onRoomKeyRequest.stream.listen((RoomKeyRequest request) async {
onRoomKeyRequestSub[name] ??= c.onRoomKeyRequest.stream.listen((
RoomKeyRequest request,
) async {
if (widget.clients.any(
((cl) =>
cl.userID == request.requestingDevice.userId &&
@ -244,22 +243,24 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
});
onKeyVerificationRequestSub[name] ??= c.onKeyVerificationRequest.stream
.listen((KeyVerification request) async {
var hidPopup = false;
request.onUpdate = () {
if (!hidPopup &&
{KeyVerificationState.done, KeyVerificationState.error}
.contains(request.state)) {
FluffyChatApp.router.pop('dialog');
}
hidPopup = true;
};
request.onUpdate = null;
hidPopup = true;
await KeyVerificationDialog(request: request).show(
FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ??
context,
);
});
var hidPopup = false;
request.onUpdate = () {
if (!hidPopup &&
{
KeyVerificationState.done,
KeyVerificationState.error,
}.contains(request.state)) {
FluffyChatApp.router.pop('dialog');
}
hidPopup = true;
};
request.onUpdate = null;
hidPopup = true;
await KeyVerificationDialog(request: request).show(
FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ??
context,
);
});
onLoginStateChanged[name] ??= c.onLoginStateChanged.stream.listen((state) {
final loggedInWithMultipleClients = widget.clients.length > 1;
if (state == LoginState.loggedOut) {
@ -273,25 +274,25 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ??
context,
).showSnackBar(
SnackBar(
content: Text(L10n.of(context).oneClientLoggedOut),
),
SnackBar(content: Text(L10n.of(context).oneClientLoggedOut)),
);
if (state != LoginState.loggedIn) {
FluffyChatApp.router.go('/rooms');
}
} else {
FluffyChatApp.router
.go(state == LoginState.loggedIn ? '/backup' : '/home');
FluffyChatApp.router.go(
state == LoginState.loggedIn ? '/backup' : '/home',
);
}
});
onUiaRequest[name] ??= c.onUiaRequest.stream.listen(uiaRequestHandler);
if (PlatformInfos.isWeb || PlatformInfos.isLinux) {
c.onSync.stream.first.then((s) {
html.Notification.requestPermission();
onNotification[name] ??=
c.onNotification.stream.listen(showLocalNotification);
onNotification[name] ??= c.onNotification.stream.listen(
showLocalNotification,
);
});
}
}
@ -322,13 +323,18 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
this,
onFcmError: (errorMsg, {Uri? link}) async {
final result = await showOkCancelAlertDialog(
context: FluffyChatApp
.router.routerDelegate.navigatorKey.currentContext ??
context:
FluffyChatApp
.router
.routerDelegate
.navigatorKey
.currentContext ??
context,
title: L10n.of(context).pushNotificationsNotAvailable,
message: errorMsg,
okLabel:
link == null ? L10n.of(context).ok : L10n.of(context).learnMore,
okLabel: link == null
? L10n.of(context).ok
: L10n.of(context).learnMore,
cancelLabel: L10n.of(context).doNotShowAgain,
);
if (result == OkCancelResult.ok && link != null) {
@ -357,11 +363,13 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final foreground = state != AppLifecycleState.inactive &&
final foreground =
state != AppLifecycleState.inactive &&
state != AppLifecycleState.paused;
for (final client in widget.clients) {
client.syncPresence =
state == AppLifecycleState.resumed ? null : PresenceType.unavailable;
client.syncPresence = state == AppLifecycleState.resumed
? null
: PresenceType.unavailable;
if (PlatformInfos.isMobile) {
client.backgroundSync = foreground;
client.requestHistoryOnLimitedTimeline = !foreground;
@ -389,10 +397,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
return Provider(
create: (_) => this,
child: widget.child,
);
return Provider(create: (_) => this, child: widget.child);
}
Future<void> dehydrateAction(BuildContext context) async {
@ -412,9 +417,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
final export = result.result;
if (export == null) return;
final exportBytes = Uint8List.fromList(
const Utf8Codec().encode(export),
);
final exportBytes = Uint8List.fromList(const Utf8Codec().encode(export));
final exportFileName =
'fluffychat-export-${DateFormat(DateFormat.YEAR_MONTH_DAY).format(DateTime.now())}.fluffybackup';

View file

@ -50,8 +50,8 @@ void showMemberActionsPopupMenu({
presenceBackgroundColor: theme.colorScheme.surfaceContainer,
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: .min,
crossAxisAlignment: .start,
children: [
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 128),
@ -109,16 +109,16 @@ void showMemberActionsPopupMenu({
const Icon(Icons.admin_panel_settings_outlined),
const SizedBox(width: 18),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: .min,
crossAxisAlignment: .start,
children: [
Text(L10n.of(context).chatPermissions),
Text(
user.powerLevel < 50
? L10n.of(context).userLevel(user.powerLevel)
: user.powerLevel < 100
? L10n.of(context).moderatorLevel(user.powerLevel)
: L10n.of(context).adminLevel(user.powerLevel),
? L10n.of(context).moderatorLevel(user.powerLevel)
: L10n.of(context).adminLevel(user.powerLevel),
style: const TextStyle(fontSize: 10),
),
],
@ -267,10 +267,7 @@ void showMemberActionsPopupMenu({
final result = await showFutureLoadingDialog(
context: context,
future: () => user.room.client.reportUser(
user.id,
reason,
),
future: () => user.room.client.reportUser(user.id, reason),
);
if (result.error != null) return;
ScaffoldMessenger.of(context).showSnackBar(

View file

@ -27,24 +27,20 @@ class SpacesNavigationRail extends StatelessWidget {
@override
Widget build(BuildContext context) {
final client = Matrix.of(context).client;
final isSettings = GoRouter.of(context)
.routeInformationProvider
.value
.uri
.path
.startsWith('/rooms/settings');
final isSettings = GoRouter.of(
context,
).routeInformationProvider.value.uri.path.startsWith('/rooms/settings');
return Material(
child: SafeArea(
child: StreamBuilder(
key: ValueKey(
client.userID.toString(),
),
key: ValueKey(client.userID.toString()),
stream: client.onSync.stream
.where((s) => s.hasRoomUpdate)
.rateLimit(const Duration(seconds: 1)),
builder: (context, _) {
final allSpaces =
client.rooms.where((room) => room.isSpace).toList();
final allSpaces = client.rooms
.where((room) => room.isSpace)
.toList();
return SizedBox(
width: FluffyThemes.isColumnMode(context)
@ -86,12 +82,13 @@ class SpacesNavigationRail extends StatelessWidget {
);
}
final space = allSpaces[i];
final displayname =
allSpaces[i].getLocalizedDisplayname(
MatrixLocals(L10n.of(context)),
);
final spaceChildrenIds =
space.spaceChildren.map((c) => c.roomId).toSet();
final displayname = allSpaces[i]
.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)),
);
final spaceChildrenIds = space.spaceChildren
.map((c) => c.roomId)
.toSet();
return NaviRailItem(
toolTip: displayname,
isSelected: activeSpaceId == space.id,

View file

@ -18,8 +18,8 @@ Future<int?> showPermissionChooser(
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: .min,
crossAxisAlignment: .stretch,
spacing: 12.0,
children: [
Text(L10n.of(context).setPermissionsLevelDescription),

View file

@ -13,10 +13,7 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dar
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import '../config/themes.dart';
Future<void> showQrCodeViewer(
BuildContext context,
String content,
) =>
Future<void> showQrCodeViewer(BuildContext context, String content) =>
showDialog(
context: context,
builder: (context) => QrCodeViewer(content: content),
@ -32,11 +29,7 @@ class QrCodeViewer extends StatelessWidget {
context: context,
future: () async {
final inviteLink = 'https://matrix.to/#/$content';
final image = QRImage(
inviteLink,
size: 256,
radius: 1,
).generate();
final image = QRImage(inviteLink, size: 256, radius: 1).generate();
return compute(encodePng, image);
},
);
@ -76,10 +69,7 @@ class QrCodeViewer extends StatelessWidget {
backgroundColor: Colors.black.withAlpha(128),
),
icon: Icon(Icons.adaptive.share_outlined),
onPressed: () => FluffyShare.share(
inviteLink,
context,
),
onPressed: () => FluffyShare.share(inviteLink, context),
color: Colors.white,
tooltip: L10n.of(context).share,
),
@ -105,11 +95,12 @@ class QrCodeViewer extends StatelessWidget {
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisSize: .min,
children: [
ConstrainedBox(
constraints:
const BoxConstraints(maxWidth: FluffyThemes.columnWidth),
constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth,
),
child: PrettyQrView.data(
data: inviteLink,
decoration: PrettyQrDecoration(

View file

@ -64,9 +64,7 @@ class _ShareScaffoldDialogState extends State<ShareScaffoldDialog> {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final rooms = Matrix.of(context)
.client
.rooms
final rooms = Matrix.of(context).client.rooms
.where(
(room) =>
room.canSendDefaultMessages &&
@ -138,8 +136,9 @@ class _ShareScaffoldDialogState extends State<ShareScaffoldDialog> {
),
controlAffinity: ListTileControlAffinity.trailing,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
),
),
secondary: Avatar(
mxContent: room.avatar,

View file

@ -12,7 +12,8 @@ class ThemeBuilder extends StatefulWidget {
BuildContext context,
ThemeMode themeMode,
Color? primaryColor,
) builder;
)
builder;
final String themeModeSettingsKey;
final String primaryColorSettingsKey;
@ -38,28 +39,26 @@ class ThemeController extends State<ThemeBuilder> {
Color? get primaryColor => _primaryColor;
static ThemeController of(BuildContext context) =>
Provider.of<ThemeController>(
context,
listen: false,
);
Provider.of<ThemeController>(context, listen: false);
void _loadData(dynamic _) async {
final preferences =
_sharedPreferences ??= await SharedPreferences.getInstance();
final preferences = _sharedPreferences ??=
await SharedPreferences.getInstance();
final rawThemeMode = preferences.getString(widget.themeModeSettingsKey);
final rawColor = preferences.getInt(widget.primaryColorSettingsKey);
setState(() {
_themeMode = ThemeMode.values
.singleWhereOrNull((value) => value.name == rawThemeMode);
_themeMode = ThemeMode.values.singleWhereOrNull(
(value) => value.name == rawThemeMode,
);
_primaryColor = rawColor == null ? null : Color(rawColor);
});
}
Future<void> setThemeMode(ThemeMode newThemeMode) async {
final preferences =
_sharedPreferences ??= await SharedPreferences.getInstance();
final preferences = _sharedPreferences ??=
await SharedPreferences.getInstance();
await preferences.setString(widget.themeModeSettingsKey, newThemeMode.name);
setState(() {
_themeMode = newThemeMode;
@ -67,8 +66,8 @@ class ThemeController extends State<ThemeBuilder> {
}
Future<void> setPrimaryColor(Color? newPrimaryColor) async {
final preferences =
_sharedPreferences ??= await SharedPreferences.getInstance();
final preferences = _sharedPreferences ??=
await SharedPreferences.getInstance();
if (newPrimaryColor == null) {
await preferences.remove(widget.primaryColorSettingsKey);
} else {
@ -93,11 +92,8 @@ class ThemeController extends State<ThemeBuilder> {
return Provider(
create: (_) => this,
child: DynamicColorBuilder(
builder: (light, _) => widget.builder(
context,
themeMode,
primaryColor ?? light?.primary,
),
builder: (light, _) =>
widget.builder(context, themeMode, primaryColor ?? light?.primary),
),
);
}

View file

@ -21,9 +21,7 @@ class UnreadRoomsBadge extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
final unreadCount = Matrix.of(context)
.client
.rooms
final unreadCount = Matrix.of(context).client.rooms
.where(filter)
.where((r) => (r.isUnread || r.membership == Membership.invite))
.length;
@ -31,17 +29,11 @@ class UnreadRoomsBadge extends StatelessWidget {
badgeStyle: b.BadgeStyle(
badgeColor: theme.colorScheme.primary,
elevation: 4,
borderSide: BorderSide(
color: theme.colorScheme.surface,
width: 2,
),
borderSide: BorderSide(color: theme.colorScheme.surface, width: 2),
),
badgeContent: Text(
unreadCount.toString(),
style: TextStyle(
color: theme.colorScheme.onPrimary,
fontSize: 12,
),
style: TextStyle(color: theme.colorScheme.onPrimary, fontSize: 12),
),
showBadge: unreadCount != 0,
badgeAnimation: const b.BadgeAnimation.scale(),