diff --git a/lib/components/matrix.dart b/lib/components/matrix.dart index 3a9b8a2..27b2219 100644 --- a/lib/components/matrix.dart +++ b/lib/components/matrix.dart @@ -71,6 +71,9 @@ class MatrixState extends State { File wallpaper; bool renderHtml = false; + String swipeToEndAction; + String swipeToStartAction = 'reply'; + String jitsiInstance = 'https://meet.jit.si/'; void clean() async { @@ -283,6 +286,16 @@ class MatrixState extends State { store.getItem('chat.fluffy.renderHtml').then((final render) async { renderHtml = render == '1'; }); + store + .getItem('dev.inex.furrychat.swipeToEndAction') + .then((final action) async { + swipeToEndAction = action ?? swipeToEndAction; + }); + store + .getItem('dev.inex.furrychat.swipeToStartAction') + .then((final action) async { + swipeToStartAction = action ?? swipeToStartAction; + }); } if (kIsWeb) { onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true); diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 588e861..caf9aa8 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -514,6 +514,11 @@ "type": "text", "placeholders": {} }, + "edit": "Edit", + "@edit": { + "type": "text", + "placeholders": {} + }, "editDisplayname": "Edit displayname", "@editDisplayname": { "type": "text", @@ -1439,6 +1444,16 @@ "type": "text", "placeholders": {} }, + "swipeToEndAction": "Swipe to right action", + "@swipeToEndAction": { + "type": "text", + "placeholders": {} + }, + "swipeToStartAction": "Swipe to left action", + "@swipeToStartAction": { + "type": "text", + "placeholders": {} + }, "donate": "Donate", "@donate": { "type": "text", diff --git a/lib/views/chat.dart b/lib/views/chat.dart index b991841..f989262 100644 --- a/lib/views/chat.dart +++ b/lib/views/chat.dart @@ -317,8 +317,10 @@ class _ChatState extends State<_Chat> { return true; } - void forwardEventsAction(BuildContext context) async { - if (selectedEvents.length == 1) { + void forwardEventsAction(BuildContext context, {Event event}) async { + if (event != null) { + Matrix.of(context).shareContent = event.content; + } else if (selectedEvents.length == 1) { Matrix.of(context).shareContent = selectedEvents.first.content; } else { Matrix.of(context).shareContent = { @@ -412,6 +414,128 @@ class _ChatState extends State<_Chat> { e.type != 'm.reaction') .toList(); + SwipeDirection _getSwipeDirection(Event event) { + var swipeToEndAction = Matrix.of(context).swipeToEndAction; + var swipeToStartAction = Matrix.of(context).swipeToStartAction; + var client = Matrix.of(context).client; + if (event.senderId != client.userID && swipeToEndAction == 'edit') { + swipeToEndAction = null; + } + if (event.senderId != client.userID && swipeToStartAction == 'edit') { + swipeToStartAction = null; + } + if (swipeToEndAction != null && swipeToStartAction != null) { + return SwipeDirection.horizontal; + } + if (swipeToEndAction != null) { + return SwipeDirection.startToEnd; + } + if (swipeToStartAction != null) { + return SwipeDirection.endToStart; + } + return null; + } + + Widget _getSwipeBackground(Event event, {bool isSecondary = false}) { + var alignToRight, action; + if (_getSwipeDirection(event) == SwipeDirection.horizontal) { + if (isSecondary) { + alignToRight = true; + action = Matrix.of(context).swipeToStartAction; + } else { + alignToRight = false; + action = Matrix.of(context).swipeToEndAction; + } + } else if (isSecondary) { + return null; + } else if (_getSwipeDirection(event) == SwipeDirection.endToStart) { + alignToRight = true; + action = Matrix.of(context).swipeToStartAction; + } else { + alignToRight = false; + action = Matrix.of(context).swipeToStartAction; + } + + switch (action) { + case 'reply': + return Container( + color: Theme.of(context).primaryColor.withAlpha(100), + padding: EdgeInsets.symmetric(horizontal: 12.0), + child: Row( + mainAxisAlignment: + alignToRight ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + Icon(Icons.reply_outlined), + SizedBox(width: 2.0), + Text(L10n.of(context).reply) + ], + ), + ); + case 'forward': + return Container( + color: Theme.of(context).primaryColor.withAlpha(100), + padding: EdgeInsets.symmetric(horizontal: 12.0), + child: Row( + mainAxisAlignment: + alignToRight ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + Icon(Icons.forward_outlined), + SizedBox(width: 2.0), + Text(L10n.of(context).forward) + ], + ), + ); + case 'edit': + return Container( + color: Theme.of(context).primaryColor.withAlpha(100), + padding: EdgeInsets.symmetric(horizontal: 12.0), + child: Row( + mainAxisAlignment: + alignToRight ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + Icon(Icons.edit_outlined), + SizedBox(width: 2.0), + Text(L10n.of(context).edit) + ], + ), + ); + default: + return Container( + color: Theme.of(context).primaryColor.withAlpha(100), + ); + } + } + + void _handleSwipe(SwipeDirection direction, Event event) { + var action; + if (direction == SwipeDirection.endToStart) { + action = Matrix.of(context).swipeToStartAction; + } else { + action = Matrix.of(context).swipeToEndAction; + } + + switch (action) { + case 'reply': + replyAction(replyTo: event); + break; + case 'forward': + forwardEventsAction(context, event: event); + break; + case 'edit': + setState(() { + editEvent = event; + sendController.text = editEvent + .getDisplayEvent(timeline) + .getLocalizedBody(MatrixLocals(L10n.of(context)), + withSenderNamePrefix: false, hideReply: true); + selectedEvents.clear(); + }); + inputFocus.requestFocus(); + break; + default: + } + } + @override Widget build(BuildContext context) { matrix = Matrix.of(context); @@ -672,26 +796,16 @@ class _ChatState extends State<_Chat> { child: Swipeable( key: ValueKey( filteredEvents[i - 1].eventId), - background: Container( - color: Theme.of(context) - .primaryColor - .withAlpha(100), - padding: EdgeInsets.symmetric( - horizontal: 12.0), - alignment: Alignment.centerLeft, - child: Row( - children: [ - Icon(Icons.reply), - SizedBox(width: 2.0), - Text(L10n.of(context).reply) - ], - ), - ), - direction: SwipeDirection.startToEnd, - onSwipe: (direction) { - replyAction( - replyTo: filteredEvents[i - 1]); - }, + background: _getSwipeBackground( + filteredEvents[i - 1]), + secondaryBackground: + _getSwipeBackground( + filteredEvents[i - 1], + isSecondary: true), + direction: _getSwipeDirection( + filteredEvents[i - 1]), + onSwipe: (direction) => _handleSwipe( + direction, filteredEvents[i - 1]), child: Message(filteredEvents[i - 1], onAvatarTab: (Event event) { sendController.text += diff --git a/lib/views/settings/settings_chat.dart b/lib/views/settings/settings_chat.dart index d68d346..43c6268 100644 --- a/lib/views/settings/settings_chat.dart +++ b/lib/views/settings/settings_chat.dart @@ -22,6 +22,74 @@ class ChatSettings extends StatefulWidget { } class _ChatSettingsState extends State { + String _getActionDescription(String action) { + switch (action) { + case 'reply': + return L10n.of(context).reply; + case 'forward': + return L10n.of(context).forward; + case 'edit': + return L10n.of(context).edit; + default: + return L10n.of(context).none; + } + } + + void _changeSwipeAction(bool isToEnd, String action) async { + if (isToEnd) { + Matrix.of(context).swipeToEndAction = action; + await Matrix.of(context) + .store + .setItem('chat.fluffy.swipeToEndAction', action); + setState(() => null); + } else { + Matrix.of(context).swipeToStartAction = action; + await Matrix.of(context) + .store + .setItem('chat.fluffy.swipeToStartAction', action); + setState(() => null); + } + } + + Widget _swipeActionChooser(BuildContext context, bool isToEnd) { + return ListView( + children: [ + ListTile( + title: Text(L10n.of(context).none), + leading: Icon(Icons.clear_outlined), + onTap: () { + _changeSwipeAction(isToEnd, null); + Navigator.of(context).pop(); + }, + ), + ListTile( + title: Text(L10n.of(context).reply), + leading: Icon(Icons.reply_outlined), + onTap: () { + _changeSwipeAction(isToEnd, 'reply'); + Navigator.of(context).pop(); + }, + ), + ListTile( + title: Text(L10n.of(context).forward), + leading: Icon(Icons.forward_outlined), + onTap: () { + _changeSwipeAction(isToEnd, 'forward'); + Navigator.of(context).pop(); + }, + ), + ListTile( + title: Text(L10n.of(context).edit), + leading: Icon(Icons.edit_outlined), + onTap: () { + _changeSwipeAction(isToEnd, 'edit'); + Navigator.of(context).pop(); + }, + ), + ], + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -42,6 +110,27 @@ class _ChatSettingsState extends State { }, ), ), + Divider(thickness: 1), + ListTile( + title: Text(L10n.of(context).swipeToEndAction), + onTap: () => showModalBottomSheet( + context: context, + builder: (BuildContext context) => + _swipeActionChooser(context, true), + ), + subtitle: Text( + _getActionDescription(Matrix.of(context).swipeToEndAction)), + ), + ListTile( + title: Text(L10n.of(context).swipeToStartAction), + onTap: () => showModalBottomSheet( + context: context, + builder: (BuildContext context) => + _swipeActionChooser(context, false), + ), + subtitle: Text( + _getActionDescription(Matrix.of(context).swipeToStartAction)), + ), ], ), );