import 'package:flutter/material.dart'; import 'package:f0ckapp/services/api.dart'; import 'package:f0ckapp/models/mediaitem.dart'; import 'package:f0ckapp/screens/detail.dart'; import 'dart:async'; class MediaGrid extends StatefulWidget { const MediaGrid({super.key}); @override State createState() => _MediaGridState(); } class _MediaGridState extends State { final ScrollController _scrollController = ScrollController(); List mediaItems = []; bool isLoading = false; Timer? _debounceTimer; Completer? _navigationCompleter; int _crossAxisCount = 3; @override void initState() { super.initState(); _loadMedia(); _scrollController.addListener(() { if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 100) { _debounceLoadMedia(); } }); } void _debounceLoadMedia() { _debounceTimer?.cancel(); _debounceTimer = Timer(const Duration(milliseconds: 500), _loadMedia); } Future _loadMedia() async { if (isLoading) return; setState(() => isLoading = true); try { final newMedia = await fetchMedia( older: mediaItems.isNotEmpty ? mediaItems.last.id.toString() : null, ); if (mounted) { setState(() => mediaItems.addAll(newMedia)); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Fehler beim Laden der Medien: $e')), ); } } finally { if (mounted) setState(() => isLoading = false); } } Future _refreshMedia() async { setState(() => isLoading = true); try { final freshMedia = await fetchMedia(older: null); if (mounted) { setState(() { mediaItems.clear(); mediaItems.addAll(freshMedia); }); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Fehler beim Aktualisieren: $e')), ); } } finally { if (mounted) setState(() => isLoading = false); } } Future _navigateToDetail(MediaItem item) async { print('Tapped item: ${item.id}'); if (_navigationCompleter?.isCompleted == false) return; _navigationCompleter = Completer(); try { final details = await fetchMediaDetail(item.id); if (mounted) { await Navigator.push( context, MaterialPageRoute(builder: (context) => DetailView(details: details)), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Fehler beim Laden der Details: $e')), ); } } finally { _navigationCompleter?.complete(); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: const Color.fromARGB(255, 43, 43, 43), foregroundColor: const Color.fromARGB(255, 255, 255, 255), title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('f0cks'), DropdownButton( value: _crossAxisCount, dropdownColor: const Color.fromARGB(255, 43, 43, 43), iconEnabledColor: const Color.fromARGB(255, 255, 255, 255), items: [3, 4].map((int value) { return DropdownMenuItem( value: value, child: Text('$value Spalten', style: TextStyle(color: Colors.white)), ); }).toList(), onChanged: (int? newValue) { if (newValue != null) { setState(() { _crossAxisCount = newValue; }); } }, ), ], ), ), body: RefreshIndicator( onRefresh: _refreshMedia, child: GridView.builder( controller: _scrollController, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: _crossAxisCount, crossAxisSpacing: 5.0, mainAxisSpacing: 5.0, ), itemCount: mediaItems.length + (isLoading ? 1 : 0), itemBuilder: (context, index) { if (index >= mediaItems.length) { return const Center(child: CircularProgressIndicator()); } final item = mediaItems[index]; final mode = {1: Colors.green, 2: Colors.red}[item.tagid] ?? Colors.yellow; return InkWell( onTap: () => _navigateToDetail(item), child: Stack( fit: StackFit.expand, children: [ Image.network(item.thumbnailUrl, fit: BoxFit.cover), Align( alignment: FractionalOffset.bottomRight, child: Icon(Icons.square, color: mode, size: 15.0), ), ], ), ); }, ), ), ); } @override void dispose() { _scrollController.dispose(); _debounceTimer?.cancel(); super.dispose(); } }