Drawer and user deserialize fixes

This commit is contained in:
franzap
2024-05-03 11:17:17 -03:00
parent ffe4fde21b
commit 716aa1899f
9 changed files with 140 additions and 110 deletions
+3 -1
View File
@@ -44,4 +44,6 @@ app.*.map.json
/tmp
/flutter_data
.fvm/
.fvm/
TODO.txt
+2 -7
View File
@@ -50,6 +50,7 @@ class ZapstoreApp extends StatelessWidget {
useMaterial3: true,
scaffoldBackgroundColor: kBackgroundColor,
visualDensity: VisualDensity.adaptivePlatformDensity,
// textTheme: context.theme.textTheme.copyWith(
// bodyLarge:
// TextStyle(color: Colors.white, fontWeight: FontWeight.w300))
@@ -225,13 +226,7 @@ class ScaffoldWithNavigationBar extends HookConsumerWidget {
onDestinationSelected: onDestinationSelected,
),
drawer: Drawer(
child: Column(
children: [
DrawerHeader(
child: AppDrawer(),
),
],
),
child: AppDrawer(),
),
);
}
+45 -37
View File
@@ -1,3 +1,6 @@
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:flutter_data/flutter_data.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:purplebase/purplebase.dart';
@@ -17,46 +20,47 @@ class User extends ZapstoreEvent<User> with BaseUser {
mixin UserAdapter on NostrAdapter<User> {
@override
DeserializedData<User> deserialize(Object? data, {String? key}) {
final Iterable<Map<String, dynamic>> list =
(data is Iterable ? data : [data as Map]).cast();
final k0s = list
.where((e) => e['kind'] == 0 && jsonDecode(e['content']).isNotEmpty)
.toList()
.groupSetsBy((e) => e['pubkey'] as String);
final k3 = (list.where((e) => e['kind'] == 3).toList()
..sortBy<num>((e) => e['created_at']))
.firstOrNull;
// collect contacts and then assign them to user
final included = <DataModelMixin>[];
if (k3 != null) {
final contactMaps = [];
for (final [_, id, ..._] in k3['tags'] as Iterable) {
contactMaps.add({
'id': id,
'content': '',
'pubkey': id,
'created_at': DateTime.now().millisecondsSinceEpoch ~/ 1000,
'kind': 0,
'tags': [],
});
}
final data = super.deserialize(contactMaps);
included.addAll(data.models);
}
final users = <User>[];
final list = data is Iterable ? data : [data as Map];
// first process kind 3 (if any)
for (final e in list) {
final map = e as Map<String, dynamic>;
if (map['kind'] == 3) {
final contactMaps = [];
for (final [_, id, ..._] in map['tags'] as Iterable) {
contactMaps.add({
'id': id,
'content': '',
'pubkey': id,
'created_at': DateTime.now().millisecondsSinceEpoch ~/ 1000,
'kind': 0,
'tags': [],
});
}
final data = super.deserialize(contactMaps);
users.addAll(data.models);
for (final _ in k0s.entries) {
if (_.value.length > 1) {
print('more than one for ${_.key}');
}
final k0 = _.value.first;
k0['id'] = k0['pubkey'];
k0['following'] = included.map((e) => e.id).toList();
users.addAll(super.deserialize(k0).models);
}
for (final e in list) {
final map = e as Map<String, dynamic>;
if (map['kind'] == 0) {
map['id'] = map['pubkey'];
// TODO workaround - should not assume empty is null
if (users.isNotEmpty) {
map['following'] = users.map((e) => e.id).toList();
}
final data0 = super.deserialize(map);
final user = data0.model;
if (user != null) {
users.add(user);
}
}
}
return DeserializedData(users);
return DeserializedData<User>(users, included: included);
}
@override
@@ -90,6 +94,8 @@ mixin UserAdapter on NostrAdapter<User> {
OnSuccessOne<User>? onSuccess,
OnErrorOne<User>? onError,
DataRequestLabel? label}) async {
if (id.toString().isEmpty) return null;
var publicKey = id.toString();
if (publicKey.startsWith('npub')) {
@@ -113,6 +119,8 @@ mixin UserAdapter on NostrAdapter<User> {
final result =
await notifier.query(req, relayUrls: ['wss://relay.nostr.band']);
final data = await deserializeAsync(result, save: true);
return data.models.firstWhere((e) => e.id == publicKey);
return data.models.firstWhere((e) {
return e.id == publicKey;
});
}
}
+12 -9
View File
@@ -76,14 +76,17 @@ class AppDetailScreen extends HookConsumerWidget {
child: SingleChildScrollView(
controller: scrollController,
scrollDirection: Axis.horizontal,
child: Row(
children: [
for (final i in app.tagMap['image']!)
Padding(
padding: const EdgeInsets.only(right: 12),
child: CachedNetworkImage(imageUrl: i),
),
],
child: SizedBox(
height: 320,
child: Row(
children: [
for (final i in app.tagMap['image']!)
Padding(
padding: const EdgeInsets.only(right: 12),
child: CachedNetworkImage(imageUrl: i),
),
],
),
),
),
),
@@ -92,7 +95,7 @@ class AppDetailScreen extends HookConsumerWidget {
styleSheet: MarkdownStyleSheet(
h1: TextStyle(fontWeight: FontWeight.bold),
h2: TextStyle(fontWeight: FontWeight.bold),
p: TextStyle(fontSize: 16, fontWeight: FontWeight.w300),
p: TextStyle(fontSize: 18, fontWeight: FontWeight.w300),
),
selectable: false,
data: app.content,
+10 -4
View File
@@ -5,6 +5,7 @@ import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:zapstore/main.data.dart';
import 'package:zapstore/models/app.dart';
import 'package:zapstore/utils/extensions.dart';
import 'package:zapstore/widgets/card.dart';
import 'package:zapstore/widgets/user_avatar.dart';
@@ -74,12 +75,17 @@ class SearchScreen extends HookConsumerWidget {
),
Gap(10),
if (state.hasMessage)
Text(
state.message!,
textAlign: TextAlign.center,
Expanded(
child: Center(
child: Text(
state.message!,
textAlign: TextAlign.center,
style: context.theme.textTheme.bodyLarge,
),
),
),
if (state.hasException) Text(state.exception!.toString()),
if (state.hasModel)
if (state.hasModel && state.model.isNotEmpty)
Expanded(
child: ListView.builder(
shrinkWrap: true,
-1
View File
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_data/flutter_data.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+51 -40
View File
@@ -11,51 +11,62 @@ class AppDrawer extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final u2 = ref.watch(loggedInUser);
final user = ref.watch(loggedInUser);
final controller = useTextEditingController();
return Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
padding: const EdgeInsets.only(top: 24, left: 16),
child: ListView(
children: [
// Text('npub or NIP-05'),
if (u2 == null) TextField(autocorrect: false, controller: controller),
Gap(5),
if (u2 == null)
ElevatedButton(
onPressed: () async {
ref.read(loggedInUser.notifier).state = await ref.users
.findOne(controller.text, params: {'contacts': true});
},
child: Text('Log in'),
),
if (u2 != null)
Expanded(
child: Column(
children: [
Card(
child: ListTile(
dense: true,
leading: CircularImage(
size: 20,
url: u2.avatarUrl,
),
title: Text(
u2.name ?? u2.id?.toString() ?? '',
),
subtitle: Text('following ${u2.following.length}'),
),
),
ElevatedButton(
onPressed: () async {
ref.read(loggedInUser.notifier).state = null;
controller.clear();
},
child: Text('Log out'),
),
],
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(top: 4, bottom: 4),
child: Row(
children: [
CircularImage(url: user?.avatarUrl, size: 46),
Gap(10),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (user != null)
Text(
user.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
if (user != null)
Text('${user.following.length} contacts'),
],
)
],
),
),
),
if (user == null)
TextField(
autocorrect: false,
controller: controller,
),
Gap(5),
if (user == null)
ElevatedButton(
onPressed: () async {
ref.read(loggedInUser.notifier).state = await ref.users
.findOne(controller.text, params: {'contacts': true});
},
child: Text('Log in'),
),
if (user != null)
ElevatedButton(
onPressed: () async {
ref.read(loggedInUser.notifier).state = null;
controller.clear();
},
child: Text('Log out'),
),
],
),
],
),
);
+15 -5
View File
@@ -110,8 +110,6 @@ class AuthorContainer extends StatelessWidget {
this.oneLine = true,
});
String get name => user.name ?? user.npub;
@override
Widget build(BuildContext context) {
return Padding(
@@ -120,7 +118,14 @@ class AuthorContainer extends StatelessWidget {
children: [
CircularImage(url: user.avatarUrl, size: oneLine ? 22 : 46),
Gap(10),
if (oneLine) Text('$text $name'),
if (oneLine)
Expanded(
child: Text(
'$text ${user.name}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
if (!oneLine)
Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -129,8 +134,12 @@ class AuthorContainer extends StatelessWidget {
Text(text),
Padding(
padding: const EdgeInsets.only(left: 1),
child:
Text(name, style: TextStyle(fontWeight: FontWeight.bold)),
child: Text(
user.name,
style: TextStyle(fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
)
@@ -158,6 +167,7 @@ class CircularImage extends StatelessWidget {
borderRadius: BorderRadius.circular(radius.toDouble()),
child: CachedNetworkImage(
imageUrl: url ?? '',
errorWidget: (_, __, ___) => Container(color: Colors.grey[800]),
fit: BoxFit.cover,
width: size.toDouble(),
height: size.toDouble(),
+2 -6
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:zapstore/services/session_service.dart';
import 'package:zapstore/widgets/card.dart';
class UserAvatar extends HookConsumerWidget {
const UserAvatar({super.key});
@@ -8,11 +9,6 @@ class UserAvatar extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(loggedInUser);
return CircleAvatar(
radius: 20,
// backgroundImage: AssetImage('assets/images/logo.png'),
backgroundColor: Colors.grey[850],
foregroundImage: user != null ? NetworkImage(user.avatarUrl!) : null,
);
return CircularImage(url: user?.avatarUrl, size: 46);
}
}