// gets all quotes from the database and returns them in a list import 'package:path_provider/path_provider.dart'; import 'package:flutter/material.dart'; import 'package:frontend/model/quote.dart'; import 'package:frontend/repository/quote_repository.dart'; import 'dart:math'; import 'dart:io'; class QuoteProvider extends ChangeNotifier { List _quotes = []; Set _authors = {}; bool _loading = false; String? _error; List get quotes => _quotes; Set get authors => _authors; bool get loading => _loading; String? get error => _error; List get filteredQuotes { final query = filterController.text.toLowerCase(); final filtered = _quotes.where((quote) { switch (filterBy) { case 0: return quote.author.toLowerCase().contains(query); case 1: return quote.body.toLowerCase().contains(query); default: return true; } }).toList(); // sort descending filtered.sort((a, b) => b.id.compareTo(a.id)); return filtered; } List> get sortedAuthorCounts { final counts = {}; for (var quote in filteredQuotes) { counts.update( quote.author, (value) => value + 1, ifAbsent: () => 1, ); } final list = counts.entries.toList() ..sort((a, b) => b.value.compareTo(a.value)); // descending return list; } final QuoteRepository repo = QuoteRepository(); int filterBy = 0; // 0 = by author, 1 = by quote var filterController = TextEditingController(text: ''); void clearFilterController() { filterController.text = ''; } Quote randomQuote = Quote( id: 0, body: "quote text", author: "dummy" ); bool randomQuoteDeterminator = true; String get randomQuoteText { if (randomQuoteDeterminator) { return randomQuote.body; } else { return " – ${randomQuote.author}"; } } void flipRandomQuote() { randomQuoteDeterminator = !randomQuoteDeterminator; notifyListeners(); } void nextRandomQuote() { final random = Random(); randomQuote = filteredQuotes[random.nextInt(filteredQuotes.length)]; notifyListeners(); } void filterQuotes() { notifyListeners(); } QuoteProvider() { readQuotes(); // auto-load on creation } void populateAuthors() { _authors = {}; for (var i = 0; i < quotes.length; i++) { _authors.add(quotes[i].author); } } void setFilterBy(int value) { filterBy = value; notifyListeners(); } Future readQuotes() async { _loading = true; _error = null; notifyListeners(); try { _quotes = await repo.readQuotes(); } catch (e) { _error = e.toString(); } populateAuthors(); _loading = false; notifyListeners(); } Future createQuote(Quote quote) async { final newQuote = await repo.createQuote(quote); _quotes.add(newQuote); populateAuthors(); notifyListeners(); } Future updateQuote(Quote quote) async { try { _loading = true; notifyListeners(); final updated = await repo.updateQuote(quote); final index = _quotes.indexWhere((q) => q.id == quote.id); if (index != -1) { final oldAuthor = _quotes[index].author; final newList = List.from(_quotes); newList[index] = updated; _quotes = newList; Map authorMap = {}; for (final q in quotes) { authorMap[q.author] = (authorMap[q.author] ?? 0) + 1; } if (authorMap[oldAuthor] == 1) { _authors.remove(oldAuthor); } _authors.add(updated.author); } } finally { _loading = false; notifyListeners(); } } Future deleteQuote(Quote quote) async { await repo.deleteQuote(quote); _quotes.removeWhere((q) => q.id == quote.id); populateAuthors(); notifyListeners(); } Future _getSaveDirectory() async { if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { final path = Platform.isWindows ? Platform.environment['USERPROFILE'] : Platform.environment['HOME']; return Directory(path!); } // Mobile fallback return await getApplicationDocumentsDirectory(); } Future exportQuotesToFile() async { final dir = await _getSaveDirectory(); final datetime = DateTime.now().toUtc().toString() .replaceAll(" ", "T") .replaceAll(":", "-") .replaceAll(RegExp(r"\..*"), ""); final file = File('${dir.path}/hlasky_$datetime.txt'); await file.writeAsString("${filteredQuotes.join("\n")}\n"); } Color getLeaderboardEntryBgColor(BuildContext context, int index) { switch (index) { case 0: return Colors.yellow.shade300; // 1st place case 1: return Colors.grey.shade300; // 2nd place case 2: return Colors.brown.shade300; // 3rd place default: return Theme.of(context).cardColor; // default for all other entries } } Color getLeaderboardEntryFgColor(BuildContext context, int index) { switch (index) { case 0: case 1: case 2: return Colors.black; default: return Theme.of(context).textTheme.bodyMedium!.color!; } } }