moxxy/lib/ui/widgets/conversation.dart

194 lines
5.9 KiB
Dart

import 'dart:async';
import 'package:badges/badges.dart';
import 'package:flutter/material.dart';
import 'package:moxxyv2/i18n/strings.g.dart';
import 'package:moxxyv2/shared/constants.dart';
import 'package:moxxyv2/shared/helpers.dart';
import 'package:moxxyv2/ui/constants.dart';
import 'package:moxxyv2/ui/widgets/avatar.dart';
import 'package:moxxyv2/ui/widgets/chat/typing.dart';
class ConversationsListRow extends StatefulWidget {
const ConversationsListRow(
this.avatarUrl,
this.name,
this.lastMessageBody,
this.unreadCount,
this.maxTextWidth,
this.lastChangeTimestamp,
this.update, {
this.showLock = false,
this.typingIndicator = false,
this.extra,
this.lastMessageRetracted = false,
super.key,
}
);
final String avatarUrl;
final String name;
final bool lastMessageRetracted;
final String lastMessageBody;
final int unreadCount;
final double maxTextWidth;
final int lastChangeTimestamp;
final bool update; // Should a timer run to update the timestamp
final bool typingIndicator;
final bool showLock;
final Widget? extra;
@override
ConversationsListRowState createState() => ConversationsListRowState();
}
class ConversationsListRowState extends State<ConversationsListRow> {
late String _timestampString;
late Timer? _updateTimer;
@override
void initState() {
super.initState();
final _now = DateTime.now().millisecondsSinceEpoch;
_timestampString = formatConversationTimestamp(
widget.lastChangeTimestamp,
_now,
);
// NOTE: We could also check and run the timer hourly, but who has a messenger on the
// conversation screen open for hours on end?
if (widget.update && widget.lastChangeTimestamp > -1 && _now - widget.lastChangeTimestamp >= 60 * Duration.millisecondsPerMinute) {
_updateTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
final now = DateTime.now().millisecondsSinceEpoch;
setState(() {
_timestampString = formatConversationTimestamp(
widget.lastChangeTimestamp,
now,
);
});
if (now - widget.lastChangeTimestamp >= 60 * Duration.millisecondsPerMinute) {
_updateTimer!.cancel();
_updateTimer = null;
}
});
} else {
_updateTimer = null;
}
}
@override
void dispose() {
if (_updateTimer != null) {
_updateTimer!.cancel();
}
super.dispose();
}
Widget _buildLastMessageBody() {
if (widget.typingIndicator) {
return const TypingIndicatorWidget(Colors.black, Colors.white);
}
return Text(
widget.lastMessageRetracted ?
t.messages.retracted :
widget.lastMessageBody,
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
}
@override
Widget build(BuildContext context) {
final badgeText = widget.unreadCount > 99 ? '99+' : widget.unreadCount.toString();
// TODO(Unknown): Maybe turn this into an attribute of the widget to prevent calling this
// for every conversation
final screenWidth = MediaQuery.of(context).size.width;
final width = screenWidth - 24 - 70;
final textWidth = screenWidth * 0.6;
final showTimestamp = widget.lastChangeTimestamp != timestampNever;
final showBadge = widget.unreadCount > 0;
return Padding(
padding: const EdgeInsets.all(8),
child: Row(
children: [
AvatarWrapper(
radius: 35,
avatarUrl: widget.avatarUrl,
altText: widget.name,
),
Padding(
padding: const EdgeInsets.only(left: 8),
child: LimitedBox(
maxWidth: width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget.name,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 17),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Visibility(
visible: widget.showLock,
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 6),
child: Icon(
Icons.lock,
size: 17,
),
),
),
Visibility(
visible: showTimestamp,
child: const Spacer(),
),
Visibility(
visible: showTimestamp,
child: Text(_timestampString),
),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
LimitedBox(
maxWidth: textWidth,
child: _buildLastMessageBody(),
),
Visibility(
visible: showBadge,
child: const Spacer(),
),
Visibility(
visible: widget.unreadCount > 0,
child: Badge(
badgeContent: Text(badgeText),
badgeColor: bubbleColorSent,
),
),
],
),
],
),
),
),
Visibility(
visible: widget.extra != null,
child: const Spacer(),
),
...widget.extra != null ? [widget.extra!] : [],
],
),
);
}
}