| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  | import 'dart:async'; | 
					
						
							|  |  |  | import 'dart:convert'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-20 14:13:54 +08:00
										 |  |  | import 'package:appflowy/startup/startup.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/user/application/user_auth_listener.dart'; | 
					
						
							| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  | import 'package:appflowy/user/application/user_service.dart'; | 
					
						
							|  |  |  | import 'package:appflowy_backend/dispatch/dispatch.dart'; | 
					
						
							|  |  |  | import 'package:appflowy_backend/log.dart'; | 
					
						
							|  |  |  | import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; | 
					
						
							|  |  |  | import 'package:supabase_flutter/supabase_flutter.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// A service to manage realtime interactions with Supabase.
 | 
					
						
							|  |  |  | ///
 | 
					
						
							| 
									
										
										
										
											2024-02-24 20:54:10 +07:00
										 |  |  | /// `SupabaseRealtimeService` handles subscribing to table changes in Supabase
 | 
					
						
							| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  | /// based on the authentication state of a user. The service is initialized with
 | 
					
						
							|  |  |  | /// a reference to a Supabase instance and sets up the necessary subscriptions
 | 
					
						
							|  |  |  | /// accordingly.
 | 
					
						
							| 
									
										
										
										
											2023-11-23 23:32:52 +08:00
										 |  |  | class SupabaseRealtimeService { | 
					
						
							|  |  |  |   SupabaseRealtimeService({required this.supabase}) { | 
					
						
							| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  |     _subscribeAuthState(); | 
					
						
							| 
									
										
										
										
											2023-08-20 14:13:54 +08:00
										 |  |  |     _subscribeTablesChanges(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _authStateListener.start( | 
					
						
							|  |  |  |       didSignIn: () { | 
					
						
							|  |  |  |         _subscribeTablesChanges(); | 
					
						
							|  |  |  |         isLoggingOut = false; | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2023-10-12 20:25:00 +08:00
										 |  |  |       onInvalidAuth: (message) async { | 
					
						
							| 
									
										
										
										
											2023-10-24 23:13:51 +08:00
										 |  |  |         Log.error(message); | 
					
						
							| 
									
										
										
										
											2024-01-29 10:26:45 +08:00
										 |  |  |         await channel?.unsubscribe(); | 
					
						
							| 
									
										
										
										
											2023-08-20 14:13:54 +08:00
										 |  |  |         channel = null; | 
					
						
							|  |  |  |         if (!isLoggingOut) { | 
					
						
							| 
									
										
										
										
											2023-12-30 07:05:26 +08:00
										 |  |  |           isLoggingOut = true; | 
					
						
							| 
									
										
										
										
											2023-08-20 14:13:54 +08:00
										 |  |  |           await runAppFlowy(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 16:37:36 +01:00
										 |  |  |   final Supabase supabase; | 
					
						
							|  |  |  |   final _authStateListener = UserAuthStateListener(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool isLoggingOut = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   RealtimeChannel? channel; | 
					
						
							|  |  |  |   StreamSubscription<AuthState>? authStateSubscription; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  |   void _subscribeAuthState() { | 
					
						
							|  |  |  |     final auth = Supabase.instance.client.auth; | 
					
						
							|  |  |  |     authStateSubscription = auth.onAuthStateChange.listen((state) async { | 
					
						
							| 
									
										
										
										
											2023-08-20 14:13:54 +08:00
										 |  |  |       Log.info("Supabase auth state change: ${state.event}"); | 
					
						
							| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> _subscribeTablesChanges() async { | 
					
						
							|  |  |  |     final result = await UserBackendService.getCurrentUserProfile(); | 
					
						
							| 
									
										
										
										
											2024-02-24 20:54:10 +07:00
										 |  |  |     result.fold( | 
					
						
							|  |  |  |       (userProfile) { | 
					
						
							|  |  |  |         Log.info("Start listening supabase table changes"); | 
					
						
							| 
									
										
										
										
											2024-01-30 16:05:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-24 20:54:10 +07:00
										 |  |  |         // https://supabase.com/docs/guides/realtime/postgres-changes
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const ops = RealtimeChannelConfig(ack: true); | 
					
						
							|  |  |  |         channel?.unsubscribe(); | 
					
						
							|  |  |  |         channel = supabase.client.channel("table-db-changes", opts: ops); | 
					
						
							|  |  |  |         for (final name in [ | 
					
						
							|  |  |  |           "document", | 
					
						
							|  |  |  |           "folder", | 
					
						
							|  |  |  |           "database", | 
					
						
							|  |  |  |           "database_row", | 
					
						
							|  |  |  |           "w_database", | 
					
						
							|  |  |  |         ]) { | 
					
						
							|  |  |  |           channel?.onPostgresChanges( | 
					
						
							|  |  |  |             event: PostgresChangeEvent.insert, | 
					
						
							|  |  |  |             schema: 'public', | 
					
						
							|  |  |  |             table: 'af_collab_update_$name', | 
					
						
							|  |  |  |             filter: PostgresChangeFilter( | 
					
						
							|  |  |  |               type: PostgresChangeFilterType.eq, | 
					
						
							|  |  |  |               column: 'uid', | 
					
						
							|  |  |  |               value: userProfile.id, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             callback: _onPostgresChangesCallback, | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-01-30 16:05:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         channel?.onPostgresChanges( | 
					
						
							| 
									
										
										
										
											2024-02-24 20:54:10 +07:00
										 |  |  |           event: PostgresChangeEvent.update, | 
					
						
							| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  |           schema: 'public', | 
					
						
							| 
									
										
										
										
											2024-02-24 20:54:10 +07:00
										 |  |  |           table: 'af_user', | 
					
						
							| 
									
										
										
										
											2024-01-30 16:05:56 +08:00
										 |  |  |           filter: PostgresChangeFilter( | 
					
						
							|  |  |  |             type: PostgresChangeFilterType.eq, | 
					
						
							|  |  |  |             column: 'uid', | 
					
						
							|  |  |  |             value: userProfile.id, | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |           callback: _onPostgresChangesCallback, | 
					
						
							| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  |         ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-24 20:54:10 +07:00
										 |  |  |         channel?.subscribe( | 
					
						
							|  |  |  |           (status, [err]) { | 
					
						
							|  |  |  |             Log.info( | 
					
						
							|  |  |  |               "subscribe channel statue: $status, err: $err", | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       (_) => null, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-08-20 14:13:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   Future<void> dispose() async { | 
					
						
							|  |  |  |     await _authStateListener.stop(); | 
					
						
							|  |  |  |     await authStateSubscription?.cancel(); | 
					
						
							|  |  |  |     await channel?.unsubscribe(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-01-30 16:05:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   void _onPostgresChangesCallback(PostgresChangePayload payload) { | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       final jsonStr = jsonEncode(payload); | 
					
						
							|  |  |  |       final pb = RealtimePayloadPB.create()..jsonStr = jsonStr; | 
					
						
							|  |  |  |       UserEventPushRealtimeEvent(pb).send(); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       Log.error(e); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-08-12 17:36:31 +08:00
										 |  |  | } |