💄 Optimize message flashing
This commit is contained in:
@@ -25,7 +25,7 @@ class MessageContent extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Symbols.delete,
|
Symbols.delete,
|
||||||
size: 14,
|
size: 16,
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
context,
|
context,
|
||||||
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
||||||
@@ -34,6 +34,7 @@ class MessageContent extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
item.content ?? 'Deleted a message',
|
item.content ?? 'Deleted a message',
|
||||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
fontSize: 13,
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
context,
|
context,
|
||||||
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
||||||
@@ -59,7 +60,7 @@ class MessageContent extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Symbols.edit,
|
Symbols.edit,
|
||||||
size: 14,
|
size: 16,
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
context,
|
context,
|
||||||
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
||||||
@@ -71,7 +72,7 @@ class MessageContent extends StatelessWidget {
|
|||||||
newText: item.content ?? 'Edited a message',
|
newText: item.content ?? 'Edited a message',
|
||||||
defaultTextStyle: Theme.of(
|
defaultTextStyle: Theme.of(
|
||||||
context,
|
context,
|
||||||
).textTheme.bodySmall!.copyWith(
|
).textTheme.bodyMedium!.copyWith(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
addedTextStyle: TextStyle(
|
addedTextStyle: TextStyle(
|
||||||
|
@@ -54,6 +54,8 @@ class MessageItem extends HookConsumerWidget {
|
|||||||
required this.onJump,
|
required this.onJump,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static const kFlashDuration = 300;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final remoteMessage = message.toRemoteMessage();
|
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(
|
return InkWell(
|
||||||
onLongPress: showActionMenu,
|
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) {
|
child: switch (settings.messageDisplayStyle) {
|
||||||
'irc' => MessageItemDisplayIRC(
|
'irc' => MessageItemDisplayIRC(
|
||||||
message: message,
|
message: message,
|
||||||
@@ -150,6 +206,7 @@ class MessageItem extends HookConsumerWidget {
|
|||||||
translating: translating.value,
|
translating: translating.value,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -286,54 +343,10 @@ class MessageItemDisplayBubble extends HookConsumerWidget {
|
|||||||
isCurrentUser
|
isCurrentUser
|
||||||
? Theme.of(context).colorScheme.onPrimaryContainer
|
? Theme.of(context).colorScheme.onPrimaryContainer
|
||||||
: Theme.of(context).colorScheme.onSurfaceVariant;
|
: Theme.of(context).colorScheme.onSurfaceVariant;
|
||||||
final containerColor =
|
|
||||||
isCurrentUser
|
|
||||||
? Theme.of(context).colorScheme.primaryContainer.withOpacity(0.5)
|
|
||||||
: Theme.of(context).colorScheme.surfaceContainer;
|
|
||||||
|
|
||||||
final hasBackground =
|
final hasBackground =
|
||||||
ref.watch(backgroundImageFileProvider).valueOrNull != null;
|
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 remoteMessage = message.toRemoteMessage();
|
||||||
final sender = remoteMessage.sender;
|
final sender = remoteMessage.sender;
|
||||||
|
|
||||||
@@ -364,16 +377,6 @@ class MessageItemDisplayBubble extends HookConsumerWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
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(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -468,7 +471,6 @@ class MessageItemDisplayBubble extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
MessageIndicators(
|
MessageIndicators(
|
||||||
editedAt: remoteMessage.editedAt,
|
editedAt: remoteMessage.editedAt,
|
||||||
status: message.status,
|
status: message.status,
|
||||||
@@ -510,25 +512,38 @@ class MessageItemDisplayIRC extends HookConsumerWidget {
|
|||||||
final sender = remoteMessage.sender;
|
final sender = remoteMessage.sender;
|
||||||
final textColor = Theme.of(context).colorScheme.onSurfaceVariant;
|
final textColor = Theme.of(context).colorScheme.onSurfaceVariant;
|
||||||
|
|
||||||
|
final isMultiline =
|
||||||
|
message.type == 'text' ||
|
||||||
|
message.repliedMessageId != null ||
|
||||||
|
message.forwardedMessageId != null;
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 2),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment:
|
||||||
|
isMultiline ? CrossAxisAlignment.start : CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
DateFormat('HH:mm').format(message.createdAt),
|
DateFormat('HH:mm').format(message.createdAt),
|
||||||
style: TextStyle(color: textColor.withOpacity(0.7), fontSize: 12),
|
style: TextStyle(color: textColor.withOpacity(0.7), fontSize: 12),
|
||||||
).padding(top: 2),
|
).padding(top: isMultiline ? 2 : 0),
|
||||||
AccountPfcGestureDetector(
|
AccountPfcGestureDetector(
|
||||||
uname: sender.account.name,
|
uname: sender.account.name,
|
||||||
child: ProfilePictureWidget(
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ProfilePictureWidget(
|
||||||
file: sender.account.profile.picture,
|
file: sender.account.profile.picture,
|
||||||
radius: 8,
|
radius: 8,
|
||||||
).padding(horizontal: 6, top: 2),
|
).padding(horizontal: 6, top: isMultiline ? 2 : 0),
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
sender.account.nick,
|
sender.account.nick,
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.primary),
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
Reference in New Issue
Block a user