129 lines
3.8 KiB
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);
|
|
}
|
|
}
|