diff --git a/lib/components/note_card/bottom_sheet_more.dart b/lib/components/note_card/bottom_sheet_more.dart index e0e6e395..00e97489 100644 --- a/lib/components/note_card/bottom_sheet_more.dart +++ b/lib/components/note_card/bottom_sheet_more.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:camelus/components/seen_on_relays.dart'; import 'package:camelus/config/palette.dart'; import 'package:camelus/models/nostr_note.dart'; import 'package:camelus/routes/nostr/blockedUsers/block_page.dart'; @@ -27,14 +28,13 @@ void openBottomSheetMore(context, NostrNote note) { onTap: () { // push Seen on relays - //Navigator.push( - // context, - // MaterialPageRoute( - // builder: (context) => - // SeenOnRelaysPage(note: note), - // ), - //); - //! todo: push seen on relays + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + SeenOnRelaysPage(myNote: note), + ), + ); }, child: Container( color: Palette.background, diff --git a/lib/components/seen_on_relays.dart b/lib/components/seen_on_relays.dart index c03a7afd..6da80d15 100644 --- a/lib/components/seen_on_relays.dart +++ b/lib/components/seen_on_relays.dart @@ -1,6 +1,9 @@ +import 'dart:async'; import 'dart:developer'; +import 'package:camelus/atoms/spinner_center.dart'; import 'package:camelus/config/palette.dart'; +import 'package:camelus/db/entities/db_relay_tracker.dart'; import 'package:camelus/models/nostr_note.dart'; import 'package:camelus/providers/database_provider.dart'; import 'package:camelus/services/nostr/relays/relay_tracker.dart'; @@ -8,6 +11,7 @@ import 'package:camelus/services/nostr/relays/relay_tracker.dart'; import 'package:camelus/services/nostr/relays/relays_ranking.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:isar/isar.dart'; import 'package:timeago/timeago.dart' as timeago; class SeenOnRelaysPage extends ConsumerStatefulWidget { @@ -20,13 +24,13 @@ class SeenOnRelaysPage extends ConsumerStatefulWidget { } class _SeenOnRelaysPageState extends ConsumerState { - late RelaysRanking _relaysRanking; - late RelayTracker _relayTracker; + late final Isar db; + + Completer relayTracker = Completer(); void _initServices() async { - var db = await ref.watch(databaseProvider.future); - _relaysRanking = RelaysRanking(db: db); - _relayTracker = RelayTracker(db: db); + db = await ref.read(databaseProvider.future); + _getRelayTracker(widget.myNote.pubkey); } @override @@ -40,6 +44,13 @@ class _SeenOnRelaysPageState extends ConsumerState { return timeago.format(DateTime.fromMillisecondsSinceEpoch(time * 1000)); } + Future _getRelayTracker(String pubkey) async { + final result = + await db.dbRelayTrackers.filter().pubkeyEqualTo(pubkey).findFirst(); + relayTracker.complete(result); + return result; + } + @override Widget build(BuildContext context) { return Scaffold( @@ -55,8 +66,9 @@ class _SeenOnRelaysPageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 20), - const Text('event seen on', - style: TextStyle(color: Palette.white, fontSize: 35)), + if (widget.myNote.relayHints.isNotEmpty) + const Text('note relay hints', + style: TextStyle(color: Palette.white, fontSize: 25)), ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -71,94 +83,81 @@ class _SeenOnRelaysPageState extends ConsumerState { ); }, ), - const SizedBox(height: 25), - const Text('author gossip hints', - style: TextStyle(color: Palette.white, fontSize: 35)), - FutureBuilder( - future: _relaysRanking.getBestRelays( - widget.myNote.pubkey, Direction.read), - builder: (context, snapshot) { - if (snapshot.hasData) { + const SizedBox( + height: 25, + ), + const Text('user seen on these relays', + style: TextStyle(color: Palette.white, fontSize: 25)), + const SizedBox( + height: 10, + ), + FutureBuilder( + future: relayTracker.future, + initialData: null, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return spinnerCenter(); + } + if (snapshot.data == null) { + return const Text('no data'); + } + return ListView.builder( shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: snapshot.data!.length, + itemCount: snapshot.data!.relays.length, itemBuilder: (context, index) { return ListTile( title: Text( - snapshot.data![index]['relay'], + snapshot.data!.relays.elementAt(index).relayUrl!, style: const TextStyle( color: Palette.white, fontWeight: FontWeight.bold), ), - subtitle: Text( - "score: ${snapshot.data![index]['score'].toString()}", - style: const TextStyle(color: Palette.white)), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (snapshot.data!.relays + .elementAt(index) + .lastSuggestedKind3 != + null) + Text( + 'last suggested kind3: ${_timeago(snapshot.data!.relays.elementAt(index).lastSuggestedKind3!)}', + style: + const TextStyle(color: Palette.white), + ), + if (snapshot.data!.relays + .elementAt(index) + .lastSuggestedNip05 != + null) + Text( + 'last suggested nip05: ${_timeago(snapshot.data!.relays.elementAt(index).lastSuggestedNip05!)}', + style: + const TextStyle(color: Palette.white), + ), + if (snapshot.data!.relays + .elementAt(index) + .lastSuggestedTag != + null) + Text( + 'last suggested tag: ${_timeago(snapshot.data!.relays.elementAt(index).lastSuggestedTag!)}', + style: + const TextStyle(color: Palette.white), + ), + if (snapshot.data!.relays + .elementAt(index) + .lastFetched != + null) + Text( + 'last fetched: ${_timeago(snapshot.data!.relays.elementAt(index).lastFetched!)}', + style: + const TextStyle(color: Palette.white), + ), + ], + ), ); }, ); - } else { - return const Text("Loading..."); - } - }, - ), - const SizedBox( - height: 25, - ), - const Text('recorded data', - style: TextStyle(color: Palette.white, fontSize: 35)), - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: 0, - itemBuilder: (context, index) { - // todo: implement this - return null; - // return ListTile( - // title: Text( - // widget._relayTracker.tracker[widget.myNote.pubkey] - // ?.keys - // .elementAt(index) ?? - // "not found", - // style: const TextStyle( - // color: Palette.white, - // fontWeight: FontWeight.bold), - // ), - // subtitle: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // if (widget._relayTracker - // .tracker[widget.myNote.pubkey]?.values - // .elementAt(index)['lastSuggestedKind3'] != - // null) - // Text( - // "lastSuggestedKind3: ${_timeago(widget._relayTracker.tracker[widget.myNote.pubkey]?.values.elementAt(index)['lastSuggestedKind3'])}", - // style: const TextStyle(color: Palette.white)), - // if (widget._relayTracker - // .tracker[widget.myNote.pubkey]?.values - // .elementAt(index)['lastSuggestedNip05'] != - // null) - // Text( - // "lastSuggestedNip05: ${_timeago(widget._relayTracker.tracker[widget.myNote.pubkey]?.values.elementAt(index)['lastSuggestedNip05'])}", - // style: const TextStyle(color: Palette.white)), - // if (widget._relayTracker - // .tracker[widget.myNote.pubkey]?.values - // .elementAt(index)['lastSuggestedBytag'] != - // null) - // Text( - // "lastSuggestedBytag: ${_timeago(widget._relayTracker.tracker[widget.myNote.pubkey]?.values.elementAt(index)['lastSuggestedBytag'])}", - // style: const TextStyle(color: Palette.white)), - // if (widget._relayTracker - // .tracker[widget.myNote.pubkey]?.values - // .elementAt(index)['lastFetched'] != - // null) - // Text( - // "lastFetched: ${_timeago(widget._relayTracker.tracker[widget.myNote.pubkey]?.values.elementAt(index)['lastFetched'])}", - // style: const TextStyle(color: Palette.white)), - // ], - // )); - }, - ), + }) ], ), ), diff --git a/lib/services/nostr/relays/relay_tracker.dart b/lib/services/nostr/relays/relay_tracker.dart index b1c0e351..1d0b59dd 100644 --- a/lib/services/nostr/relays/relay_tracker.dart +++ b/lib/services/nostr/relays/relay_tracker.dart @@ -135,9 +135,11 @@ class RelayTracker { /// get called when a event advertises a relay pubkey connection /// timestamp can be a string or int void trackRelays(String personPubkey, List relayUrls, - RelayTrackerAdvType nip, dynamic timestamp) { + RelayTrackerAdvType nip, dynamic timestamp) async { var relayUrlsCleaned = []; + var oldObj = await db.dbRelayTrackers.getByPubkey(personPubkey); + for (var relay in relayUrls) { try { var cleaned = RelayAddressParser.parseAddress(relay); @@ -156,6 +158,7 @@ class RelayTracker { } List trackedRelays = []; + for (var relayUrl in relayUrlsCleaned) { if (relayUrl.isEmpty) { continue; @@ -167,6 +170,13 @@ class RelayTracker { var relay = DbRelayTrackerRelay( relayUrl: relayUrl, ); + + if (oldObj != null && + oldObj.relays.any((element) => element.relayUrl == relayUrl)) { + relay = + oldObj.relays.firstWhere((element) => element.relayUrl == relayUrl); + } + switch (nip) { case RelayTrackerAdvType.kind03: relay.lastSuggestedKind3 = timestamp; @@ -186,8 +196,7 @@ class RelayTracker { var finalObj = DbRelayTracker(pubkey: personPubkey, relays: trackedRelays); - db.writeTxn(() async { - // also updates the object if it already exists + await db.writeTxn(() async { db.dbRelayTrackers.putByPubkey(finalObj); }); }