💄 Optimize message flashing
This commit is contained in:
@@ -25,7 +25,7 @@ class MessageContent extends StatelessWidget {
|
||||
children: [
|
||||
Icon(
|
||||
Symbols.delete,
|
||||
size: 14,
|
||||
size: 16,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
||||
@@ -34,6 +34,7 @@ class MessageContent extends StatelessWidget {
|
||||
Text(
|
||||
item.content ?? 'Deleted a message',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontSize: 13,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
||||
@@ -59,7 +60,7 @@ class MessageContent extends StatelessWidget {
|
||||
children: [
|
||||
Icon(
|
||||
Symbols.edit,
|
||||
size: 14,
|
||||
size: 16,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
||||
@@ -71,7 +72,7 @@ class MessageContent extends StatelessWidget {
|
||||
newText: item.content ?? 'Edited a message',
|
||||
defaultTextStyle: Theme.of(
|
||||
context,
|
||||
).textTheme.bodySmall!.copyWith(
|
||||
).textTheme.bodyMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
addedTextStyle: TextStyle(
|
||||
|
@@ -54,6 +54,8 @@ class MessageItem extends HookConsumerWidget {
|
||||
required this.onJump,
|
||||
});
|
||||
|
||||
static const kFlashDuration = 300;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final remoteMessage = message.toRemoteMessage();
|
||||
@@ -119,8 +121,62 @@ class MessageItem extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
final flashing = ref.watch(
|
||||
flashingMessagesProvider.select((set) => set.contains(message.id)),
|
||||
);
|
||||
|
||||
final isFlashing = useState(false);
|
||||
final flashTimer = useState<Timer?>(null);
|
||||
|
||||
useEffect(() {
|
||||
if (flashing) {
|
||||
if (flashTimer.value != null) return null;
|
||||
isFlashing.value = true;
|
||||
flashTimer.value = Timer.periodic(
|
||||
const Duration(milliseconds: kFlashDuration),
|
||||
(timer) {
|
||||
isFlashing.value = !isFlashing.value;
|
||||
if (timer.tick >= 6) {
|
||||
// 6 ticks: 1, 0, 1, 0, 1, 0
|
||||
timer.cancel();
|
||||
flashTimer.value = null;
|
||||
isFlashing.value = false;
|
||||
ref
|
||||
.read(flashingMessagesProvider.notifier)
|
||||
.update((set) => set.difference({message.id}));
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
flashTimer.value?.cancel();
|
||||
flashTimer.value = null;
|
||||
isFlashing.value = false;
|
||||
}
|
||||
return () {
|
||||
flashTimer.value?.cancel();
|
||||
};
|
||||
}, [flashing]);
|
||||
|
||||
final flashColor =
|
||||
isFlashing.value
|
||||
? Theme.of(context).colorScheme.primaryContainer.withOpacity(0.8)
|
||||
: Colors.transparent;
|
||||
|
||||
return InkWell(
|
||||
onLongPress: showActionMenu,
|
||||
onSecondaryTap: showActionMenu,
|
||||
onTap: () {
|
||||
// Jump to related message
|
||||
if (['messages.update', 'messages.delete'].contains(message.type) &&
|
||||
message.meta['message_id'] is String &&
|
||||
message.meta['message_id'] != null) {
|
||||
onJump(message.meta['message_id']);
|
||||
}
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
curve: Curves.easeInOut,
|
||||
duration: const Duration(milliseconds: kFlashDuration),
|
||||
decoration: BoxDecoration(color: flashColor),
|
||||
child: switch (settings.messageDisplayStyle) {
|
||||
'irc' => MessageItemDisplayIRC(
|
||||
message: message,
|
||||
@@ -150,6 +206,7 @@ class MessageItem extends HookConsumerWidget {
|
||||
translating: translating.value,
|
||||
),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -286,54 +343,10 @@ class MessageItemDisplayBubble extends HookConsumerWidget {
|
||||
isCurrentUser
|
||||
? Theme.of(context).colorScheme.onPrimaryContainer
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant;
|
||||
final containerColor =
|
||||
isCurrentUser
|
||||
? Theme.of(context).colorScheme.primaryContainer.withOpacity(0.5)
|
||||
: Theme.of(context).colorScheme.surfaceContainer;
|
||||
|
||||
final hasBackground =
|
||||
ref.watch(backgroundImageFileProvider).valueOrNull != null;
|
||||
|
||||
final flashing = ref.watch(
|
||||
flashingMessagesProvider.select((set) => set.contains(message.id)),
|
||||
);
|
||||
|
||||
final isFlashing = useState(false);
|
||||
final flashTimer = useState<Timer?>(null);
|
||||
|
||||
useEffect(() {
|
||||
if (flashing) {
|
||||
if (flashTimer.value != null) return null;
|
||||
isFlashing.value = true;
|
||||
flashTimer.value = Timer.periodic(const Duration(milliseconds: 200), (
|
||||
timer,
|
||||
) {
|
||||
isFlashing.value = !isFlashing.value;
|
||||
if (timer.tick >= 4) {
|
||||
// 4 ticks: true, false, true, false
|
||||
timer.cancel();
|
||||
flashTimer.value = null;
|
||||
isFlashing.value = false;
|
||||
ref
|
||||
.read(flashingMessagesProvider.notifier)
|
||||
.update((set) => set.difference({message.id}));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
flashTimer.value?.cancel();
|
||||
flashTimer.value = null;
|
||||
isFlashing.value = false;
|
||||
}
|
||||
return () {
|
||||
flashTimer.value?.cancel();
|
||||
};
|
||||
}, [flashing]);
|
||||
|
||||
final flashColor =
|
||||
isFlashing.value
|
||||
? Theme.of(context).colorScheme.primary.withOpacity(0.8)
|
||||
: containerColor;
|
||||
|
||||
final remoteMessage = message.toRemoteMessage();
|
||||
final sender = remoteMessage.sender;
|
||||
|
||||
@@ -364,16 +377,6 @@ class MessageItemDisplayBubble extends HookConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Flexible(
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
decoration: BoxDecoration(
|
||||
color: flashColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -468,7 +471,6 @@ class MessageItemDisplayBubble extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
MessageIndicators(
|
||||
editedAt: remoteMessage.editedAt,
|
||||
status: message.status,
|
||||
@@ -510,25 +512,38 @@ class MessageItemDisplayIRC extends HookConsumerWidget {
|
||||
final sender = remoteMessage.sender;
|
||||
final textColor = Theme.of(context).colorScheme.onSurfaceVariant;
|
||||
|
||||
final isMultiline =
|
||||
message.type == 'text' ||
|
||||
message.repliedMessageId != null ||
|
||||
message.forwardedMessageId != null;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 2),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
isMultiline ? CrossAxisAlignment.start : CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
DateFormat('HH:mm').format(message.createdAt),
|
||||
style: TextStyle(color: textColor.withOpacity(0.7), fontSize: 12),
|
||||
).padding(top: 2),
|
||||
).padding(top: isMultiline ? 2 : 0),
|
||||
AccountPfcGestureDetector(
|
||||
uname: sender.account.name,
|
||||
child: ProfilePictureWidget(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
ProfilePictureWidget(
|
||||
file: sender.account.profile.picture,
|
||||
radius: 8,
|
||||
).padding(horizontal: 6, top: 2),
|
||||
),
|
||||
).padding(horizontal: 6, top: isMultiline ? 2 : 0),
|
||||
Text(
|
||||
sender.account.nick,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Gap(8),
|
||||
Expanded(
|
||||
|
Reference in New Issue
Block a user