feat: Display presences in the app
This commit is contained in:
parent
a3db754b85
commit
7930150cb4
12 changed files with 187 additions and 33 deletions
|
|
@ -4,8 +4,10 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/utils/date_time_extension.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/presence_builder.dart';
|
||||
|
||||
class ChatAppBarTitle extends StatelessWidget {
|
||||
final ChatController controller;
|
||||
|
|
@ -34,16 +36,37 @@ class ChatAppBarTitle extends StatelessWidget {
|
|||
MatrixLocals(L10n.of(context)!),
|
||||
),
|
||||
size: 32,
|
||||
presenceUserId: room.directChatMatrixID,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
subtitle: PresenceBuilder(
|
||||
userId: room.directChatMatrixID,
|
||||
builder: (context, presence) {
|
||||
final lastActiveTimestamp = presence?.lastActiveTimestamp;
|
||||
if (presence?.currentlyActive == true) {
|
||||
return Text(L10n.of(context)!.currentlyActive);
|
||||
}
|
||||
if (lastActiveTimestamp != null) {
|
||||
return Text(
|
||||
L10n.of(context)!.lastActiveAgo(
|
||||
lastActiveTimestamp.localizedTimeShort(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ class EventInfoDialog extends StatelessWidget {
|
|||
leading: Avatar(
|
||||
mxContent: event.senderFromMemoryOrFallback.avatarUrl,
|
||||
name: event.senderFromMemoryOrFallback.calcDisplayname(),
|
||||
presenceUserId: event.senderId,
|
||||
),
|
||||
title: Text(L10n.of(context)!.sender),
|
||||
subtitle: Text(
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ class Message extends StatelessWidget {
|
|||
return Avatar(
|
||||
mxContent: user.avatarUrl,
|
||||
name: user.calcDisplayname(),
|
||||
presenceUserId: user.stateKey,
|
||||
onTap: () => onAvatarTab(event),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ class MessageContent extends StatelessWidget {
|
|||
leading: Avatar(
|
||||
mxContent: sender.avatarUrl,
|
||||
name: sender.calcDisplayname(),
|
||||
presenceUserId: sender.stateKey,
|
||||
),
|
||||
title: Text(sender.calcDisplayname()),
|
||||
subtitle: Text(event.originServerTs.localizedTime(context)),
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ class _AdaptableReactorsDialog extends StatelessWidget {
|
|||
mxContent: reactor.avatarUrl,
|
||||
name: reactor.displayName,
|
||||
client: client,
|
||||
presenceUserId: reactor.stateKey,
|
||||
),
|
||||
label: Text(reactor.displayName!),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ class SeenByRow extends StatelessWidget {
|
|||
name: user.calcDisplayname(),
|
||||
size: 16,
|
||||
fontSize: 9,
|
||||
presenceUserId: user.stateKey,
|
||||
),
|
||||
),
|
||||
if (seenByUsers.length > maxAvatars)
|
||||
|
|
|
|||
|
|
@ -81,8 +81,11 @@ class ParticipantListItem extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
subtitle: Text(user.id),
|
||||
leading:
|
||||
Avatar(mxContent: user.avatarUrl, name: user.calcDisplayname()),
|
||||
leading: Avatar(
|
||||
mxContent: user.avatarUrl,
|
||||
name: user.calcDisplayname(),
|
||||
presenceUserId: user.stateKey,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,6 +158,11 @@ class ChatListItem extends StatelessWidget {
|
|||
: 14.0
|
||||
: 0.0;
|
||||
final hasNotifications = room.notificationCount > 0;
|
||||
final backgroundColor = selected
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
: activeChat
|
||||
? Theme.of(context).colorScheme.secondaryContainer
|
||||
: null;
|
||||
final displayname = room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
);
|
||||
|
|
@ -169,11 +174,7 @@ class ChatListItem extends StatelessWidget {
|
|||
child: Material(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: selected
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
: activeChat
|
||||
? Theme.of(context).colorScheme.secondaryContainer
|
||||
: Colors.transparent,
|
||||
color: backgroundColor,
|
||||
child: ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -0.5),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
|
|
@ -192,6 +193,8 @@ class ChatListItem extends StatelessWidget {
|
|||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
onTap: onLongPress,
|
||||
presenceUserId: room.directChatMatrixID,
|
||||
presenceBackgroundColor: backgroundColor,
|
||||
),
|
||||
title: Row(
|
||||
children: <Widget>[
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ class _InviteContactListTile extends StatelessWidget {
|
|||
leading: Avatar(
|
||||
mxContent: avatarUrl,
|
||||
name: displayname,
|
||||
presenceUserId: userId,
|
||||
),
|
||||
title: Text(
|
||||
displayname,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/utils/date_time_extension.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/presence_builder.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'user_bottom_sheet.dart';
|
||||
|
||||
|
|
@ -30,7 +32,47 @@ class UserBottomSheetView extends StatelessWidget {
|
|||
leading: CloseButton(
|
||||
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
||||
),
|
||||
title: Text(displayname.trim().split(' ').first),
|
||||
title: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(displayname.trim().split(' ').first),
|
||||
subtitle: PresenceBuilder(
|
||||
userId: userId,
|
||||
client: client,
|
||||
builder: (context, presence) {
|
||||
if (presence == null) return const SizedBox.shrink();
|
||||
|
||||
final dotColor = presence.presence.isOnline
|
||||
? Colors.green
|
||||
: presence.presence.isUnavailable
|
||||
? Colors.orange
|
||||
: Colors.red;
|
||||
|
||||
final lastActiveTimestamp = presence.lastActiveTimestamp;
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: dotColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
if (presence.currentlyActive == true)
|
||||
Text(L10n.of(context)!.currentlyActive),
|
||||
if (lastActiveTimestamp != null)
|
||||
Text(
|
||||
L10n.of(context)!.lastActiveAgo(
|
||||
lastActiveTimestamp.localizedTimeShort(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
if (userId != client.userID &&
|
||||
!client.ignoredUsers.contains(userId))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue