diff --git a/lib/component/cust_state.dart b/lib/component/cust_state.dart new file mode 100644 index 0000000..6ff88d7 --- /dev/null +++ b/lib/component/cust_state.dart @@ -0,0 +1,38 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +abstract class CustState extends State { + bool isInited = false; + + bool readyComplete = false; + + @override + Widget build(BuildContext context) { + Widget w = doBuild(context); + + if (!isInited) { + isInited = true; + WidgetsBinding.instance.addPostFrameCallback((_) { + this.onReady(context); + readyComplete = true; + }); + } + + return w; + } + + Widget doBuild(BuildContext context); + + Future onReady(BuildContext context); + + // @override + // void dispose() { + // super.dispose(); + // } + + // @override + // void initState() { + // super.initState(); + // } +} diff --git a/lib/component/webview/webview_component.dart b/lib/component/webview/webview_component.dart index b6e7f5a..0fd5d59 100644 --- a/lib/component/webview/webview_component.dart +++ b/lib/component/webview/webview_component.dart @@ -214,7 +214,7 @@ class _WebViewComponent extends State } checkPermission(context, AppType.WEB, code, AuthType.GET_PUBLIC_KEY, - () { + (app) { nip07Reject(resultId, "Forbid"); }, (app, signer) { print("confirm get pubkey"); @@ -243,7 +243,7 @@ class _WebViewComponent extends State var eventKind = eventObj["kind"]; if (eventKind is int) { checkPermission(context, AppType.WEB, code, AuthType.SIGN_EVENT, - eventKind: eventKind, authDetail: content, () { + eventKind: eventKind, authDetail: content, (app) { nip07Reject(resultId, "Forbid"); }, (app, signer) async { var tags = eventObj["tags"]; @@ -281,7 +281,7 @@ class _WebViewComponent extends State return; } - checkPermission(context, AppType.WEB, code, AuthType.GET_RELAYS, () { + checkPermission(context, AppType.WEB, code, AuthType.GET_RELAYS, (app) { nip07Reject(resultId, "Forbid"); }, (app, signer) { // TODO handle getRelays @@ -319,7 +319,7 @@ class _WebViewComponent extends State } checkPermission(context, AppType.WEB, code, AuthType.NIP04_ENCRYPT, - () { + (app) { nip07Reject(resultId, "Forbid"); }, (app, signer) async { var resultStr = await signer.encrypt(pubkey, plaintext); @@ -351,7 +351,7 @@ class _WebViewComponent extends State } checkPermission(context, AppType.WEB, code, AuthType.NIP04_DECRYPT, - () { + (app) { nip07Reject(resultId, "Forbid"); }, (app, signer) async { var app = appProvider.getApp(AppType.WEB, code); @@ -386,7 +386,7 @@ class _WebViewComponent extends State } checkPermission(context, AppType.WEB, code, AuthType.NIP44_ENCRYPT, - () { + (app) { nip07Reject(resultId, "Forbid"); }, (app, signer) async { var resultStr = await signer.nip44Encrypt(pubkey, plaintext); @@ -418,7 +418,7 @@ class _WebViewComponent extends State } checkPermission(context, AppType.WEB, code, AuthType.NIP44_DECRYPT, - () { + (app) { nip07Reject(resultId, "Forbid"); }, (app, signer) async { var resultStr = await signer.nip44Decrypt(pubkey, ciphertext); diff --git a/lib/const/auth_type.dart b/lib/const/auth_type.dart index cb48478..a917897 100644 --- a/lib/const/auth_type.dart +++ b/lib/const/auth_type.dart @@ -1,3 +1,5 @@ +import 'package:flutter/material.dart'; + class AuthType { static const GET_PUBLIC_KEY = 1; @@ -12,4 +14,24 @@ class AuthType { static const NIP44_ENCRYPT = 6; static const NIP44_DECRYPT = 7; + + static String getAuthName(BuildContext context, int authType) { + if (authType == GET_PUBLIC_KEY) { + return "Get Public Key"; + } else if (authType == SIGN_EVENT) { + return "Sign Event"; + } else if (authType == GET_RELAYS) { + return "Get Relays"; + } else if (authType == NIP04_ENCRYPT) { + return "Encrypt (NIP-04)"; + } else if (authType == NIP04_DECRYPT) { + return "Decrypt (NIP-04)"; + } else if (authType == NIP44_ENCRYPT) { + return "Encrypt (NIP-44)"; + } else if (authType == NIP44_DECRYPT) { + return "Decrypt (NIP-44)"; + } + + return "Unknow"; + } } diff --git a/lib/data/auth_log_db.dart b/lib/data/auth_log_db.dart index 329d1ee..1a50a7f 100644 --- a/lib/data/auth_log_db.dart +++ b/lib/data/auth_log_db.dart @@ -1 +1,35 @@ -class AuthLogDB {} +import 'package:nowser/data/auth_log.dart'; +import 'package:sqflite/sqflite.dart'; + +import 'db.dart'; + +class AuthLogDB { + static Future> list( + {DatabaseExecutor? db, int? appId, int? skip, int? limit}) async { + List objs = []; + List? arguments = []; + db = await DB.getDB(db); + var sql = "select * from auth_log where 1 = 1"; + if (appId != null) { + sql += " and app_id = ?"; + arguments.add(appId); + } + sql += " order by created_at desc"; + if (skip != null && limit != null) { + sql += " limit ?, ?"; + arguments.add(skip); + arguments.add(limit); + } + List> list = await db.rawQuery(sql, arguments); + for (var i = 0; i < list.length; i++) { + var json = list[i]; + objs.add(AuthLog.fromJson(json)); + } + return objs; + } + + static Future insert(AuthLog o, {DatabaseExecutor? db}) async { + db = await DB.getDB(db); + return await db.insert("auth_log", o.toJson()); + } +} diff --git a/lib/provider/android_signer_mixin.dart b/lib/provider/android_signer_mixin.dart index 3b3772c..b3a4e70 100644 --- a/lib/provider/android_signer_mixin.dart +++ b/lib/provider/android_signer_mixin.dart @@ -92,9 +92,12 @@ mixin AndroidSignerMixin on PermissionCheckMixin { } checkPermission(context, AppType.ANDROID_APP, code!, authType, - eventKind: eventKind, authDetail: playload, () { + eventKind: eventKind, authDetail: playload, (app) { // this place should do some about reject - // saveAuthLog(app, authType, eventKind, playload, AuthResult.OK); + if (app != null) { + saveAuthLog( + app, authType, eventKind, playload, AuthResult.REJECT); + } receiveIntent.ReceiveIntent.setResult( receiveIntent.kActivityResultCanceled, shouldFinish: true, diff --git a/lib/provider/app_provider.dart b/lib/provider/app_provider.dart index 61accaf..3ba272e 100644 --- a/lib/provider/app_provider.dart +++ b/lib/provider/app_provider.dart @@ -11,12 +11,18 @@ class AppProvider extends ChangeNotifier { List get appList => _list; + Map _appMap = {}; + Map> appPermissions = {}; Future reload() async { + _appMap = {}; appPermissions = {}; var allApp = await AppDB.all(); _list = allApp; + for (var app in allApp) { + _appMap[app.id!] = app; + } notifyListeners(); } @@ -91,6 +97,10 @@ class AppProvider extends ChangeNotifier { } } + App? getAppById(int appId) { + return _appMap[appId]; + } + App? getApp(int appType, String code) { for (var app in _list) { if (app.appType == appType && app.code == code) { diff --git a/lib/provider/permission_check_mixin.dart b/lib/provider/permission_check_mixin.dart index adef65a..c8e29a9 100644 --- a/lib/provider/permission_check_mixin.dart +++ b/lib/provider/permission_check_mixin.dart @@ -4,6 +4,7 @@ import 'package:nowser/component/auth_dialog/auth_app_connect_dialog.dart'; import 'package:nowser/component/auth_dialog/auth_dialog.dart'; import 'package:nowser/const/auth_result.dart'; import 'package:nowser/data/auth_log.dart'; +import 'package:nowser/data/auth_log_db.dart'; import 'package:nowser/main.dart'; import '../const/connect_type.dart'; @@ -11,7 +12,7 @@ import '../data/app.dart'; mixin PermissionCheckMixin { Future checkPermission(BuildContext context, int appType, String code, - int authType, Function reject, Function(App, NostrSigner) confirm, + int authType, Function(App?) reject, Function(App, NostrSigner) confirm, {int? eventKind, String? authDetail}) async { var app = appProvider.getApp(appType, code); if (app == null) { @@ -24,13 +25,14 @@ mixin PermissionCheckMixin { if (app == null) { // not allow connect - reject(); + reject(null); return; } var signer = getSigner(app.pubkey!); if (signer == null) { - reject(); + saveAuthLog(app, authType, eventKind, authDetail, AuthResult.REJECT); + reject(app); return; } @@ -48,7 +50,7 @@ mixin PermissionCheckMixin { return; } else if (permissionCheckResult == AuthResult.REJECT) { saveAuthLog(app, authType, eventKind, authDetail, AuthResult.REJECT); - reject(); + reject(app); return; } @@ -62,13 +64,24 @@ mixin PermissionCheckMixin { } saveAuthLog(app, authType, eventKind, authDetail, AuthResult.REJECT); - reject(); + reject(app); return; } void saveAuthLog(App app, int authType, int? eventKind, String? authDetail, int authResult) { - // TODO + if (app.id != null) { + var authLog = AuthLog( + appId: app.id, + authType: authType, + eventKind: eventKind, + content: authDetail, + authResult: authResult, + createdAt: DateTime.now().millisecondsSinceEpoch ~/ 1000, + ); + + AuthLogDB.insert(authLog); + } } // this method should override diff --git a/lib/router/me/me_router.dart b/lib/router/me/me_router.dart index d2849ae..3aebba5 100644 --- a/lib/router/me/me_router.dart +++ b/lib/router/me/me_router.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:nowser/component/cust_state.dart'; import 'package:nowser/component/text_input/text_input_dialog.dart'; import 'package:nowser/component/user/user_name_component.dart'; import 'package:nowser/component/user/user_pic_component.dart'; import 'package:nowser/const/base.dart'; import 'package:nowser/const/router_path.dart'; +import 'package:nowser/data/auth_log_db.dart'; import 'package:nowser/provider/app_provider.dart'; import 'package:nowser/provider/key_provider.dart'; import 'package:nowser/router/me/me_router_log_item_component.dart'; @@ -11,6 +13,7 @@ import 'package:nowser/router/me/me_router_web_item_component.dart'; import 'package:nowser/util/router_util.dart'; import 'package:provider/provider.dart'; +import '../../data/auth_log.dart'; import '../keys/keys_router.dart'; import 'me_router_app_item_component.dart'; @@ -21,9 +24,19 @@ class MeRouter extends StatefulWidget { } } -class _MeRouter extends State { +class _MeRouter extends CustState { + List authLogs = []; + @override - Widget build(BuildContext context) { + Future onReady(BuildContext context) async { + var list = await AuthLogDB.list(skip: 0, limit: 10); + setState(() { + authLogs = list; + }); + } + + @override + Widget doBuild(BuildContext context) { var mediaQueryData = MediaQuery.of(context); var themeData = Theme.of(context); var _appProvider = Provider.of(context); @@ -150,7 +163,7 @@ class _MeRouter extends State { List appWidgetList = []; var appList = _appProvider.appList; var length = appList.length; - for (var i = 0; i < length && i < 3; i++) { + for (var i = 0; i < length && i < 5; i++) { var app = appList[i]; appWidgetList.add(Container( child: MeRouterAppItemComponent(app), @@ -184,18 +197,12 @@ class _MeRouter extends State { // TODO add zap send list here! List logList = []; - logList.add(Container( - child: MeRouterLogItemComponent(), - )); - logList.add(Divider()); - logList.add(Container( - child: MeRouterLogItemComponent(), - )); - logList.add(Divider()); - logList.add(Container( - child: MeRouterLogItemComponent(), - )); - logList.add(Divider()); + for (var authLog in authLogs) { + logList.add(Container( + child: MeRouterLogItemComponent(authLog), + )); + logList.add(Divider()); + } logList.add(Container( alignment: Alignment.center, child: Text( diff --git a/lib/router/me/me_router_log_item_component.dart b/lib/router/me/me_router_log_item_component.dart index 11571bc..2f1546b 100644 --- a/lib/router/me/me_router_log_item_component.dart +++ b/lib/router/me/me_router_log_item_component.dart @@ -1,7 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:nostr_sdk/utils/string_util.dart'; +import 'package:nowser/const/auth_result.dart'; +import 'package:nowser/const/auth_type.dart'; import 'package:nowser/const/base.dart'; +import 'package:nowser/data/auth_log.dart'; +import 'package:nowser/provider/app_provider.dart'; +import 'package:provider/provider.dart'; class MeRouterLogItemComponent extends StatefulWidget { + AuthLog authLog; + + MeRouterLogItemComponent(this.authLog); + @override State createState() { return _MeRouterLogItemComponent(); @@ -11,23 +21,80 @@ class MeRouterLogItemComponent extends StatefulWidget { class _MeRouterLogItemComponent extends State { @override Widget build(BuildContext context) { + var appProvider = Provider.of(context); + var app = appProvider.getAppById(widget.authLog.appId!); + + var appName = ""; + if (app != null) { + if (StringUtil.isNotBlank(app.name)) { + appName = app.name!; + } else if (StringUtil.isNotBlank(app.code)) { + appName = app.code!; + } + } + var appNameWidget = Container( constraints: BoxConstraints(maxWidth: 80), margin: EdgeInsets.only( left: Base.BASE_PADDING_HALF, - right: Base.BASE_PADDING, + right: Base.BASE_PADDING_HALF, ), child: Text( - "APP Name APP Name", + appName, maxLines: 1, overflow: TextOverflow.ellipsis, ), ); + late Widget resultWidget; + if (widget.authLog.authResult == AuthResult.OK) { + resultWidget = Card.filled( + color: Colors.green, + child: Container( + padding: EdgeInsets.only( + left: Base.BASE_PADDING_HALF, + right: Base.BASE_PADDING_HALF, + top: 2, + bottom: 2, + ), + child: Text( + "approve", + style: TextStyle(color: Colors.white), + ), + ), + ); + } else { + resultWidget = Card.filled( + color: Colors.red, + child: Container( + padding: EdgeInsets.only( + left: Base.BASE_PADDING_HALF, + right: Base.BASE_PADDING_HALF, + ), + child: Text( + "reject", + style: TextStyle(color: Colors.white), + ), + ), + ); + } + + String authContent = ""; + var authType = widget.authLog.authType!; + authContent += AuthType.getAuthName(context, authType); + + if (authType == AuthType.SIGN_EVENT) { + authContent += " EventKind(${widget.authLog.eventKind})"; + } else if (authType >= AuthType.NIP04_ENCRYPT) { + if (StringUtil.isNotBlank(widget.authLog.content)) { + authContent += widget.authLog.content!; + } + } + var logWidget = Expanded( child: Container( child: Text( - "Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log", + authContent, maxLines: 1, overflow: TextOverflow.ellipsis, ), @@ -41,6 +108,10 @@ class _MeRouterLogItemComponent extends State { child: Row( children: [ appNameWidget, + Container( + margin: EdgeInsets.only(right: Base.BASE_PADDING_HALF), + child: resultWidget, + ), logWidget, rightIconWidget, ],