sveltefire icon indicating copy to clipboard operation
sveltefire copied to clipboard

<Doc>: No difference between "Loading" and "Doesn't Exist"

Open Kimeiga opened this issue 2 years ago • 7 comments

<script>
	/**
	 * @type {string}
	 */
	export let entry;

	import { doc, setDoc } from 'firebase/firestore';
	import { SignedIn, SignedOut, Doc } from 'sveltefire';
	import { firestore } from '$lib/firebase'; // your firestore instance
	let editingNote = false;
	let newNote = '';

	function editNote(n) {
		editingNote = true;
		newNote = n;
	}

	async function saveNote(id) {
		editingNote = false;

		// set doc in firestore to newNote data
		await setDoc(doc(firestore, 'words', id), {
			note: newNote
		});
	}
</script>

{entry}

<Doc ref={`words/${entry}`} let:data>
	{#if !editingNote && data?.note === undefined}
		<SignedIn>
			<button on:click={editNote(data?.note)}>Add Note</button>
		</SignedIn>
	{:else if !editingNote}
		<p>{data?.note}</p>
		<SignedIn>
			<button on:click={editNote(data?.note)}>{data?.note ? 'Edit Note' : 'Add Note'}</button>
		</SignedIn>
	{:else if editingNote}
		<form on:submit|preventDefault={saveNote(entry)}>
			<textarea bind:value={newNote} />
			<button type="submit">Save</button>
			<button type="reset" on:click={() => (editingNote = false)}>Cancel</button>
		</form>
	{/if}

	<p slot="loading">Loading...</p>
</Doc>

I can't get this to continue beyond the loading state despite everything else working. This component is Note.svelte and it is instantiated within the context of a <FirebaseApp> so everything else is available.

Without the log option, I can't see what's wrong with the Doc component.

I turned off ssr in the svelte config as well:

import adapter from '@sveltejs/adapter-auto';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	ssr: false,
	kit: {
		// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
		// If your environment is not supported or you settled on a specific environment, switch out the adapter.
		// See https://kit.svelte.dev/docs/adapters for more information about adapters.
		adapter: adapter()
	}
};

export default config;

What is going wrong?

Nothing in console as well btw

Kimeiga avatar Oct 26 '23 18:10 Kimeiga

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {

    // This rule allows anyone with your Firestore database reference to view, edit,
    // and delete all data in your Firestore database. It is useful for getting
    // started, but it is configured to expire after 30 days because it
    // leaves your app open to attackers. At that time, all client
    // requests to your Firestore database will be denied.
    //
    // Make sure to write security rules for your app before that time, or else
    // all client requests to your Firestore database will be denied until you Update
    // your rules
    match /{document=**} {
      allow read: if true;
      allow write: if request.auth.uid != null;
    }
  }
}

the firestore rules allow authenticated writes and I definitely can read. I am signed in on my app as well

Kimeiga avatar Oct 26 '23 19:10 Kimeiga

Ok so specifically, there's no way to tell if a Document is just not there, or if it's still loading since the loading slot is triggered in both cases.

I'm making a dictionary app where you can take notes on words, and those words are in a collection called "words" and they are located at words/{word}

I would like to have the ability to do

<Doc ref={`words/${word}`} let:data>
  {data.note}
  <div slot="no-data">
    <button>Add data</button>
  </div>
  <div slot="loading">
    Loading
  </div>
</div>

Kimeiga avatar Oct 26 '23 19:10 Kimeiga

{#if data == undefined} hi {/if}

I tried every permutation of these but I don't think anything like this will work because only the loading state is shown if there's no data

<script lang="ts" generics="Data extends DocumentData">
  import type {
    DocumentData,
    DocumentReference,
    Firestore,
  } from "firebase/firestore";
  import { docStore } from "../stores/firestore.js";
  import { getFirebaseContext } from "../stores/sdk.js";

  export let ref: string | DocumentReference<Data>;
  export let startWith: Data | undefined = undefined;

  const { firestore } = getFirebaseContext();

  let store = docStore(firestore!, ref, startWith);

  interface $$Slots {
    default: {
      data: Data;
      ref: DocumentReference<Data> | null;
      firestore?: Firestore;
    };
    loading: {};
  }
</script>

{#if $store !== undefined && $store !== null}
  <slot data={$store} ref={store.ref} {firestore} />
{:else}
  <slot name="loading" />
{/if}

Kimeiga avatar Oct 26 '23 19:10 Kimeiga

I tried a few other things

<script>
const post = docStore(firestore, `words/${entry}`);
console.log(post);
console.log($post);

let docSnap;
onMount(async () => {
docSnap = await getDoc(doc(firestore, `words/${entry}`));
});
</script>
....


<div slot="loading">
{#if docSnap && docSnap.exists()}
	exists
{:else}
	doesn't exist
{/if}

{#if $post == undefined}
	hi
{:else}
	loading...
{/if}
</div>

exists() returns after a second true or false so maybe we could have <Doc> have the following behavior

Loading exists() comes back false -> Doesn't-Exist exists() comes back true -> Shows Content

Kimeiga avatar Oct 26 '23 19:10 Kimeiga

<script>
	/**
	 * @type {string}
	 */
	export let entry;

	import { doc, getDoc, setDoc } from 'firebase/firestore';
	import { SignedIn, SignedOut, Doc, docStore } from 'sveltefire';
	import { firestore } from '$lib/firebase'; // your firestore instance
	import { onMount } from 'svelte';
	let editingNote = false;
	let newNote = '';

	function editNote(n) {
		editingNote = true;
		newNote = n;
	}

	async function saveNote(id) {
		editingNote = false;

		// set doc in firestore to newNote data
		await setDoc(doc(firestore, 'words', id), {
			note: newNote
		});
	}
	const post = docStore(firestore, `words/${entry}`);
	console.log(post);
	console.log($post);

	let docSnap;
	onMount(async () => {
		docSnap = await getDoc(doc(firestore, `words/${entry}`));
	});

	let exists = true;
	$: exists = docSnap && docSnap.exists();
</script>

<Doc ref={`words/${entry}`} let:data let:ref>
	{#if !editingNote}
		<p>{data?.note}</p>
		<SignedIn>
			<button on:click={editNote(data?.note)}>{data?.note ? 'Edit Note' : 'Add Note'}</button>
		</SignedIn>
	{:else if editingNote}
		<form on:submit|preventDefault={saveNote(entry)}>
			<textarea bind:value={newNote} />
			<button type="submit">Save</button>
			<button type="reset" on:click={() => (editingNote = false)}>Cancel</button>
		</form>
	{/if}

	<div slot="loading">
		{#if exists}
			<span>Loading</span>
		{:else}
			<SignedIn>
				<form on:submit|preventDefault={saveNote(entry)}>
					<textarea bind:value={newNote} />
					<button type="submit">Add Note</button>
					<button type="reset" on:click={() => (editingNote = false)}>Cancel</button>
				</form>
			</SignedIn>
		{/if}
	</div>
</Doc>

I ended up having to do this; sort of buggy and annoying though

Kimeiga avatar Oct 26 '23 19:10 Kimeiga

https://github.com/codediodeio/sveltefire/issues/96

anasmohammed361 avatar Dec 06 '23 03:12 anasmohammed361

i'll drop a pr for this

anasmohammed361 avatar Dec 06 '23 03:12 anasmohammed361