399 lines
11 KiB
Dart
399 lines
11 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:pocketbase/pocketbase.dart';
|
|
|
|
import 'package:listenmeister/models/models.dart';
|
|
|
|
class ApiService {
|
|
final PocketBase pb;
|
|
|
|
ApiService(this.pb);
|
|
|
|
String? get userId => pb.authStore.record?.id;
|
|
|
|
Future<void> login(String email, String password) async {
|
|
await pb.collection('users').authWithPassword(email, password);
|
|
}
|
|
|
|
Future<void> register(String email, String password) async {
|
|
await pb
|
|
.collection('users')
|
|
.create(
|
|
body: {
|
|
'email': email,
|
|
'password': password,
|
|
'passwordConfirm': password,
|
|
'emailVisibility': true,
|
|
},
|
|
);
|
|
await login(email, password);
|
|
}
|
|
|
|
void logout() {
|
|
pb.authStore.clear();
|
|
}
|
|
|
|
Stream<void> watchLists() {
|
|
if (userId == null) return Stream.value(null);
|
|
|
|
late final StreamController<void> controller;
|
|
Future<void> Function()? unsubscribe;
|
|
|
|
controller = StreamController<void>(
|
|
onListen: () async {
|
|
unsubscribe = await pb.collection('lists').subscribe('*', (e) {
|
|
if (!controller.isClosed) {
|
|
controller.add(null);
|
|
}
|
|
}, filter: 'owner = "$userId" || members ?~ "$userId"');
|
|
},
|
|
onCancel: () {
|
|
unsubscribe?.call();
|
|
},
|
|
);
|
|
|
|
return controller.stream;
|
|
}
|
|
|
|
Future<List<Liste>> getLists() async {
|
|
if (userId == null) return [];
|
|
final List<RecordModel> res = await pb
|
|
.collection('lists')
|
|
.getFullList(filter: 'owner = "$userId" || members ?~ "$userId"');
|
|
return res.map((r) => Liste.fromJson(r.toJson())).toList();
|
|
}
|
|
|
|
Future<Liste> createList(String title) async {
|
|
if (userId == null) throw Exception('Nicht eingeloggt');
|
|
final RecordModel rec = await pb
|
|
.collection('lists')
|
|
.create(body: {'title': title, 'owner': userId});
|
|
return Liste.fromJson(rec.toJson());
|
|
}
|
|
|
|
Future<void> updateList(
|
|
String listId, {
|
|
String? title,
|
|
List<String>? members,
|
|
String? layoutId,
|
|
bool clearLayout = false,
|
|
}) async {
|
|
if (title == null && members == null && layoutId == null && !clearLayout) {
|
|
return;
|
|
}
|
|
final body = <String, dynamic>{};
|
|
if (title != null) {
|
|
body['title'] = title;
|
|
}
|
|
if (members != null) {
|
|
body['members'] = members;
|
|
}
|
|
if (layoutId != null) {
|
|
body['layout'] = layoutId;
|
|
}
|
|
if (clearLayout && layoutId == null) {
|
|
body['layout'] = null;
|
|
}
|
|
await pb.collection('lists').update(listId, body: body);
|
|
}
|
|
|
|
Future<void> deleteList(String listId) async {
|
|
await pb.collection('lists').delete(listId);
|
|
}
|
|
|
|
Stream<void> watchItems(String listId) {
|
|
late final StreamController<void> controller;
|
|
Future<void> Function()? unsubscribe;
|
|
|
|
controller = StreamController<void>(
|
|
onListen: () async {
|
|
unsubscribe = await pb.collection('items').subscribe('*', (e) {
|
|
if (!controller.isClosed) {
|
|
controller.add(null);
|
|
}
|
|
}, filter: 'list = "$listId"');
|
|
},
|
|
onCancel: () {
|
|
unsubscribe?.call();
|
|
},
|
|
);
|
|
|
|
return controller.stream;
|
|
}
|
|
|
|
Future<List<Item>> getItems(String listId) async {
|
|
final List<RecordModel> res = await pb
|
|
.collection('items')
|
|
.getFullList(filter: 'list = "$listId"', sort: 'position');
|
|
return res.map((r) => Item.fromJson(r.toJson())).toList();
|
|
}
|
|
|
|
Future<Item> createItem(
|
|
String listId,
|
|
String name,
|
|
int position, {
|
|
String? sectionId,
|
|
}) async {
|
|
final RecordModel rec = await pb
|
|
.collection('items')
|
|
.create(
|
|
body: {
|
|
'list': listId,
|
|
'name': name,
|
|
'position': position,
|
|
'checked': false,
|
|
if (sectionId != null) 'section': sectionId,
|
|
},
|
|
);
|
|
return Item.fromJson(rec.toJson());
|
|
}
|
|
|
|
Future<void> deleteItem(String itemId) async {
|
|
await pb.collection('items').delete(itemId);
|
|
}
|
|
|
|
Future<void> updateItem(
|
|
String itemId, {
|
|
bool? checked,
|
|
String? name,
|
|
String? sectionId,
|
|
bool clearSection = false,
|
|
}) async {
|
|
if (checked == null && name == null && sectionId == null && !clearSection) {
|
|
return;
|
|
}
|
|
final Map<String, dynamic> body = <String, dynamic>{};
|
|
if (checked != null) {
|
|
body['checked'] = checked;
|
|
}
|
|
if (name != null) {
|
|
body['name'] = name;
|
|
}
|
|
if (sectionId != null) {
|
|
body['section'] = sectionId;
|
|
}
|
|
if (clearSection && sectionId == null) {
|
|
body['section'] = null;
|
|
}
|
|
await pb.collection('items').update(itemId, body: body);
|
|
}
|
|
|
|
Future<RecordModel?> findUserByEmail(String email) async {
|
|
try {
|
|
return await pb.collection('users').getFirstListItem('email = "$email"');
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<List<RecordModel>> getUsersByIds(List<String> ids) async {
|
|
if (ids.isEmpty) return [];
|
|
final String filter = ids.map((id) => 'id = "$id"').join(' || ');
|
|
return await pb.collection('users').getFullList(filter: filter);
|
|
}
|
|
|
|
Stream<void> watchListsForLayout(String layoutId) {
|
|
late final StreamController<void> controller;
|
|
Future<void> Function()? unsubscribe;
|
|
|
|
controller = StreamController<void>(
|
|
onListen: () async {
|
|
unsubscribe = await pb.collection('lists').subscribe('*', (event) {
|
|
if (!controller.isClosed) {
|
|
controller.add(null);
|
|
}
|
|
}, filter: 'layout = "$layoutId"');
|
|
},
|
|
onCancel: () => unsubscribe?.call(),
|
|
);
|
|
|
|
return controller.stream;
|
|
}
|
|
|
|
Future<List<Liste>> getListsForLayout(String layoutId) async {
|
|
final List<RecordModel> res = await pb
|
|
.collection('lists')
|
|
.getFullList(filter: 'layout = "$layoutId"');
|
|
return res.map((r) => Liste.fromJson(r.toJson())).toList();
|
|
}
|
|
|
|
Stream<void> watchStoreLayouts() {
|
|
if (userId == null) return Stream.value(null);
|
|
|
|
late final StreamController<void> controller;
|
|
Future<void> Function()? unsubscribe;
|
|
|
|
controller = StreamController<void>(
|
|
onListen: () async {
|
|
final String filter = 'owner = "$userId" || isPublic = true';
|
|
unsubscribe = await pb.collection('store_layouts').subscribe('*', (e) {
|
|
if (!controller.isClosed) {
|
|
controller.add(null);
|
|
}
|
|
}, filter: filter);
|
|
},
|
|
onCancel: () => unsubscribe?.call(),
|
|
);
|
|
|
|
return controller.stream;
|
|
}
|
|
|
|
Stream<void> watchStoreLayout(String layoutId) {
|
|
late final StreamController<void> controller;
|
|
Future<void> Function()? unsubscribe;
|
|
|
|
controller = StreamController<void>(
|
|
onListen: () async {
|
|
unsubscribe = await pb.collection('store_layouts').subscribe(layoutId, (
|
|
e,
|
|
) {
|
|
if (!controller.isClosed) {
|
|
controller.add(null);
|
|
}
|
|
});
|
|
},
|
|
onCancel: () => unsubscribe?.call(),
|
|
);
|
|
|
|
return controller.stream;
|
|
}
|
|
|
|
Future<List<StoreLayout>> getStoreLayouts({bool includePublic = true}) async {
|
|
if (userId == null) return [];
|
|
final List<String> filterParts = ['owner = "$userId"'];
|
|
if (includePublic) {
|
|
filterParts.add('isPublic = true');
|
|
}
|
|
final String filter = filterParts.join(' || ');
|
|
final List<RecordModel> res = await pb
|
|
.collection('store_layouts')
|
|
.getFullList(filter: filter, sort: '-updated');
|
|
final layouts = res.map((r) => StoreLayout.fromJson(r.toJson())).toList();
|
|
layouts.sort((a, b) {
|
|
final bool aOwn = a.owner == userId;
|
|
final bool bOwn = b.owner == userId;
|
|
if (aOwn == bOwn) {
|
|
return a.name.toLowerCase().compareTo(b.name.toLowerCase());
|
|
}
|
|
return aOwn ? -1 : 1;
|
|
});
|
|
return layouts;
|
|
}
|
|
|
|
Future<StoreLayout> createStoreLayout({
|
|
required String name,
|
|
double? latitude,
|
|
double? longitude,
|
|
String? address,
|
|
bool isPublic = false,
|
|
}) async {
|
|
if (userId == null) throw Exception('Nicht eingeloggt');
|
|
final Map<String, dynamic> body = {
|
|
'name': name,
|
|
'owner': userId,
|
|
'isPublic': isPublic,
|
|
};
|
|
if (latitude != null) body['latitude'] = latitude;
|
|
if (longitude != null) body['longitude'] = longitude;
|
|
if (address != null && address.isNotEmpty) body['address'] = address;
|
|
|
|
final RecordModel rec = await pb
|
|
.collection('store_layouts')
|
|
.create(body: body);
|
|
return StoreLayout.fromJson(rec.toJson());
|
|
}
|
|
|
|
Future<StoreLayout?> getStoreLayoutById(String layoutId) async {
|
|
try {
|
|
final RecordModel rec = await pb
|
|
.collection('store_layouts')
|
|
.getOne(layoutId);
|
|
return StoreLayout.fromJson(rec.toJson());
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<void> updateStoreLayout(
|
|
String layoutId, {
|
|
String? name,
|
|
double? latitude,
|
|
double? longitude,
|
|
String? address,
|
|
bool? isPublic,
|
|
bool clearLatitude = false,
|
|
bool clearLongitude = false,
|
|
bool clearAddress = false,
|
|
}) async {
|
|
final Map<String, dynamic> body = {};
|
|
if (name != null) body['name'] = name;
|
|
if (latitude != null) body['latitude'] = latitude;
|
|
if (clearLatitude && latitude == null) body['latitude'] = null;
|
|
if (longitude != null) body['longitude'] = longitude;
|
|
if (clearLongitude && longitude == null) body['longitude'] = null;
|
|
if (address != null) body['address'] = address;
|
|
if (clearAddress && address == null) body['address'] = null;
|
|
if (isPublic != null) body['isPublic'] = isPublic;
|
|
|
|
if (body.isEmpty) return;
|
|
|
|
await pb.collection('store_layouts').update(layoutId, body: body);
|
|
}
|
|
|
|
Future<void> deleteStoreLayout(String layoutId) async {
|
|
await pb.collection('store_layouts').delete(layoutId);
|
|
}
|
|
|
|
Stream<void> watchStoreSections(String layoutId) {
|
|
late final StreamController<void> controller;
|
|
Future<void> Function()? unsubscribe;
|
|
|
|
controller = StreamController<void>(
|
|
onListen: () async {
|
|
unsubscribe = await pb.collection('store_sections').subscribe('*', (e) {
|
|
if (!controller.isClosed) {
|
|
controller.add(null);
|
|
}
|
|
}, filter: 'layout = "$layoutId"');
|
|
},
|
|
onCancel: () => unsubscribe?.call(),
|
|
);
|
|
|
|
return controller.stream;
|
|
}
|
|
|
|
Future<List<StoreSection>> getStoreSections(String layoutId) async {
|
|
final List<RecordModel> res = await pb
|
|
.collection('store_sections')
|
|
.getFullList(filter: 'layout = "$layoutId"', sort: 'position');
|
|
return res.map((r) => StoreSection.fromJson(r.toJson())).toList();
|
|
}
|
|
|
|
Future<StoreSection> createStoreSection({
|
|
required String layoutId,
|
|
required String name,
|
|
int position = 0,
|
|
}) async {
|
|
final RecordModel rec = await pb
|
|
.collection('store_sections')
|
|
.create(body: {'layout': layoutId, 'name': name, 'position': position});
|
|
return StoreSection.fromJson(rec.toJson());
|
|
}
|
|
|
|
Future<void> updateStoreSection(
|
|
String sectionId, {
|
|
String? name,
|
|
int? position,
|
|
}) async {
|
|
final Map<String, dynamic> body = {};
|
|
if (name != null) body['name'] = name;
|
|
if (position != null) body['position'] = position;
|
|
if (body.isEmpty) return;
|
|
await pb.collection('store_sections').update(sectionId, body: body);
|
|
}
|
|
|
|
Future<void> deleteStoreSection(String sectionId) async {
|
|
await pb.collection('store_sections').delete(sectionId);
|
|
}
|
|
}
|