fix: correct member counts and member removal flow

This commit is contained in:
Lukas Müllner 2026-03-05 10:56:20 +01:00
parent c412eda221
commit bb389d6f78
2 changed files with 49 additions and 25 deletions

View file

@ -0,0 +1,7 @@
-- Run this in Supabase SQL Editor to allow a read-only viewer role.
alter table public.collection_members
drop constraint if exists collection_members_role_check;
alter table public.collection_members
add constraint collection_members_role_check
check (role in ('owner', 'member', 'viewer'));

View file

@ -115,16 +115,32 @@ class CollectionService {
itemCounts[collectionId] = (itemCounts[collectionId] ?? 0) + 1; itemCounts[collectionId] = (itemCounts[collectionId] ?? 0) + 1;
} }
// Fetch all members for these collections in a single query and count them in memory.
final members = await supabase
.from('collection_members')
.select('id, collection_id')
.inFilter('collection_id', collectionIdList);
final memberCounts = <String, int>{}; final memberCounts = <String, int>{};
for (final member in members) { await Future.wait(collectionIdList.map((collectionId) async {
final collectionId = member['collection_id'] as String; try {
memberCounts[collectionId] = (memberCounts[collectionId] ?? 0) + 1; final rows = await supabase.rpc('get_collection_members', params: {
'p_collection_id': collectionId,
});
memberCounts[collectionId] = (rows as List).length;
} catch (_) {
// Keep fallback below when RPC fails.
}
}));
// Fallback member count for collections where RPC did not return data.
final unresolvedIds = collectionIdList
.where((id) => !memberCounts.containsKey(id))
.toList(growable: false);
if (unresolvedIds.isNotEmpty) {
final members = await supabase
.from('collection_members')
.select('id, collection_id')
.inFilter('collection_id', unresolvedIds);
for (final member in members) {
final collectionId = member['collection_id'] as String;
memberCounts[collectionId] = (memberCounts[collectionId] ?? 0) + 1;
}
} }
final collections = <Collection>[]; final collections = <Collection>[];
@ -316,11 +332,22 @@ class CollectionService {
throw Exception('This user is already a member of this collection.'); throw Exception('This user is already a member of this collection.');
} }
await supabase.from('collection_members').insert({ try {
'collection_id': collectionId, await supabase.from('collection_members').insert({
'user_id': userId, 'collection_id': collectionId,
'role': normalizedRole, 'user_id': userId,
}); 'role': normalizedRole,
});
} catch (e) {
final message = e.toString();
if (normalizedRole == 'viewer' &&
message.contains('collection_members_role_check')) {
throw Exception(
'Viewer role is not enabled in your database yet. Please apply the viewer-role migration first.',
);
}
rethrow;
}
} }
/// Remove a member from a collection. /// Remove a member from a collection.
@ -343,17 +370,7 @@ class CollectionService {
throw Exception('Only the collection owner can remove members.'); throw Exception('Only the collection owner can remove members.');
} }
final target = await supabase if (memberUserId == collection['owner_id']) {
.from('collection_members')
.select('role, user_id')
.eq('user_id', memberUserId)
.eq('collection_id', collectionId)
.maybeSingle();
if (target == null) {
throw Exception('Member not found.');
}
if (target['role'] == 'owner') {
throw Exception('Collection owner cannot be removed.'); throw Exception('Collection owner cannot be removed.');
} }