diff --git a/CHANGELOG.md b/CHANGELOG.md index 0334203..ef7a8b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## [1.6.0] - 2025-06-17 +#### [@rickypid](https://github.com/rickypid) + +ℹ️ ℹ️ **Recommended scheme migration** ℹ️ ℹ️ + +### Fixed + +* Fixed #29 update messages list after delete message +* Fixed update room last message trigger + +### Dependencies + +* Upgraded `supabase_flutter` to `^2.9.1` +* Upgraded `meta` to `^1.16.0` +* Added `web` `^1.1.1` + ## [1.5.0] - 2025-02-06 #### [@rickypid](https://github.com/rickypid) diff --git a/doc/docs/guides/supabse-trigges.md b/doc/docs/guides/supabse-trigges.md index 6b2af28..b15b33a 100644 --- a/doc/docs/guides/supabse-trigges.md +++ b/doc/docs/guides/supabse-trigges.md @@ -13,20 +13,47 @@ CREATE OR REPLACE FUNCTION chats.update_last_messages() SET search_path = '' AS $$ DECLARE + latest_message jsonb; ts_in_milliseconds bigint; + affected_room_id bigint; BEGIN - SELECT EXTRACT(epoch FROM NOW()) * 1000 INTO ts_in_milliseconds; - UPDATE chats.rooms - SET "updatedAt" = ts_in_milliseconds, - "lastMessages" = jsonb_build_array(NEW) - WHERE id = NEW."roomId"; - RETURN NEW; + IF TG_OP = 'DELETE' THEN + affected_room_id := OLD."roomId"; + ELSE + affected_room_id := NEW."roomId"; + END IF; + + SELECT to_jsonb(m) + INTO latest_message + FROM chats.messages m + WHERE m."roomId" = affected_room_id + ORDER BY m."createdAt" DESC + LIMIT 1; + + IF latest_message IS DISTINCT FROM ( + SELECT value FROM jsonb_array_elements( + (SELECT "lastMessages" FROM chats.rooms WHERE id = affected_room_id) + ) LIMIT 1 + ) THEN + SELECT EXTRACT(epoch FROM NOW()) * 1000 INTO ts_in_milliseconds; + + UPDATE chats.rooms + SET "updatedAt" = ts_in_milliseconds, + "lastMessages" = jsonb_build_array(latest_message) + WHERE id = affected_room_id; + END IF; + + IF TG_OP = 'DELETE' THEN + RETURN OLD; + ELSE + RETURN NEW; + END IF; END; $$ LANGUAGE plpgsql; drop trigger if exists update_last_messages_trigger on chats.messages; CREATE TRIGGER update_last_messages_trigger - AFTER INSERT OR UPDATE ON chats.messages + AFTER INSERT OR UPDATE OR DELETE ON chats.messages FOR EACH ROW EXECUTE FUNCTION chats.update_last_messages(); ``` diff --git a/example/lib/src/pages/room.dart b/example/lib/src/pages/room.dart index 0918b89..4d713c4 100644 --- a/example/lib/src/pages/room.dart +++ b/example/lib/src/pages/room.dart @@ -223,6 +223,28 @@ class _RoomPageState extends State { enabled: true, onTextChanged: (text) => _chatController.onTyping(), ), + onMessageLongPress: (context, p1) async { + final confirm = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Confirmation of deletion'), + content: Text('Do you really want to delete this message?'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text('Cancel'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text('Delete'), + ), + ], + ), + ); + if (confirm == true) { + await _chatController.deleteMessage(widget.room.id, p1.id); + } + }, ), ), ), diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 1357f2a..f13f00d 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -2,7 +2,7 @@ name: example description: A new Flutter project. publish_to: 'none' -version: 1.5.0 +version: 1.5.1 environment: sdk: '>=3.4.0 <4.0.0' @@ -11,7 +11,7 @@ dependencies: cupertino_icons: ^1.0.8 dio: ^5.8.0+1 faker: ^2.2.0 - file_picker: ^8.3.1 + file_picker: ^10.2.0 file_saver: ^0.2.14 flutter: sdk: flutter @@ -20,14 +20,14 @@ dependencies: flutter_login: ^5.0.0 flutter_supabase_chat_core: path: ../ - flutter_svg: ^2.0.17 - http: ^1.3.0 + flutter_svg: ^2.2.0 + http: ^1.4.0 image_picker: ^1.1.2 infinite_scroll_pagination: ^4.1.0 - open_filex: ^4.6.0 + open_filex: ^4.7.0 path_provider: ^2.1.5 - supabase_flutter: ^2.8.3 - timeago: ^3.7.0 + supabase_flutter: ^2.9.1 + timeago: ^3.7.1 dev_dependencies: diff --git a/example/utils/sql/02_database_trigger.sql b/example/utils/sql/02_database_trigger.sql index 720a357..ba261b8 100644 --- a/example/utils/sql/02_database_trigger.sql +++ b/example/utils/sql/02_database_trigger.sql @@ -30,50 +30,46 @@ AS $$ DECLARE latest_message jsonb; ts_in_milliseconds bigint; + affected_room_id bigint; BEGIN - SELECT jsonb_build_object( - 'id', id, - 'createdAt', "createdAt", - 'metadata', metadata, - 'duration', duration, - 'mimeType', "mimeType", - 'name', name, - 'remoteId', "remoteId", - 'repliedMessage', "repliedMessage", - 'roomId', "roomId", - 'showStatus', "showStatus", - 'size', size, - 'status', status, - 'type', type, - 'updatedAt', "updatedAt", - 'uri', uri, - 'waveForm', "waveForm", - 'isLoading', "isLoading", - 'height', height, - 'width', width, - 'previewData', "previewData", - 'authorId', "authorId", - 'text', text - ) - INTO latest_message - FROM chats.messages - WHERE "roomId" = NEW."roomId" - ORDER BY "createdAt" DESC - LIMIT 1; - IF latest_message IS DISTINCT FROM (SELECT "lastMessages" FROM chats.rooms WHERE id = NEW."roomId") THEN - SELECT EXTRACT(epoch FROM NOW()) * 1000 INTO ts_in_milliseconds; - UPDATE chats.rooms - SET "updatedAt" = ts_in_milliseconds, - "lastMessages" = jsonb_build_array(latest_message) - WHERE id = NEW."roomId"; - END IF; + IF TG_OP = 'DELETE' THEN + affected_room_id := OLD."roomId"; + ELSE + affected_room_id := NEW."roomId"; + END IF; + + SELECT to_jsonb(m) + INTO latest_message + FROM chats.messages m + WHERE m."roomId" = affected_room_id + ORDER BY m."createdAt" DESC + LIMIT 1; + + IF latest_message IS DISTINCT FROM ( + SELECT value FROM jsonb_array_elements( + (SELECT "lastMessages" FROM chats.rooms WHERE id = affected_room_id) + ) LIMIT 1 + ) THEN + SELECT EXTRACT(epoch FROM NOW()) * 1000 INTO ts_in_milliseconds; + + UPDATE chats.rooms + SET "updatedAt" = ts_in_milliseconds, + "lastMessages" = jsonb_build_array(latest_message) + WHERE id = affected_room_id; + END IF; + + IF TG_OP = 'DELETE' THEN + RETURN OLD; + ELSE RETURN NEW; + END IF; END; $$ LANGUAGE plpgsql; + drop trigger if exists update_last_messages_trigger on chats.messages; CREATE TRIGGER update_last_messages_trigger - AFTER INSERT OR UPDATE ON chats.messages + AFTER INSERT OR UPDATE OR DELETE ON chats.messages FOR EACH ROW EXECUTE FUNCTION chats.update_last_messages(); diff --git a/lib/src/class/supabase_chat_controller.dart b/lib/src/class/supabase_chat_controller.dart index bf3301b..f182b3d 100644 --- a/lib/src/class/supabase_chat_controller.dart +++ b/lib/src/class/supabase_chat_controller.dart @@ -75,8 +75,10 @@ class SupabaseChatController { .order('createdAt', ascending: false) .range(pageSize * _currentPage, (_currentPage * pageSize) + pageSize); - void _onData(List> data) { - for (var val in data) { + void _onData( + List> newData, + ) { + for (var val in newData) { final author = _room.users.firstWhere( (u) => u.id == val['authorId'], orElse: () => types.User(id: val['authorId'] as String), @@ -158,6 +160,17 @@ class SupabaseChatController { 'typing': typing, }; + /// Removes message. + Future deleteMessage(String roomId, String messageId) async { + final result = + await SupabaseChatCore.instance.deleteMessage(roomId, messageId); + if (result) { + _messages.removeWhere((e) => messageId == e.id); + _messagesController.sink.add(_messages); + } + return result; + } + void dispose() { _typingChannel.untrack(); } diff --git a/lib/src/class/supabase_chat_core.dart b/lib/src/class/supabase_chat_core.dart index 4957eff..eea714c 100644 --- a/lib/src/class/supabase_chat_core.dart +++ b/lib/src/class/supabase_chat_core.dart @@ -282,13 +282,15 @@ class SupabaseChatCore { } /// Removes message. - Future deleteMessage(String roomId, String messageId) async { - await client + Future deleteMessage(String roomId, String messageId) async { + final result = await client .schema(config.schema) .from(config.messagesTableName) .delete() .eq('roomId', roomId) - .eq('id', messageId); + .eq('id', messageId) + .select(); + return result.isNotEmpty; } /// Removes room. diff --git a/lib/src/widgets/user_status_detector/web_platform.dart b/lib/src/widgets/user_status_detector/web_platform.dart index 75eb894..4450203 100644 --- a/lib/src/widgets/user_status_detector/web_platform.dart +++ b/lib/src/widgets/user_status_detector/web_platform.dart @@ -1,30 +1,32 @@ -// ignore: avoid_web_libraries_in_flutter -import 'dart:html'; - +import 'dart:js_interop'; import 'package:flutter/material.dart'; +import 'package:web/web.dart' as web; import 'platforms_widgets_binding.dart'; PlatformsWidgetsBinding getInstance() => WebWidgetsBinding(); class WebWidgetsBinding extends PlatformsWidgetsBinding { + late JSFunction _focusListener; + late JSFunction _blurListener; + @override void addObserver(WidgetsBindingObserver state) { - window.addEventListener('focus', (event) => onFocus(event, state)); - window.addEventListener('blur', (event) => onBlur(event, state)); - } + _focusListener = ((web.Event _) { + state.didChangeAppLifecycleState(AppLifecycleState.resumed); + }).toJS; - @override - void removeObserver(WidgetsBindingObserver state) { - window.removeEventListener('focus', (event) => onFocus(event, state)); - window.removeEventListener('blur', (event) => onBlur(event, state)); - } + _blurListener = ((web.Event _) { + state.didChangeAppLifecycleState(AppLifecycleState.paused); + }).toJS; - void onFocus(Event e, WidgetsBindingObserver state) { - state.didChangeAppLifecycleState(AppLifecycleState.resumed); + web.window.addEventListener('focus', _focusListener); + web.window.addEventListener('blur', _blurListener); } - void onBlur(Event e, WidgetsBindingObserver state) { - state.didChangeAppLifecycleState(AppLifecycleState.paused); + @override + void removeObserver(WidgetsBindingObserver state) { + web.window.removeEventListener('focus', _focusListener); + web.window.removeEventListener('blur', _blurListener); } } diff --git a/pubspec.yaml b/pubspec.yaml index 320a7a0..7e85840 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_supabase_chat_core description: > Actively maintained, community-driven Supabase BaaS for chat applications with an optional chat UI. -version: 1.5.0 +version: 1.6.0 homepage: https://flutter-supabase-chat-core.insideapp.it repository: https://github.com/insideapp-srl/flutter_supabase_chat_core @@ -14,10 +14,11 @@ dependencies: flutter: sdk: flutter flutter_chat_types: ^3.6.2 - meta: ^1.15.0 + meta: ^1.16.0 mime: '>=1.0.2 <3.0.0' - supabase_flutter: ^2.8.3 + supabase_flutter: ^2.9.1 uuid: ^4.5.1 + web: ^1.1.1 dev_dependencies: flutter_lints: ^5.0.0