Settings model to persist logged in user

This commit is contained in:
franzap
2024-05-10 15:45:03 -03:00
parent 025e3d1f9f
commit f4609a31ee
8 changed files with 119 additions and 24 deletions
+4
View File
@@ -9,12 +9,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:zapstore/models/app.dart';
import 'package:zapstore/models/file_metadata.dart';
import 'package:zapstore/models/release.dart';
import 'package:zapstore/models/settings.dart';
import 'package:zapstore/models/user.dart';
final adapterProvidersMap = <String, Provider<Adapter<DataModelMixin>>>{
'apps': appsAdapterProvider,
'fileMetadata': fileMetadataAdapterProvider,
'releases': releasesAdapterProvider,
'settings': settingsAdapterProvider,
'users': usersAdapterProvider
};
@@ -22,6 +24,7 @@ extension AdapterWidgetRefX on WidgetRef {
Adapter<App> get apps => watch(appsAdapterProvider)..internalWatch = watch;
Adapter<FileMetadata> get fileMetadata => watch(fileMetadataAdapterProvider)..internalWatch = watch;
Adapter<Release> get releases => watch(releasesAdapterProvider)..internalWatch = watch;
Adapter<Settings> get settings => watch(settingsAdapterProvider)..internalWatch = watch;
Adapter<User> get users => watch(usersAdapterProvider)..internalWatch = watch;
}
@@ -30,5 +33,6 @@ extension AdapterRefX on Ref {
Adapter<App> get apps => watch(appsAdapterProvider)..internalWatch = watch as Watcher;
Adapter<FileMetadata> get fileMetadata => watch(fileMetadataAdapterProvider)..internalWatch = watch as Watcher;
Adapter<Release> get releases => watch(releasesAdapterProvider)..internalWatch = watch as Watcher;
Adapter<Settings> get settings => watch(settingsAdapterProvider)..internalWatch = watch as Watcher;
Adapter<User> get users => watch(usersAdapterProvider)..internalWatch = watch as Watcher;
}
+25
View File
@@ -0,0 +1,25 @@
import 'package:flutter_data/flutter_data.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:zapstore/models/user.dart';
part 'settings.g.dart';
@JsonSerializable()
@DataAdapter([SettingsAdapter])
class Settings extends DataModel<Settings> {
@override
String get id => '_';
final BelongsTo<User> user = BelongsTo();
bool isLoggedIn = false;
}
mixin SettingsAdapter on Adapter<Settings> {
@override
Future<void> onInitialized() async {
// ensure it's always present
if (!existsId('_')) {
Settings().saveLocal();
}
super.onInitialized();
}
}
+68
View File
@@ -0,0 +1,68 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'settings.dart';
// **************************************************************************
// AdapterGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, duplicate_ignore
mixin _$SettingsAdapter on Adapter<Settings> {
static final Map<String, RelationshipMeta> _kSettingsRelationshipMetas = {
'user': RelationshipMeta<User>(
name: 'user',
type: 'users',
kind: 'BelongsTo',
instance: (_) => (_ as Settings).user,
)
};
@override
Map<String, RelationshipMeta> get relationshipMetas =>
_kSettingsRelationshipMetas;
@override
Settings deserializeLocal(map, {String? key}) {
map = transformDeserialize(map);
return internalWrapStopInit(() => _$SettingsFromJson(map), key: key);
}
@override
Map<String, dynamic> serializeLocal(model, {bool withRelationships = true}) {
final map = _$SettingsToJson(model);
return transformSerialize(map, withRelationships: withRelationships);
}
}
final _settingsFinders = <String, dynamic>{};
class $SettingsAdapter = Adapter<Settings>
with _$SettingsAdapter, SettingsAdapter;
final settingsAdapterProvider = Provider<Adapter<Settings>>(
(ref) => $SettingsAdapter(ref, InternalHolder(_settingsFinders)));
extension SettingsAdapterX on Adapter<Settings> {
SettingsAdapter get settingsAdapter => this as SettingsAdapter;
}
extension SettingsRelationshipGraphNodeX on RelationshipGraphNode<Settings> {
RelationshipGraphNode<User> get user {
final meta = _$SettingsAdapter._kSettingsRelationshipMetas['user']
as RelationshipMeta<User>;
return meta.clone(
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
}
}
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Settings _$SettingsFromJson(Map<String, dynamic> json) =>
Settings()..isLoggedIn = json['isLoggedIn'] as bool;
Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
'isLoggedIn': instance.isLoggedIn,
};
+5 -6
View File
@@ -14,7 +14,6 @@ import 'package:zapstore/main.data.dart';
import 'package:zapstore/models/app.dart';
import 'package:zapstore/models/release.dart';
import 'package:zapstore/models/user.dart';
import 'package:zapstore/services/session_service.dart';
import 'package:zapstore/utils/extensions.dart';
import 'package:zapstore/widgets/card.dart';
import 'package:zapstore/widgets/pill_widget.dart';
@@ -116,7 +115,7 @@ class AppDetailScreen extends HookConsumerWidget {
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
SizedBox(child: Text('Source')),
SizedBox(child: Text('Source ')),
Flexible(
child: AutoSizeText(
app.repository!,
@@ -172,7 +171,7 @@ class AppDetailScreen extends HookConsumerWidget {
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text('SHA-256 hash'),
Text('SHA-256 hash '),
Flexible(
child: Text(
'${app.latestMetadata!.hash!.substring(0, 26)}...',
@@ -454,8 +453,8 @@ class InstallButton extends ConsumerWidget {
child: switch (app.status) {
AppInstallStatus.noArch =>
Text('Sorry, release does not support your device'),
AppInstallStatus.downgrade =>
Text('Installed version is higher, can\'t downgrade'),
AppInstallStatus.downgrade => Text(
'Installed version ${app.installedVersion ?? ''} is higher, can\'t downgrade'),
AppInstallStatus.updated => Text('Open'),
_ => switch (progress) {
IdleInstallProgress() => app.canUpdate
@@ -492,7 +491,7 @@ class InstallAlertDialog extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(loggedInUser);
final user = ref.settings.watchOne('_').model!.user.value;
return AlertDialog(
elevation: 10,
title: Text(
+3 -4
View File
@@ -7,7 +7,6 @@ import 'package:purplebase/purplebase.dart';
import 'package:zapstore/main.dart';
import 'package:zapstore/main.data.dart';
import 'package:zapstore/models/app.dart';
import 'package:zapstore/services/session_service.dart';
import 'package:zapstore/utils/extensions.dart';
class SettingsScreen extends HookConsumerWidget {
@@ -16,6 +15,8 @@ class SettingsScreen extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final controller = useTextEditingController();
final user = ref.settings.watchOne('_').model!.user.value;
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
@@ -44,7 +45,6 @@ class SettingsScreen extends HookConsumerWidget {
loadingWidget: SizedBox(
width: 14, height: 14, child: CircularProgressIndicator()),
onPressed: () async {
final user = ref.read(loggedInUser);
if (user == null) {
scaffoldKey.currentState!.openDrawer();
return;
@@ -61,8 +61,7 @@ class SettingsScreen extends HookConsumerWidget {
child: child,
);
},
child: Text(
ref.watch(loggedInUser) != null ? 'Send' : 'Tap to log in'),
child: Text(user != null ? 'Send' : 'Tap to log in'),
),
],
),
-4
View File
@@ -1,4 +0,0 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:zapstore/models/user.dart';
final loggedInUser = StateProvider<User?>((ref) => null);
+12 -8
View File
@@ -4,7 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:zapstore/main.data.dart';
import 'package:zapstore/services/session_service.dart';
import 'package:zapstore/models/settings.dart';
import 'package:zapstore/widgets/card.dart';
class AppDrawer extends HookConsumerWidget {
@@ -12,7 +12,11 @@ class AppDrawer extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(loggedInUser);
final user = ref.settings
.watchOne('_', alsoWatch: (_) => {_.user})
.model
?.user
.value;
final controller = useTextEditingController();
return Padding(
@@ -44,8 +48,8 @@ class AppDrawer extends HookConsumerWidget {
color: Colors.lightBlue, size: 18),
],
),
if (user.following.isNotEmpty)
Text('${user.following.length} contacts'),
// if (user.following.isNotEmpty)
// Text('${user.following.length} contacts'),
],
),
],
@@ -69,9 +73,9 @@ class AppDrawer extends HookConsumerWidget {
child: CircularProgressIndicator(),
),
onPressed: () async {
ref.read(loggedInUser.notifier).state = await ref.users
.findOne(controller.text.trim(),
params: {'contacts': true});
final user =
await ref.users.findOne(controller.text.trim());
ref.settings.findOneLocalById('_')!.user.value = user;
},
builder: (context, child, callback, buttonState) {
return ElevatedButton(
@@ -84,7 +88,7 @@ class AppDrawer extends HookConsumerWidget {
if (user != null)
ElevatedButton(
onPressed: () async {
ref.read(loggedInUser.notifier).state = null;
ref.settings.findOneLocalById('_')!.user.value = null;
controller.clear();
},
child: Text('Log out'),
+2 -2
View File
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:zapstore/services/session_service.dart';
import 'package:zapstore/main.data.dart';
import 'package:zapstore/widgets/card.dart';
class UserAvatar extends HookConsumerWidget {
@@ -8,7 +8,7 @@ class UserAvatar extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(loggedInUser);
final user = ref.settings.watchOne('_').model!.user.value;
return CircularImage(url: user?.avatarUrl, size: 46);
}
}