mirror of
https://github.com/zapstore/zapstore.git
synced 2026-10-05 20:48:24 +00:00
Drawer and user deserialize fixes
This commit is contained in:
+3
-1
@@ -44,4 +44,6 @@ app.*.map.json
|
||||
|
||||
/tmp
|
||||
/flutter_data
|
||||
.fvm/
|
||||
.fvm/
|
||||
|
||||
TODO.txt
|
||||
+2
-7
@@ -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
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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
@@ -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
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user