sveltefire icon indicating copy to clipboard operation
sveltefire copied to clipboard

Loading state for user

Open yannikinniger opened this issue 5 years ago • 9 comments

First of good job, this is a fantastic library!

Now to the feature request: It would be great to have a loading or empty state for the User as well. Just looks a little funny jumping very quickly from the login to whatever page the user wants to see.

yannikinniger avatar Nov 28 '19 10:11 yannikinniger

I hear you.

The problem is that Firebase does not broadcast a loading state, so it's not as simple as it would seem. https://stackoverflow.com/a/50684239/3808414

One solution (already implemented) is to save the current user to sessionStorage. If the user is logged in, they will not see the flash of the login on a full refresh.

<User persist={sessionStorage}>

But I am open to other ideas, so we can leave this issue open...

codediodeio avatar Nov 28 '19 15:11 codediodeio

Finally found some time to look at the code of the lib.

How about introducing a variable like in the docStore? And then just reassign it once the data arrived. Maybe a we would need a timeout as well in case it's not loading.

  let _loading = true;

  const store = writable(cached, () => {
    const teardown = auth.onAuthStateChanged(u => {
      set(u);
      persist && opts.persist.setItem(k, JSON.stringify(u));
      _loading = false;
    });
    return () => teardown;
  });

  const { subscribe, set } = store;

  return {
    subscribe,
    auth,
    get loading() {
      return _loading;
    },
  };

Otherwise the sessionStorage works perfectly fine. In most cases the user is probably not logged in anyway if the sessionStorage is empty.

yannikinniger avatar Dec 19 '19 13:12 yannikinniger

It is also possible to handle the Authentication with a server session cookie. Then you can have SSR on the user data. The user data will then be shown as soon as the page is rendered. It requires you to keep a __session cookie on the server based on the login details. When you log a user in, you send the token to the server. The server validates it and saves it as the __session cookie (note the double underscore before session. That is if you want to host your server on Firebase as this is the only cookie they allow). Then you can pass the user data to sapper and use it in your SSR. This video explains how to do it Sveltecasts SSR-auth

pjarnfelt avatar Jan 19 '20 20:01 pjarnfelt

Firebase does not broadcast a loading state

Why not just have cached = undefined to start with (overwritten if persisted value exists) instead of null?

I always treated my internal user state variable as "ternary"

undefined = loading null = user logged out user object = logged in

Also is readable store more appropriate if end user is not writing to it?

https://svelte.dev/repl/68476b7019874b5dbdfba8b8b4f0c3a7?version=3.18.2

cdock1029 avatar Feb 14 '20 17:02 cdock1029

I replaced sveltefire's User component with something radically simpler, and loading states work great:

<script>
  import { getContext, createEventDispatcher } from "svelte";

  const firebase = getContext('firebase').getFirebase();
  const dispatch = createEventDispatcher();
  let user
  firebase.auth().onAuthStateChanged((u) => {
    user = u
    dispatch("user", { user })
  })
</script>

{#if user === undefined}
  <slot name="loading" />
{:else if user !== null}
  <slot {user} auth={firebase.auth()} />
{:else}
  <slot name="signed-out" />
{/if}

Things I ripped out:

  • persisting into sessionStore (I'm ok with taking the hit of that one request if user refreshes the page, for now; would be pretty easy to add back)
  • the whole concept of a separate store, just one let user is fine
  • going through the assertApp helper (because it's not exported, and I didn't feel the need for louder messages on programmer error)
  • the weird before and after slots; those can be done where this component is used

EDIT: It does not unsubscribe from firebase events yet. Should be pretty easy.

tv42 avatar Aug 13 '20 20:08 tv42

And this one should get unsubscribe right and also uses https://firebase.google.com/docs/reference/js/firebase.auth.Auth#currentuser if it is set.

EDIT: also dispatch event after currentUser trick.

<script>
  import { getContext, createEventDispatcher, onMount } from "svelte";

  const firebase = getContext('firebase').getFirebase();
  const dispatch = createEventDispatcher();
  let user
  onMount(() => {
    const auth = firebase.auth()
    if (auth.currentUser !== null) {
      user = auth.currentUser
      dispatch("user", { user })
    }
    const unsubscribe = auth.onAuthStateChanged((u) => {
      user = u
      dispatch("user", { user })
    })
    return unsubscribe
  })
</script>

{#if user === undefined}
  <slot name="loading" />
{:else if user !== null}
  <slot {user} auth={firebase.auth()} />
{:else}
  <slot name="signed-out" />
{/if}

tv42 avatar Aug 13 '20 20:08 tv42

I hear you.

The problem is that Firebase does not broadcast a loading state, so it's not as simple as it would seem. https://stackoverflow.com/a/50684239/3808414

One solution (already implemented) is to save the current user to sessionStorage. If the user is logged in, they will not see the flash of the login on a full refresh.

<User persist={sessionStorage}>

But I am open to other ideas, so we can leave this issue open...

Hey it seems like this has been removed in subsequent releases? I'm very new to Svelte and web development in general and looking for a solution to this issue's problem that's as out-of-the-box as possible :)

konradbender avatar Nov 22 '23 13:11 konradbender

Any update here? I would love this feature!

jacksonthall22 avatar Dec 28 '23 02:12 jacksonthall22

Refreshing the page flashes the "logged out" view for a split second in the current version, would love a solution for a seamless UX!

MadeInPierre avatar Mar 04 '24 16:03 MadeInPierre