moxxy/lib/ui/widgets/conversation.dart

193 lines
5.2 KiB
Dart

import "dart:async";
import "package:moxxyv2/ui/constants.dart";
import "package:moxxyv2/ui/widgets/avatar.dart";
import "package:moxxyv2/ui/widgets/chat/typing.dart";
import "package:moxxyv2/shared/helpers.dart";
import "package:moxxyv2/shared/constants.dart";
import "package:flutter/material.dart";
import "package:badges/badges.dart";
class ConversationsListRow extends StatefulWidget {
final String avatarUrl;
final String name;
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;
const ConversationsListRow(
this.avatarUrl,
this.name,
this.lastMessageBody,
this.unreadCount,
this.maxTextWidth,
this.lastChangeTimestamp,
this.update, {
this.typingIndicator = false,
Key? key
}
) : super(key: key);
@override
_ConversationsListRowState createState() => _ConversationsListRowState(
avatarUrl,
name,
lastMessageBody,
unreadCount,
maxTextWidth,
lastChangeTimestamp,
update,
typingIndicator: typingIndicator
);
}
class _ConversationsListRowState extends State<ConversationsListRow> {
final String avatarUrl;
final String name;
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;
late String _timestampString;
late Timer? _updateTimer;
_ConversationsListRowState(
this.avatarUrl,
this.name,
this.lastMessageBody,
this.unreadCount,
this.maxTextWidth,
this.lastChangeTimestamp,
this.update, {
this.typingIndicator = false
}
) {
final _now = DateTime.now().millisecondsSinceEpoch;
_timestampString = formatConversationTimestamp(
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 (update && lastChangeTimestamp > -1 && _now - lastChangeTimestamp >= 60 * Duration.millisecondsPerMinute) {
_updateTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
final now = DateTime.now().millisecondsSinceEpoch;
setState(() {
_timestampString = formatConversationTimestamp(
lastChangeTimestamp,
now
);
});
if (now - lastChangeTimestamp >= 60 * Duration.millisecondsPerMinute) {
_updateTimer!.cancel();
_updateTimer = null;
}
});
} else {
_updateTimer = null;
}
}
@override
void dispose() {
if (_updateTimer != null) {
_updateTimer!.cancel();
}
super.dispose();
}
Widget _buildLastMessageBody() {
if (typingIndicator) {
return TypingIndicatorWidget(Colors.black, Colors.white);
}
return Text(
lastMessageBody,
maxLines: 1,
overflow: TextOverflow.ellipsis
);
}
@override
Widget build(BuildContext context) {
String badgeText = unreadCount > 99 ? "99+" : unreadCount.toString();
return Stack(
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: AvatarWrapper(
radius: 35.0,
avatarUrl: avatarUrl,
// TODO: Make this consistent by moving this inside the AvatarWrapper widget
alt: Text(name[0] + name[1])
)
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
constraints: BoxConstraints(
maxWidth: maxTextWidth
),
child: Text(
name,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 17),
maxLines: 1,
overflow: TextOverflow.ellipsis
)
),
// TODO: Change color and font size
Container(
constraints: BoxConstraints(
maxWidth: maxTextWidth
),
// TODO: Colors
child: _buildLastMessageBody()
)
]
)
),
const Spacer(),
Visibility(
visible: unreadCount > 0,
child: Padding(
padding: const EdgeInsetsDirectional.only(end: 8.0),
child: Badge(
badgeContent: Text(badgeText),
badgeColor: bubbleColorSent
)
)
)
]
),
Visibility(
visible: lastChangeTimestamp != timestampNever,
child: Positioned(
top: 8,
right: 8,
child: Text(
_timestampString
)
)
)
]
);
}
}