From 99d7595f2d8d9086ae4a5f4caf42e1a3efc83324 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Wed, 21 Dec 2022 03:08:56 -0500 Subject: [PATCH] Added category add/remove (no recolour/rename for now) --- lib/pages/settings.dart | 110 +++++++++++++++++++++++++++ lib/providers/settings_provider.dart | 9 +++ lib/providers/source_provider.dart | 10 ++- 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 279f6f9..b57d9f5 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -1,8 +1,12 @@ +import 'dart:math'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/components/generated_form.dart'; +import 'package:obtainium/components/generated_form_modal.dart'; import 'package:obtainium/custom_errors.dart'; +import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/logs_provider.dart'; import 'package:obtainium/providers/settings_provider.dart'; import 'package:obtainium/providers/source_provider.dart'; @@ -17,11 +21,27 @@ class SettingsPage extends StatefulWidget { State createState() => _SettingsPageState(); } +// Generates a random light color +// Courtesy of ChatGPT 😭 (with a bugfix 🥳) +Color generateRandomLightColor() { + // Create a random number generator + final Random random = Random(); + + // Generate random hue, saturation, and value values + final double hue = random.nextDouble() * 360; + final double saturation = 0.5 + random.nextDouble() * 0.5; + final double value = 0.9 + random.nextDouble() * 0.1; + + // Create a HSV color with the random values + return HSVColor.fromAHSV(1.0, hue, saturation, value).toColor(); +} + class _SettingsPageState extends State { @override Widget build(BuildContext context) { SettingsProvider settingsProvider = context.watch(); SourceProvider sourceProvider = SourceProvider(); + AppsProvider appsProvider = context.read(); if (settingsProvider.prefs == null) { settingsProvider.initializeSettings(); } @@ -157,6 +177,8 @@ class _SettingsPageState extends State { height: 16, ); + var categories = settingsProvider.categories; + return Scaffold( backgroundColor: Theme.of(context).colorScheme.surface, body: CustomScrollView(slivers: [ @@ -232,6 +254,94 @@ class _SettingsPageState extends State { color: Theme.of(context).colorScheme.primary), ), ...sourceSpecificFields, + intervalDropdown, + const Divider( + height: 48, + ), + Text( + 'Categories', // TODO + style: TextStyle( + color: Theme.of(context).colorScheme.primary), + ), + height16, + Wrap( + children: [ + ...categories.entries.toList().map((e) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4), + child: Chip( + label: Text(e.key), + backgroundColor: Color(e.value), + visualDensity: VisualDensity.compact, + onDeleted: () { + showDialog?>( + context: context, + builder: (BuildContext ctx) { + return GeneratedFormModal( + // TODO + title: 'Delete Category?', + message: + 'All Apps in ${e.key} will be set to uncategorized.', + items: []); + }).then((value) { + if (value != null) { + setState(() { + categories.remove(e.key); + settingsProvider.categories = + categories; + }); + appsProvider.saveApps(appsProvider + .apps.values + .where((element) => + element.app.category == + e.key) + .map((e) { + var a = e.app; + a.category = null; + return a; + }).toList()); + } + }); + }, + )); + }), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4), + child: IconButton( + onPressed: () { + showDialog?>( + context: context, + builder: (BuildContext ctx) { + // TODO + return GeneratedFormModal( + title: 'Add Category', + items: [ + [ + GeneratedFormItem('label', + label: 'Label') + ] + ]); + }).then((value) { + String? label = value?['label']; + if (label != null) { + setState(() { + categories[label] = + generateRandomLightColor() + .value; + settingsProvider.categories = + categories; + }); + } + }); + }, + icon: const Icon(Icons.add), + visualDensity: VisualDensity.compact, + tooltip: tr('add'), + )) + ], + ) ], ))), SliverToBoxAdapter( diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 02bdb54..b0e66a1 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -1,5 +1,7 @@ // Exposes functions used to save/load app settings +import 'dart:convert'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -144,4 +146,11 @@ class SettingsProvider with ChangeNotifier { prefs?.setString(settingId, value); notifyListeners(); } + + Map get categories => + Map.from(jsonDecode(prefs?.getString('categories') ?? '{}')); + + set categories(Map cats) { + prefs?.setString('categories', jsonEncode(cats)); + } } diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 5966456..0160004 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -47,6 +47,7 @@ class App { late Map additionalSettings; late DateTime? lastUpdateCheck; bool pinned = false; + String? category; App( this.id, this.url, @@ -58,7 +59,8 @@ class App { this.preferredApkIndex, this.additionalSettings, this.lastUpdateCheck, - this.pinned); + this.pinned, + {this.category}); @override String toString() { @@ -107,7 +109,8 @@ class App { json['lastUpdateCheck'] == null ? null : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), - json['pinned'] ?? false); + json['pinned'] ?? false, + category: json['category']); } Map toJson() => { @@ -121,7 +124,8 @@ class App { 'preferredApkIndex': preferredApkIndex, 'additionalSettings': jsonEncode(additionalSettings), 'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch, - 'pinned': pinned + 'pinned': pinned, + 'category': category }; }