mirror of
https://github.com/zapstore/zapstore.git
synced 2026-10-06 04:58:23 +00:00
Modal for wot
This commit is contained in:
@@ -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
@@ -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));
|
||||
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.'),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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
@@ -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
@@ -57,7 +57,7 @@ dependency_overrides:
|
||||
purplebase:
|
||||
git:
|
||||
url: https://github.com/purplebase/purplebase
|
||||
ref: 617e018201a875281b7c484bcd79ead7fea078d6
|
||||
ref: f6a6a7
|
||||
flutter_data:
|
||||
path: ../flutter_data
|
||||
|
||||
|
||||
Reference in New Issue
Block a user