feat: Swipe to archive rooms

pull/1425/head
Krille 10 months ago
parent fcc43e3328
commit 3f9c7f3e91
No known key found for this signature in database
GPG Key ID: E067ECD60F1A0652

@ -35,29 +35,31 @@ class ChatListItem extends StatelessWidget {
super.key, super.key,
}); });
Future<void> archiveAction(BuildContext context) async { Future<bool> archiveAction(BuildContext context, {bool? confirmed}) async {
{ {
if ([Membership.leave, Membership.ban].contains(room.membership)) { if ([Membership.leave, Membership.ban].contains(room.membership)) {
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
future: () => room.forget(), future: () => room.forget(),
); );
return; return true;
} }
final confirmed = await showOkCancelAlertDialog( confirmed ??= (await showOkCancelAlertDialog(
useRootNavigator: false, useRootNavigator: false,
context: context, context: context,
title: L10n.of(context).areYouSure, title: L10n.of(context).areYouSure,
okLabel: L10n.of(context).yes, okLabel: L10n.of(context).leave,
cancelLabel: L10n.of(context).no, cancelLabel: L10n.of(context).cancel,
message: L10n.of(context).archiveRoomDescription, message: L10n.of(context).archiveRoomDescription,
); isDestructiveAction: true,
if (confirmed == OkCancelResult.cancel) return; )) ==
OkCancelResult.ok;
if (!confirmed) return false;
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
future: () => room.leave(), future: () => room.leave(),
); );
return; return true;
} }
} }
@ -93,294 +95,323 @@ class ChatListItem extends StatelessWidget {
: room.getState(EventTypes.RoomMember, lastEvent.senderId) == null; : room.getState(EventTypes.RoomMember, lastEvent.senderId) == null;
final space = this.space; final space = this.space;
return Padding( return Dismissible(
padding: const EdgeInsets.symmetric( key: ValueKey(room.id),
horizontal: 8, confirmDismiss: (_) async =>
vertical: 1, (await showOkCancelAlertDialog(
useRootNavigator: false,
context: context,
title: L10n.of(context).areYouSure,
okLabel: L10n.of(context).leave,
cancelLabel: L10n.of(context).cancel,
message: L10n.of(context).archiveRoomDescription,
isDestructiveAction: true,
)) ==
OkCancelResult.ok,
onDismissed: (_) => archiveAction(context, confirmed: true),
background: Material(
color: theme.colorScheme.errorContainer,
child: Icon(
Icons.archive_outlined,
color: theme.colorScheme.onErrorContainer,
),
), ),
child: Material( child: Padding(
borderRadius: BorderRadius.circular(AppConfig.borderRadius), padding: const EdgeInsets.symmetric(
clipBehavior: Clip.hardEdge, horizontal: 8,
color: backgroundColor, vertical: 1,
child: FutureBuilder( ),
future: room.loadHeroUsers(), child: Material(
builder: (context, snapshot) => HoverBuilder( borderRadius: BorderRadius.circular(AppConfig.borderRadius),
builder: (context, listTileHovered) => ListTile( clipBehavior: Clip.hardEdge,
visualDensity: const VisualDensity(vertical: -0.5), color: backgroundColor,
contentPadding: const EdgeInsets.symmetric(horizontal: 8), child: FutureBuilder(
onLongPress: () => onLongPress?.call(context), future: room.loadHeroUsers(),
leading: HoverBuilder( builder: (context, snapshot) => HoverBuilder(
builder: (context, hovered) => AnimatedScale( builder: (context, listTileHovered) => ListTile(
duration: FluffyThemes.animationDuration, visualDensity: const VisualDensity(vertical: -0.5),
curve: FluffyThemes.animationCurve, contentPadding: const EdgeInsets.symmetric(horizontal: 8),
scale: hovered ? 1.1 : 1.0, onLongPress: () => onLongPress?.call(context),
child: SizedBox( leading: HoverBuilder(
width: Avatar.defaultSize, builder: (context, hovered) => AnimatedScale(
height: Avatar.defaultSize, duration: FluffyThemes.animationDuration,
child: Stack( curve: FluffyThemes.animationCurve,
children: [ scale: hovered ? 1.1 : 1.0,
if (space != null) child: SizedBox(
width: Avatar.defaultSize,
height: Avatar.defaultSize,
child: Stack(
children: [
if (space != null)
Positioned(
top: 0,
left: 0,
child: Avatar(
border: BorderSide(
width: 2,
color: backgroundColor ??
theme.colorScheme.surface,
),
borderRadius: BorderRadius.circular(
AppConfig.borderRadius / 4,
),
mxContent: space.avatar,
size: Avatar.defaultSize * 0.75,
name: space.getLocalizedDisplayname(),
onTap: () => onLongPress?.call(context),
),
),
Positioned( Positioned(
top: 0, bottom: 0,
left: 0, right: 0,
child: Avatar( child: Avatar(
border: BorderSide( border: space == null
width: 2, ? null
color: backgroundColor ?? : BorderSide(
theme.colorScheme.surface, width: 2,
), color: backgroundColor ??
borderRadius: BorderRadius.circular( theme.colorScheme.surface,
AppConfig.borderRadius / 4, ),
), borderRadius: room.isSpace
mxContent: space.avatar, ? BorderRadius.circular(
size: Avatar.defaultSize * 0.75, AppConfig.borderRadius / 4,
name: space.getLocalizedDisplayname(), )
: null,
mxContent: room.avatar,
size: space != null
? Avatar.defaultSize * 0.75
: Avatar.defaultSize,
name: displayname,
presenceUserId: directChatMatrixId,
presenceBackgroundColor: backgroundColor,
onTap: () => onLongPress?.call(context), onTap: () => onLongPress?.call(context),
), ),
), ),
Positioned( Positioned(
bottom: 0, top: 0,
right: 0, right: 0,
child: Avatar( child: GestureDetector(
border: space == null onTap: () => onLongPress?.call(context),
? null child: AnimatedScale(
: BorderSide( duration: FluffyThemes.animationDuration,
width: 2, curve: FluffyThemes.animationCurve,
color: backgroundColor ?? scale: listTileHovered ? 1.0 : 0.0,
theme.colorScheme.surface, child: Material(
color: backgroundColor,
borderRadius: BorderRadius.circular(16),
child: const Icon(
Icons.arrow_drop_down_circle_outlined,
size: 18,
), ),
borderRadius: room.isSpace
? BorderRadius.circular(
AppConfig.borderRadius / 4,
)
: null,
mxContent: room.avatar,
size: space != null
? Avatar.defaultSize * 0.75
: Avatar.defaultSize,
name: displayname,
presenceUserId: directChatMatrixId,
presenceBackgroundColor: backgroundColor,
onTap: () => onLongPress?.call(context),
),
),
Positioned(
top: 0,
right: 0,
child: GestureDetector(
onTap: () => onLongPress?.call(context),
child: AnimatedScale(
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
scale: listTileHovered ? 1.0 : 0.0,
child: Material(
color: backgroundColor,
borderRadius: BorderRadius.circular(16),
child: const Icon(
Icons.arrow_drop_down_circle_outlined,
size: 18,
), ),
), ),
), ),
), ),
), ],
], ),
), ),
), ),
), ),
), title: Row(
title: Row( children: <Widget>[
children: <Widget>[ Expanded(
Expanded( child: Text(
child: Text( displayname,
displayname, maxLines: 1,
maxLines: 1, overflow: TextOverflow.ellipsis,
overflow: TextOverflow.ellipsis, softWrap: false,
softWrap: false, style: unread || room.hasNewMessages
style: unread || room.hasNewMessages ? const TextStyle(fontWeight: FontWeight.bold)
? const TextStyle(fontWeight: FontWeight.bold) : null,
: null,
),
),
if (isMuted)
const Padding(
padding: EdgeInsets.only(left: 4.0),
child: Icon(
Icons.notifications_off_outlined,
size: 16,
), ),
), ),
if (room.isFavourite || room.membership == Membership.invite) if (isMuted)
Padding( const Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(left: 4.0),
right: hasNotifications ? 4.0 : 0.0, child: Icon(
Icons.notifications_off_outlined,
size: 16,
),
), ),
child: Icon( if (room.isFavourite ||
Icons.push_pin, room.membership == Membership.invite)
size: 16, Padding(
color: theme.colorScheme.primary, padding: EdgeInsets.only(
right: hasNotifications ? 4.0 : 0.0,
),
child: Icon(
Icons.push_pin,
size: 16,
color: theme.colorScheme.primary,
),
), ),
), if (!room.isSpace &&
if (!room.isSpace && lastEvent != null &&
lastEvent != null && room.membership != Membership.invite)
room.membership != Membership.invite) Padding(
Padding( padding: const EdgeInsets.only(left: 4.0),
padding: const EdgeInsets.only(left: 4.0), child: Text(
child: Text( lastEvent.originServerTs.localizedTimeShort(context),
lastEvent.originServerTs.localizedTimeShort(context), style: TextStyle(
style: TextStyle( fontSize: 13,
fontSize: 13, color: unread
color: unread ? theme.colorScheme.secondary
? theme.colorScheme.secondary : theme.textTheme.bodyMedium!.color,
: theme.textTheme.bodyMedium!.color, ),
), ),
), ),
), if (room.isSpace)
if (room.isSpace) const Icon(
const Icon( Icons.arrow_circle_right_outlined,
Icons.arrow_circle_right_outlined, size: 18,
size: 18, ),
),
],
),
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (typingText.isEmpty &&
ownMessage &&
room.lastEvent!.status.isSending) ...[
const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
),
const SizedBox(width: 4),
], ],
AnimatedContainer( ),
width: typingText.isEmpty ? 0 : 18, subtitle: Row(
clipBehavior: Clip.hardEdge, mainAxisAlignment: MainAxisAlignment.center,
decoration: const BoxDecoration(), children: <Widget>[
duration: FluffyThemes.animationDuration, if (typingText.isEmpty &&
curve: FluffyThemes.animationCurve, ownMessage &&
padding: const EdgeInsets.only(right: 4), room.lastEvent!.status.isSending) ...[
child: Icon( const SizedBox(
Icons.edit_outlined, width: 16,
color: theme.colorScheme.secondary, height: 16,
size: 14, child:
CircularProgressIndicator.adaptive(strokeWidth: 2),
),
const SizedBox(width: 4),
],
AnimatedContainer(
width: typingText.isEmpty ? 0 : 18,
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(),
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
padding: const EdgeInsets.only(right: 4),
child: Icon(
Icons.edit_outlined,
color: theme.colorScheme.secondary,
size: 14,
),
), ),
), Expanded(
Expanded( child: room.isSpace && room.membership == Membership.join
child: room.isSpace && room.membership == Membership.join ? Text(
? Text( L10n.of(context).countChatsAndCountParticipants(
L10n.of(context).countChatsAndCountParticipants( room.spaceChildren.length.toString(),
room.spaceChildren.length.toString(), (room.summary.mJoinedMemberCount ?? 1)
(room.summary.mJoinedMemberCount ?? 1).toString(), .toString(),
), ),
) )
: typingText.isNotEmpty : typingText.isNotEmpty
? Text( ? Text(
typingText, typingText,
style: TextStyle(
color: theme.colorScheme.primary,
),
maxLines: 1,
softWrap: false,
)
: FutureBuilder(
key: ValueKey(
'${lastEvent?.eventId}_${lastEvent?.type}',
),
future: needLastEventSender
? lastEvent.calcLocalizedBody(
MatrixLocals(L10n.of(context)),
hideReply: true,
hideEdit: true,
plaintextBody: true,
removeMarkdown: true,
withSenderNamePrefix: (!isDirectChat ||
directChatMatrixId !=
room.lastEvent?.senderId),
)
: null,
initialData:
lastEvent?.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)),
hideReply: true,
hideEdit: true,
plaintextBody: true,
removeMarkdown: true,
withSenderNamePrefix: (!isDirectChat ||
directChatMatrixId !=
room.lastEvent?.senderId),
),
builder: (context, snapshot) => Text(
room.membership == Membership.invite
? isDirectChat
? L10n.of(context).invitePrivateChat
: L10n.of(context).inviteGroupChat
: snapshot.data ??
L10n.of(context).emptyChat,
softWrap: false,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
fontWeight: unread || room.hasNewMessages color: theme.colorScheme.primary,
? FontWeight.bold ),
: null, maxLines: 1,
color: theme.colorScheme.onSurfaceVariant, softWrap: false,
decoration: room.lastEvent?.redacted == true )
? TextDecoration.lineThrough : FutureBuilder(
: null, key: ValueKey(
'${lastEvent?.eventId}_${lastEvent?.type}',
),
future: needLastEventSender
? lastEvent.calcLocalizedBody(
MatrixLocals(L10n.of(context)),
hideReply: true,
hideEdit: true,
plaintextBody: true,
removeMarkdown: true,
withSenderNamePrefix:
(!isDirectChat ||
directChatMatrixId !=
room.lastEvent?.senderId),
)
: null,
initialData:
lastEvent?.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)),
hideReply: true,
hideEdit: true,
plaintextBody: true,
removeMarkdown: true,
withSenderNamePrefix: (!isDirectChat ||
directChatMatrixId !=
room.lastEvent?.senderId),
),
builder: (context, snapshot) => Text(
room.membership == Membership.invite
? isDirectChat
? L10n.of(context).invitePrivateChat
: L10n.of(context).inviteGroupChat
: snapshot.data ??
L10n.of(context).emptyChat,
softWrap: false,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: unread || room.hasNewMessages
? FontWeight.bold
: null,
color: theme.colorScheme.onSurfaceVariant,
decoration:
room.lastEvent?.redacted == true
? TextDecoration.lineThrough
: null,
),
), ),
), ),
),
),
const SizedBox(width: 8),
AnimatedContainer(
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
padding: const EdgeInsets.symmetric(horizontal: 7),
height: unreadBubbleSize,
width: !hasNotifications && !unread && !room.hasNewMessages
? 0
: (unreadBubbleSize - 9) *
room.notificationCount.toString().length +
9,
decoration: BoxDecoration(
color: room.highlightCount > 0 ||
room.membership == Membership.invite
? Colors.red
: hasNotifications || room.markedUnread
? theme.colorScheme.primary
: theme.colorScheme.primaryContainer,
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
), ),
child: Center( const SizedBox(width: 8),
child: hasNotifications AnimatedContainer(
? Text( duration: FluffyThemes.animationDuration,
room.notificationCount.toString(), curve: FluffyThemes.animationCurve,
style: TextStyle( padding: const EdgeInsets.symmetric(horizontal: 7),
color: room.highlightCount > 0 height: unreadBubbleSize,
? Colors.white width:
: hasNotifications !hasNotifications && !unread && !room.hasNewMessages
? theme.colorScheme.onPrimary ? 0
: theme.colorScheme.onPrimaryContainer, : (unreadBubbleSize - 9) *
fontSize: 13, room.notificationCount.toString().length +
), 9,
) decoration: BoxDecoration(
: const SizedBox.shrink(), color: room.highlightCount > 0 ||
room.membership == Membership.invite
? Colors.red
: hasNotifications || room.markedUnread
? theme.colorScheme.primary
: theme.colorScheme.primaryContainer,
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
),
child: Center(
child: hasNotifications
? Text(
room.notificationCount.toString(),
style: TextStyle(
color: room.highlightCount > 0
? Colors.white
: hasNotifications
? theme.colorScheme.onPrimary
: theme
.colorScheme.onPrimaryContainer,
fontSize: 13,
),
)
: const SizedBox.shrink(),
),
), ),
), ],
], ),
onTap: onTap,
trailing: onForget == null
? null
: IconButton(
icon: const Icon(Icons.delete_outlined),
onPressed: onForget,
),
), ),
onTap: onTap,
trailing: onForget == null
? null
: IconButton(
icon: const Icon(Icons.delete_outlined),
onPressed: onForget,
),
), ),
), ),
), ),

Loading…
Cancel
Save