4 TS/JS helper functions I use everyday developing a nostr client.

Here a four JavaScript / TS helper functions that I use everyday developing a nostr client.
4 TS/JS helper functions I use everyday developing a nostr client.

In the world of development, simplicity is often the key to success. Nostr Protocol embodies this principle perfectly, offering a modular, extensible, and easily comprehensible framework for your projects. Libraries like nostr-tools provide invaluable assistance, ensuring a smooth development process with the complexity tucked away behind the scenes.

However, Nostr is not without its nuances, and there are aspects that extend beyond the out-of-the-box solutions provided by these libraries. Here are some of the helper functions I have written while developing Current

E-tags: roots, replies, and mentions

If a note is a reply to another note, it can be referenced as defined in NIP-10. Using this approach we can display all the replies to a particular note, or even subscribe to the whole conversation tree. In order to do this we need to extract information about the parent, or the root from the tag array of the event. This helper runs through all the tags of a post and collects those references while supporting the deprecated positional approach, as well as markers.

function parseReplyRootMention(event: Event): [string?, string?, string?] {
  let reply = null;
  let root = null;
  let mention = null;

  const replyTags: string[] = [];
  const rootTags: string[] = [];
  const mentionTags: string[] = [];

  for (const tag of event.tags) {
    if (tag[0] !== 'e') continue;
    if (tag[3] === 'root') {
      rootTags.push(tag[1]);
    } else if (tag[3] === 'reply') {
      replyTags.push(tag[1]);
    } else if (tag[3] === 'mention') {
      mentionTags.push(tag[1]);
    }
  }
  if (mentionTags.length > 0) {
    mention = mentionTags[0];
  }
  if (rootTags.length > 0) {
    root = rootTags[0];
    reply = replyTags.length > 0 ? replyTags[0] : rootTags[0];
  } else if (replyTags.length > 0) {
    root = reply = replyTags[0];
  } else if (event.tags.length > 0) {
    const eTags = event.tags.filter((tag) => tag[0] === 'e');
    if (eTags.length > 0) {
      reply = eTags[eTags.length - 1][1];
      root = eTags[0][1];
    }
  }

  return [reply, root, mention];
}

Building threads

Related to root and reply is the building of threads. A thread is the chain of notes that goes all the way back to the root post of the conversation tree. This helper takes a hashmap of a conversation’s notes and recursively constructs a list, starting at the starting point and going back until the root post is reached. This function relies on each note having a “repliesTo” property that has the parent’s id.

function buildThread(note: Kind1Note, allnotes, thread = []) {
  const parentNoteId = note.repliesTo;
  if (!parentNoteId || !allnotes[parentNoteId]) {
      return [note, ...thread];
  }
  const parentNote = allnotes[parentNoteId];
  const newThread = [note, ...thread];
  return buildThread(parentNote, allnotes, newThread);
}

Notes: Extracting Images

Pretty much every nostr client allows users to add images to their notes. However, most of these clients will simply append the image to the end of the post (-> the position of the image URL inside the note is meaningless). Because of this, we decided to separate images and text in our user interface. In order to do so, I needed to extract and remove image URLs from notes and save them in a new variable. This helper does exactly that. It uses regex to match all image URLs in a post, saves them in a list and replaces the matches with empty strings.

function parseAndReplaceImages(event: Event): [string, string[]] {
  const images: string[] = [];
  const parsedContent = event.content.replace(imageRegex, (match) => {
    images.push(match);
    return '';
  });
  return [parsedContent, images];
}

Sorting by date

Especially with JavaScript runtimes brining their own sorting implementation (mostly insertion sort), this seems pretty trivial, but I wrote this one-liner so many times, that I then created a helper function regardless. It does exactly what it sounds like: it sorts notes by their creation timestamp.

function sortNotes(notes: Event[]) {
  return notes.sort((a, b) => b.created_at - a.created_at);
}

Write a comment
No comments yet.