mirror of
https://github.com/haorendashu/nowser.git
synced 2025-12-18 18:14:21 +01:00
init nostr and add userinfo provider
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:nostr_sdk/nip19/nip19.dart';
|
import 'package:nostr_sdk/nip19/nip19.dart';
|
||||||
|
import 'package:nostr_sdk/utils/string_util.dart';
|
||||||
|
import 'package:nowser/data/metadata.dart';
|
||||||
|
import 'package:nowser/provider/userinfo_provider.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class UserNameComponent extends StatefulWidget {
|
class UserNameComponent extends StatefulWidget {
|
||||||
String pubkey;
|
String pubkey;
|
||||||
@@ -17,15 +21,29 @@ class UserNameComponent extends StatefulWidget {
|
|||||||
class _UserNameComponent extends State<UserNameComponent> {
|
class _UserNameComponent extends State<UserNameComponent> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
return Selector<UserinfoProvider, Metadata?>(
|
||||||
|
builder: (context, metadata, child) {
|
||||||
var npub = Nip19.encodePubKey(widget.pubkey);
|
var npub = Nip19.encodePubKey(widget.pubkey);
|
||||||
if (!widget.fullNpubName) {
|
if (!widget.fullNpubName) {
|
||||||
npub = Nip19.encodeSimplePubKey(widget.pubkey);
|
npub = Nip19.encodeSimplePubKey(widget.pubkey);
|
||||||
}
|
}
|
||||||
|
var name = npub;
|
||||||
|
|
||||||
|
if (metadata != null) {
|
||||||
|
if (StringUtil.isNotBlank(metadata.displayName)) {
|
||||||
|
name = metadata.displayName!;
|
||||||
|
} else if (StringUtil.isNotBlank(metadata.name)) {
|
||||||
|
name = metadata.name!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Text(
|
return Text(
|
||||||
npub,
|
name,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
);
|
);
|
||||||
|
}, selector: (context, _provider) {
|
||||||
|
return _provider.getMetadata(widget.pubkey);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:nostr_sdk/utils/string_util.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import '../../data/metadata.dart';
|
||||||
|
import '../../provider/userinfo_provider.dart';
|
||||||
|
import '../image_component.dart';
|
||||||
|
|
||||||
class UserPicComponent extends StatefulWidget {
|
class UserPicComponent extends StatefulWidget {
|
||||||
|
String pubkey;
|
||||||
|
|
||||||
double width;
|
double width;
|
||||||
|
|
||||||
UserPicComponent({required this.width});
|
UserPicComponent({
|
||||||
|
required this.pubkey,
|
||||||
|
required this.width,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() {
|
State<StatefulWidget> createState() {
|
||||||
@@ -14,11 +25,23 @@ class UserPicComponent extends StatefulWidget {
|
|||||||
class _UserPicComponent extends State<UserPicComponent> {
|
class _UserPicComponent extends State<UserPicComponent> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
return Selector<UserinfoProvider, Metadata?>(
|
||||||
|
builder: (context, metadata, child) {
|
||||||
Widget innerWidget = Icon(
|
Widget innerWidget = Icon(
|
||||||
Icons.account_circle,
|
Icons.account_circle,
|
||||||
size: widget.width,
|
size: widget.width,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (metadata != null && StringUtil.isNotBlank(metadata.picture)) {
|
||||||
|
innerWidget = ImageComponent(
|
||||||
|
imageUrl: metadata.picture!,
|
||||||
|
width: widget.width,
|
||||||
|
height: widget.width,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
placeholder: (context, url) => CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
height: widget.width,
|
height: widget.width,
|
||||||
@@ -29,5 +52,10 @@ class _UserPicComponent extends State<UserPicComponent> {
|
|||||||
),
|
),
|
||||||
child: innerWidget,
|
child: innerWidget,
|
||||||
);
|
);
|
||||||
|
}, selector: (context, _provider) {
|
||||||
|
if (StringUtil.isNotBlank(widget.pubkey)) {
|
||||||
|
return _provider.getMetadata(widget.pubkey);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
lib/const/client_connected.dart
Normal file
7
lib/const/client_connected.dart
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
class ClientConneccted {
|
||||||
|
static int UN_CONNECT = -1;
|
||||||
|
|
||||||
|
static int CONNECTING = 1;
|
||||||
|
|
||||||
|
static int CONNECTED = 2;
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import 'package:sqflite/sqflite.dart';
|
|||||||
import '../const/base.dart';
|
import '../const/base.dart';
|
||||||
|
|
||||||
class DB {
|
class DB {
|
||||||
static const _VERSION = 2;
|
static const _VERSION = 3;
|
||||||
|
|
||||||
static const _dbName = "nowser.db";
|
static const _dbName = "nowser.db";
|
||||||
|
|
||||||
@@ -43,14 +43,28 @@ class DB {
|
|||||||
"create table bookmark(id integer not null constraint bookmark_pk primary key autoincrement,title text,url text not null,favicon text,weight integer,added_to_index integer, added_to_qa integer,created_at integer);");
|
"create table bookmark(id integer not null constraint bookmark_pk primary key autoincrement,title text,url text not null,favicon text,weight integer,added_to_index integer, added_to_qa integer,created_at integer);");
|
||||||
db.execute(
|
db.execute(
|
||||||
"create table browser_history(id integer not null constraint browser_history_pk primary key autoincrement,title text,url text not null,favicon text,created_at integer);");
|
"create table browser_history(id integer not null constraint browser_history_pk primary key autoincrement,title text,url text not null,favicon text,created_at integer);");
|
||||||
|
|
||||||
|
db.execute(
|
||||||
|
"create table event(id text,pubkey text,created_at integer,kind integer,tags text,content text);");
|
||||||
|
db.execute("create unique index event_key_index_id_uindex on event (id);");
|
||||||
|
db.execute("create index event_date_index on event (kind, created_at);");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> _onUpgrade(
|
static Future<void> _onUpgrade(
|
||||||
Database db, int oldVersion, int newVersion) async {
|
Database db, int oldVersion, int newVersion) async {
|
||||||
if (oldVersion == 1) {
|
if (oldVersion < 2) {
|
||||||
db.execute(
|
db.execute(
|
||||||
"alter table bookmark add added_to_qa integer after added_to_index");
|
"alter table bookmark add added_to_qa integer after added_to_index");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 3) {
|
||||||
|
db.execute(
|
||||||
|
"create table event(id text,pubkey text,created_at integer,kind integer,tags text,content text);");
|
||||||
|
db.execute(
|
||||||
|
"create unique index event_key_index_id_uindex on event (id);");
|
||||||
|
db.execute(
|
||||||
|
"create index event_date_index on event (kind, created_at);");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Database> getCurrentDatabase() async {
|
static Future<Database> getCurrentDatabase() async {
|
||||||
|
|||||||
106
lib/data/event_db.dart
Normal file
106
lib/data/event_db.dart
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:nostr_sdk/event.dart';
|
||||||
|
import 'package:nostr_sdk/utils/string_util.dart';
|
||||||
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
|
import 'db.dart';
|
||||||
|
|
||||||
|
class EventDB {
|
||||||
|
static Future<List<Event>> list(List<int> kinds, int skip, limit,
|
||||||
|
{DatabaseExecutor? db, List<String>? pubkeys}) async {
|
||||||
|
db = await DB.getDB(db);
|
||||||
|
List<Event> l = [];
|
||||||
|
List<dynamic> args = [];
|
||||||
|
|
||||||
|
var sql = "select * from event where kind in(";
|
||||||
|
for (var kind in kinds) {
|
||||||
|
sql += "?,";
|
||||||
|
args.add(kind);
|
||||||
|
}
|
||||||
|
sql = sql.substring(0, sql.length - 1);
|
||||||
|
sql += ")";
|
||||||
|
if (pubkeys != null && pubkeys.isNotEmpty) {
|
||||||
|
if (pubkeys.length == 1) {
|
||||||
|
sql += " and pubkey = ? ";
|
||||||
|
args.add(pubkeys.first);
|
||||||
|
} else {
|
||||||
|
sql += " and pubkey in(";
|
||||||
|
for (var pubkey in pubkeys) {
|
||||||
|
sql += "?,";
|
||||||
|
args.add(pubkey);
|
||||||
|
}
|
||||||
|
sql = sql.substring(0, sql.length - 1);
|
||||||
|
sql += ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += " order by created_at desc limit ?, ?";
|
||||||
|
args.add(skip);
|
||||||
|
args.add(limit);
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> list = await db.rawQuery(sql, args);
|
||||||
|
for (var listObj in list) {
|
||||||
|
l.add(loadFromJson(listObj));
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<int> insert(Event o, {DatabaseExecutor? db}) async {
|
||||||
|
db = await DB.getDB(db);
|
||||||
|
var jsonObj = o.toJson();
|
||||||
|
var tags = jsonEncode(o.tags);
|
||||||
|
jsonObj["tags"] = tags;
|
||||||
|
jsonObj.remove("sig");
|
||||||
|
try {
|
||||||
|
return await db.insert("event", jsonObj);
|
||||||
|
} catch (e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Event?> get(String id, {DatabaseExecutor? db}) async {
|
||||||
|
db = await DB.getDB(db);
|
||||||
|
var list = await db.query("event", where: "id = ?", whereArgs: [id]);
|
||||||
|
if (list.isNotEmpty) {
|
||||||
|
return Event.fromJson(list[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Event?> execute(String sql, List<Object?> arguments,
|
||||||
|
{DatabaseExecutor? db}) async {
|
||||||
|
db = await DB.getDB(db);
|
||||||
|
await db.execute(sql, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Event?> delete(String id, {DatabaseExecutor? db}) async {
|
||||||
|
db = await DB.getDB(db);
|
||||||
|
await db.execute("delete from event where id = ?", [id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> deleteAll({DatabaseExecutor? db}) async {
|
||||||
|
db = await DB.getDB(db);
|
||||||
|
db.execute("delete from event ", []);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future update(Event o, {DatabaseExecutor? db}) async {
|
||||||
|
db = await DB.getDB(db);
|
||||||
|
var jsonObj = o.toJson();
|
||||||
|
var tags = jsonEncode(o.tags);
|
||||||
|
jsonObj["tags"] = tags;
|
||||||
|
jsonObj.remove("sig");
|
||||||
|
await db.update("event", jsonObj, where: "id = ?", whereArgs: [o.id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Event loadFromJson(Map<String, dynamic> data) {
|
||||||
|
Map<String, dynamic> m = {};
|
||||||
|
m.addAll(data);
|
||||||
|
|
||||||
|
var tagsStr = data["tags"];
|
||||||
|
var tagsObj = jsonDecode(tagsStr);
|
||||||
|
m["tags"] = tagsObj;
|
||||||
|
m["sig"] = "";
|
||||||
|
return Event.fromJson(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
lib/data/metadata.dart
Normal file
70
lib/data/metadata.dart
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
class Metadata {
|
||||||
|
String? pubkey;
|
||||||
|
String? name;
|
||||||
|
String? displayName;
|
||||||
|
String? picture;
|
||||||
|
String? banner;
|
||||||
|
String? website;
|
||||||
|
String? about;
|
||||||
|
String? nip05;
|
||||||
|
String? lud16;
|
||||||
|
String? lud06;
|
||||||
|
int? updated_at;
|
||||||
|
int? valid;
|
||||||
|
|
||||||
|
Metadata({
|
||||||
|
this.pubkey,
|
||||||
|
this.name,
|
||||||
|
this.displayName,
|
||||||
|
this.picture,
|
||||||
|
this.banner,
|
||||||
|
this.website,
|
||||||
|
this.about,
|
||||||
|
this.nip05,
|
||||||
|
this.lud16,
|
||||||
|
this.lud06,
|
||||||
|
this.updated_at,
|
||||||
|
this.valid,
|
||||||
|
});
|
||||||
|
|
||||||
|
Metadata.fromJson(Map<String, dynamic> json) {
|
||||||
|
pubkey = json['pub_key'];
|
||||||
|
name = json['name'];
|
||||||
|
displayName = json['display_name'];
|
||||||
|
picture = json['picture'];
|
||||||
|
banner = json['banner'];
|
||||||
|
website = json['website'];
|
||||||
|
about = json['about'];
|
||||||
|
if (json['nip05'] != null && json['nip05'] is String) {
|
||||||
|
nip05 = json['nip05'];
|
||||||
|
}
|
||||||
|
lud16 = json['lud16'];
|
||||||
|
lud06 = json['lud06'];
|
||||||
|
if (json['updated_at'] != null && json['updated_at'] is int) {
|
||||||
|
updated_at = json['updated_at'];
|
||||||
|
}
|
||||||
|
valid = json['valid'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toFullJson() {
|
||||||
|
var data = toJson();
|
||||||
|
data['pub_key'] = this.pubkey;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['name'] = this.name;
|
||||||
|
data['display_name'] = this.displayName;
|
||||||
|
data['picture'] = this.picture;
|
||||||
|
data['banner'] = this.banner;
|
||||||
|
data['website'] = this.website;
|
||||||
|
data['about'] = this.about;
|
||||||
|
data['nip05'] = this.nip05;
|
||||||
|
data['lud16'] = this.lud16;
|
||||||
|
data['lud06'] = this.lud06;
|
||||||
|
data['updated_at'] = this.updated_at;
|
||||||
|
data['valid'] = this.valid;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,9 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:nostr_sdk/client_utils/keys.dart';
|
||||||
|
import 'package:nostr_sdk/nostr.dart';
|
||||||
|
import 'package:nostr_sdk/signer/local_nostr_signer.dart';
|
||||||
import 'package:nostr_sdk/utils/platform_util.dart';
|
import 'package:nostr_sdk/utils/platform_util.dart';
|
||||||
import 'package:nostr_sdk/utils/string_util.dart';
|
import 'package:nostr_sdk/utils/string_util.dart';
|
||||||
import 'package:nowser/data/db.dart';
|
import 'package:nowser/data/db.dart';
|
||||||
@@ -18,6 +21,8 @@ import 'package:nowser/provider/build_in_relay_provider.dart';
|
|||||||
import 'package:nowser/provider/download_provider.dart';
|
import 'package:nowser/provider/download_provider.dart';
|
||||||
import 'package:nowser/provider/key_provider.dart';
|
import 'package:nowser/provider/key_provider.dart';
|
||||||
import 'package:nowser/provider/permission_check_mixin.dart';
|
import 'package:nowser/provider/permission_check_mixin.dart';
|
||||||
|
import 'package:nowser/provider/relay_provider.dart';
|
||||||
|
import 'package:nowser/provider/userinfo_provider.dart';
|
||||||
import 'package:nowser/provider/web_provider.dart';
|
import 'package:nowser/provider/web_provider.dart';
|
||||||
import 'package:nowser/router/about_me/about_me_router.dart';
|
import 'package:nowser/router/about_me/about_me_router.dart';
|
||||||
import 'package:nowser/router/app_detail/app_detail_router.dart';
|
import 'package:nowser/router/app_detail/app_detail_router.dart';
|
||||||
@@ -75,6 +80,12 @@ BookmarkProvider bookmarkProvider = BookmarkProvider();
|
|||||||
|
|
||||||
late MediaDataCache mediaDataCache;
|
late MediaDataCache mediaDataCache;
|
||||||
|
|
||||||
|
late UserinfoProvider userinfoProvider;
|
||||||
|
|
||||||
|
late RelayProvider relayProvider;
|
||||||
|
|
||||||
|
Nostr? nostr;
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
||||||
rootIsolateToken = RootIsolateToken.instance!;
|
rootIsolateToken = RootIsolateToken.instance!;
|
||||||
@@ -117,6 +128,11 @@ Future<void> main() async {
|
|||||||
|
|
||||||
await doInit();
|
await doInit();
|
||||||
|
|
||||||
|
relayProvider = RelayProvider.getInstance();
|
||||||
|
var tempPrivateKey = generatePrivateKey();
|
||||||
|
nostr = await relayProvider.genNostrWithKey(tempPrivateKey);
|
||||||
|
userinfoProvider = await UserinfoProvider.getInstance();
|
||||||
|
|
||||||
mediaDataCache = MediaDataCache();
|
mediaDataCache = MediaDataCache();
|
||||||
await bookmarkProvider.init();
|
await bookmarkProvider.init();
|
||||||
|
|
||||||
@@ -239,6 +255,12 @@ class _MyApp extends State<MyApp> {
|
|||||||
ListenableProvider<DownloadProvider>.value(
|
ListenableProvider<DownloadProvider>.value(
|
||||||
value: downloadProvider,
|
value: downloadProvider,
|
||||||
),
|
),
|
||||||
|
ListenableProvider<UserinfoProvider>.value(
|
||||||
|
value: userinfoProvider,
|
||||||
|
),
|
||||||
|
ListenableProvider<RelayProvider>.value(
|
||||||
|
value: relayProvider,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
builder: BotToastInit(),
|
builder: BotToastInit(),
|
||||||
|
|||||||
350
lib/provider/relay_provider.dart
Normal file
350
lib/provider/relay_provider.dart
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:bot_toast/bot_toast.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:nesigner_adapter/nesigner.dart';
|
||||||
|
import 'package:nesigner_adapter/nesigner_util.dart';
|
||||||
|
import 'package:nostr_sdk/event.dart';
|
||||||
|
import 'package:nostr_sdk/event_kind.dart';
|
||||||
|
import 'package:nostr_sdk/nip02/nip02.dart';
|
||||||
|
import 'package:nostr_sdk/nip07/nip07_signer.dart';
|
||||||
|
import 'package:nostr_sdk/nip19/nip19.dart';
|
||||||
|
import 'package:nostr_sdk/nip46/nostr_remote_signer.dart';
|
||||||
|
import 'package:nostr_sdk/nip46/nostr_remote_signer_info.dart';
|
||||||
|
import 'package:nostr_sdk/nip55/android_nostr_signer.dart';
|
||||||
|
import 'package:nostr_sdk/nip65/nip65.dart';
|
||||||
|
import 'package:nostr_sdk/nostr.dart';
|
||||||
|
import 'package:nostr_sdk/relay/relay.dart';
|
||||||
|
import 'package:nostr_sdk/relay/relay_base.dart';
|
||||||
|
import 'package:nostr_sdk/relay/relay_isolate.dart';
|
||||||
|
import 'package:nostr_sdk/relay/relay_mode.dart';
|
||||||
|
import 'package:nostr_sdk/relay/relay_status.dart';
|
||||||
|
import 'package:nostr_sdk/relay/relay_type.dart';
|
||||||
|
import 'package:nostr_sdk/relay_local/relay_local.dart';
|
||||||
|
import 'package:nostr_sdk/signer/local_nostr_signer.dart';
|
||||||
|
import 'package:nostr_sdk/signer/nostr_signer.dart';
|
||||||
|
import 'package:nostr_sdk/signer/pubkey_only_nostr_signer.dart';
|
||||||
|
import 'package:nostr_sdk/utils/platform_util.dart';
|
||||||
|
import 'package:nostr_sdk/utils/string_util.dart';
|
||||||
|
|
||||||
|
import '../const/client_connected.dart';
|
||||||
|
import '../main.dart';
|
||||||
|
import 'data_util.dart';
|
||||||
|
|
||||||
|
class RelayProvider extends ChangeNotifier {
|
||||||
|
static RelayProvider? _relayProvider;
|
||||||
|
|
||||||
|
List<String> relayAddrs = [];
|
||||||
|
|
||||||
|
Map<String, RelayStatus> relayStatusMap = {};
|
||||||
|
|
||||||
|
RelayStatus? relayStatusLocal;
|
||||||
|
|
||||||
|
Map<String, RelayStatus> _tempRelayStatusMap = {};
|
||||||
|
|
||||||
|
static RelayProvider getInstance() {
|
||||||
|
if (_relayProvider == null) {
|
||||||
|
_relayProvider = RelayProvider();
|
||||||
|
// _relayProvider!._load();
|
||||||
|
}
|
||||||
|
return _relayProvider!;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadRelayAddrs(String? content) {
|
||||||
|
var relays = parseRelayAddrs(content);
|
||||||
|
if (relays.isEmpty) {
|
||||||
|
relays = [
|
||||||
|
"wss://nos.lol",
|
||||||
|
"wss://nostr.wine",
|
||||||
|
"wss://atlas.nostr.land",
|
||||||
|
"wss://relay.damus.io",
|
||||||
|
"wss://nostr-relay.app",
|
||||||
|
"wss://nostr.oxtr.dev",
|
||||||
|
"wss://relayable.org",
|
||||||
|
"wss://relay.primal.net",
|
||||||
|
"wss://relay.nostr.bg",
|
||||||
|
"wss://relay.nostr.band",
|
||||||
|
"wss://yabu.me",
|
||||||
|
"wss://nostr.mom"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
relayAddrs = relays;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> parseRelayAddrs(String? content) {
|
||||||
|
List<String> relays = [];
|
||||||
|
if (StringUtil.isBlank(content)) {
|
||||||
|
return relays;
|
||||||
|
}
|
||||||
|
|
||||||
|
var relayStatuses = NIP02.parseContenToRelays(content!);
|
||||||
|
for (var relayStatus in relayStatuses) {
|
||||||
|
var addr = relayStatus.addr;
|
||||||
|
relays.add(addr);
|
||||||
|
|
||||||
|
var oldRelayStatus = relayStatusMap[addr];
|
||||||
|
if (oldRelayStatus != null) {
|
||||||
|
oldRelayStatus.readAccess = relayStatus.readAccess;
|
||||||
|
oldRelayStatus.writeAccess = relayStatus.writeAccess;
|
||||||
|
} else {
|
||||||
|
relayStatusMap[addr] = relayStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return relays;
|
||||||
|
}
|
||||||
|
|
||||||
|
RelayStatus? getRelayStatus(String addr) {
|
||||||
|
return relayStatusMap[addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
String relayNumStr() {
|
||||||
|
var normalLength = relayAddrs.length;
|
||||||
|
|
||||||
|
int connectedNum = 0;
|
||||||
|
var it = relayStatusMap.values;
|
||||||
|
for (var status in it) {
|
||||||
|
if (status.connected == ClientConneccted.CONNECTED) {
|
||||||
|
connectedNum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "$connectedNum / $normalLength";
|
||||||
|
}
|
||||||
|
|
||||||
|
int total() {
|
||||||
|
return relayAddrs.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Nostr?> genNostrWithKey(String key) async {
|
||||||
|
NostrSigner? nostrSigner;
|
||||||
|
if (Nip19.isPubkey(key)) {
|
||||||
|
nostrSigner = PubkeyOnlyNostrSigner(Nip19.decode(key));
|
||||||
|
} else if (AndroidNostrSigner.isAndroidNostrSignerKey(key)) {
|
||||||
|
var pubkey = AndroidNostrSigner.getPubkeyFromKey(key);
|
||||||
|
var package = AndroidNostrSigner.getPackageFromKey(key);
|
||||||
|
nostrSigner = AndroidNostrSigner(pubkey: pubkey, package: package);
|
||||||
|
} else if (NIP07Signer.isWebNostrSignerKey(key)) {
|
||||||
|
var pubkey = NIP07Signer.getPubkey(key);
|
||||||
|
nostrSigner = NIP07Signer(pubkey: pubkey);
|
||||||
|
} else if (NostrRemoteSignerInfo.isBunkerUrl(key)) {
|
||||||
|
var info = NostrRemoteSignerInfo.parseBunkerUrl(key);
|
||||||
|
if (info == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasConnected = false;
|
||||||
|
if (StringUtil.isNotBlank(info.userPubkey)) {
|
||||||
|
hasConnected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nostrSigner = NostrRemoteSigner(RelayMode.BASE_MODE, info);
|
||||||
|
await (nostrSigner as NostrRemoteSigner)
|
||||||
|
.connect(sendConnectRequest: !hasConnected);
|
||||||
|
|
||||||
|
if (StringUtil.isBlank(info.userPubkey)) {
|
||||||
|
await nostrSigner.pullPubkey();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await nostrSigner.getPublicKey() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (Nesigner.isNesignerKey(key)) {
|
||||||
|
var pinCode = Nesigner.getPinCodeFromKey(key);
|
||||||
|
var pubkey = Nesigner.getPubkeyFromKey(key);
|
||||||
|
nostrSigner = Nesigner(pinCode, pubkey: pubkey);
|
||||||
|
try {
|
||||||
|
if (!(await (nostrSigner as Nesigner).start())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
nostrSigner = LocalNostrSigner(key);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nostrSigner == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await genNostr(nostrSigner);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Nostr?> genNostr(NostrSigner signer) async {
|
||||||
|
var pubkey = await signer.getPublicKey();
|
||||||
|
if (pubkey == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var _nostr = Nostr(signer, pubkey, [], genTempRelay, onNotice: null);
|
||||||
|
log("nostr init over");
|
||||||
|
|
||||||
|
loadRelayAddrs(null);
|
||||||
|
|
||||||
|
for (var relayAddr in relayAddrs) {
|
||||||
|
log("begin to init $relayAddr");
|
||||||
|
var custRelay = genRelay(relayAddr);
|
||||||
|
try {
|
||||||
|
_nostr.addRelay(custRelay, init: true);
|
||||||
|
} catch (e) {
|
||||||
|
log("relay $relayAddr add to pool error ${e.toString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _nostr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRelayStatusChange() {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addRelay(String relayAddr) {
|
||||||
|
if (!relayAddrs.contains(relayAddr)) {
|
||||||
|
relayAddrs.add(relayAddr);
|
||||||
|
_doAddRelay(relayAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _doAddRelay(String relayAddr,
|
||||||
|
{bool init = false, int relayType = RelayType.NORMAL}) {
|
||||||
|
var custRelay = genRelay(relayAddr, relayType: relayType);
|
||||||
|
log("begin to init $relayAddr");
|
||||||
|
nostr!.addRelay(custRelay,
|
||||||
|
autoSubscribe: true, init: init, relayType: relayType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeRelay(String relayAddr) {
|
||||||
|
if (relayAddrs.contains(relayAddr)) {
|
||||||
|
relayAddrs.remove(relayAddr);
|
||||||
|
relayStatusMap.remove(relayAddr);
|
||||||
|
nostr!.removeRelay(relayAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> getWritableRelays() {
|
||||||
|
List<String> list = [];
|
||||||
|
for (var addr in relayAddrs) {
|
||||||
|
var relayStatus = relayStatusMap[addr];
|
||||||
|
if (relayStatus != null && relayStatus.writeAccess) {
|
||||||
|
list.add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RelayStatus> _getRelayStatuses() {
|
||||||
|
List<RelayStatus> relayStatuses = [];
|
||||||
|
for (var addr in relayAddrs) {
|
||||||
|
var relayStatus = relayStatusMap[addr];
|
||||||
|
if (relayStatus != null) {
|
||||||
|
relayStatuses.add(relayStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return relayStatuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
Relay genRelay(String relayAddr, {int relayType = RelayType.NORMAL}) {
|
||||||
|
var relayStatus = relayStatusMap[relayAddr];
|
||||||
|
if (relayStatus == null) {
|
||||||
|
relayStatus = RelayStatus(relayAddr, relayType: relayType);
|
||||||
|
relayStatusMap[relayAddr] = relayStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _doGenRelay(relayStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
Relay _doGenRelay(RelayStatus relayStatus) {
|
||||||
|
var relayAddr = relayStatus.addr;
|
||||||
|
|
||||||
|
return RelayBase(
|
||||||
|
relayAddr,
|
||||||
|
relayStatus,
|
||||||
|
)..relayStatusCallback = onRelayStatusChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
void relayUpdateByContactListEvent(Event event) {
|
||||||
|
List<String> oldRelays = []..addAll(relayAddrs);
|
||||||
|
loadRelayAddrs(event.content);
|
||||||
|
_updateRelays(oldRelays);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateRelays(List<String> oldRelays) {
|
||||||
|
List<String> needToRemove = [];
|
||||||
|
List<String> needToAdd = [];
|
||||||
|
for (var oldRelay in oldRelays) {
|
||||||
|
if (!relayAddrs.contains(oldRelay)) {
|
||||||
|
// new addrs don't contain old relay, need to remove
|
||||||
|
needToRemove.add(oldRelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var relayAddr in relayAddrs) {
|
||||||
|
if (!oldRelays.contains(relayAddr)) {
|
||||||
|
// old addrs don't contain new relay, need to add
|
||||||
|
needToAdd.add(relayAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var relayAddr in needToRemove) {
|
||||||
|
relayStatusMap.remove(relayAddr);
|
||||||
|
nostr!.removeRelay(relayAddr);
|
||||||
|
}
|
||||||
|
for (var relayAddr in needToAdd) {
|
||||||
|
_doAddRelay(relayAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
// sharedPreferences.remove(DataKey.RELAY_LIST);
|
||||||
|
relayStatusMap.clear();
|
||||||
|
loadRelayAddrs(null);
|
||||||
|
_tempRelayStatusMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RelayStatus> tempRelayStatus() {
|
||||||
|
List<RelayStatus> list = []..addAll(_tempRelayStatusMap.values);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Relay genTempRelay(String addr) {
|
||||||
|
var rs = _tempRelayStatusMap[addr];
|
||||||
|
if (rs == null) {
|
||||||
|
rs = RelayStatus(addr);
|
||||||
|
_tempRelayStatusMap[addr] = rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _doGenRelay(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanTempRelays() {
|
||||||
|
List<String> needRemoveList = [];
|
||||||
|
var now = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
for (var entry in _tempRelayStatusMap.entries) {
|
||||||
|
var addr = entry.key;
|
||||||
|
var status = entry.value;
|
||||||
|
|
||||||
|
if (now - status.connectTime.millisecondsSinceEpoch > 1000 * 60 * 10 &&
|
||||||
|
(status.lastNoteTime == null ||
|
||||||
|
((now - status.lastNoteTime!.millisecondsSinceEpoch) >
|
||||||
|
1000 * 60 * 10)) &&
|
||||||
|
(status.lastQueryTime == null ||
|
||||||
|
((now - status.lastQueryTime!.millisecondsSinceEpoch) >
|
||||||
|
1000 * 60 * 10))) {
|
||||||
|
// init time over 10 min
|
||||||
|
// last note time over 10 min
|
||||||
|
// last query time over 10 min
|
||||||
|
needRemoveList.add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var addr in needRemoveList) {
|
||||||
|
if (!nostr!.tempRelayHasSubscription(addr)) {
|
||||||
|
// don't contain subscription, remote!
|
||||||
|
_tempRelayStatusMap.remove(addr);
|
||||||
|
nostr!.removeTempRelay(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
227
lib/provider/userinfo_provider.dart
Normal file
227
lib/provider/userinfo_provider.dart
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:nostr_sdk/event.dart';
|
||||||
|
import 'package:nostr_sdk/event_kind.dart';
|
||||||
|
import 'package:nostr_sdk/event_mem_box.dart';
|
||||||
|
import 'package:nostr_sdk/filter.dart';
|
||||||
|
import 'package:nostr_sdk/nip02/contact_list.dart';
|
||||||
|
import 'package:nostr_sdk/nip65/relay_list_metadata.dart';
|
||||||
|
import 'package:nostr_sdk/utils/later_function.dart';
|
||||||
|
import 'package:nostr_sdk/utils/string_util.dart';
|
||||||
|
|
||||||
|
import '../data/event_db.dart';
|
||||||
|
import '../data/metadata.dart';
|
||||||
|
import '../main.dart';
|
||||||
|
|
||||||
|
class UserinfoProvider extends ChangeNotifier with LaterFunction {
|
||||||
|
Map<String, RelayListMetadata> _relayListMetadataCache = {};
|
||||||
|
|
||||||
|
Map<String, Metadata> _metadataCache = {};
|
||||||
|
|
||||||
|
Map<String, ContactList> _contactListMap = {};
|
||||||
|
|
||||||
|
Map<String, int> _handingPubkeys = {};
|
||||||
|
|
||||||
|
EventMemBox _penddingEvents = EventMemBox(sortAfterAdd: false);
|
||||||
|
|
||||||
|
static UserinfoProvider? _userinfoProvider;
|
||||||
|
|
||||||
|
static Future<UserinfoProvider> getInstance() async {
|
||||||
|
if (_userinfoProvider == null) {
|
||||||
|
_userinfoProvider = UserinfoProvider();
|
||||||
|
// lazyTimeMS begin bigger and request less
|
||||||
|
_userinfoProvider!.laterTimeMS = 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _userinfoProvider!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Metadata? getMetadata(String pubkey, {bool loadData = true}) {
|
||||||
|
var metadata = _metadataCache[pubkey];
|
||||||
|
if (metadata != null) {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadData) {
|
||||||
|
_handleDataNotfound(pubkey);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> _checkingFromDBPubKeys = [];
|
||||||
|
|
||||||
|
List<String> _needUpdatePubKeys = [];
|
||||||
|
|
||||||
|
void _handleDataNotfound(String pubkey) {
|
||||||
|
if (!_checkingFromDBPubKeys.contains(pubkey) &&
|
||||||
|
!_handingPubkeys.containsKey(pubkey)) {
|
||||||
|
_checkingFromDBPubKeys.add(pubkey);
|
||||||
|
EventDB.list(
|
||||||
|
[
|
||||||
|
EventKind.METADATA,
|
||||||
|
EventKind.RELAY_LIST_METADATA,
|
||||||
|
EventKind.CONTACT_LIST,
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
100,
|
||||||
|
pubkeys: [pubkey]).then((eventList) {
|
||||||
|
// print("${eventList.length} metadata find from db.");
|
||||||
|
_penddingEvents.addList(eventList);
|
||||||
|
if (eventList.length < 3) {
|
||||||
|
_needUpdatePubKeys.add(pubkey);
|
||||||
|
}
|
||||||
|
_checkingFromDBPubKeys.remove(pubkey);
|
||||||
|
later(_laterCallback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _laterCallback() {
|
||||||
|
if (_needUpdatePubKeys.isNotEmpty) {
|
||||||
|
_laterSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_penddingEvents.isEmpty()) {
|
||||||
|
_handlePenddingEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handlePenddingEvents() {
|
||||||
|
for (var event in _penddingEvents.all()) {
|
||||||
|
_handingPubkeys.remove(event.pubkey);
|
||||||
|
|
||||||
|
if (event.kind == EventKind.METADATA) {
|
||||||
|
if (StringUtil.isBlank(event.content)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check cache
|
||||||
|
var oldMetadata = _metadataCache[event.pubkey];
|
||||||
|
if (oldMetadata == null) {
|
||||||
|
// insert
|
||||||
|
EventDB.insert(event);
|
||||||
|
_eventToMetadataCache(event);
|
||||||
|
} else if (oldMetadata.updated_at! < event.createdAt) {
|
||||||
|
// update, remote old event and insert new event
|
||||||
|
EventDB.execute("delete from event where kind = ? and pubkey = ?",
|
||||||
|
[EventKind.METADATA, event.pubkey]);
|
||||||
|
EventDB.insert(event);
|
||||||
|
_eventToMetadataCache(event);
|
||||||
|
}
|
||||||
|
} else if (event.kind == EventKind.RELAY_LIST_METADATA) {
|
||||||
|
// this is relayInfoMetadata, only set to cache, not update UI
|
||||||
|
var oldRelayListMetadata = _relayListMetadataCache[event.pubkey];
|
||||||
|
if (oldRelayListMetadata == null) {
|
||||||
|
// insert
|
||||||
|
EventDB.insert(event);
|
||||||
|
_eventToRelayListCache(event);
|
||||||
|
} else if (event.createdAt > oldRelayListMetadata.createdAt) {
|
||||||
|
// update, remote old event and insert new event
|
||||||
|
EventDB.execute("delete from event where kind = ? and pubkey = ?",
|
||||||
|
[EventKind.RELAY_LIST_METADATA, event.pubkey]);
|
||||||
|
EventDB.insert(event);
|
||||||
|
_eventToRelayListCache(event);
|
||||||
|
}
|
||||||
|
} else if (event.kind == EventKind.CONTACT_LIST) {
|
||||||
|
var oldContactList = _contactListMap[event.pubkey];
|
||||||
|
if (oldContactList == null) {
|
||||||
|
// insert
|
||||||
|
EventDB.insert(event);
|
||||||
|
_eventToContactList(event);
|
||||||
|
} else if (event.createdAt > oldContactList.createdAt) {
|
||||||
|
// update, remote old event and insert new event
|
||||||
|
EventDB.execute(
|
||||||
|
"delete from event where key_index = ? and kind = ? and pubkey = ?",
|
||||||
|
[EventKind.CONTACT_LIST, event.pubkey]);
|
||||||
|
EventDB.insert(event);
|
||||||
|
_eventToContactList(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_penddingEvents.clear();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onEvent(Event event) {
|
||||||
|
_penddingEvents.add(event);
|
||||||
|
later(_laterCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _laterSearch() {
|
||||||
|
if (_needUpdatePubKeys.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!nostr!.readable()) {
|
||||||
|
// // the nostr isn't readable later handle it again.
|
||||||
|
// later(_laterCallback, null);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> filters = [];
|
||||||
|
for (var pubkey in _needUpdatePubKeys) {
|
||||||
|
{
|
||||||
|
var filter = Filter(
|
||||||
|
kinds: [
|
||||||
|
EventKind.METADATA,
|
||||||
|
],
|
||||||
|
authors: [pubkey],
|
||||||
|
limit: 1,
|
||||||
|
);
|
||||||
|
filters.add(filter.toJson());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var filter = Filter(
|
||||||
|
kinds: [
|
||||||
|
EventKind.RELAY_LIST_METADATA,
|
||||||
|
],
|
||||||
|
authors: [pubkey],
|
||||||
|
limit: 1,
|
||||||
|
);
|
||||||
|
filters.add(filter.toJson());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var filter = Filter(
|
||||||
|
kinds: [
|
||||||
|
EventKind.CONTACT_LIST,
|
||||||
|
],
|
||||||
|
authors: [pubkey],
|
||||||
|
limit: 1,
|
||||||
|
);
|
||||||
|
filters.add(filter.toJson());
|
||||||
|
}
|
||||||
|
if (filters.length > 20) {
|
||||||
|
nostr!.query(filters, onEvent);
|
||||||
|
filters = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filters.isNotEmpty) {
|
||||||
|
nostr!.query(filters, onEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var pubkey in _needUpdatePubKeys) {
|
||||||
|
_handingPubkeys[pubkey] = 1;
|
||||||
|
}
|
||||||
|
_needUpdatePubKeys.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _eventToMetadataCache(Event event) {
|
||||||
|
var jsonObj = jsonDecode(event.content);
|
||||||
|
var md = Metadata.fromJson(jsonObj);
|
||||||
|
md.pubkey = event.pubkey;
|
||||||
|
md.updated_at = event.createdAt;
|
||||||
|
_metadataCache[event.pubkey] = md;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _eventToRelayListCache(Event event) {
|
||||||
|
RelayListMetadata rlm = RelayListMetadata.fromEvent(event);
|
||||||
|
_relayListMetadataCache[rlm.pubkey] = rlm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _eventToContactList(Event event) {
|
||||||
|
var contactList = ContactList.fromJson(event.tags, event.createdAt);
|
||||||
|
_contactListMap[event.pubkey] = contactList;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ class _KeysItemComponent extends State<KeysItemComponent> {
|
|||||||
right: Base.BASE_PADDING_HALF,
|
right: Base.BASE_PADDING_HALF,
|
||||||
),
|
),
|
||||||
child: UserPicComponent(
|
child: UserPicComponent(
|
||||||
|
pubkey: widget.pubkey,
|
||||||
width: 26,
|
width: 26,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:bot_toast/bot_toast.dart';
|
import 'package:bot_toast/bot_toast.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:nostr_sdk/utils/string_util.dart';
|
||||||
import 'package:nowser/component/cust_state.dart';
|
import 'package:nowser/component/cust_state.dart';
|
||||||
import 'package:nowser/component/text_input/text_input_dialog.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_name_component.dart';
|
||||||
@@ -62,6 +63,7 @@ class _MeRouter extends CustState<MeRouter> {
|
|||||||
var mediaQueryData = MediaQuery.of(context);
|
var mediaQueryData = MediaQuery.of(context);
|
||||||
var themeData = Theme.of(context);
|
var themeData = Theme.of(context);
|
||||||
var _appProvider = Provider.of<AppProvider>(context);
|
var _appProvider = Provider.of<AppProvider>(context);
|
||||||
|
var _keyProvider = Provider.of<KeyProvider>(context);
|
||||||
s = S.of(context);
|
s = S.of(context);
|
||||||
|
|
||||||
var listWidgetMargin = const EdgeInsets.only(
|
var listWidgetMargin = const EdgeInsets.only(
|
||||||
@@ -69,33 +71,31 @@ class _MeRouter extends CustState<MeRouter> {
|
|||||||
bottom: Base.BASE_PADDING,
|
bottom: Base.BASE_PADDING,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var pubkeys = _keyProvider.pubkeys;
|
||||||
|
String defaultPubkey = "";
|
||||||
|
if (pubkeys.isNotEmpty) {
|
||||||
|
defaultPubkey = pubkeys.first;
|
||||||
|
}
|
||||||
|
|
||||||
List<Widget> defaultUserWidgets = [];
|
List<Widget> defaultUserWidgets = [];
|
||||||
defaultUserWidgets.add(Container(
|
defaultUserWidgets.add(Container(
|
||||||
margin: const EdgeInsets.only(
|
margin: const EdgeInsets.only(
|
||||||
left: Base.BASE_PADDING,
|
left: Base.BASE_PADDING,
|
||||||
),
|
),
|
||||||
child: UserPicComponent(width: 50),
|
child: UserPicComponent(pubkey: defaultPubkey, width: 50),
|
||||||
));
|
));
|
||||||
defaultUserWidgets.add(Container(
|
Widget addOrNameWidget = GestureDetector(
|
||||||
margin: const EdgeInsets.only(left: Base.BASE_PADDING),
|
|
||||||
child: Selector<KeyProvider, List<String>>(
|
|
||||||
builder: (context, npubList, child) {
|
|
||||||
if (npubList.isNotEmpty) {
|
|
||||||
var pubkey = npubList.first;
|
|
||||||
return UserNameComponent(pubkey);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
KeysRouter.addKey(context);
|
KeysRouter.addKey(context);
|
||||||
},
|
},
|
||||||
child: Text(s.Click_and_Login),
|
child: Text(s.Click_and_Login),
|
||||||
);
|
);
|
||||||
},
|
if (StringUtil.isNotBlank(defaultPubkey)) {
|
||||||
selector: (context, _provider) {
|
addOrNameWidget = UserNameComponent(defaultPubkey);
|
||||||
return _provider.pubkeys;
|
}
|
||||||
},
|
defaultUserWidgets.add(Container(
|
||||||
),
|
margin: const EdgeInsets.only(left: Base.BASE_PADDING),
|
||||||
|
child: addOrNameWidget,
|
||||||
));
|
));
|
||||||
defaultUserWidgets.add(Expanded(child: Container()));
|
defaultUserWidgets.add(Expanded(child: Container()));
|
||||||
defaultUserWidgets.add(Container(
|
defaultUserWidgets.add(Container(
|
||||||
@@ -111,30 +111,33 @@ class _MeRouter extends CustState<MeRouter> {
|
|||||||
margin: const EdgeInsets.only(
|
margin: const EdgeInsets.only(
|
||||||
top: Base.BASE_PADDING,
|
top: Base.BASE_PADDING,
|
||||||
),
|
),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
RouterUtil.router(context, RouterPath.KEYS);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: defaultUserWidgets,
|
children: defaultUserWidgets,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var memberListWidget = Selector<KeyProvider, List<String>>(
|
Widget memberListWidget = Container(
|
||||||
builder: (context, npubList, child) {
|
height: Base.BASE_PADDING,
|
||||||
if (npubList.isEmpty) {
|
);
|
||||||
return Container();
|
if (pubkeys.length > 1) {
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> memberList = [];
|
List<Widget> memberList = [];
|
||||||
for (var pubkey in npubList) {
|
for (var pubkey in pubkeys) {
|
||||||
memberList.add(Container(
|
memberList.add(Container(
|
||||||
margin: EdgeInsets.only(left: Base.BASE_PADDING_HALF),
|
margin: const EdgeInsets.only(left: Base.BASE_PADDING_HALF),
|
||||||
child: UserPicComponent(width: 30),
|
child: UserPicComponent(pubkey: pubkey, width: 30),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
memberList.add(Expanded(child: Container()));
|
memberList.add(Expanded(child: Container()));
|
||||||
memberList.add(GestureDetector(
|
memberList.add(GestureDetector(
|
||||||
child: Icon(Icons.chevron_right),
|
child: const Icon(Icons.chevron_right),
|
||||||
));
|
));
|
||||||
|
memberListWidget = Container(
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: themeData.cardColor,
|
color: themeData.cardColor,
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
@@ -142,7 +145,7 @@ class _MeRouter extends CustState<MeRouter> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
margin: listWidgetMargin,
|
margin: listWidgetMargin,
|
||||||
padding: EdgeInsets.all(Base.BASE_PADDING),
|
padding: const EdgeInsets.all(Base.BASE_PADDING),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
RouterUtil.router(context, RouterPath.KEYS);
|
RouterUtil.router(context, RouterPath.KEYS);
|
||||||
@@ -153,11 +156,7 @@ class _MeRouter extends CustState<MeRouter> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
selector: (context, _provider) {
|
|
||||||
return _provider.pubkeys;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> webItemList = [];
|
List<Widget> webItemList = [];
|
||||||
webItemList.add(MeRouterWebItemComponent(
|
webItemList.add(MeRouterWebItemComponent(
|
||||||
|
|||||||
Reference in New Issue
Block a user