Modal for wot

This commit is contained in:
franzap
2024-05-07 22:04:27 -03:00
parent c307d7e0e1
commit 5df614159f
9 changed files with 182 additions and 77 deletions
+3
View File
@@ -166,6 +166,8 @@ class ScaffoldWithNestedNavigation extends HookConsumerWidget {
}
}
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
class ScaffoldWithNavigationBar extends HookConsumerWidget {
const ScaffoldWithNavigationBar({
super.key,
@@ -181,6 +183,7 @@ class ScaffoldWithNavigationBar extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final initializer = ref.watch(newInitializer);
return Scaffold(
key: scaffoldKey,
body: Padding(
padding: const EdgeInsets.fromLTRB(16, 20, 16, 0),
child: initializer.when(
+3 -3
View File
@@ -172,9 +172,6 @@ extension AppX on App {
return;
}
final hash = latestMetadata!.hash!;
final size = latestMetadata!.size ?? '';
final adapter = DataModel.adapterFor(this) as AppAdapter;
final notifier =
adapter.ref.read(installationProgressProvider(id!.toString()).notifier);
@@ -189,6 +186,9 @@ extension AppX on App {
}
}
final hash = latestMetadata!.hash!;
final size = latestMetadata!.size ?? '';
final dir = await getApplicationSupportDirectory();
final file = File(path.join(dir.path, hash));
+5 -1
View File
@@ -113,7 +113,7 @@ mixin UserAdapter on NostrAdapter<User> {
DataRequestLabel? label}) async {
if (id.toString().isEmpty) return null;
var publicKey = id.toString();
String? publicKey = id.toString();
if (publicKey.startsWith('npub')) {
publicKey = publicKey.hexKey;
@@ -127,6 +127,10 @@ mixin UserAdapter on NostrAdapter<User> {
);
}
if (publicKey == null) {
return null;
}
final req = RelayRequest(
authors: {publicKey},
kinds: {kind, if (params?['contacts'] != null) 3},
+114 -34
View File
@@ -7,9 +7,11 @@ import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:zapstore/main.dart';
import 'package:zapstore/main.data.dart';
import 'package:zapstore/models/app.dart';
import 'package:zapstore/models/release.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';
@@ -84,39 +86,7 @@ class AppDetailScreen extends HookConsumerWidget {
Gap(10),
Padding(
padding: const EdgeInsets.only(right: 14),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (app.developer.isPresent)
GestureDetector(
onTap: () async {
final url = Uri.parse(
'https://primal.net/p/${app.developer.value!.npub}');
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
},
child: AuthorContainer(
user: app.developer.value!,
text: 'Built by',
oneLine: false),
),
if (app.signer.isPresent)
GestureDetector(
onTap: () async {
final url = Uri.parse(
'https://primal.net/p/${app.signer.value!.npub}');
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
},
child: AuthorContainer(
user: app.signer.value!,
text: 'Signed by',
oneLine: false),
),
],
),
child: SignerAndDeveloperRow(app: app),
),
Gap(30),
Container(
@@ -202,6 +172,48 @@ class AppDetailScreen extends HookConsumerWidget {
}
}
class SignerAndDeveloperRow extends StatelessWidget {
const SignerAndDeveloperRow({
super.key,
required this.app,
});
final App app;
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (app.developer.isPresent)
GestureDetector(
onTap: () async {
final url = Uri.parse(
'https://primal.net/p/${app.developer.value!.npub}');
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
},
child: AuthorContainer(
user: app.developer.value!, text: 'Built by', oneLine: false),
),
if (app.signer.isPresent)
GestureDetector(
onTap: () async {
final url =
Uri.parse('https://primal.net/p/${app.signer.value!.npub}');
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
},
child: AuthorContainer(
user: app.signer.value!, text: 'Signed by', oneLine: false),
),
],
);
}
}
class VersionedAppHeader extends StatelessWidget {
const VersionedAppHeader({
super.key,
@@ -356,7 +368,19 @@ class InstallButton extends ConsumerWidget {
LaunchApp.openApp(androidPackageName: app.id!.toString());
},
_ => switch (progress) {
IdleInstallProgress() => () => app.install(),
IdleInstallProgress() => () {
// show trust dialog only if first install
if (app.canInstall) {
showDialog(
context: context,
builder: (BuildContext context) {
return InstallAlertDialog(app: app);
},
);
} else if (app.canUpdate) {
app.install();
}
},
ErrorInstallProgress(e: final e) => () {
// show error and reset state to idle
context.showError(e.toString());
@@ -402,3 +426,59 @@ class InstallButton extends ConsumerWidget {
);
}
}
class InstallAlertDialog extends ConsumerWidget {
const InstallAlertDialog({
super.key,
required this.app,
});
final App app;
@override
Widget build(BuildContext context, WidgetRef ref) {
return AlertDialog(
elevation: 10,
title: Text(
'Are you sure you want to install ${app.name}?',
style: TextStyle(fontWeight: FontWeight.bold),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'By installing this app you are trusting the signer and the developer now and for all future updates. Make sure you know who they are.'),
Gap(20),
SignerAndDeveloperRow(app: app),
// AppDrawer(),
],
),
actions: [
if (ref.read(loggedInUser) != null)
TextButton(
onPressed: () {
app.install();
// NOTE: can't use context.pop()
Navigator.of(context).pop();
},
child: Text('Install'),
),
if (ref.read(loggedInUser) == null)
TextButton(
onPressed: () {
Navigator.of(context).pop();
scaffoldKey.currentState!.openDrawer();
},
child: Text('Log in to view web of trust'),
),
TextButton(
onPressed: () {
// NOTE: can't use context.pop()
Navigator.of(context).pop();
},
child: Text('Go back'),
),
],
);
}
}
+46 -36
View File
@@ -15,42 +15,52 @@ class SettingsScreen extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final controller = useTextEditingController();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Share your feedback',
style: context.theme.textTheme.headlineLarge,
),
Gap(10),
Text('Comments, suggestions and error reports welcome here.'),
Gap(20),
TextField(
controller: controller,
autofocus: true,
maxLines: 10,
),
Gap(20),
AsyncButtonBuilder(
loadingWidget: SizedBox(
width: 14, height: 14, child: CircularProgressIndicator()),
onPressed: () async {
final user = ref.read(loggedInUser);
final text = '${controller.text.trim()} [from ${user?.npub}]';
final event = BaseEvent.partial(content: text).sign(kI);
// TODO only send to relay.zap.store?
await ref.apps.nostrAdapter.notifier.publish(event);
controller.clear();
},
builder: (context, child, callback, buttonState) {
return ElevatedButton(
onPressed: callback,
child: child,
);
},
child: Text('Send'),
),
],
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Share your feedback',
style: context.theme.textTheme.headlineLarge!
.copyWith(fontWeight: FontWeight.bold),
),
Gap(10),
Text('Comments, suggestions and error reports welcome here.'),
Gap(20),
TextField(
controller: controller,
autofocus: true,
maxLines: 10,
),
Gap(20),
AsyncButtonBuilder(
loadingWidget: SizedBox(
width: 14, height: 14, child: CircularProgressIndicator()),
onPressed: () async {
final user = ref.read(loggedInUser);
final text = '${controller.text.trim()} [from ${user?.npub}]';
final event = BaseEvent.partial(content: text).sign(kI);
await ref.apps.nostrAdapter.notifier
.publish(event, relayUrls: ['wss://relay.zap.store']);
controller.clear();
},
builder: (context, child, callback, buttonState) {
return ElevatedButton(
onPressed: callback,
child: child,
);
},
child: Text('Send'),
),
Divider(),
Gap(30),
Text(
'How does zap.store work',
style: context.theme.textTheme.headlineLarge!
.copyWith(fontWeight: FontWeight.bold),
),
],
),
);
}
}
+4
View File
@@ -88,6 +88,10 @@ class AppDrawer extends HookConsumerWidget {
},
child: Text('Log out'),
),
Gap(20),
if (user == null)
Text(
'Feel free to log in with fran@zap.store if you don\'t want to use yours.'),
],
),
],
+4
View File
@@ -168,6 +168,10 @@ class CircularImage extends StatelessWidget {
height: size,
width: size,
color: Colors.grey[800],
child: Icon(
Icons.person,
color: Colors.blueGrey,
),
);
return ClipRRect(
borderRadius: BorderRadius.circular(radius.toDouble()),
+2 -2
View File
@@ -816,8 +816,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "617e018201a875281b7c484bcd79ead7fea078d6"
resolved-ref: "617e018201a875281b7c484bcd79ead7fea078d6"
ref: f6a6a7
resolved-ref: f6a6a71c5fb791e9eb82e2678ba5e5abaeb01462
url: "https://github.com/purplebase/purplebase"
source: git
version: "0.1.0"
+1 -1
View File
@@ -57,7 +57,7 @@ dependency_overrides:
purplebase:
git:
url: https://github.com/purplebase/purplebase
ref: 617e018201a875281b7c484bcd79ead7fea078d6
ref: f6a6a7
flutter_data:
path: ../flutter_data