import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:listenmeister/main.dart'; import 'package:listenmeister/models/models.dart'; import 'package:listenmeister/pages/list_detail.dart'; import 'package:listenmeister/pages/layouts.dart'; import 'package:listenmeister/providers/theme.dart'; class ListsPage extends StatefulWidget { const ListsPage({super.key}); @override State createState() => _ListsPageState(); } class _ListsPageState extends State { late Future> _listsFuture; StreamSubscription? _listsSubscription; Map _layoutNameById = {}; bool _layoutsLoading = false; @override void initState() { super.initState(); _loadLists(); _listsSubscription = apiService.watchLists().listen((_) { _loadLists(); }); } @override void dispose() { _listsSubscription?.cancel(); super.dispose(); } void _loadLists() { _listsFuture = apiService.getLists(); setState(() {}); _refreshLayoutCache(); } Future _refreshLayoutCache() async { if (!mounted) return; setState(() => _layoutsLoading = true); try { final layouts = await apiService.getStoreLayouts(); if (!mounted) return; setState(() { _layoutNameById = { for (final layout in layouts) layout.id: layout.name, }; _layoutsLoading = false; }); } catch (_) { if (!mounted) return; setState(() => _layoutsLoading = false); } } Future _showListMenu(Liste list) async { final String? result = await showModalBottomSheet( context: context, builder: (c) => Wrap( children: [ if (list.owner == apiService.userId) ListTile( leading: const Icon(Icons.store_mall_directory_outlined), title: const Text('Layout zuordnen'), onTap: () => Navigator.pop(c, 'layout'), ), if (list.owner == apiService.userId) const Divider(height: 0), ListTile( leading: const Icon(Icons.edit), title: const Text('Umbenennen'), onTap: () => Navigator.pop(c, 'edit'), ), ListTile( leading: const Icon(Icons.delete), title: const Text('Löschen'), onTap: () => Navigator.pop(c, 'delete'), ), ], ), ); if (result == 'edit') { _editList(list); } else if (result == 'layout') { _assignLayout(list); } else if (result == 'delete') { _deleteList(list); } } Future _assignLayout(Liste list) async { if (list.owner != apiService.userId) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Nur Besitzer können Layouts zuordnen.'), ), ); } return; } List layouts = []; try { layouts = await apiService.getStoreLayouts(); if (!mounted) return; } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Layouts konnten nicht geladen werden: $e')), ); return; } const String noLayoutOption = '__no_layout__'; final String? currentId = list.layoutId; final String? selection = await showModalBottomSheet( context: context, builder: (context) { return SafeArea( child: ListView( shrinkWrap: true, children: [ ListTile( leading: const Icon(Icons.close), title: const Text('Kein Layout zuordnen'), trailing: currentId == null ? const Icon(Icons.check, size: 20) : null, onTap: () => Navigator.pop(context, noLayoutOption), ), if (layouts.isNotEmpty) const Divider(height: 0), if (layouts.isEmpty) const Padding( padding: EdgeInsets.all(16), child: Text('Keine Layouts verfügbar.'), ), ...layouts.map((layout) { return ListTile( leading: Icon( layout.owner == apiService.userId ? Icons.store_mall_directory : Icons.share, ), title: Text(layout.name), subtitle: layout.address != null && layout.address!.isNotEmpty ? Text(layout.address!) : null, trailing: currentId == layout.id ? const Icon(Icons.check, size: 20) : null, onTap: () => Navigator.pop(context, layout.id), ); }), ], ), ); }, ); if (!mounted) return; if (selection == null) return; final String? newLayoutId = selection == noLayoutOption ? null : selection; if (newLayoutId == currentId) return; try { await apiService.updateList( list.id, layoutId: newLayoutId, clearLayout: newLayoutId == null, ); list.layoutId = newLayoutId; _loadLists(); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Layout konnte nicht gespeichert werden: $e')), ); } } } Future _editList(Liste list) async { final TextEditingController titleCtl = TextEditingController( text: list.title, ); final bool? ok = await showDialog( context: context, builder: (c) => AlertDialog( title: const Text('Liste umbenennen'), content: TextField( controller: titleCtl, decoration: const InputDecoration(labelText: 'Titel'), autofocus: true, ), actions: [ TextButton( onPressed: () => Navigator.pop(c, false), child: const Text('Abbrechen'), ), TextButton( onPressed: () => Navigator.pop(c, true), child: const Text('Speichern'), ), ], ), ); if (ok != true || titleCtl.text.trim().isEmpty) return; try { await apiService.updateList(list.id, title: titleCtl.text.trim()); _loadLists(); } catch (e) { if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Fehler: $e'))); } } } Future _deleteList(Liste list) async { final bool? ok = await showDialog( context: context, builder: (c) => AlertDialog( title: const Text('Liste löschen?'), content: Text( 'Möchtest du die Liste "${list.title}" wirklich löschen?', ), actions: [ TextButton( onPressed: () => Navigator.pop(c, false), child: const Text('Abbrechen'), ), TextButton( onPressed: () => Navigator.pop(c, true), child: const Text('Löschen'), ), ], ), ); if (ok != true) return; try { await apiService.deleteList(list.id); _loadLists(); } catch (e) { if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Fehler: $e'))); } } } Future _createList() async { final TextEditingController titleCtl = TextEditingController(); final bool? ok = await showDialog( context: context, builder: (c) => AlertDialog( title: const Text('Neue Liste'), content: TextField( controller: titleCtl, decoration: const InputDecoration(labelText: 'Titel'), autofocus: true, ), actions: [ TextButton( onPressed: () => Navigator.pop(c, false), child: const Text('Abbrechen'), ), TextButton( onPressed: () => Navigator.pop(c, true), child: const Text('Erstellen'), ), ], ), ); if (ok != true || titleCtl.text.trim().isEmpty) return; try { await apiService.createList(titleCtl.text.trim()); _loadLists(); } catch (e) { if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Fehler: $e'))); } } } void _openList(Liste list) { Navigator.push( context, MaterialPageRoute(builder: (_) => ListDetailPage(list: list)), ).then((_) => _loadLists()); } @override Widget build(BuildContext context) { final ThemeProvider themeProvider = Provider.of( context, listen: false, ); return Scaffold( appBar: AppBar( title: const Text('Deine Listen'), actions: [ IconButton( tooltip: 'Ladenlayouts', icon: const Icon(Icons.store_mall_directory_outlined), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (_) => const LayoutsPage()), ); }, ), PopupMenuButton( onSelected: (mode) => themeProvider.setThemeMode(mode), itemBuilder: (context) => [ const PopupMenuItem(value: ThemeMode.light, child: Text('Hell')), const PopupMenuItem(value: ThemeMode.dark, child: Text('Dunkel')), const PopupMenuItem( value: ThemeMode.system, child: Text('System'), ), ], icon: const Icon(Icons.brightness_6_outlined), ), IconButton( tooltip: 'Ausloggen', icon: const Icon(Icons.logout), onPressed: () { apiService.logout(); }, ), ], ), body: FutureBuilder>( future: _listsFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text('Fehler: ${snapshot.error}')); } final lists = snapshot.data ?? []; if (lists.isEmpty) { return const Center(child: Text('Keine Listen gefunden.')); } return ListView.builder( itemCount: lists.length, itemBuilder: (c, i) { final l = lists[i]; final String? layoutName = l.layoutId != null ? _layoutNameById[l.layoutId!] : null; final String? subtitle = layoutName != null ? 'Layout: $layoutName' : l.layoutId != null ? (_layoutsLoading ? 'Layout wird geladen...' : 'Layout-ID: ${l.layoutId}') : null; Widget? trailing; if (l.owner == apiService.userId) { trailing = IconButton( tooltip: l.layoutId != null ? 'Layout wechseln' : 'Layout zuordnen', icon: Icon( l.layoutId != null ? Icons.store_mall_directory : Icons.store_mall_directory_outlined, ), onPressed: () => _assignLayout(l), ); } else if (l.layoutId != null) { trailing = const Icon(Icons.store_mall_directory_outlined); } return ListTile( title: Text(l.title), subtitle: subtitle != null ? Text(subtitle) : null, onTap: () => _openList(l), onLongPress: () => _showListMenu(l), trailing: trailing, ); }, ); }, ), floatingActionButton: FloatingActionButton( onPressed: _createList, child: const Icon(Icons.add), ), ); } }