Roadbound-BRR/lib/models/channels/text_channel.dart

129 lines
3.8 KiB
Dart

import "package:bus_running_record/models/channels/base_channel.dart";
import "package:supabase_flutter/supabase_flutter.dart";
class TextChannelMessage {
const TextChannelMessage({
required this.id,
required this.channelId,
required this.authorUserId,
required this.content,
required this.createdAt,
});
final String id;
final String channelId;
final String authorUserId;
final String content;
final DateTime? createdAt;
factory TextChannelMessage.fromMap(Map<String, dynamic> map) {
return TextChannelMessage(
id: (map["id"] ?? "").toString(),
channelId: (map["channel_id"] ?? "").toString(),
authorUserId: (map["author_user_id"] ?? "").toString(),
content: (map["content"] ?? "").toString(),
createdAt: DateTime.tryParse((map["created_at"] ?? "").toString()),
);
}
Map<String, dynamic> toJson() {
return {
"id": id,
"channel_id": channelId,
"author_user_id": authorUserId,
"content": content,
"created_at": createdAt?.toIso8601String(),
};
}
}
class TextChannel extends BaseChannel {
TextChannel({
required super.client,
required super.id,
required super.organizationId,
required super.name,
required super.description,
required super.slug,
required super.isPrivate,
required super.position,
}) : super(kind: ChannelKind.text);
factory TextChannel.fromApi({
required SupabaseClient client,
required Map<String, dynamic> map,
}) {
return TextChannel(
client: client,
id: (map["id"] ?? "").toString(),
organizationId: (map["organization_id"] ?? "").toString(),
name: (map["name"] ?? "").toString(),
description: (map["description"] ?? map["topic"] ?? "").toString(),
slug: (map["slug"] ?? "").toString(),
isPrivate: map["is_private"] == true,
position: (map["position"] as num?)?.toInt() ?? 0,
);
}
Future<List<TextChannelMessage>> listMessages({int limit = 50}) async {
final rows = await client
.from("messages")
.select("id, channel_id, author_user_id, content, created_at")
.eq("channel_id", id)
.order("created_at", ascending: true)
.limit(limit);
return BaseChannel.asList(rows)
.map((row) => TextChannelMessage.fromMap(BaseChannel.asMap(row)))
.where((message) => message.id.isNotEmpty)
.toList();
}
Future<TextChannelMessage?> sendMessage(String content) async {
final trimmed = content.trim();
if (trimmed.isEmpty) return null;
final authorUserId = client.auth.currentUser?.id;
if (authorUserId == null || authorUserId.isEmpty) {
throw StateError("Cannot send message without an authenticated user.");
}
final inserted = await client
.from("messages")
.insert({
"channel_id": id,
"author_user_id": authorUserId,
"content": trimmed,
})
.select("id, channel_id, author_user_id, content, created_at")
.single();
return TextChannelMessage.fromMap(BaseChannel.asMap(inserted));
}
RealtimeChannel subscribeToMessages({
required void Function() onMessageChanged,
void Function(RealtimeSubscribeStatus status, Object? error)? onStatus,
}) {
final topic = "messages:$id";
return client.channel(topic)
..onPostgresChanges(
event: PostgresChangeEvent.all,
schema: "public",
table: "messages",
filter: PostgresChangeFilter(
type: PostgresChangeFilterType.eq,
column: "channel_id",
value: id,
),
callback: (_) => onMessageChanged(),
)
..subscribe((status, [error]) {
onStatus?.call(status, error);
});
}
Future<void> unsubscribe(RealtimeChannel channel) async {
await client.removeChannel(channel);
}
}