mirror of
https://github.com/haorendashu/nowser.git
synced 2025-12-18 02:04:18 +01:00
app edit page
This commit is contained in:
@@ -6,4 +6,5 @@ class RouterPath {
|
||||
static const String KEYS = "/keys";
|
||||
static const String APPS = "/apps";
|
||||
static const String ADD_REMOTE_APP = "/addRemoteApp";
|
||||
static const String APP_DETAIL = "/appDetail";
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class AppDB {
|
||||
|
||||
static Future update(App o, {DatabaseExecutor? db}) async {
|
||||
db = await DB.getDB(db);
|
||||
await db.update("app", o.toJson(), where: "id = ?", whereArgs: [o.pubkey]);
|
||||
await db.update("app", o.toJson(), where: "id = ?", whereArgs: [o.id]);
|
||||
}
|
||||
|
||||
static Future<void> delete(int id, {DatabaseExecutor? db}) async {
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:nowser/provider/app_provider.dart';
|
||||
import 'package:nowser/provider/key_provider.dart';
|
||||
import 'package:nowser/provider/permission_check_mixin.dart';
|
||||
import 'package:nowser/provider/web_provider.dart';
|
||||
import 'package:nowser/router/app_detail/app_detail_router.dart';
|
||||
import 'package:nowser/router/apps/add_remote_app_router.dart';
|
||||
import 'package:nowser/router/apps/apps_router.dart';
|
||||
import 'package:nowser/router/index/index_router.dart';
|
||||
@@ -100,6 +101,7 @@ class _MyApp extends State<MyApp> {
|
||||
RouterPath.KEYS: (context) => KeysRouter(),
|
||||
RouterPath.APPS: (context) => AppsRouter(),
|
||||
RouterPath.ADD_REMOTE_APP: (context) => AddRemoteAppRouter(),
|
||||
RouterPath.APP_DETAIL: (context) => AppDetailRouter(),
|
||||
};
|
||||
|
||||
return MultiProvider(
|
||||
|
||||
@@ -39,6 +39,13 @@ class AppProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> update(App app) async {
|
||||
app.updatedAt = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
AppDB.update(app);
|
||||
reload();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> add(App app) async {
|
||||
if (await AppDB.insert(app) > 0) {
|
||||
_list.add(app);
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nowser/component/tag_component.dart';
|
||||
import 'package:nowser/const/auth_type.dart';
|
||||
|
||||
class AppDetailPermissionItemComponent extends StatefulWidget {
|
||||
bool allow;
|
||||
|
||||
int authType;
|
||||
|
||||
int? eventKind;
|
||||
|
||||
Function(bool, int, int?)? onDelete;
|
||||
|
||||
AppDetailPermissionItemComponent(this.allow, this.authType,
|
||||
{this.eventKind, this.onDelete});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _AppDetailPermissionItemComponent();
|
||||
}
|
||||
}
|
||||
|
||||
class _AppDetailPermissionItemComponent
|
||||
extends State<AppDetailPermissionItemComponent> {
|
||||
bool tapFirst = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var permissionText = AuthType.getAuthName(context, widget.authType);
|
||||
if (widget.eventKind != null) {
|
||||
permissionText += " (EventKind ${widget.eventKind})";
|
||||
}
|
||||
var main = TagComponent(permissionText);
|
||||
|
||||
if (!tapFirst) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
tapFirst = true;
|
||||
});
|
||||
},
|
||||
child: main,
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (widget.onDelete != null) {
|
||||
widget.onDelete!(widget.allow, widget.authType, widget.eventKind);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
main,
|
||||
Icon(Icons.delete),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
350
lib/router/app_detail/app_detail_router.dart
Normal file
350
lib/router/app_detail/app_detail_router.dart
Normal file
@@ -0,0 +1,350 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nostr_sdk/nip19/nip19.dart';
|
||||
import 'package:nostr_sdk/utils/string_util.dart';
|
||||
import 'package:nowser/component/app/app_type_component.dart';
|
||||
import 'package:nowser/component/image_component.dart';
|
||||
import 'package:nowser/const/connect_type.dart';
|
||||
import 'package:nowser/data/app.dart';
|
||||
import 'package:nowser/main.dart';
|
||||
import 'package:nowser/router/app_detail/app_detail_permission_item_component.dart';
|
||||
import 'package:nowser/util/router_util.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../component/appbar_back_btn_component.dart';
|
||||
import '../../const/base.dart';
|
||||
import '../../provider/key_provider.dart';
|
||||
|
||||
class AppDetailRouter extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _AppDetailRouter();
|
||||
}
|
||||
}
|
||||
|
||||
class _AppDetailRouter extends State<AppDetailRouter> {
|
||||
late TextEditingController nameController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
nameController = TextEditingController();
|
||||
nameController.addListener(() {
|
||||
if (app != null && !changed && nameController.text != app!.name) {
|
||||
setState(() {
|
||||
changed = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
App? app;
|
||||
|
||||
bool changed = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
var arg = RouterUtil.routerArgs(context);
|
||||
if (arg != null && arg is App) {
|
||||
if (app == null || app!.id != arg.id) {
|
||||
app = App.fromJson(arg.toJson());
|
||||
print(app!.name);
|
||||
nameController.text = app!.name ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
if (app == null) {
|
||||
RouterUtil.back(context);
|
||||
return Container();
|
||||
}
|
||||
|
||||
List<Widget> list = [];
|
||||
|
||||
var baseMargin = EdgeInsets.only(bottom: Base.BASE_PADDING);
|
||||
|
||||
Widget imageWidget = Icon(
|
||||
Icons.image,
|
||||
size: 80,
|
||||
);
|
||||
if (StringUtil.isNotBlank(app!.image)) {
|
||||
imageWidget = ImageComponent(
|
||||
imageUrl: app!.image!,
|
||||
width: 80,
|
||||
height: 80,
|
||||
);
|
||||
}
|
||||
|
||||
list.add(Container(
|
||||
margin: baseMargin,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(right: Base.BASE_PADDING * 2),
|
||||
child: Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
child: imageWidget,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AppTypeComponent(app!.appType!),
|
||||
Text(
|
||||
app!.code!,
|
||||
style: TextStyle(color: themeData.hintColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
|
||||
list.add(Container(
|
||||
margin: baseMargin,
|
||||
child: TextField(
|
||||
controller: nameController,
|
||||
decoration: InputDecoration(hintText: "Name"),
|
||||
),
|
||||
));
|
||||
|
||||
var keyWidget =
|
||||
Selector<KeyProvider, List<String>>(builder: (context, pubkeys, child) {
|
||||
List<DropdownMenuItem<String>> items = [];
|
||||
for (var pubkey in pubkeys) {
|
||||
items.add(DropdownMenuItem(
|
||||
value: pubkey,
|
||||
child: Text(
|
||||
Nip19.encodePubKey(pubkey),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
));
|
||||
}
|
||||
return DropdownButton<String>(
|
||||
items: items,
|
||||
isExpanded: true,
|
||||
onChanged: null,
|
||||
value: app!.pubkey,
|
||||
);
|
||||
}, selector: (context, provider) {
|
||||
return provider.pubkeys;
|
||||
});
|
||||
list.add(Container(
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(right: Base.BASE_PADDING),
|
||||
child: Text("Pubkey:"),
|
||||
),
|
||||
Expanded(child: keyWidget),
|
||||
],
|
||||
),
|
||||
));
|
||||
|
||||
List<DropdownMenuItem<int>> connectTypeItems = [];
|
||||
connectTypeItems.add(DropdownMenuItem(
|
||||
child: Text("Fully trust"), value: ConnectType.FULLY_TRUST));
|
||||
connectTypeItems.add(DropdownMenuItem(
|
||||
child: Text("Reasonable"), value: ConnectType.REASONABLE));
|
||||
connectTypeItems.add(DropdownMenuItem(
|
||||
child: Text("Alway reject"), value: ConnectType.ALWAY_REJECT));
|
||||
list.add(Container(
|
||||
margin: baseMargin,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(right: Base.BASE_PADDING),
|
||||
child: Text("ConnectType:"),
|
||||
),
|
||||
Expanded(
|
||||
child: DropdownButton<int>(
|
||||
items: connectTypeItems,
|
||||
isExpanded: true,
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
changed = true;
|
||||
app!.connectType = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
value: app!.connectType,
|
||||
)),
|
||||
],
|
||||
),
|
||||
));
|
||||
|
||||
if (app!.connectType == ConnectType.REASONABLE) {
|
||||
if (StringUtil.isNotBlank(app!.alwaysAllow)) {
|
||||
var permissionItems =
|
||||
getPermissionItems(context, app!.alwaysAllow!, true);
|
||||
|
||||
if (permissionItems.isNotEmpty) {
|
||||
list.add(Container(
|
||||
margin: baseMargin,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
"Always Allow:",
|
||||
),
|
||||
));
|
||||
list.add(Container(
|
||||
width: double.infinity,
|
||||
child: Wrap(
|
||||
spacing: Base.BASE_PADDING,
|
||||
runSpacing: Base.BASE_PADDING,
|
||||
children: permissionItems,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtil.isNotBlank(app!.alwaysReject)) {
|
||||
var permissionItems =
|
||||
getPermissionItems(context, app!.alwaysReject!, false);
|
||||
|
||||
if (permissionItems.isNotEmpty) {
|
||||
list.add(Container(
|
||||
margin: baseMargin,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
"Always Reject:",
|
||||
),
|
||||
));
|
||||
list.add(Container(
|
||||
width: double.infinity,
|
||||
child: Wrap(
|
||||
spacing: Base.BASE_PADDING,
|
||||
runSpacing: Base.BASE_PADDING,
|
||||
children: permissionItems,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> actions = [];
|
||||
if (changed == true) {
|
||||
actions.add(GestureDetector(
|
||||
onTap: appUpdate,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(Base.BASE_PADDING),
|
||||
child: Icon(Icons.done),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: AppbarBackBtnComponent(),
|
||||
title: Text(
|
||||
"App Detail",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: themeData.textTheme.bodyLarge!.fontSize,
|
||||
),
|
||||
),
|
||||
actions: actions,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(Base.BASE_PADDING),
|
||||
child: Column(
|
||||
children: list,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> getPermissionItems(
|
||||
BuildContext context, String permissionText, bool allow) {
|
||||
var permissionTexts = permissionText.split(";");
|
||||
List<Widget> permissionItems = [];
|
||||
for (var permissionText in permissionTexts) {
|
||||
var strs = permissionText.split("-");
|
||||
var authType = int.tryParse(strs[0]);
|
||||
if (authType != null) {
|
||||
if (strs.length > 1) {
|
||||
var eventKindStrs = strs[1].split(",");
|
||||
for (var eventKindStr in eventKindStrs) {
|
||||
var eventKind = int.tryParse(eventKindStr);
|
||||
if (eventKind != null) {
|
||||
permissionItems.add(AppDetailPermissionItemComponent(
|
||||
allow,
|
||||
authType,
|
||||
eventKind: eventKind,
|
||||
onDelete: onPermissionDelete,
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
permissionItems.add(AppDetailPermissionItemComponent(
|
||||
allow,
|
||||
authType,
|
||||
onDelete: onPermissionDelete,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return permissionItems;
|
||||
}
|
||||
|
||||
onPermissionDelete(bool allow, int authType, int? eventKind) {
|
||||
var sourceText = app!.alwaysAllow;
|
||||
if (!allow) {
|
||||
sourceText = app!.alwaysReject;
|
||||
}
|
||||
|
||||
List<String> permissions = [];
|
||||
if (StringUtil.isNotBlank(sourceText)) {
|
||||
var permissionTexts = sourceText!.split(";");
|
||||
for (var permissionText in permissionTexts) {
|
||||
var strs = permissionText.split("-");
|
||||
var _authType = int.tryParse(strs[0]);
|
||||
if (_authType != authType) {
|
||||
permissions.add(permissionText);
|
||||
} else {
|
||||
// authType same, check eventKind
|
||||
if (eventKind == null || strs.length <= 1) {
|
||||
continue;
|
||||
} else {
|
||||
// should check eventKind
|
||||
List<String> checkedEventKindStrs = [];
|
||||
if (strs.length > 1) {
|
||||
var eventKindStrs = strs[1].split(",");
|
||||
for (var eventKindStr in eventKindStrs) {
|
||||
if (eventKindStr == "$eventKind") {
|
||||
continue;
|
||||
} else {
|
||||
checkedEventKindStrs.add(eventKindStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checkedEventKindStrs.isNotEmpty) {
|
||||
permissions.add("$_authType-${checkedEventKindStrs.join(",")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allow) {
|
||||
app!.alwaysAllow = permissions.join(";");
|
||||
} else {
|
||||
app!.alwaysReject = permissions.join(";");
|
||||
}
|
||||
changed = true;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void appUpdate() {
|
||||
app!.name = nameController.text;
|
||||
appProvider.update(app!);
|
||||
RouterUtil.back(context);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,9 @@ import 'package:nostr_sdk/utils/string_util.dart';
|
||||
import 'package:nowser/component/app/app_type_component.dart';
|
||||
import 'package:nowser/component/image_component.dart';
|
||||
import 'package:nowser/const/app_type.dart';
|
||||
import 'package:nowser/const/router_path.dart';
|
||||
import 'package:nowser/data/app.dart';
|
||||
import 'package:nowser/util/router_util.dart';
|
||||
|
||||
import '../../const/base.dart';
|
||||
|
||||
@@ -57,14 +59,20 @@ class _MeRouterAppItemComponent extends State<MeRouterAppItemComponent> {
|
||||
child: Icon(Icons.chevron_right),
|
||||
);
|
||||
|
||||
return Container(
|
||||
child: Row(
|
||||
children: [
|
||||
imageWidget,
|
||||
Expanded(child: titleWidget),
|
||||
typeWidget,
|
||||
rightIconWidget,
|
||||
],
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
RouterUtil.router(context, RouterPath.APP_DETAIL, widget.app);
|
||||
},
|
||||
behavior: HitTestBehavior.translucent,
|
||||
child: Container(
|
||||
child: Row(
|
||||
children: [
|
||||
imageWidget,
|
||||
Expanded(child: titleWidget),
|
||||
typeWidget,
|
||||
rightIconWidget,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user