221 lines
5.3 KiB
Dart
221 lines
5.3 KiB
Dart
// 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<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 = '';
|
||
}
|
||
|
||
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<void> readQuotes() async {
|
||
_loading = true;
|
||
_error = null;
|
||
notifyListeners();
|
||
|
||
try {
|
||
_quotes = await repo.readQuotes();
|
||
} catch (e) {
|
||
_error = e.toString();
|
||
}
|
||
|
||
populateAuthors();
|
||
|
||
_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<Directory> _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<void> 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!;
|
||
}
|
||
}
|
||
} |