Implemented per stream subscription recovery with polling fallback
This commit is contained in:
@@ -14,6 +14,7 @@ import 'profile_provider.dart';
|
||||
import 'supabase_provider.dart';
|
||||
import 'tickets_provider.dart';
|
||||
import 'user_offices_provider.dart';
|
||||
import 'stream_recovery.dart';
|
||||
import '../utils/app_time.dart';
|
||||
|
||||
// Helper to insert activity log rows while sanitizing nulls and
|
||||
@@ -137,12 +138,48 @@ final tasksProvider = StreamProvider<List<Task>>((ref) {
|
||||
return Stream.value(const <Task>[]);
|
||||
}
|
||||
|
||||
// NOTE: Supabase stream builder does not support `.range(...)` —
|
||||
// apply pagination and remaining filters client-side after mapping.
|
||||
final baseStream = client.from('tasks').stream(primaryKey: ['id']);
|
||||
// Wrap realtime stream with recovery logic
|
||||
final wrapper = StreamRecoveryWrapper<Task>(
|
||||
stream: client.from('tasks').stream(primaryKey: ['id']),
|
||||
onPollData: () async {
|
||||
final data = await client.from('tasks').select();
|
||||
return data.cast<Map<String, dynamic>>().map(Task.fromMap).toList();
|
||||
},
|
||||
fromMap: Task.fromMap,
|
||||
);
|
||||
|
||||
return baseStream.asyncMap((rows) async {
|
||||
final rowsList = (rows as List<dynamic>).cast<Map<String, dynamic>>();
|
||||
ref.onDispose(wrapper.dispose);
|
||||
|
||||
// Process tasks with filtering/pagination after recovery
|
||||
return wrapper.stream.asyncMap((result) async {
|
||||
final rowsList = result.data
|
||||
.map(
|
||||
(task) => <String, dynamic>{
|
||||
'id': task.id,
|
||||
'task_number': task.taskNumber,
|
||||
'office_id': task.officeId,
|
||||
'ticket_id': task.ticketId,
|
||||
'title': task.title,
|
||||
'description': task.description,
|
||||
'status': task.status,
|
||||
'priority': task.priority,
|
||||
'creator_id': task.creatorId,
|
||||
'created_at': task.createdAt.toIso8601String(),
|
||||
'started_at': task.startedAt?.toIso8601String(),
|
||||
'completed_at': task.completedAt?.toIso8601String(),
|
||||
'requested_by': task.requestedBy,
|
||||
'noted_by': task.notedBy,
|
||||
'received_by': task.receivedBy,
|
||||
'queue_order': task.queueOrder,
|
||||
'request_type': task.requestType,
|
||||
'request_type_other': task.requestTypeOther,
|
||||
'request_category': task.requestCategory,
|
||||
'action_taken': task.actionTaken,
|
||||
'cancellation_reason': task.cancellationReason,
|
||||
'cancelled_at': task.cancelledAt?.toIso8601String(),
|
||||
},
|
||||
)
|
||||
.toList();
|
||||
|
||||
final allowedTicketIds =
|
||||
ticketsAsync.valueOrNull?.map((ticket) => ticket.id).toList() ??
|
||||
@@ -325,22 +362,46 @@ final tasksQueryProvider = StateProvider<TaskQuery>((ref) => const TaskQuery());
|
||||
|
||||
final taskAssignmentsProvider = StreamProvider<List<TaskAssignment>>((ref) {
|
||||
final client = ref.watch(supabaseClientProvider);
|
||||
return client
|
||||
.from('task_assignments')
|
||||
.stream(primaryKey: ['task_id', 'user_id'])
|
||||
.map((rows) => rows.map(TaskAssignment.fromMap).toList());
|
||||
|
||||
final wrapper = StreamRecoveryWrapper<TaskAssignment>(
|
||||
stream: client
|
||||
.from('task_assignments')
|
||||
.stream(primaryKey: ['task_id', 'user_id']),
|
||||
onPollData: () async {
|
||||
final data = await client.from('task_assignments').select();
|
||||
return data.map(TaskAssignment.fromMap).toList();
|
||||
},
|
||||
fromMap: TaskAssignment.fromMap,
|
||||
);
|
||||
|
||||
ref.onDispose(wrapper.dispose);
|
||||
return wrapper.stream.map((result) => result.data);
|
||||
});
|
||||
|
||||
/// Stream of activity logs for a single task.
|
||||
final taskActivityLogsProvider =
|
||||
StreamProvider.family<List<TaskActivityLog>, String>((ref, taskId) {
|
||||
final client = ref.watch(supabaseClientProvider);
|
||||
return client
|
||||
.from('task_activity_logs')
|
||||
.stream(primaryKey: ['id'])
|
||||
.eq('task_id', taskId)
|
||||
.order('created_at', ascending: false)
|
||||
.map((rows) => rows.map((r) => TaskActivityLog.fromMap(r)).toList());
|
||||
|
||||
final wrapper = StreamRecoveryWrapper<TaskActivityLog>(
|
||||
stream: client
|
||||
.from('task_activity_logs')
|
||||
.stream(primaryKey: ['id'])
|
||||
.eq('task_id', taskId)
|
||||
.order('created_at', ascending: false),
|
||||
onPollData: () async {
|
||||
final data = await client
|
||||
.from('task_activity_logs')
|
||||
.select()
|
||||
.eq('task_id', taskId)
|
||||
.order('created_at', ascending: false);
|
||||
return data.map((r) => TaskActivityLog.fromMap(r)).toList();
|
||||
},
|
||||
fromMap: TaskActivityLog.fromMap,
|
||||
);
|
||||
|
||||
ref.onDispose(wrapper.dispose);
|
||||
return wrapper.stream.map((result) => result.data);
|
||||
});
|
||||
|
||||
final taskAssignmentsControllerProvider = Provider<TaskAssignmentsController>((
|
||||
|
||||
Reference in New Issue
Block a user