Refactor WoT related widgets

This commit is contained in:
franzap
2024-11-11 23:37:18 -03:00
parent 1c2865b5d1
commit a4a933db70
4 changed files with 165 additions and 134 deletions
+1 -1
View File
@@ -133,7 +133,7 @@ mixin UserAdapter on NostrAdapter<User> {
}
Future<List<User>> getTrusted(String npub1, String npub2) async {
final url = 'https://trustgraph.live/api/trust/$npub1/$npub2';
final url = 'https://trustgraph.live/api/fwf/$npub1/$npub2';
final users = await sendRequest(
Uri.parse(url),
onSuccess: (response, label) async {
+108
View File
@@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
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/models/settings.dart';
import 'package:zapstore/models/user.dart';
import 'package:zapstore/widgets/app_drawer.dart';
import 'package:zapstore/widgets/author_container.dart';
import 'package:zapstore/widgets/wot_container.dart';
class InstallAlertDialog extends ConsumerWidget {
const InstallAlertDialog({
super.key,
required this.app,
});
final App app;
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.settings
.watchOne('_', alsoWatch: (_) => {_.user})
.model!
.user
.value;
return AlertDialog(
elevation: 10,
title: Text(
'Are you sure you want to install ${app.name}?',
style: TextStyle(fontWeight: FontWeight.bold),
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'By installing this app you are trusting the signer now and for all future updates. Make sure you know who they are.'),
Gap(20),
if (app.signer.value != null)
AuthorContainer(
user: app.signer.value!, text: 'Signed by', oneLine: true),
Gap(20),
if (app.signer.value != null)
WebOfTrustContainer(
fromNpub: user?.npub ?? kFranzapNpub,
toNpub: app.signer.value!.npub,
),
if (user == null)
LoginContainer(
minimal: true,
labelText: 'Log in to view your own web of trust',
),
if (app.latestMetadata?.urls.isNotEmpty ?? false)
Padding(
padding: const EdgeInsets.only(top: 16, bottom: 16),
child: RichText(
text: WidgetSpan(
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text('This app will be downloaded from '),
Text(
Uri.parse(app.latestMetadata!.urls.first).host,
style: TextStyle(
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
),
),
Text(' and verified.'),
],
),
),
),
),
],
),
),
actions: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: TextButton(
onPressed: () {
app.install();
// NOTE: can't use context.pop()
Navigator.of(context).pop();
},
child: user != null
? Text('Install', style: TextStyle(fontWeight: FontWeight.bold))
: Text(
'I trust the signer, install the app',
textAlign: TextAlign.right,
),
),
),
TextButton(
onPressed: () {
// NOTE: can't use context.pop()
Navigator.of(context).pop();
},
child: Text('Go back'),
),
],
);
}
}
+1 -98
View File
@@ -13,6 +13,7 @@ import 'package:zapstore/utils/extensions.dart';
import 'package:zapstore/utils/system_info.dart';
import 'package:zapstore/widgets/app_drawer.dart';
import 'package:zapstore/widgets/author_container.dart';
import 'package:zapstore/widgets/install_alert_dialog.dart';
import 'package:zapstore/widgets/spinning_logo.dart';
import 'package:zapstore/widgets/wot_container.dart';
@@ -138,102 +139,4 @@ class InstallButton extends HookConsumerWidget {
}
}
class InstallAlertDialog extends ConsumerWidget {
const InstallAlertDialog({
super.key,
required this.app,
});
final App app;
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.settings
.watchOne('_', alsoWatch: (_) => {_.user})
.model!
.user
.value;
return AlertDialog(
elevation: 10,
title: Text(
'Are you sure you want to install ${app.name}?',
style: TextStyle(fontWeight: FontWeight.bold),
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'By installing this app you are trusting the signer now and for all future updates. Make sure you know who they are.'),
Gap(20),
// SignerAndDeveloperRow(app: app),
if (app.signer.value != null)
AuthorContainer(
user: app.signer.value!, text: 'Signed by', oneLine: true),
Gap(20),
if (app.signer.value != null)
WebOfTrustContainer(
fromNpub: user?.npub ?? kFranzapNpub,
toNpub: app.signer.value!.npub,
),
if (user == null)
LoginContainer(
minimal: true,
labelText: 'Log in to view your own web of trust',
),
if (app.latestMetadata?.urls.isNotEmpty ?? false)
Padding(
padding: const EdgeInsets.only(top: 16, bottom: 16),
child: RichText(
text: WidgetSpan(
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text('This app will be downloaded from '),
Text(
Uri.parse(app.latestMetadata!.urls.first).host,
style: TextStyle(
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
),
),
Text(' and verified.'),
],
),
),
),
),
],
),
),
actions: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: TextButton(
onPressed: () {
app.install();
// NOTE: can't use context.pop()
Navigator.of(context).pop();
},
child: user != null
? Text('Install', style: TextStyle(fontWeight: FontWeight.bold))
: Text(
'I trust the signer, install the app',
textAlign: TextAlign.right,
),
),
),
TextButton(
onPressed: () {
// NOTE: can't use context.pop()
Navigator.of(context).pop();
},
child: Text('Go back'),
),
],
);
}
}
final kUpdateColor = Colors.blue[700]!;
+55 -35
View File
@@ -19,8 +19,10 @@ class WebOfTrustContainer extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return switch (
ref.watch(followsWhoFollowProvider((from: fromNpub, to: toNpub)))) {
final data =
ref.watch(followsWhoFollowProvider((from: fromNpub, to: toNpub)));
return switch (data) {
AsyncData<List<User>>(value: final trustedUsers) => Builder(
builder: (context) {
final hasUser =
@@ -32,39 +34,12 @@ class WebOfTrustContainer extends HookConsumerWidget {
return Text(
'No trusted users between you and the signer. (This may be a service error)');
}
return RichText(
text: TextSpan(
children: [
if (hasUser && fromNpub != kFranzapNpub)
TextSpan(text: 'You, '),
for (final trustedUser in trustedUsersWithoutLoggedInUser)
TextSpan(
style: TextStyle(height: 1.6),
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
RoundedImage(
url: trustedUser.avatarUrl, size: 20),
Text(
' ${trustedUser.nameOrNpub}${trustedUsersWithoutLoggedInUser.indexOf(trustedUser) == trustedUsersWithoutLoggedInUser.length - 1 ? '' : ', '}',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
if (trustedUsersWithoutLoggedInUser
.indexOf(trustedUser) ==
trustedUsersWithoutLoggedInUser.length - 1)
TextSpan(
text: ' and others follow this signer on nostr.',
)
],
),
],
),
return UsersRichText(
preSpan: hasUser && fromNpub != kFranzapNpub
? TextSpan(text: 'You, ')
: null,
users: trustedUsersWithoutLoggedInUser,
);
},
),
@@ -91,6 +66,51 @@ class WebOfTrustContainer extends HookConsumerWidget {
}
}
class UsersRichText extends StatelessWidget {
const UsersRichText({
super.key,
this.preSpan,
required this.users,
});
final TextSpan? preSpan;
final List<User> users;
@override
Widget build(BuildContext context) {
return RichText(
text: TextSpan(
children: [
if (preSpan != null) preSpan!,
for (final user in users)
TextSpan(
style: TextStyle(height: 1.6),
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
RoundedImage(url: user.avatarUrl, size: 20),
Text(
' ${user.nameOrNpub}${users.indexOf(user) == users.length - 1 ? '' : ', '}',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
if (users.indexOf(user) == users.length - 1)
TextSpan(
text: ' and others follow this signer on nostr.',
)
],
),
],
),
);
}
}
final followsWhoFollowProvider = FutureProvider.autoDispose
.family<List<User>, ({String from, String to})>((ref, arg) async {
final _ =