Add caching to session filter and remove some redundant providers

This commit is contained in:
Nutcake 2023-07-11 18:44:20 +02:00
parent 56350ea2c7
commit e87521df9d
11 changed files with 639 additions and 590 deletions

View file

@ -1,17 +1,28 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:contacts_plus_plus/apis/session_api.dart'; import 'package:contacts_plus_plus/apis/session_api.dart';
import 'package:contacts_plus_plus/clients/api_client.dart'; import 'package:contacts_plus_plus/clients/api_client.dart';
import 'package:contacts_plus_plus/clients/settings_client.dart';
import 'package:contacts_plus_plus/models/session.dart'; import 'package:contacts_plus_plus/models/session.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
class SessionClient extends ChangeNotifier { class SessionClient extends ChangeNotifier {
final ApiClient apiClient; final ApiClient apiClient;
final SettingsClient settingsClient;
Future<List<Session>>? _sessionsFuture; Future<List<Session>>? _sessionsFuture;
SessionFilterSettings _filterSettings = SessionFilterSettings.empty(); SessionFilterSettings _filterSettings = SessionFilterSettings.empty();
SessionClient({required this.apiClient}); SessionClient({required this.apiClient, required this.settingsClient}) {
_filterSettings = SessionFilterSettings(
name: "",
hostName: "",
includeEnded: settingsClient.currentSettings.sessionViewLastIncludeEnded.valueOrDefault,
includeIncompatible: settingsClient.currentSettings.sessionViewLastIncludeIncompatible.valueOrDefault,
minActiveUsers: settingsClient.currentSettings.sessionViewLastMinimumUsers.valueOrDefault,
includeEmptyHeadless: settingsClient.currentSettings.sessionViewLastIncludeEmpty.valueOrDefault,
);
}
SessionFilterSettings get filterSettings => _filterSettings; SessionFilterSettings get filterSettings => _filterSettings;
@ -22,12 +33,16 @@ class SessionClient extends ChangeNotifier {
reloadSessions(); reloadSessions();
} }
void reloadSessions() { void initSessions() {
_sessionsFuture = SessionApi.getSessions(apiClient, filterSettings: _filterSettings).then( _sessionsFuture = SessionApi.getSessions(apiClient, filterSettings: _filterSettings).then(
(value) => value.sorted( (value) => value.sorted(
(a, b) => b.sessionUsers.length.compareTo(a.sessionUsers.length), (a, b) => b.sessionUsers.length.compareTo(a.sessionUsers.length),
), ),
); );
}
void reloadSessions() {
initSessions();
notifyListeners(); notifyListeners();
} }
} }

View file

@ -8,17 +8,12 @@ import 'package:contacts_plus_plus/clients/messaging_client.dart';
import 'package:contacts_plus_plus/clients/session_client.dart'; import 'package:contacts_plus_plus/clients/session_client.dart';
import 'package:contacts_plus_plus/clients/settings_client.dart'; import 'package:contacts_plus_plus/clients/settings_client.dart';
import 'package:contacts_plus_plus/models/sem_ver.dart'; import 'package:contacts_plus_plus/models/sem_ver.dart';
import 'package:contacts_plus_plus/widgets/friends/friends_list_app_bar.dart';
import 'package:contacts_plus_plus/widgets/homepage.dart'; import 'package:contacts_plus_plus/widgets/homepage.dart';
import 'package:contacts_plus_plus/widgets/inventory/inventory_browser_app_bar.dart';
import 'package:contacts_plus_plus/widgets/login_screen.dart'; import 'package:contacts_plus_plus/widgets/login_screen.dart';
import 'package:contacts_plus_plus/widgets/sessions/session_list_app_bar.dart';
import 'package:contacts_plus_plus/widgets/settings_app_bar.dart';
import 'package:contacts_plus_plus/widgets/update_notifier.dart'; import 'package:contacts_plus_plus/widgets/update_notifier.dart';
import 'package:dynamic_color/dynamic_color.dart'; import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:flutter_phoenix/flutter_phoenix.dart'; import 'package:flutter_phoenix/flutter_phoenix.dart';
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
@ -35,8 +30,6 @@ void main() async {
debug: kDebugMode, debug: kDebugMode,
); );
Provider.debugCheckInvalidValueType = null;
await Hive.initFlutter(); await Hive.initFlutter();
final dateFormat = DateFormat.Hms(); final dateFormat = DateFormat.Hms();
@ -156,19 +149,19 @@ class _ContactsPlusPlusState extends State<ContactsPlusPlus> {
return _authData.isAuthenticated return _authData.isAuthenticated
? MultiProvider( ? MultiProvider(
providers: [ providers: [
Provider( ChangeNotifierProvider(
create: (context) => MessagingClient( create: (context) => MessagingClient(
apiClient: clientHolder.apiClient, apiClient: clientHolder.apiClient,
notificationClient: clientHolder.notificationClient, notificationClient: clientHolder.notificationClient,
), ),
dispose: (context, value) => value.dispose(),
), ),
Provider( ChangeNotifierProvider(
create: (context) => SessionClient( create: (context) => SessionClient(
apiClient: clientHolder.apiClient, apiClient: clientHolder.apiClient,
settingsClient: clientHolder.settingsClient,
), ),
), ),
Provider( ChangeNotifierProvider(
create: (context) => InventoryClient( create: (context) => InventoryClient(
apiClient: clientHolder.apiClient, apiClient: clientHolder.apiClient,
), ),

View file

@ -40,31 +40,47 @@ class Settings {
final SettingsEntry<String> lastDismissedVersion; final SettingsEntry<String> lastDismissedVersion;
final SettingsEntry<String> machineId; final SettingsEntry<String> machineId;
final SettingsEntry<int> themeMode; final SettingsEntry<int> themeMode;
final SettingsEntry<int> sessionViewLastMinimumUsers;
final SettingsEntry<bool> sessionViewLastIncludeEnded;
final SettingsEntry<bool> sessionViewLastIncludeEmpty;
final SettingsEntry<bool> sessionViewLastIncludeIncompatible;
Settings({ Settings({
SettingsEntry<bool>? notificationsDenied, SettingsEntry<bool>? notificationsDenied,
SettingsEntry<int>? lastOnlineStatus, SettingsEntry<int>? lastOnlineStatus,
SettingsEntry<int>? themeMode, SettingsEntry<int>? themeMode,
SettingsEntry<String>? lastDismissedVersion, SettingsEntry<String>? lastDismissedVersion,
SettingsEntry<String>? machineId SettingsEntry<String>? machineId,
}) SettingsEntry<int>? sessionViewLastMinimumUsers,
: notificationsDenied = notificationsDenied ?? const SettingsEntry<bool>(deflt: false), SettingsEntry<bool>? sessionViewLastIncludeEnded,
SettingsEntry<bool>? sessionViewLastIncludeEmpty,
SettingsEntry<bool>? sessionViewLastIncludeIncompatible,
}) : notificationsDenied = notificationsDenied ?? const SettingsEntry<bool>(deflt: false),
lastOnlineStatus = lastOnlineStatus ?? SettingsEntry<int>(deflt: OnlineStatus.online.index), lastOnlineStatus = lastOnlineStatus ?? SettingsEntry<int>(deflt: OnlineStatus.online.index),
themeMode = themeMode ?? SettingsEntry<int>(deflt: ThemeMode.dark.index), themeMode = themeMode ?? SettingsEntry<int>(deflt: ThemeMode.dark.index),
lastDismissedVersion = lastDismissedVersion ?? SettingsEntry<String>(deflt: SemVer.zero().toString()), lastDismissedVersion = lastDismissedVersion ?? SettingsEntry<String>(deflt: SemVer.zero().toString()),
machineId = machineId ?? SettingsEntry<String>(deflt: const Uuid().v4()); machineId = machineId ?? SettingsEntry<String>(deflt: const Uuid().v4()),
sessionViewLastMinimumUsers = sessionViewLastMinimumUsers ?? const SettingsEntry<int>(deflt: 0),
sessionViewLastIncludeEnded = sessionViewLastIncludeEnded ?? const SettingsEntry<bool>(deflt: false),
sessionViewLastIncludeEmpty = sessionViewLastIncludeEmpty ?? const SettingsEntry<bool>(deflt: true),
sessionViewLastIncludeIncompatible =
sessionViewLastIncludeIncompatible ?? const SettingsEntry<bool>(deflt: false);
factory Settings.fromMap(Map map) { factory Settings.fromMap(Map map) {
return Settings( return Settings(
notificationsDenied: retrieveEntryOrNull<bool>(map["notificationsDenied"]), notificationsDenied: getEntryOrNull<bool>(map["notificationsDenied"]),
lastOnlineStatus: retrieveEntryOrNull<int>(map["lastOnlineStatus"]), lastOnlineStatus: getEntryOrNull<int>(map["lastOnlineStatus"]),
themeMode: retrieveEntryOrNull<int>(map["themeMode"]), themeMode: getEntryOrNull<int>(map["themeMode"]),
lastDismissedVersion: retrieveEntryOrNull<String>(map["lastDismissedVersion"]), lastDismissedVersion: getEntryOrNull<String>(map["lastDismissedVersion"]),
machineId: retrieveEntryOrNull<String>(map["machineId"]), machineId: getEntryOrNull<String>(map["machineId"]),
sessionViewLastMinimumUsers: getEntryOrNull<int>(map["sessionViewLastMinimumUsers"]),
sessionViewLastIncludeEnded: getEntryOrNull<bool>(map["sessionViewLastIncludeEnded"]),
sessionViewLastIncludeEmpty: getEntryOrNull<bool>(map["sessionViewLastIncludeEmpty"]),
sessionViewLastIncludeIncompatible: getEntryOrNull<bool>(map["sessionViewLastIncludeIncompatible"]),
); );
} }
static SettingsEntry<T>? retrieveEntryOrNull<T>(Map? map) { static SettingsEntry<T>? getEntryOrNull<T>(Map? map) {
if (map == null) return null; if (map == null) return null;
try { try {
return SettingsEntry<T>.fromMap(map); return SettingsEntry<T>.fromMap(map);
@ -80,6 +96,10 @@ class Settings {
"themeMode": themeMode.toMap(), "themeMode": themeMode.toMap(),
"lastDismissedVersion": lastDismissedVersion.toMap(), "lastDismissedVersion": lastDismissedVersion.toMap(),
"machineId": machineId.toMap(), "machineId": machineId.toMap(),
"sessionViewLastMinimumUsers": sessionViewLastMinimumUsers.toMap(),
"sessionViewLastIncludeEnded": sessionViewLastIncludeEnded.toMap(),
"sessionViewLastIncludeEmpty": sessionViewLastIncludeEmpty.toMap(),
"sessionViewLastIncludeIncompatible": sessionViewLastIncludeIncompatible.toMap(),
}; };
} }
@ -91,6 +111,10 @@ class Settings {
int? themeMode, int? themeMode,
String? lastDismissedVersion, String? lastDismissedVersion,
String? machineId, String? machineId,
int? sessionViewLastMinimumUsers,
bool? sessionViewLastIncludeEnded,
bool? sessionViewLastIncludeEmpty,
bool? sessionViewLastIncludeIncompatible,
}) { }) {
return Settings( return Settings(
notificationsDenied: this.notificationsDenied.passThrough(notificationsDenied), notificationsDenied: this.notificationsDenied.passThrough(notificationsDenied),
@ -98,6 +122,11 @@ class Settings {
themeMode: this.themeMode.passThrough(themeMode), themeMode: this.themeMode.passThrough(themeMode),
lastDismissedVersion: this.lastDismissedVersion.passThrough(lastDismissedVersion), lastDismissedVersion: this.lastDismissedVersion.passThrough(lastDismissedVersion),
machineId: this.machineId.passThrough(machineId), machineId: this.machineId.passThrough(machineId),
sessionViewLastMinimumUsers: this.sessionViewLastMinimumUsers.passThrough(sessionViewLastMinimumUsers),
sessionViewLastIncludeEnded: this.sessionViewLastIncludeEnded.passThrough(sessionViewLastIncludeEnded),
sessionViewLastIncludeEmpty: this.sessionViewLastIncludeEmpty.passThrough(sessionViewLastIncludeEmpty),
sessionViewLastIncludeIncompatible:
this.sessionViewLastIncludeIncompatible.passThrough(sessionViewLastIncludeIncompatible),
); );
} }
} }

View file

@ -5,7 +5,6 @@ import 'package:contacts_plus_plus/widgets/friends/friend_list_tile.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class FriendsList extends StatefulWidget { class FriendsList extends StatefulWidget {
const FriendsList({super.key}); const FriendsList({super.key});
@ -19,12 +18,13 @@ class _FriendsListState extends State<FriendsList> with AutomaticKeepAliveClient
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
return ChangeNotifierProvider.value( return Consumer<MessagingClient>(
value: Provider.of<MessagingClient>(context, listen: false), builder: (context, mClient, _) {
child: Stack( return Stack(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
children: [ children: [
Consumer<MessagingClient>(builder: (context, mClient, _) { Builder(
builder: (context) {
if (mClient.initStatus == null) { if (mClient.initStatus == null) {
return const LinearProgressIndicator(); return const LinearProgressIndicator();
} else if (mClient.initStatus!.isNotEmpty) { } else if (mClient.initStatus!.isNotEmpty) {
@ -62,7 +62,8 @@ class _FriendsListState extends State<FriendsList> with AutomaticKeepAliveClient
}, },
); );
} }
}), },
),
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: ExpandingInputFab( child: ExpandingInputFab(
@ -81,7 +82,8 @@ class _FriendsListState extends State<FriendsList> with AutomaticKeepAliveClient
), ),
), ),
], ],
), );
},
); );
} }

View file

@ -47,9 +47,7 @@ class _FriendsListAppBarState extends State<FriendsListAppBar> with AutomaticKee
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
return ChangeNotifierProvider.value( return AppBar(
value: Provider.of<MessagingClient>(context, listen: false),
child: AppBar(
title: const Text("Contacts++"), title: const Text("Contacts++"),
actions: [ actions: [
FutureBuilder( FutureBuilder(
@ -198,7 +196,6 @@ class _FriendsListAppBarState extends State<FriendsListAppBar> with AutomaticKee
), ),
) )
], ],
),
); );
} }

View file

@ -36,9 +36,7 @@ class _InventoryBrowserState extends State<InventoryBrowser> with AutomaticKeepA
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
return ChangeNotifierProvider.value( return Consumer<InventoryClient>(builder: (BuildContext context, InventoryClient iClient, Widget? child) {
value: Provider.of<InventoryClient>(context),
child: Consumer<InventoryClient>(builder: (BuildContext context, InventoryClient iClient, Widget? child) {
return FutureBuilder<NeosDirectory>( return FutureBuilder<NeosDirectory>(
future: iClient.directoryFuture, future: iClient.directoryFuture,
builder: (context, snapshot) { builder: (context, snapshot) {
@ -224,8 +222,7 @@ class _InventoryBrowserState extends State<InventoryBrowser> with AutomaticKeepA
), ),
); );
}); });
}), });
);
} }
@override @override

View file

@ -48,9 +48,7 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider.value( return Consumer<InventoryClient>(
value: Provider.of<InventoryClient>(context),
child: Consumer<InventoryClient>(
builder: (BuildContext context, InventoryClient iClient, Widget? child) { builder: (BuildContext context, InventoryClient iClient, Widget? child) {
return AnimatedSwitcher( return AnimatedSwitcher(
duration: const Duration(milliseconds: 350), duration: const Duration(milliseconds: 350),
@ -243,7 +241,6 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
), ),
); );
}, },
),
); );
} }
} }

View file

@ -1,6 +1,8 @@
import 'dart:math'; import 'dart:math';
import 'package:contacts_plus_plus/client_holder.dart';
import 'package:contacts_plus_plus/clients/session_client.dart'; import 'package:contacts_plus_plus/clients/session_client.dart';
import 'package:contacts_plus_plus/clients/settings_client.dart';
import 'package:contacts_plus_plus/models/session.dart'; import 'package:contacts_plus_plus/models/session.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -44,9 +46,20 @@ class _SessionFilterDialogState extends State<SessionFilterDialog> {
super.dispose(); super.dispose();
} }
Future<void> _updateSettings() async {
final settingsClient = ClientHolder.of(context).settingsClient;
await settingsClient.changeSettings(settingsClient.currentSettings.copyWith(
sessionViewLastMinimumUsers: _currentFilter.minActiveUsers,
sessionViewLastIncludeEnded: _currentFilter.includeEnded,
sessionViewLastIncludeEmpty: _currentFilter.includeEmptyHeadless,
sessionViewLastIncludeIncompatible: _currentFilter.includeIncompatible,
));
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(
insetPadding: const EdgeInsets.all(24),
title: const Text("Filter"), title: const Text("Filter"),
content: SizedBox( content: SizedBox(
width: double.infinity, width: double.infinity,
@ -109,7 +122,8 @@ class _SessionFilterDialogState extends State<SessionFilterDialog> {
IconButton( IconButton(
onPressed: () { onPressed: () {
setState(() { setState(() {
_currentFilter = _currentFilter.copyWith(minActiveUsers: _currentFilter.minActiveUsers + 1, includeEmptyHeadless: false); _currentFilter = _currentFilter.copyWith(
minActiveUsers: _currentFilter.minActiveUsers + 1, includeEmptyHeadless: false);
}); });
}, },
icon: const Icon(Icons.add_circle_outline), icon: const Icon(Icons.add_circle_outline),
@ -128,7 +142,9 @@ class _SessionFilterDialogState extends State<SessionFilterDialog> {
SessionFilterCheckbox( SessionFilterCheckbox(
label: "Include Empty Headless", label: "Include Empty Headless",
value: _currentFilter.includeEmptyHeadless && _currentFilter.minActiveUsers == 0, value: _currentFilter.includeEmptyHeadless && _currentFilter.minActiveUsers == 0,
onChanged: _currentFilter.minActiveUsers > 0 ? null : (value) { onChanged: _currentFilter.minActiveUsers > 0
? null
: (value) {
setState(() { setState(() {
_currentFilter = _currentFilter.copyWith(includeEmptyHeadless: value); _currentFilter = _currentFilter.copyWith(includeEmptyHeadless: value);
}); });
@ -155,9 +171,10 @@ class _SessionFilterDialogState extends State<SessionFilterDialog> {
child: const Text("Cancel"), child: const Text("Cancel"),
), ),
TextButton( TextButton(
onPressed: () { onPressed: () async {
Provider.of<SessionClient>(context, listen: false).filterSettings = _currentFilter; Provider.of<SessionClient>(context, listen: false).filterSettings = _currentFilter;
Navigator.of(context).pop(); Navigator.of(context).pop();
await _updateSettings();
}, },
child: const Text("Okay"), child: const Text("Okay"),
), ),

View file

@ -21,7 +21,7 @@ class _SessionListState extends State<SessionList> with AutomaticKeepAliveClient
super.didChangeDependencies(); super.didChangeDependencies();
final sClient = Provider.of<SessionClient>(context, listen: false); final sClient = Provider.of<SessionClient>(context, listen: false);
if (sClient.sessionsFuture == null) { if (sClient.sessionsFuture == null) {
sClient.reloadSessions(); sClient.initSessions();
} }
} }

View file

@ -1,4 +1,6 @@
import 'package:contacts_plus_plus/client_holder.dart';
import 'package:contacts_plus_plus/clients/session_client.dart'; import 'package:contacts_plus_plus/clients/session_client.dart';
import 'package:contacts_plus_plus/clients/settings_client.dart';
import 'package:contacts_plus_plus/widgets/sessions/session_filter_dialog.dart'; import 'package:contacts_plus_plus/widgets/sessions/session_filter_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -23,7 +25,7 @@ class _SessionListAppBarState extends State<SessionListAppBar> {
final sessionClient = Provider.of<SessionClient>(context, listen: false); final sessionClient = Provider.of<SessionClient>(context, listen: false);
await showDialog( await showDialog(
context: context, context: context,
builder: (context) => Provider.value( builder: (context) => ChangeNotifierProvider.value(
value: sessionClient, value: sessionClient,
child: SessionFilterDialog( child: SessionFilterDialog(
lastFilter: sessionClient.filterSettings, lastFilter: sessionClient.filterSettings,

View file

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.4.1+1 version: 1.4.2+1
environment: environment:
sdk: '>=3.0.1' sdk: '>=3.0.1'