feat: Enhance roomlist context menu

This commit is contained in:
Christian Pauly 2020-10-02 15:50:59 +02:00
parent 41ceb84b47
commit 493b7000c6
3 changed files with 268 additions and 237 deletions

View file

@ -1,7 +1,6 @@
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/chat.dart'; import 'package:fluffychat/views/chat.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:bot_toast/bot_toast.dart'; import 'package:bot_toast/bot_toast.dart';
import 'package:pedantic/pedantic.dart'; import 'package:pedantic/pedantic.dart';
@ -18,11 +17,20 @@ import '../dialogs/send_file_dialog.dart';
class ChatListItem extends StatelessWidget { class ChatListItem extends StatelessWidget {
final Room room; final Room room;
final bool activeChat; final bool activeChat;
final bool selected;
final Function onForget; final Function onForget;
final Function onTap;
final Function onLongPress;
const ChatListItem(this.room, {this.activeChat = false, this.onForget}); const ChatListItem(this.room,
{this.activeChat = false,
this.selected = false,
this.onTap,
this.onLongPress,
this.onForget});
void clickAction(BuildContext context) async { void clickAction(BuildContext context) async {
if (onTap != null) return onTap();
if (!activeChat) { if (!activeChat) {
if (room.membership == Membership.invite && if (room.membership == Membership.invite &&
await SimpleDialogs(context) await SimpleDialogs(context)
@ -94,19 +102,7 @@ class ChatListItem extends StatelessWidget {
} }
} }
Future<void> _toggleFavouriteRoom(BuildContext context) => Future<void> archiveAction(BuildContext context) async {
SimpleDialogs(context).tryRequestWithLoadingDialog(
room.setFavourite(!room.isFavourite),
);
Future<void> _toggleMuted(BuildContext context) =>
SimpleDialogs(context).tryRequestWithLoadingDialog(
room.setPushRuleState(room.pushRuleState == PushRuleState.notify
? PushRuleState.mentions_only
: PushRuleState.notify),
);
Future<bool> archiveAction(BuildContext context) async {
{ {
if ([Membership.leave, Membership.ban].contains(room.membership)) { if ([Membership.leave, Membership.ban].contains(room.membership)) {
final success = await SimpleDialogs(context) final success = await SimpleDialogs(context)
@ -117,66 +113,20 @@ class ChatListItem extends StatelessWidget {
return success; return success;
} }
final confirmed = await SimpleDialogs(context).askConfirmation(); final confirmed = await SimpleDialogs(context).askConfirmation();
if (!confirmed) { if (!confirmed) return;
return false; await SimpleDialogs(context).tryRequestWithLoadingDialog(room.leave());
} return;
final success = await SimpleDialogs(context)
.tryRequestWithLoadingDialog(room.leave());
if (success == false) {
return false;
}
return true;
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isMuted = room.pushRuleState != PushRuleState.notify; final isMuted = room.pushRuleState != PushRuleState.notify;
final slideableKey = GlobalKey(); return Center(
return Slidable(
key: slideableKey,
secondaryActions: <Widget>[
if ([Membership.join, Membership.invite].contains(room.membership))
IconSlideAction(
caption: isMuted
? L10n.of(context).unmuteChat
: L10n.of(context).muteChat,
color: Colors.blueGrey,
icon:
isMuted ? Icons.notifications_active : Icons.notifications_off,
onTap: () => _toggleMuted(context),
),
if ([Membership.join, Membership.invite].contains(room.membership))
IconSlideAction(
caption: room.isFavourite
? L10n.of(context).unpin
: L10n.of(context).pin,
color: Colors.blue,
icon: room.isFavourite ? Icons.favorite_border : Icons.favorite,
onTap: () => _toggleFavouriteRoom(context),
),
if ([Membership.join, Membership.invite].contains(room.membership))
IconSlideAction(
caption: L10n.of(context).leave,
color: Colors.red,
icon: Icons.archive,
onTap: () => archiveAction(context),
),
if ([Membership.leave, Membership.ban].contains(room.membership))
IconSlideAction(
caption: L10n.of(context).delete,
color: Colors.red,
icon: Icons.delete_forever,
onTap: () => archiveAction(context),
),
],
actionPane: SlidableDrawerActionPane(),
child: Center(
child: Material( child: Material(
color: chatListItemColor(context, activeChat), color: chatListItemColor(context, activeChat, selected),
child: ListTile( child: ListTile(
onLongPress: () => (slideableKey.currentState as SlidableState) onLongPress: onLongPress,
.open(actionType: SlideActionType.secondary),
leading: Avatar(room.avatar, room.displayname), leading: Avatar(room.avatar, room.displayname),
title: Row( title: Row(
children: <Widget>[ children: <Widget>[
@ -236,8 +186,7 @@ class ChatListItem extends StatelessWidget {
room.lastEvent?.getLocalizedBody( room.lastEvent?.getLocalizedBody(
L10n.of(context), L10n.of(context),
withSenderNamePrefix: !room.isDirectChat || withSenderNamePrefix: !room.isDirectChat ||
room.lastEvent.senderId == room.lastEvent.senderId == room.client.userID,
room.client.userID,
hideReply: true, hideReply: true,
) ?? ) ??
'', '',
@ -275,7 +224,6 @@ class ChatListItem extends StatelessWidget {
onTap: () => clickAction(context), onTap: () => clickAction(context),
), ),
), ),
),
); );
} }
} }

View file

@ -112,8 +112,10 @@ final ThemeData amoledTheme = ThemeData.dark().copyWith(
), ),
); );
Color chatListItemColor(BuildContext context, bool activeChat) => Color chatListItemColor(BuildContext context, bool activeChat, bool selected) =>
Theme.of(context).brightness == Brightness.light selected
? Theme.of(context).primaryColor.withAlpha(50)
: Theme.of(context).brightness == Brightness.light
? activeChat ? activeChat
? Color(0xFFE8E8E8) ? Color(0xFFE8E8E8)
: Colors.white : Colors.white

View file

@ -25,7 +25,7 @@ import 'new_group.dart';
import 'new_private_chat.dart'; import 'new_private_chat.dart';
import 'settings.dart'; import 'settings.dart';
enum SelectMode { normal, share } enum SelectMode { normal, share, select }
class ChatListView extends StatelessWidget { class ChatListView extends StatelessWidget {
@override @override
@ -59,9 +59,15 @@ class _ChatListState extends State<ChatList> {
PublicRoomsResponse publicRoomsResponse; PublicRoomsResponse publicRoomsResponse;
bool loadingPublicRooms = false; bool loadingPublicRooms = false;
String searchServer; String searchServer;
final _selectedRoomIds = <String>{};
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
void _toggleSelection(String roomId) =>
setState(() => _selectedRoomIds.contains(roomId)
? _selectedRoomIds.remove(roomId)
: _selectedRoomIds.add(roomId));
Future<void> waitForFirstSync(BuildContext context) async { Future<void> waitForFirstSync(BuildContext context) async {
var client = Matrix.of(context).client; var client = Matrix.of(context).client;
if (client.prevBatch?.isEmpty ?? true) { if (client.prevBatch?.isEmpty ?? true) {
@ -215,6 +221,39 @@ class _ChatListState extends State<ChatList> {
super.dispose(); super.dispose();
} }
Future<void> _toggleFavouriteRoom(BuildContext context) {
final room = Matrix.of(context).client.getRoomById(_selectedRoomIds.single);
return SimpleDialogs(context).tryRequestWithLoadingDialog(
room.setFavourite(!room.isFavourite),
);
}
Future<void> _toggleMuted(BuildContext context) {
final room = Matrix.of(context).client.getRoomById(_selectedRoomIds.single);
return SimpleDialogs(context).tryRequestWithLoadingDialog(
room.setPushRuleState(room.pushRuleState == PushRuleState.notify
? PushRuleState.mentions_only
: PushRuleState.notify),
);
}
Future<void> _archiveAction(BuildContext context) async {
final confirmed = await SimpleDialogs(context).askConfirmation();
if (!confirmed) return;
await SimpleDialogs(context)
.tryRequestWithLoadingDialog(_archiveSelectedRooms(context));
setState(() => null);
}
Future<void> _archiveSelectedRooms(BuildContext context) async {
final client = Matrix.of(context).client;
while (_selectedRoomIds.isNotEmpty) {
final roomId = _selectedRoomIds.first;
await client.getRoomById(roomId).leave();
_selectedRoomIds.remove(roomId);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StreamBuilder<LoginState>( return StreamBuilder<LoginState>(
@ -232,10 +271,15 @@ class _ChatListState extends State<ChatList> {
stream: Matrix.of(context).onShareContentChanged.stream, stream: Matrix.of(context).onShareContentChanged.stream,
builder: (context, snapshot) { builder: (context, snapshot) {
final selectMode = Matrix.of(context).shareContent == null final selectMode = Matrix.of(context).shareContent == null
? _selectedRoomIds.isEmpty
? SelectMode.normal ? SelectMode.normal
: SelectMode.select
: SelectMode.share; : SelectMode.share;
if (selectMode == SelectMode.share) {
_selectedRoomIds.clear();
}
return Scaffold( return Scaffold(
drawer: selectMode == SelectMode.share drawer: selectMode != SelectMode.normal
? null ? null
: Drawer( : Drawer(
child: SafeArea( child: SafeArea(
@ -290,17 +334,44 @@ class _ChatListState extends State<ChatList> {
), ),
), ),
appBar: AppBar( appBar: AppBar(
centerTitle: false,
elevation: _scrolledToTop ? 0 : null, elevation: _scrolledToTop ? 0 : null,
leading: selectMode != SelectMode.share leading: selectMode == SelectMode.share
? null ? IconButton(
: IconButton(
icon: Icon(Icons.close), icon: Icon(Icons.close),
onPressed: () => onPressed: () =>
Matrix.of(context).shareContent = null, Matrix.of(context).shareContent = null,
), )
: selectMode == SelectMode.select
? IconButton(
icon: Icon(Icons.close),
onPressed: () =>
setState(_selectedRoomIds.clear),
)
: null,
titleSpacing: 0, titleSpacing: 0,
actions: selectMode != SelectMode.select
? null
: [
if (_selectedRoomIds.length == 1)
IconButton(
icon: Icon(Icons.favorite_border_outlined),
onPressed: () => _toggleFavouriteRoom(context),
),
if (_selectedRoomIds.length == 1)
IconButton(
icon: Icon(Icons.notifications_none),
onPressed: () => _toggleMuted(context),
),
IconButton(
icon: Icon(Icons.archive),
onPressed: () => _archiveAction(context),
),
],
title: selectMode == SelectMode.share title: selectMode == SelectMode.share
? Text(L10n.of(context).share) ? Text(L10n.of(context).share)
: selectMode == SelectMode.select
? Text(_selectedRoomIds.length.toString())
: Container( : Container(
height: 40, height: 40,
padding: EdgeInsets.only(right: 8), padding: EdgeInsets.only(right: 8),
@ -337,7 +408,7 @@ class _ChatListState extends State<ChatList> {
), ),
floatingActionButton: floatingActionButton:
(AdaptivePageLayout.columnMode(context) || (AdaptivePageLayout.columnMode(context) ||
selectMode == SelectMode.share) selectMode != SelectMode.normal)
? null ? null
: FloatingActionButton( : FloatingActionButton(
child: Icon(Icons.add), child: Icon(Icons.add),
@ -436,24 +507,23 @@ class _ChatListState extends State<ChatList> {
itemBuilder: itemBuilder:
(BuildContext context, int i) { (BuildContext context, int i) {
if (i == 0) { if (i == 0) {
final displayPresences = directChats
.isNotEmpty &&
selectMode == SelectMode.normal;
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
(directChats.isEmpty || AnimatedContainer(
selectMode == duration: Duration(
SelectMode.share) milliseconds: 500),
? Container() height:
: PreferredSize( displayPresences ? 78 : 0,
preferredSize: child: !displayPresences
Size.fromHeight(82), ? null
child: Container( : ListView.builder(
height: 78,
child:
ListView.builder(
scrollDirection: scrollDirection:
Axis.horizontal, Axis.horizontal,
itemCount: itemCount: directChats
directChats
.length, .length,
itemBuilder: (BuildContext itemBuilder: (BuildContext
context, context,
@ -463,7 +533,6 @@ class _ChatListState extends State<ChatList> {
i]), i]),
), ),
), ),
),
], ],
); );
} }
@ -471,6 +540,18 @@ class _ChatListState extends State<ChatList> {
return i < rooms.length return i < rooms.length
? ChatListItem( ? ChatListItem(
rooms[i], rooms[i],
selected: _selectedRoomIds
.contains(rooms[i].id),
onTap: selectMode ==
SelectMode.select
? () => _toggleSelection(
rooms[i].id)
: null,
onLongPress: selectMode !=
SelectMode.share
? () => _toggleSelection(
rooms[i].id)
: null,
activeChat: activeChat:
widget.activeChat == widget.activeChat ==
rooms[i].id, rooms[i].id,