Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions commet/lib/ui/atoms/room_panel.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'dart:async';

import 'package:commet/client/components/direct_messages/direct_message_component.dart';
import 'package:commet/client/components/typing_indicators/typing_indicator_component.dart';
import 'package:commet/client/components/user_presence/user_presence_component.dart';
import 'package:commet/client/member.dart';
import 'package:commet/client/room.dart';
import 'package:commet/ui/atoms/adaptive_context_menu.dart';
import 'package:commet/ui/atoms/room_panel_view.dart';
Expand All @@ -26,6 +28,7 @@ class _RoomPanelState extends State<RoomPanel> {
late List<StreamSubscription> subs;
String? directMessagePartner;
UserPresence? presence = null;
List<Member> typingUsers = [];

@override
void initState() {
Expand All @@ -35,6 +38,18 @@ class _RoomPanelState extends State<RoomPanel> {
var dm = widget.room.client.getComponent<DirectMessagesComponent>();
directMessagePartner = dm?.getDirectMessagePartnerId(widget.room);

var typing = widget.room.getComponent<TypingIndicatorComponent>();

if (typing != null) {
subs.add(typing.onTypingUsersUpdated.listen((_) {
setState(() {
typingUsers = typing.typingUsers;
});
}));

typingUsers = typing.typingUsers;
}

if (directMessagePartner != null) {
final presenceComponent =
widget.room.client.getComponent<UserPresenceComponent>();
Expand Down Expand Up @@ -108,6 +123,7 @@ class _RoomPanelState extends State<RoomPanel> {
: null,
body: widget.room.lastMessage?.plainTextBody,
notificationCount: widget.room.notificationCount,
typingMembers: typingUsers,
highlightNotificationCount:
widget.room.displayHighlightedNotificationCount,
),
Expand Down
16 changes: 14 additions & 2 deletions commet/lib/ui/atoms/room_panel_view.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'package:commet/client/components/user_presence/user_presence_component.dart';
import 'package:commet/client/member.dart';
import 'package:commet/ui/atoms/dot_indicator.dart';
import 'package:commet/ui/atoms/notification_badge.dart';
import 'package:commet/ui/atoms/shimmer_loading.dart';
import 'package:commet/ui/molecules/typing_indicators_widget.dart';
import 'package:commet/ui/molecules/user_panel.dart';
import 'package:flutter/material.dart';
import 'package:tiamat/tiamat.dart';
Expand Down Expand Up @@ -30,6 +32,7 @@ class RoomPanelView extends StatefulWidget {
this.showUserAvatar = false,
this.notificationCount = 0,
this.highlightNotificationCount = 0,
this.typingMembers,
super.key});
final ImageProvider? avatar;
final ImageProvider? userAvatar;
Expand All @@ -46,6 +49,7 @@ class RoomPanelView extends StatefulWidget {
final String? secondaryButtonLabel;
final Future<void> Function()? onSecondaryButtonPressed;
final bool showUserAvatar;
final List<Member>? typingMembers;
final bool loading;
final String? directMessagePartner;
final double random;
Expand Down Expand Up @@ -182,8 +186,16 @@ class _RoomPanelViewState extends State<RoomPanelView> {
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (widget.body != null)
Flexible(child: recentEvent())
if (widget.typingMembers?.isNotEmpty ==
true)
SizedBox(
height: 16,
child: TypingIndicatorAnimation()),
if (widget.typingMembers?.isNotEmpty !=
true &&
widget.body != null)
SizedBox(
height: 16, child: recentEvent()),
],
),
),
Expand Down
126 changes: 71 additions & 55 deletions commet/lib/ui/molecules/typing_indicators_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ class _TypingIndicatorsWidgetState extends State<TypingIndicatorsWidget> {

late List<Member> typingMembers;

late List<GlobalKey> blobKeys = [
GlobalKey(),
GlobalKey(),
GlobalKey(),
];

Timer? timer;

late String currentText = "";

String typingUsers(int howMany, String user1, String user2, String user3) =>
Expand All @@ -47,59 +39,25 @@ class _TypingIndicatorsWidgetState extends State<TypingIndicatorsWidget> {
typingMembers = widget.component.typingUsers;
if (typingMembers.isNotEmpty) {
currentText = getTypingText();
startTimer();
}
super.initState();
}

@override
void dispose() {
timer?.cancel();
sub?.cancel();
super.dispose();
}

int prevIndex = 0;

void onTimer(Timer timer) {
var r = Random().nextInt(3);

if (r == prevIndex) {
r += 1;
r = r % blobKeys.length;
}

prevIndex = r;

var key = blobKeys[r];

if (key.currentState == null) {
return;
}

var state = key.currentState! as __SingleTypingIndicatorBlobState;
state.controller.forward(from: 0);
}

void onTypingUsersUpdated(void event) {
setState(() {
typingMembers = widget.component.typingUsers;
if (typingMembers.isNotEmpty) {
currentText = getTypingText();
if (timer == null) {
startTimer();
}
} else {
timer?.cancel();
timer = null;
}
});
}

void startTimer() {
timer = Timer.periodic(const Duration(milliseconds: 250), onTimer);
}

@override
Widget build(BuildContext context) {
return SizedBox(
Expand All @@ -112,19 +70,7 @@ class _TypingIndicatorsWidgetState extends State<TypingIndicatorsWidget> {
child: Row(children: [
Padding(
padding: const EdgeInsets.fromLTRB(4, 0, 4, 0),
child: Row(
children: [
_SingleTypingIndicatorBlob(
key: blobKeys[0],
),
_SingleTypingIndicatorBlob(
key: blobKeys[1],
),
_SingleTypingIndicatorBlob(
key: blobKeys[2],
),
],
),
child: TypingIndicatorAnimation(),
),
tiamat.Text.labelLow(currentText)
])),
Expand Down Expand Up @@ -196,3 +142,73 @@ class __SingleTypingIndicatorBlobState extends State<_SingleTypingIndicatorBlob>
);
}
}

class TypingIndicatorAnimation extends StatefulWidget {
const TypingIndicatorAnimation({super.key});

@override
State<TypingIndicatorAnimation> createState() =>
_TypingIndicatorAnimationState();
}

class _TypingIndicatorAnimationState extends State<TypingIndicatorAnimation> {
late List<GlobalKey> blobKeys = [
GlobalKey(),
GlobalKey(),
GlobalKey(),
];

Timer? timer;
int prevIndex = 0;

@override
void initState() {
super.initState();
startTimer();
}

@override
void dispose() {
timer?.cancel();
super.dispose();
}

void startTimer() {
timer = Timer.periodic(const Duration(milliseconds: 250), onTimer);
}

void onTimer(Timer timer) {
var r = Random().nextInt(3);

if (r == prevIndex) {
r += 1;
r = r % blobKeys.length;
}

prevIndex = r;

var key = blobKeys[r];

if (key.currentState == null) {
return;
}

var state = key.currentState! as __SingleTypingIndicatorBlobState;
state.controller.forward(from: 0);
}

@override
Widget build(BuildContext context) {
return Row(children: [
_SingleTypingIndicatorBlob(
key: blobKeys[0],
),
_SingleTypingIndicatorBlob(
key: blobKeys[1],
),
_SingleTypingIndicatorBlob(
key: blobKeys[2],
),
]);
}
}
Loading