Files

231 lines
7.4 KiB
Dart

import 'package:flutter/material.dart';
import '../data/share_service.dart';
import '../data/share.dart';
class ShareBottomSheet extends StatefulWidget {
const ShareBottomSheet({
super.key,
required this.igelId,
required this.shareService,
required this.accessRole, // AccessRole.owner/editor/viewer
required this.token,
});
final int igelId;
final ShareService shareService;
final String token;
final AccessRole accessRole;
@override
State<ShareBottomSheet> createState() => _ShareBottomSheetState();
}
class _ShareBottomSheetState extends State<ShareBottomSheet> {
final _emailCtrl = TextEditingController();
String _role = 'viewer';
Future<List<Share>>? _future;
final ScrollController _scrollCtrl = ScrollController();
@override
void initState() {
super.initState();
_future = widget.shareService
.listShares(igelId: widget.igelId, token: widget.token);
}
Future<void> _reload() async {
setState(() {
_future = widget.shareService
.listShares(igelId: widget.igelId, token: widget.token);
});
}
Future<void> _invite() async {
final email = _emailCtrl.text.trim();
if (email.isEmpty) return;
await widget.shareService.invite(
igelId: widget.igelId,
email: email,
role: _role,
token: widget.token,
);
_emailCtrl.clear();
await _reload();
}
Future<void> _updateRole(Share s, String newRole) async {
await widget.shareService
.updateRole(shareId: s.id, role: newRole, token: widget.token);
await _reload();
}
Future<void> _revoke(Share s) async {
await widget.shareService.revoke(shareId: s.id, token: widget.token);
await _reload();
}
@override
void dispose() {
_scrollCtrl.dispose();
_emailCtrl.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final canInvite = widget.accessRole == AccessRole.owner;
return SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(children: [
const Icon(Icons.share),
const SizedBox(width: 8),
Text('Igel teilen',
style: Theme.of(context).textTheme.titleLarge),
const Spacer(),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
]),
const SizedBox(height: 12),
// Einladen (nur Owner)
if (canInvite)
_InviteRow(
emailCtrl: _emailCtrl,
role: _role,
onRoleChanged: (v) => setState(() => _role = v),
onInvite: _invite,
),
if (canInvite) const SizedBox(height: 16),
// Liste der Freigaben
Expanded(
child: FutureBuilder<List<Share>>(
future: _future,
builder: (context, snap) {
if (snap.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snap.hasError) {
return Center(child: Text('Fehler: ${snap.error}'));
}
final items = snap.data ?? const [];
if (items.isEmpty) {
return const Center(child: Text('Keine Freigaben'));
}
return ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: Scrollbar(
controller: _scrollCtrl,
child: ListView.separated(
controller: _scrollCtrl,
primary: false,
itemCount: items.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (ctx, i) {
final s = items[i];
// Priorität: pending -> invitedEmail, accepted -> targetEmail
final label =
(s.invitedEmail != null && s.invitedEmail!.isNotEmpty)
? s.invitedEmail!
: ((s.targetEmail != null &&
s.targetEmail!.isNotEmpty)
? s.targetEmail!
: 'Unbekannt');
return ListTile(
leading: Icon(
s.status == 'accepted'
? Icons.check_circle_outline
: Icons.hourglass_bottom,
),
title: Text(label),
subtitle:
Text('Rolle: ${s.role} · Status: ${s.status}'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (canInvite && s.status == 'accepted')
PopupMenuButton<String>(
onSelected: (v) => _updateRole(s, v),
itemBuilder: (_) => const [
PopupMenuItem(
value: 'viewer', child: Text('Viewer')),
PopupMenuItem(
value: 'editor', child: Text('Editor')),
],
child: const Icon(Icons.admin_panel_settings),
),
if (canInvite)
IconButton(
icon: const Icon(Icons.remove_circle_outline),
tooltip: 'Zugriff entfernen',
onPressed: () => _revoke(s),
),
],
),
);
},
),
),
);
},
),
),
],
),
),
);
}
}
class _InviteRow extends StatelessWidget {
const _InviteRow({
required this.emailCtrl,
required this.role,
required this.onRoleChanged,
required this.onInvite,
});
final TextEditingController emailCtrl;
final String role;
final ValueChanged<String> onRoleChanged;
final VoidCallback onInvite;
@override
Widget build(BuildContext context) {
return Row(children: [
Expanded(
child: TextField(
controller: emailCtrl,
decoration: const InputDecoration(labelText: 'E-Mail einladen'),
keyboardType: TextInputType.emailAddress,
),
),
const SizedBox(width: 8),
DropdownButton<String>(
value: role,
items: const [
DropdownMenuItem(value: 'viewer', child: Text('Viewer')),
DropdownMenuItem(value: 'editor', child: Text('Editor')),
],
onChanged: (v) {
if (v != null) onRoleChanged(v);
},
),
const SizedBox(width: 8),
ElevatedButton.icon(
onPressed: onInvite,
icon: const Icon(Icons.send),
label: const Text('Einladen'),
),
]);
}
}