Files
nowser/lib/router/app_detail/app_detail_router.dart
2025-07-22 21:48:14 +08:00

357 lines
9.7 KiB
Dart

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/component/user/user_name_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 '../../generated/l10n.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;
late S s;
@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());
nameController.text = app!.name ?? "";
}
}
if (app == null) {
RouterUtil.back(context);
return Container();
}
s = S.of(context);
List<Widget> list = [];
var baseMargin = const EdgeInsets.only(bottom: Base.BASE_PADDING);
Widget imageWidget = const 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: s.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: UserNameComponent(
pubkey,
fullNpubName: true,
showBoth: true,
),
));
}
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("${s.Pubkey}:"),
),
Expanded(child: keyWidget),
],
),
));
List<DropdownMenuItem<int>> connectTypeItems = [];
connectTypeItems.add(DropdownMenuItem(
value: ConnectType.FULLY_TRUST, child: Text(s.Fully_trust)));
connectTypeItems.add(DropdownMenuItem(
value: ConnectType.REASONABLE, child: Text(s.Reasonable)));
connectTypeItems.add(DropdownMenuItem(
value: ConnectType.ALWAY_REJECT, child: Text(s.Alway_reject)));
list.add(Container(
margin: baseMargin,
child: Row(
children: [
Container(
margin: const EdgeInsets.only(right: Base.BASE_PADDING),
child: Text("${s.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(
"${s.Always_Allow}:",
),
));
list.add(Container(
margin: baseMargin,
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(
"${s.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(
s.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);
}
}