Files
hlaskovnik-reimagined/frontend/lib/provider/quote_provider.dart
T
2026-04-22 19:45:24 +02:00

243 lines
6.0 KiB
Dart
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// gets all quotes from the database and returns them in a list
import 'package:file_saver/file_saver.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/foundation.dart'; // for kIsWeb
import 'dart:convert';
import 'dart:typed_data';
import 'package:go_router/go_router.dart';
import 'package:file_picker/file_picker.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<Quote> _quotes = [];
Set<String> _authors = {};
bool _loading = false;
String? _error;
List<Quote> get quotes => _quotes;
Set<String> get authors => _authors;
bool get loading => _loading;
String? get error => _error;
List<Quote> 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<MapEntry<String, int>> get sortedAuthorCounts {
final counts = <String, int>{};
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 = '';
}
late Quote randomQuote;
bool randomQuoteDeterminator = true;
String exportDir = "";
String get randomQuoteText {
if (randomQuoteDeterminator) {
return randomQuote.body;
} else {
return " ${randomQuote.author}";
}
}
void flipRandomQuote() {
randomQuoteDeterminator = !randomQuoteDeterminator;
notifyListeners();
}
void nextRandomQuote() {
if (filteredQuotes.isEmpty) return;
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<void> readQuotes() async {
_loading = true;
notifyListeners();
try {
_quotes = await repo.readQuotes();
populateAuthors();
if (_quotes.isNotEmpty) {
nextRandomQuote();
}
} catch (e) {
_error = e.toString();
}
_loading = false;
notifyListeners();
}
Future<void> createQuote(Quote quote) async {
final newQuote = await repo.createQuote(quote);
_quotes.add(newQuote);
populateAuthors();
notifyListeners();
}
Future<void> 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<Quote>.from(_quotes);
newList[index] = updated;
_quotes = newList;
Map<String, int> 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<void> deleteQuote(Quote quote) async {
await repo.deleteQuote(quote);
_quotes.removeWhere((q) => q.id == quote.id);
populateAuthors();
notifyListeners();
}
Future<bool> exportWithSAF() async {
try {
final datetime = DateTime.now().toUtc().toString()
.replaceAll(" ", "T")
.replaceAll(":", "-")
.replaceAll(RegExp(r"\..*"), "");
final filename = 'hlasky_$datetime.txt';
final content = filteredQuotes.map((q) => "${q.body} ${q.author}").join('\n') + '\n';
final bytes = Uint8List.fromList(utf8.encode(content));
// Show folder picker
final folderPath = await FilePicker.platform.getDirectoryPath();
if (folderPath == null) return false; // user cancelled
exportDir = folderPath;
final file = File('$folderPath/$filename');
await file.writeAsBytes(bytes);
return true; // export successful
} catch (e) {
return false; // something went wrong
}
}
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!;
}
}
void showListPageFilteredByAuthor(BuildContext context, int index) {
filterController.text = sortedAuthorCounts[index].key;
filterBy = 0; // filter by author
// navigate to the main page
GoRouter.of(context).go('/');
Future.microtask(() => notifyListeners());
}
void resetLeaderboardFilter() {
filterController.text = '';
filterBy = 0;
notifyListeners();
}
}