From 336cb87a06564ee3c2cd6b4988c8cc0bcd17d427 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Wed, 26 Feb 2025 21:02:07 +0100 Subject: [PATCH] Implement some sort of filtering --- lib/game.dart | 15 ++++-- lib/main.dart | 142 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 113 insertions(+), 44 deletions(-) diff --git a/lib/game.dart b/lib/game.dart index a1638c7..ef40497 100644 --- a/lib/game.dart +++ b/lib/game.dart @@ -55,10 +55,17 @@ class Game { throw Exception('No version found for $name'); } actualVersion = version; - lastUpdated = - parseRfc822Date( - response.headers['last-modified'] ?? '', - ).toIso8601String(); + try { + // Some sites use weird ass dogshit fucking formats + // We cannot really reliably parse every single one of them + // So - fuck it + lastUpdated = + parseRfc822Date( + response.headers['last-modified'] ?? '', + ).toIso8601String(); + } catch (e) { + lastUpdated = DateTime.now().toIso8601String(); + } } } diff --git a/lib/main.dart b/lib/main.dart index e6ec744..abb81d0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:gamer_updater/db.dart'; import 'package:gamer_updater/game.dart'; import 'package:gamer_updater/widgets/new_game_card.dart'; import 'package:gamer_updater/widgets/game_card.dart'; +import 'dart:async'; void main() async { await DB.init(); @@ -54,6 +55,9 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { late Map games = {}; + String searchQuery = ""; + final TextEditingController _searchController = TextEditingController(); + Timer? _debounce; @override void initState() { @@ -61,13 +65,20 @@ class _MyHomePageState extends State { _refreshGames(); } + @override + void dispose() { + _searchController.dispose(); + _debounce?.cancel(); + super.dispose(); + } + Future _refreshGames() async { final games = await GameRepository.getAll(); games.forEach((key, game) { game.updateActualVersion().then((_) { GameRepository.upsert(game); setState(() { - games[game.name] = game; + this.games[game.name] = game; }); }); }); @@ -76,6 +87,24 @@ class _MyHomePageState extends State { }); } + List get filteredGames { + if (searchQuery.isEmpty) { + return games.values.toList(); + } + return games.values.where((game) => + game.name.toLowerCase().contains(searchQuery.toLowerCase()) + ).toList(); + } + + void _onSearchChanged(String value) { + if (_debounce?.isActive ?? false) _debounce!.cancel(); + _debounce = Timer(const Duration(milliseconds: 100), () { + setState(() { + searchQuery = value; + }); + }); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -86,48 +115,81 @@ class _MyHomePageState extends State { IconButton(icon: const Icon(Icons.refresh), onPressed: _refreshGames), ], ), - body: RefreshIndicator( - onRefresh: _refreshGames, - child: SingleChildScrollView( - padding: const EdgeInsets.all(8), - child: Wrap( - spacing: 8, - runSpacing: 8, - children: [ - ...games.values.map( - (game) => SizedBox( - width: (MediaQuery.of(context).size.width - 24) / 2, - child: GameCard( - game: game, - onGameUpdated: (game) async { - game = await GameRepository.upsert(game); - setState(() { - games[game.name] = game; - }); - }, - onDelete: () async { - await GameRepository.delete(game); - setState(() { - games.remove(game.name); - }); - }, - ), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: _searchController, + decoration: InputDecoration( + hintText: 'Search games...', + prefixIcon: const Icon(Icons.search), + suffixIcon: searchQuery.isNotEmpty + ? IconButton( + icon: const Icon(Icons.clear), + onPressed: () { + _searchController.clear(); + setState(() { + searchQuery = ""; + }); + }, + ) + : null, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), ), ), - SizedBox( - width: (MediaQuery.of(context).size.width - 24) / 2, - child: NewGameCard( - onGameCreated: (game) async { - game = await GameRepository.upsert(game); - setState(() { - games[game.name] = game; - }); - }, - ), - ), - ], + onChanged: _onSearchChanged, + ), ), - ), + Expanded( + child: RefreshIndicator( + onRefresh: _refreshGames, + child: SingleChildScrollView( + padding: const EdgeInsets.all(8), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + ...filteredGames.map( + (game) => SizedBox( + key: ValueKey(game.name), + width: (MediaQuery.of(context).size.width - 24) / 2, + child: GameCard( + key: ValueKey('card_${game.name}'), + game: game, + onGameUpdated: (game) async { + game = await GameRepository.upsert(game); + setState(() { + games[game.name] = game; + }); + }, + onDelete: () async { + await GameRepository.delete(game); + setState(() { + games.remove(game.name); + }); + }, + ), + ), + ), + SizedBox( + width: (MediaQuery.of(context).size.width - 24) / 2, + child: NewGameCard( + onGameCreated: (game) async { + game = await GameRepository.upsert(game); + setState(() { + games[game.name] = game; + }); + }, + ), + ), + ], + ), + ), + ), + ), + ], ), ); }