Developing Geonotes — Basic MVP  — Ep. 7

Developing Geonotes — Basic MVP — Ep. 7

It's time to complete the set of basic functionalities!

This past week I worked on completing the basic set of functionality for the app, like creating a custom note at the current location and viewing all your notes, in addition to implementing a basic workflow to deploy on Expo.

🤖 GitHub workflow for the app

Similarly to what I did for the backend environment in the Episode 6, I wanted to make sure that all changes were deployed to the Expo platform for internal distribution.

I did this by leveraging Expo's release channels functionality: for each push on the main or develop branch a new release on the production release channel is made.

In time, once this app actually reaches production, different branches will deploy to different release channels.

✨ Completing base functionalities

The first functionality I had to add was to create a custom note at your current location. So far, a static message was defined in the note.

I created a small one-field form with a multiline text field to write in. I have also added a temporary character limit while I figure out how much text I want your note to be able to contain. Ideally, I would not want the text in the note to scroll.

I also added a list of all the user's created notes in the profile, that displays the time and location, and a preview of the note itself. The user is also able to permanently delete any of their notes.

🔧 Some technical changes

One of my main goals is to be very efficient with the network resources, both to save on user's bandwith and to be able to scale my backend resources better with the number of users.

Part of this is achievable through a good caching system, which fortunately is provided out of the box in Apollo Client.

This means that, if done correctly, I can skip many unneeded network requests. For example, once a new note is created, I do not need to refetch the list of my notes since they'll probably be the ones I already have in cache plus the just created one.

For this reason I created a set of cache utils that will help me with reliably update the current state of the system without requesting it each time from the server. Source

export const updateQueryCache = <T>(
  cache: ApolloCache<unknown>,
  query: DocumentNode,
  variables: object = {},
  recipe: (draft: WritableDraft<T>) => void
) => {
  const cached = cache.readQuery<T>({ query, variables });

  if (cached) {
    cache.writeQuery<T>({
      query,
      variables,
      data: produce(cached, recipe as any),
    });
  }
};

export const updateCacheWith =
  <T>(f: (cache: ApolloCache<T>, data: T) => void) =>
  (cache: ApolloCache<T>, mutation: Omit<FetchResult<T>, "context">) => {
    if (!cache) return;
    if (!mutation) return;
    if (!mutation.data) return;

    return f(cache, mutation.data);
  };

With this system in place, I can quickly update existing queries in the cache by defining a method for each mutation: Source

export function updateCacheAfterCreateNote(cache: ApolloCache<CreateNoteMutation>, data: CreateNoteMutation) {
  const userId = firebase.auth().currentUser?.uid;

  if (!userId) {
    return;
  }

  updateQueryCache<MyNotesQuery>(cache, MyNotesDocument, { userId }, (draft) => {
    draft.notes.splice(0, 0, {
      __typename: "note",
      id: data.create_note.note.id,
      content: data.create_note.note.content,
      createdAt: data.create_note.note.createdAt,
    });
  });
}

🚧 Next steps

Now that all the basic functionalities are in place, I want to add some initial visual polish to the app, even though I'll probably rework it before release.

🎙 How to follow the project

I'll be posting updates throughout the development process and as I learn new thing regarding development, design, and marketing.

If you'd like to have even more real-time updates you can