igel share

This commit is contained in:
2025-10-24 23:34:56 +02:00
parent 1cd4aebbeb
commit 74414e7728
9 changed files with 1493 additions and 520 deletions
+19
View File
@@ -0,0 +1,19 @@
import 'share.dart';
class AccessCache {
// Map<igelId, AccessRole>
final _map = <int, AccessRole>{};
void setRole(int igelId, AccessRole role) => _map[igelId] = role;
AccessRole getRole(int igelId) => _map[igelId] ?? AccessRole.none;
}
AccessRole roleFromString(String s) {
switch (s) {
case 'viewer':
return AccessRole.viewer;
case 'editor':
return AccessRole.editor;
default:
return AccessRole.none;
}
}
+66
View File
@@ -0,0 +1,66 @@
class Share {
final int id;
final int hedgehogId;
final int ownerUserId;
final int? targetUserId;
final String? invitedEmail;
final String? targetEmail; // bei accepted (vom Backend)
final String role; // 'viewer' | 'editor'
final String status; // 'pending' | 'accepted' | 'revoked'
final DateTime? createdAt;
final DateTime? updatedAt;
Share({
required this.id,
required this.hedgehogId,
required this.ownerUserId,
this.targetUserId,
this.invitedEmail,
this.targetEmail,
required this.role,
required this.status,
this.createdAt,
this.updatedAt,
});
factory Share.fromJson(Map<String, dynamic> j) => Share(
id: j['id'] as int,
hedgehogId: j['hedgehog_id'] as int,
ownerUserId: j['owner_user_id'] as int,
targetUserId: j['target_user_id'] as int?,
invitedEmail: j['invited_email'] as String?,
targetEmail: j['target_email'] as String?,
role: j['role'] as String,
status: j['status'] as String,
createdAt:
j['created_at'] != null ? DateTime.tryParse(j['created_at']) : null,
updatedAt:
j['updated_at'] != null ? DateTime.tryParse(j['updated_at']) : null,
);
}
class SharedIgelItem {
final int id; // igel.id
final String name;
final String role; // viewer|editor
final int ownerUserId;
final String? ownerEmail; // <— NEU
SharedIgelItem({
required this.id,
required this.name,
required this.role,
required this.ownerUserId,
this.ownerEmail, // <— NEU
});
factory SharedIgelItem.fromJson(Map<String, dynamic> j) => SharedIgelItem(
id: j['id'] as int,
name: j['name'] as String,
role: j['role'] as String,
ownerUserId: j['owner_user_id'] as int,
ownerEmail: j['owner_email'] as String?, // <— NEU
);
}
enum AccessRole { owner, editor, viewer, none }
+85
View File
@@ -0,0 +1,85 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'share.dart';
class ShareService {
ShareService({http.Client? client, required this.baseUrl})
: _client = client ?? http.Client();
final http.Client _client;
final String baseUrl; // z.B. https://api.windesign.at
Map<String, String> _auth(String token, {Map<String, String>? extra}) =>
{'Authorization': 'Bearer $token', if (extra != null) ...extra};
Future<List<Share>> listShares(
{required int igelId, required String token}) async {
final r = await _client.get(
Uri.parse('$baseUrl/hedgehogs.php?r=/igel/$igelId/shares'),
headers: _auth(token),
);
if (r.statusCode != 200)
throw Exception('listShares failed (${r.statusCode}) ${r.body}');
final List data = jsonDecode(r.body) as List;
return data.map((e) => Share.fromJson(e as Map<String, dynamic>)).toList();
}
Future<void> invite(
{required int igelId,
required String email,
required String role,
required String token}) async {
final r = await _client.post(
Uri.parse('$baseUrl/hedgehogs.php?r=/igel/$igelId/shares'),
headers: _auth(token, extra: {'Content-Type': 'application/json'}),
body: jsonEncode({'email': email, 'role': role}),
);
if (r.statusCode != 201)
throw Exception('invite failed (${r.statusCode}) ${r.body}');
}
Future<void> updateRole(
{required int shareId,
required String role,
required String token}) async {
final r = await _client.patch(
Uri.parse('$baseUrl/hedgehogs.php?r=/shares/$shareId'),
headers: _auth(token, extra: {'Content-Type': 'application/json'}),
body: jsonEncode({'role': role}),
);
if (r.statusCode != 200)
throw Exception('updateRole failed (${r.statusCode}) ${r.body}');
}
Future<void> revoke({required int shareId, required String token}) async {
final r = await _client.delete(
Uri.parse('$baseUrl/hedgehogs.php?r=/shares/$shareId'),
headers: _auth(token),
);
if (r.statusCode != 200)
throw Exception('revoke failed (${r.statusCode}) ${r.body}');
}
Future<void> acceptInvite(
{required String tokenJwt, required String inviteToken}) async {
final r = await _client.post(
Uri.parse('$baseUrl/hedgehogs.php?r=/invites/accept'),
headers: _auth(tokenJwt, extra: {'Content-Type': 'application/json'}),
body: jsonEncode({'token': inviteToken}),
);
if (r.statusCode != 200)
throw Exception('acceptInvite failed (${r.statusCode}) ${r.body}');
}
Future<List<SharedIgelItem>> listSharedWithMe({required String token}) async {
final r = await _client.get(
Uri.parse('$baseUrl/hedgehogs.php?r=/me/shared'),
headers: _auth(token),
);
if (r.statusCode != 200)
throw Exception('listSharedWithMe failed (${r.statusCode}) ${r.body}');
final List data = jsonDecode(r.body) as List;
return data
.map((e) => SharedIgelItem.fromJson(e as Map<String, dynamic>))
.toList();
}
}