anasfik / nostr

Develop Scalable Dart/Flutter Nostr clients quickly and easily
MIT License
40 stars 11 forks source link

REST style requests #2

Closed PatrickGeyer closed 1 year ago

PatrickGeyer commented 1 year ago

Hi there,

Do you have plans to add REST style endpoints?

For example if you wanted to load up a single post, you could call

NostrEvent event = await Nostr.instance.relaysService.getEvent(req);

Or to load a list:

List<NostrEvent> event = await Nostr.instance.relaysService.listEvents(req);
PatrickGeyer commented 1 year ago

Any advice on how to integrate this into a flutter app? I'm used to working with Rest, so working with streams is a bit weird for me, but I'm sure it can be figured out with help of YouTube. Just wondered if you had a sample package you could link to that shows how to use it and do error handling etc. What happens if websocket disconnects before I execute the query for example, seems like error handling would be tricky. Maybe best to write a wrapper around this package for use in an actual app?

anasfik commented 1 year ago

Hi, thank you for reaching out here!

No, there are no plans to have a rest API calls endpoints in order to fetch for events, since Nostr relays are actually web sockets that are meant for connecting in real-time with them. so I worked to hide that complexity as much as to provide a direct Streams to be used.

can you tell me what you expect if we had Future methods like the ones you showcased, I mean can you tell me an example of the request you want to send and then tell me until when the Future should resolve and return events?

anasfik commented 1 year ago

Yeah, please check the example/ folder, it contains many examples for using the package that you can learn, try from.

anasfik commented 1 year ago

But please, I understand that it is not the usual way that you used to work to make requests to remote sources, for that you can fill here ith as much questions you have.

PatrickGeyer commented 1 year ago

Hi @anasfik, thank you for all your work. The examples are a great start for me.

I think I've seen a lot of people work with Streams in Flutter, so maybe I should just get used to that instead. I just meant wrapping the stream into a Future so that we can further abstract away the websocket backend. So basically returning the received websocket events when a nostr end-of-stored-events is returned from the relays. I bet after I get used to working with streams this won't be necessary though :D

PatrickGeyer commented 1 year ago

@anasfik how would you go about displaying a list of results in a flutter app. E.g. if you query all events of kind 4, you get a stream. A StreamBuilder will just display the latest result. How do you display items in a list that come out of a stream..?

anasfik commented 1 year ago

Yeah, in general, learning Dart Stream is really helpful, it got you understand how many things work inside of Flutter, and I highly recommend learning them.

anasfik commented 1 year ago

Yes, if you listen to a specific request event, you will get a Stream<NostrEvent>, and the reason for this is that some other kinds such as 0 will be very handy to manage and update your users' metadata. with your use case you should not assign that stream directly inside a StreamBuilder rather than listening to that stream to collect all events inside a List<NostrEvent> as an example. with code taking this StatefulWidget:

class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  List<NostrEvent> events = [];
  NostrEventsStream? nostrEventsSTream;
  NostrRequest? nostrRequest;

  @override
  void initState() {
    super.initState();

    _initVariables();

    nostrEventsSTream!.stream.listen((event) {
      setState(() {
        events.add(event);
        print("event got with id: ${event.id}");
      });
    });
  }

  void _initVariables() {
    nostrRequest = NostrRequest(filters: [
      NostrFilter(
        kinds: [4],
        limit: 10,
      ),
    ]);

    nostrEventsSTream = Nostr.instance.relaysService
        .startEventsSubscription(request: nostrRequest!);
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: events.length,
      itemBuilder: (context, index) {
        final event = events[index];
        return ListTile(
          title: Text(event.content),
        );
      },
    );
  }
}

in this example, we did declare a variable for holding the nostr events, a variable for the nostr events stream holder, and the actual variable for its request.

in the initState, we did initialize their values and listened to the stream of events, when every new event is emitted to our stream what will happen is that it will add that event to our events list and update the state.

with this code, you should expect to see all events of that request popping out into your app UI.

Note: this only includes the widget code, but make sure you connected to some relays first to ensure the source of data.

anasfik commented 1 year ago

That being said, I don't think you will find this that easy to get if you're new to Flutter, your point seems now ti me very expected and a must to have. I am planning to provide more Future based methods for direct use. until then feel free to ask to learn more about this.

PatrickGeyer commented 1 year ago
Screenshot 2023-05-19 at 12 18 14

How can I list only events that have a certain tag? I want to search events by application-specific custom tag, but can't seem to find a place for that in NostrFilter class.

anasfik commented 1 year ago

Hin sorry for the delay, Nostr NIPs contain the use of t which I guess you should use as an example saying that we want kind 1 events that are referenced with "nostr" tag, this is the request:

final req = NostrRequest(
 filters: [
   NostrFilter(
      kind: [1], 
      t: ["nostr"],
      limit: 50,
   ),
  ],
 );
anasfik commented 1 year ago

I will close this issue for the current moment, if you have any other please fill another issue so this one don't lose its context.