mirror of
https://github.com/zapstore/zapstore.git
synced 2026-10-05 12:38:24 +00:00
Developer screen WIP, remove concept of developer and just refer to signer as developer
This commit is contained in:
+6
-18
@@ -34,12 +34,10 @@ class App extends base.App with DataModelMixin<App> {
|
||||
|
||||
final HasMany<Release> releases;
|
||||
final BelongsTo<User> signer;
|
||||
final BelongsTo<User> developer;
|
||||
final BelongsTo<LocalApp> localApp;
|
||||
|
||||
App.fromJson(super.map)
|
||||
: developer = belongsTo(map['developer']),
|
||||
releases = hasMany(map['releases']),
|
||||
: releases = hasMany(map['releases']),
|
||||
signer = belongsTo(map['signer']),
|
||||
localApp = belongsTo(map['localApp']),
|
||||
super.fromJson();
|
||||
@@ -74,6 +72,9 @@ class App extends base.App with DataModelMixin<App> {
|
||||
bool get hasCertificateMismatch =>
|
||||
localApp.value?.status == AppInstallStatus.certificateMismatch;
|
||||
|
||||
bool get isSelfSigned => !(signer.value!.pubkey == kZapstorePubkey &&
|
||||
identifier != kZapstoreAppIdentifier);
|
||||
|
||||
Future<void> install({bool alwaysTrustSigner = false}) async {
|
||||
if (!canInstall && !canUpdate || hasCertificateMismatch) {
|
||||
throw Exception(
|
||||
@@ -221,7 +222,7 @@ mixin AppAdapter on Adapter<App> {
|
||||
// If looking up multiple apps via `#d`, use `queriedAtMap`
|
||||
// to find the earliest queried at Date, to be used in the
|
||||
// next request `since` field
|
||||
if (params.containsKey('#d') && !params['skipCache']) {
|
||||
if (params.containsKey('#d') && !(params['skipCache'] ?? false)) {
|
||||
DateTime? earliestQueryAt;
|
||||
final identifiers = <String>{...params['#d']};
|
||||
|
||||
@@ -348,21 +349,8 @@ mixin AppAdapter on Adapter<App> {
|
||||
|
||||
for (final Map<String, dynamic> map in list) {
|
||||
final tags = map['tags'];
|
||||
final pubkey = map['pubkey'];
|
||||
map['localApp'] = base.BaseUtil.getTag(tags, 'd')!;
|
||||
|
||||
// Find developer
|
||||
final zapTag = base.BaseUtil.getTag(tags, 'zap');
|
||||
if (pubkey == kZapstorePubkey) {
|
||||
if (zapTag != null) {
|
||||
map['developer'] = zapTag;
|
||||
}
|
||||
if (map['localApp'] == kZapstoreAppIdentifier) {
|
||||
map['developer'] = pubkey;
|
||||
}
|
||||
} else {
|
||||
map['developer'] = pubkey;
|
||||
}
|
||||
map['signer'] = map['pubkey'];
|
||||
}
|
||||
|
||||
return super.deserialize(data);
|
||||
|
||||
+1
-13
@@ -19,16 +19,11 @@ mixin _$AppAdapter on Adapter<App> {
|
||||
),
|
||||
'signer': RelationshipMeta<User>(
|
||||
name: 'signer',
|
||||
inverseName: 'apps',
|
||||
type: 'users',
|
||||
kind: 'BelongsTo',
|
||||
instance: (_) => (_ as App).signer,
|
||||
),
|
||||
'developer': RelationshipMeta<User>(
|
||||
name: 'developer',
|
||||
type: 'users',
|
||||
kind: 'BelongsTo',
|
||||
instance: (_) => (_ as App).developer,
|
||||
),
|
||||
'localApp': RelationshipMeta<LocalApp>(
|
||||
name: 'localApp',
|
||||
type: 'localApps',
|
||||
@@ -81,13 +76,6 @@ extension AppRelationshipGraphNodeX on RelationshipGraphNode<App> {
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
|
||||
RelationshipGraphNode<User> get developer {
|
||||
final meta = _$AppAdapter._kAppRelationshipMetas['developer']
|
||||
as RelationshipMeta<User>;
|
||||
return meta.clone(
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
|
||||
RelationshipGraphNode<LocalApp> get localApp {
|
||||
final meta = _$AppAdapter._kAppRelationshipMetas['localApp']
|
||||
as RelationshipMeta<LocalApp>;
|
||||
|
||||
@@ -164,11 +164,7 @@ mixin NostrAdapter<T extends DataModelMixin<T>> on Adapter<T> {
|
||||
final metadataIds =
|
||||
releases.map((r) => r.event.linkedEvents).nonNulls.expand((_) => _);
|
||||
final apps = releases.map((r) => r.app.value).nonNulls;
|
||||
|
||||
final userIds = {
|
||||
for (final app in apps) app.signer.id,
|
||||
for (final app in apps) app.developer.id,
|
||||
}.nonNulls;
|
||||
final userIds = apps.map((a) => a.signer.id).nonNulls;
|
||||
|
||||
// Metadata and users probably go to separate relays
|
||||
// so query in parallel
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:flutter_data/flutter_data.dart';
|
||||
import 'package:purplebase/purplebase.dart' as base;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/models/nostr_adapter.dart';
|
||||
import 'package:zapstore/models/settings.dart';
|
||||
import 'package:zapstore/models/verify_reputation_dvm.dart';
|
||||
@@ -24,6 +25,7 @@ class User extends base.User with DataModelMixin<User> {
|
||||
: followers = hasMany(map['followers']),
|
||||
following = hasMany(map['following']),
|
||||
settings = belongsTo(map['settings']),
|
||||
apps = hasMany(map['apps']),
|
||||
super.fromJson();
|
||||
|
||||
User.fromPubkey(String pubkey)
|
||||
@@ -45,6 +47,9 @@ class User extends base.User with DataModelMixin<User> {
|
||||
@DataRelationship(inverse: 'user')
|
||||
final BelongsTo<Settings> settings;
|
||||
|
||||
@DataRelationship(inverse: 'signer')
|
||||
final HasMany<App> apps;
|
||||
|
||||
String get nameOrNpub => name ?? npub.shorten;
|
||||
|
||||
// Zaps
|
||||
|
||||
@@ -30,6 +30,13 @@ mixin _$UserAdapter on Adapter<User> {
|
||||
type: 'settings',
|
||||
kind: 'BelongsTo',
|
||||
instance: (_) => (_ as User).settings,
|
||||
),
|
||||
'apps': RelationshipMeta<App>(
|
||||
name: 'apps',
|
||||
inverseName: 'signer',
|
||||
type: 'apps',
|
||||
kind: 'HasMany',
|
||||
instance: (_) => (_ as User).apps,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -84,4 +91,11 @@ extension UserRelationshipGraphNodeX on RelationshipGraphNode<User> {
|
||||
return meta.clone(
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
|
||||
RelationshipGraphNode<App> get apps {
|
||||
final meta =
|
||||
_$UserAdapter._kUserRelationshipMetas['apps'] as RelationshipMeta<App>;
|
||||
return meta.clone(
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-14
@@ -4,10 +4,12 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:zapstore/main.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/models/nostr_adapter.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
import 'package:zapstore/navigation/app_initializer.dart';
|
||||
import 'package:zapstore/navigation/desktop_scaffold.dart';
|
||||
import 'package:zapstore/navigation/mobile_scaffold.dart';
|
||||
import 'package:zapstore/screens/app_detail_screen.dart';
|
||||
import 'package:zapstore/screens/developer_screen.dart';
|
||||
import 'package:zapstore/screens/search_screen.dart';
|
||||
import 'package:zapstore/screens/settings_screen.dart';
|
||||
import 'package:zapstore/screens/updates_screen.dart';
|
||||
@@ -37,13 +39,7 @@ final appRouter = GoRouter(
|
||||
GoRoute(
|
||||
path: '/',
|
||||
builder: (context, state) => SearchScreen(),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'details',
|
||||
builder: (context, state) =>
|
||||
AppDetailScreen(model: state.extra as App),
|
||||
),
|
||||
],
|
||||
routes: [appDetailsRoute, developerRoute],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -53,13 +49,7 @@ final appRouter = GoRouter(
|
||||
GoRoute(
|
||||
path: '/updates',
|
||||
builder: (context, state) => UpdatesScreen(),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'details',
|
||||
builder: (context, state) =>
|
||||
AppDetailScreen(model: state.extra as App),
|
||||
),
|
||||
],
|
||||
routes: [appDetailsRoute, developerRoute],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -79,6 +69,16 @@ final appRouter = GoRouter(
|
||||
],
|
||||
);
|
||||
|
||||
final appDetailsRoute = GoRoute(
|
||||
path: 'details',
|
||||
builder: (context, state) => AppDetailScreen(model: state.extra as App),
|
||||
);
|
||||
|
||||
final developerRoute = GoRoute(
|
||||
path: 'developer',
|
||||
builder: (context, state) => DeveloperScreen(model: state.extra as User),
|
||||
);
|
||||
|
||||
class ScaffoldWithNestedNavigation extends HookConsumerWidget {
|
||||
const ScaffoldWithNestedNavigation({
|
||||
Key? key,
|
||||
|
||||
@@ -14,9 +14,10 @@ import 'package:zapstore/models/release.dart';
|
||||
import 'package:zapstore/utils/extensions.dart';
|
||||
import 'package:zapstore/widgets/install_button.dart';
|
||||
import 'package:zapstore/widgets/release_card.dart';
|
||||
import 'package:zapstore/widgets/signer_and_developer_row.dart';
|
||||
import 'package:zapstore/widgets/signer_container.dart';
|
||||
import 'package:zapstore/widgets/users_rich_text.dart';
|
||||
import 'package:zapstore/widgets/versioned_app_header.dart';
|
||||
import 'package:zapstore/widgets/zap_button.dart';
|
||||
import 'package:zapstore/widgets/zap_receipts.dart';
|
||||
|
||||
class AppDetailScreen extends HookConsumerWidget {
|
||||
@@ -43,7 +44,6 @@ class AppDetailScreen extends HookConsumerWidget {
|
||||
_.releases,
|
||||
_.releases.artifacts,
|
||||
_.signer,
|
||||
_.developer
|
||||
});
|
||||
|
||||
final app = state.model ?? model;
|
||||
@@ -166,9 +166,20 @@ class AppDetailScreen extends HookConsumerWidget {
|
||||
Gap(10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 14),
|
||||
child: SignerAndDeveloperRow(app: app),
|
||||
child: SignerContainer(app: app),
|
||||
),
|
||||
ZapReceipts(app: app),
|
||||
if (app.latestMetadata != null && app.isSelfSigned)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Gap(10),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ZapButton(app: app)),
|
||||
Gap(10),
|
||||
ZapReceipts(app: app),
|
||||
],
|
||||
),
|
||||
Gap(20),
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
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';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
import 'package:zapstore/widgets/app_card.dart';
|
||||
import 'package:zapstore/widgets/author_container.dart';
|
||||
import 'package:zapstore/widgets/relevant_who_follow_container.dart';
|
||||
import 'package:zapstore/widgets/rounded_image.dart';
|
||||
import 'package:zapstore/widgets/user_avatar.dart';
|
||||
import 'package:zapstore/widgets/zap_receipts.dart';
|
||||
|
||||
class DeveloperScreen extends HookConsumerWidget {
|
||||
final User model;
|
||||
|
||||
DeveloperScreen({
|
||||
required this.model,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
useFuture(useMemoized(() async {
|
||||
if (model.pubkey != kZapstorePubkey) {
|
||||
await ref.apps.findAll(
|
||||
params: {
|
||||
'authors': [model.pubkey],
|
||||
'ignoreReturn': false,
|
||||
},
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
final state = ref.users.watchOne(model.id!, alsoWatch: (_) => {_.apps});
|
||||
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
RoundedImage(url: model.avatarUrl, size: 80),
|
||||
Gap(10),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
model.nameOrNpub,
|
||||
style: TextStyle(fontSize: 34, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Gap(20),
|
||||
// if (model.apps.first.latestMetadata != null)
|
||||
// ZapReceipts(developer: model),
|
||||
RelevantWhoFollowContainer(
|
||||
toNpub: model.npub,
|
||||
loadingText: 'Loading connections...',
|
||||
),
|
||||
Gap(10),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
final url = Uri.parse('https://npub.world/${model.npub}');
|
||||
launchUrl(url);
|
||||
},
|
||||
child: Text(
|
||||
'View ${model.nameOrNpub} on nostr',
|
||||
),
|
||||
),
|
||||
Gap(20),
|
||||
Text('Signed apps',
|
||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
|
||||
Gap(10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (state.hasModel)
|
||||
for (final app in model.apps.where((a) => a.isSelfSigned))
|
||||
AppCard(model: app, showUpdate: true),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -23,13 +23,8 @@ class UpdatesScreen extends HookConsumerWidget {
|
||||
key: UniqueKey(),
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text('Installed apps',
|
||||
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
Text('Installed apps',
|
||||
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold)),
|
||||
if (updatedApps.isEmpty && updatableApps.isEmpty)
|
||||
SpinningLogo(size: 80),
|
||||
if (updatableApps.isNotEmpty)
|
||||
|
||||
@@ -7,7 +7,6 @@ import 'package:remove_markdown/remove_markdown.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
import 'package:zapstore/utils/extensions.dart';
|
||||
import 'package:zapstore/widgets/author_container.dart';
|
||||
import 'package:zapstore/widgets/install_button.dart';
|
||||
@@ -33,9 +32,7 @@ class AppCard extends HookConsumerWidget {
|
||||
|
||||
final isUpdate = app.canUpdate && showUpdate;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
context.go('${showUpdate ? '/updates' : ''}/details', extra: app);
|
||||
},
|
||||
onTap: () => context.push('/details', extra: app),
|
||||
child: Card(
|
||||
margin: EdgeInsets.only(top: 6, bottom: 6),
|
||||
elevation: 0,
|
||||
@@ -82,10 +79,7 @@ class AppCard extends HookConsumerWidget {
|
||||
softWrap: true,
|
||||
),
|
||||
Gap(6),
|
||||
// Only show Signed by apps' original authors
|
||||
if (app.signer.isPresent &&
|
||||
(app.signer.value!.pubkey != kZapstorePubkey ||
|
||||
{kZapstoreAppIdentifier}.contains(app.identifier)))
|
||||
if (app.signer.isPresent && app.isSelfSigned)
|
||||
AuthorContainer(
|
||||
user: app.signer.value!,
|
||||
beforeText: 'Signed by',
|
||||
@@ -186,7 +180,7 @@ class TinyAppCard extends HookConsumerWidget {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (app != null) {
|
||||
context.go('/details', extra: app);
|
||||
context.push('/details', extra: app);
|
||||
}
|
||||
},
|
||||
child: Card(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:purplebase/purplebase.dart' as base;
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
import 'package:zapstore/screens/settings_screen.dart';
|
||||
@@ -10,8 +11,10 @@ class RelevantWhoFollowContainer extends HookConsumerWidget {
|
||||
const RelevantWhoFollowContainer({
|
||||
super.key,
|
||||
required this.toNpub,
|
||||
this.loadingText = 'Checking reputation...',
|
||||
});
|
||||
|
||||
final String loadingText;
|
||||
final String toNpub;
|
||||
|
||||
@override
|
||||
@@ -19,16 +22,23 @@ class RelevantWhoFollowContainer extends HookConsumerWidget {
|
||||
final signedInUser = ref.watch(signedInUserProvider);
|
||||
final data = ref.watch(relevantWhoFollowProvider((to: toNpub)));
|
||||
|
||||
if (signedInUser == null) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return switch (data) {
|
||||
AsyncData<List<User>>(value: final trustedUsers) => Builder(
|
||||
AsyncData<List<User>>(value: final users) => Builder(
|
||||
builder: (context) {
|
||||
if (trustedUsers.isEmpty) {
|
||||
// Find relevant who follow, remove the target that comes included
|
||||
final relevantWhoFollow =
|
||||
users.where((u) => u.pubkey.npub != toNpub).toList();
|
||||
if (relevantWhoFollow.isEmpty) {
|
||||
return Text(
|
||||
'No reputable profiles follow the signer. Make sure you know the signer.');
|
||||
}
|
||||
|
||||
return UsersRichText(
|
||||
users: trustedUsers,
|
||||
users: relevantWhoFollow,
|
||||
signedInUser: signedInUser,
|
||||
onlyUseCommaSeparator: true,
|
||||
trailingText: ' and others follow this signer on nostr.',
|
||||
@@ -42,7 +52,7 @@ class RelevantWhoFollowContainer extends HookConsumerWidget {
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
children: [
|
||||
Text('Checking reputation...'),
|
||||
Text(loadingText),
|
||||
Gap(10),
|
||||
SmallCircularProgressIndicator(),
|
||||
],
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/widgets/author_container.dart';
|
||||
|
||||
class SignerAndDeveloperRow extends ConsumerWidget {
|
||||
const SignerAndDeveloperRow({
|
||||
super.key,
|
||||
required this.app,
|
||||
});
|
||||
|
||||
final App app;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final settingsState = ref.settings.watchOne('_', remote: false);
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
if (app.developer.isPresent)
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
final url =
|
||||
Uri.parse('https://njump.me/${app.developer.value!.npub}');
|
||||
launchUrl(url);
|
||||
},
|
||||
child: AuthorContainer(
|
||||
user: app.developer.value!,
|
||||
beforeText: 'Developed by',
|
||||
oneLine: false),
|
||||
),
|
||||
if (app.signer.isPresent)
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
final url =
|
||||
Uri.parse('https://njump.me/${app.signer.value!.npub}');
|
||||
launchUrl(url);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
AuthorContainer(
|
||||
user: app.signer.value!,
|
||||
beforeText: 'Signed by',
|
||||
oneLine: false,
|
||||
afterText: settingsState.hasModel &&
|
||||
settingsState.model!.trustedUsers
|
||||
.contains(app.signer.value!)
|
||||
? '(trusted)'
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/widgets/author_container.dart';
|
||||
|
||||
class SignerContainer extends ConsumerWidget {
|
||||
const SignerContainer({
|
||||
super.key,
|
||||
required this.app,
|
||||
});
|
||||
|
||||
final App app;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final settingsState = ref.settings.watchOne('_', remote: false);
|
||||
|
||||
if (!app.signer.isPresent) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
if (app.isSelfSigned) {
|
||||
context.push('/developer', extra: app.signer.value);
|
||||
} else {
|
||||
final url = Uri.parse('https://npub.world/${app.signer.value!.npub}');
|
||||
launchUrl(url);
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
AuthorContainer(
|
||||
user: app.signer.value!,
|
||||
beforeText: 'Signed by',
|
||||
oneLine: false,
|
||||
afterText: settingsState.hasModel &&
|
||||
settingsState.model!.trustedUsers
|
||||
.contains(app.signer.value!)
|
||||
? '(trusted)'
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ class ZapButton extends HookConsumerWidget {
|
||||
final amountController = TextEditingController();
|
||||
final commentController = TextEditingController();
|
||||
|
||||
if (app.developer.value?.lud16 == null) {
|
||||
if (app.signer.value?.lud16 == null) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import 'package:zapstore/models/zap_receipt.dart';
|
||||
import 'package:zapstore/screens/settings_screen.dart';
|
||||
import 'package:zapstore/utils/extensions.dart';
|
||||
import 'package:zapstore/widgets/users_rich_text.dart';
|
||||
import 'package:zapstore/widgets/zap_button.dart';
|
||||
|
||||
class ZapReceipts extends HookConsumerWidget {
|
||||
ZapReceipts({
|
||||
@@ -33,30 +32,21 @@ class ZapReceipts extends HookConsumerWidget {
|
||||
// NOTE: These watchers will become more efficient
|
||||
ref.users.watchAll();
|
||||
|
||||
final fileMetadata = app.latestMetadata;
|
||||
// NOTE: Only show zaps for self-signed apps (at least for now)
|
||||
final isSelfSigned = app.signer.value == app.developer.value;
|
||||
if (fileMetadata == null || !isSelfSigned) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
final zapReceipts = ref.watch(zapReceiptsNotifier(fileMetadata));
|
||||
final zapReceipts = ref.watch(zapReceiptsNotifier(app.latestMetadata!));
|
||||
|
||||
if (zapReceipts.isLoading) {
|
||||
return _wrap(
|
||||
Row(
|
||||
children: [
|
||||
Gap(4),
|
||||
Text('Loading zaps...'),
|
||||
Gap(10),
|
||||
SmallCircularProgressIndicator(),
|
||||
],
|
||||
),
|
||||
return Row(
|
||||
children: [
|
||||
Gap(4),
|
||||
Text('Loading zaps...'),
|
||||
Gap(10),
|
||||
SmallCircularProgressIndicator(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
if (zapReceipts.value!.isEmpty) {
|
||||
return _wrap(Container());
|
||||
return Container();
|
||||
}
|
||||
|
||||
final receipts = zapReceipts.value!;
|
||||
@@ -75,36 +65,20 @@ class ZapReceipts extends HookConsumerWidget {
|
||||
return totalUserAmount.wrapped;
|
||||
});
|
||||
|
||||
return _wrap(
|
||||
UsersRichText(
|
||||
leadingTextSpan: TextSpan(
|
||||
style: TextStyle(fontSize: 16, height: 1.7),
|
||||
children: [
|
||||
TextSpan(text: '⚡'),
|
||||
TextSpan(
|
||||
text: ' $formattedSatsAmount sats ',
|
||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
TextSpan(text: 'zapped by'),
|
||||
],
|
||||
),
|
||||
users: zappers,
|
||||
signedInUser: signedInUser,
|
||||
maxUsersToDisplay: kMaxUsersToDisplay,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _wrap(Widget inner) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 10,
|
||||
return UsersRichText(
|
||||
leadingTextSpan: TextSpan(
|
||||
style: TextStyle(fontSize: 16, height: 1.7),
|
||||
children: [
|
||||
SizedBox(width: double.maxFinite, child: ZapButton(app: app)),
|
||||
inner,
|
||||
TextSpan(text: '⚡'),
|
||||
TextSpan(
|
||||
text: ' $formattedSatsAmount sats ',
|
||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
TextSpan(text: 'zapped by'),
|
||||
],
|
||||
),
|
||||
users: zappers,
|
||||
signedInUser: signedInUser,
|
||||
maxUsersToDisplay: kMaxUsersToDisplay,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -119,13 +93,10 @@ class ZapReceiptsNotifier extends StateNotifier<AsyncValue<Set<ZapReceipt>>> {
|
||||
|
||||
ZapReceiptsNotifier(this.ref, this.fileMetadata) : super(AsyncLoading()) {
|
||||
final adapter = ref.users.nostrAdapter;
|
||||
final developerPubkey =
|
||||
fileMetadata.event.getTag('zap') ?? fileMetadata.event.pubkey;
|
||||
final developerPubkey = fileMetadata.event.pubkey;
|
||||
final app = fileMetadata.release.value!.app.value!;
|
||||
|
||||
// If it is a Zapstore-signed release from the indexer, return
|
||||
if (developerPubkey == kZapstorePubkey &&
|
||||
fileMetadata.release.value!.app.value!.identifier !=
|
||||
kZapstoreAppIdentifier) {
|
||||
if (!app.isSelfSigned) {
|
||||
state = AsyncData({});
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user