Skip to content

Commit

Permalink
Merge branch 'master' into feature/error-handling
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyVoisin authored Dec 18, 2023
2 parents ac5ff58 + 80af67a commit dbc5f8b
Show file tree
Hide file tree
Showing 22 changed files with 606 additions and 125 deletions.
263 changes: 187 additions & 76 deletions README.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions docs/src/components/SideNav.astro
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
<li><a href="/storage/upload-task">&ltUploadTask&gt</a></li>
<li><a href="/storage/download-url">&ltDownloadURL&gt</a></li>
<li><a href="/storage/storage-list">&ltStorageList&gt</a></li>
<li class="heading">realtime db</li>
<li><a href="/rtdb/node-store">nodeStore</a></li>
<li><a href="/rtdb/node-list-store">nodeListStore</a></li>
<li><a href="/rtdb/node-component">&ltNode&gt</a></li>
<li><a href="/rtdb/node-list-component">&ltNodeList&gt</a></li>
<li class="heading">analytics</li>
<li><a href="/guide/todo">&ltPageView&gt</a></li>
</ul>
Expand Down
40 changes: 40 additions & 0 deletions docs/src/pages/rtdb/node-component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: Node Component
pubDate: 2023-07-23
description: SvelteFire Node Component API reference
layout: ../../layouts/MainLayout.astro
---

# Node

The `Node` component is a wrapper around the `nodeStore`. It renders the node data and handles the loading state.

### Props

- `path` - RealtimeDB path string (e.g. `posts/hi-mom`)
- `startWith` - (optional) initial value to use before the data is fetched

### Slots

- `default` - The node data
- `loading` - Loading state

### Slot Props

- `data` - The node data
- `path` - The Database reference
- `rtdb` - The Database instance

### Example

```svelte
<script>
import { Node } from 'sveltefire';
</script>
<Node path={'posts/id'} let:data>
<p>{data?.title}</p>
<p slot="loading">Loading...</p>
</Node>
```
46 changes: 46 additions & 0 deletions docs/src/pages/rtdb/node-list-component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: NodeList Component
pubDate: 2023-07-23
description: SvelteFire NodeList Component API reference
layout: ../../layouts/MainLayout.astro
---

# NodeList

The `NodeList` component is a wrapper around the `nodeListStore`. It renders the node list data and handles the loading state.

### Props

- `path` - RealtimeDB reference
- `startWith` - (optional) initial value to use before the collection is fetched

### Slots

- `default` - The node list data
- `loading` - Loading state

### Slot Props

- `data` - An array of nodes
- `ref` - The Database node reference
- `rtdb` - The Database instance
- `count` - The number of nodes returned by the query

### Example

```svelte
<script>
import { NodeList } from 'sveltefire';
</script>
<NodeList path={'posts'} let:data let:count>
<p>Found {count} posts</p>
{#each data as post}
<p>{post.title}</p>
{/each}
<p slot="loading">Loading...</p>
</NodeList>
```
31 changes: 31 additions & 0 deletions docs/src/pages/rtdb/node-list-store.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: nodeListStore
pubDate: 2023-11-23
description: SvelteFire nodeStore API reference
layout: ../../layouts/MainLayout.astro
---

# nodeListStore

Subscribes to RealtimeDB node list data and listens to real-time updates.

### Parameters

- `rtdb` - RealtimeDB instance
- `path` - A RealtimeDB path string (e.g. `posts`)
- `startWith` - (optional) initial value to use before the data is fetched

### Example

```svelte
<script>
import { nodeListStore } from 'sveltefire';
import { rtdb } from '$lib/firebase'; // your rtdb instance
const posts = nodeListStore(rtdb, 'posts');
</script>
{#each $posts as post}
<p>{post.title}</p>
{/each}
```
29 changes: 29 additions & 0 deletions docs/src/pages/rtdb/node-store.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: nodeStore
pubDate: 2023-11-23
description: SvelteFire nodeStore API reference
layout: ../../layouts/MainLayout.astro
---

# nodeStore

Subscribes to RealtimeDB node and listens to realtime updates.

### Parameters

- `rtdb` - RealtimeDB instance
- `path` - A RealtimeDB path string (e.g. `posts/hi-mom`)
- `startWith` - (optional) initial value to use before the data is fetched

### Example

```svelte
<script>
import { nodeStore } from 'sveltefire';
import { rtdb } from '$lib/rtdb'; // your RealtimeDB instance
const post = nodeStore(rtdb, 'posts/id');
</script>
{$post?.title}
```
9 changes: 4 additions & 5 deletions firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"firestore": {
"port": 8080
},
"database": {
"port": 9000
},
"storage": {
"port": 9199
},
Expand All @@ -22,10 +25,6 @@
},
"hosting": {
"public": "docs/dist",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sveltefire",
"version": "0.4.2",
"version": "0.4.3",
"scripts": {
"dev": "vite dev",
"build": "vite build && npm run package",
Expand Down
2 changes: 1 addition & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { defineConfig } from '@playwright/test';

export default defineConfig({
webServer: {
command: 'firebase emulators:exec --only firestore,storage,auth "npm run build && npm run preview"',
command: 'firebase emulators:exec --only firestore,database,storage,auth "npm run build && npm run preview"',
port: 4173
},
testDir: 'tests',
Expand Down
4 changes: 3 additions & 1 deletion src/lib/components/FirebaseApp.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
import { setFirebaseContext } from "$lib/stores/sdk.js";
import type { Auth } from "firebase/auth";
import type { Firestore } from "firebase/firestore";
import type { Database } from "firebase/database";
import type { FirebaseStorage } from "firebase/storage";
export let firestore: Firestore;
export let rtdb: Database;
export let auth: Auth;
export let storage: FirebaseStorage;
setFirebaseContext({ firestore, auth, storage });
setFirebaseContext({ firestore, rtdb, auth, storage });
</script>

<slot />
22 changes: 22 additions & 0 deletions src/lib/components/Node.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts">
import { nodeStore } from "../stores/rtdb.js";
import { getFirebaseContext } from "../stores/sdk.js";
import type { DatabaseReference, Database } from "firebase/database";
export let path: string;
export let startWith: any = undefined;
const { rtdb } = getFirebaseContext();
let store = nodeStore(rtdb!, path, startWith);
interface $$Slots {
default: { data: any; ref: DatabaseReference | null; rtdb?: Database };
loading: {};
}
</script>

{#if $store !== undefined}
<slot data={$store} ref={store.ref} {rtdb} />
{:else}
<slot name="loading" />
{/if}
27 changes: 27 additions & 0 deletions src/lib/components/NodeList.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script lang="ts">
import { nodeListStore } from "../stores/rtdb.js";
import { getFirebaseContext } from "../stores/sdk.js";
import type { DatabaseReference, Database } from "firebase/database";
export let path: string;
export let startWith: any[] = [];
const { rtdb } = getFirebaseContext();
let store = nodeListStore(rtdb!, path, startWith);
interface $$Slots {
default: {
data: any[];
ref: DatabaseReference | null;
count: number;
rtdb?: Database;
};
loading: {};
}
</script>

{#if $store !== undefined}
<slot data={$store} ref={store.ref} count={$store?.length ?? 0} {rtdb} />
{:else}
<slot name="loading" />
{/if}
5 changes: 4 additions & 1 deletion src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import StorageList from './components/StorageList.svelte';
import UploadTask from './components/UploadTask.svelte';
import { userStore } from './stores/auth.js';
import { docStore, collectionStore } from './stores/firestore.js';
import { nodeStore, nodeListStore } from './stores/rtdb.js';
import { getFirebaseContext } from './stores/sdk.js';
import { downloadUrlStore, storageListStore, uploadTaskStore } from './stores/storage.js';

Expand All @@ -28,6 +29,8 @@ export {
uploadTaskStore,
docStore,
collectionStore,
nodeStore,
nodeListStore,
userStore,
getFirebaseContext,
}
}
65 changes: 65 additions & 0 deletions src/lib/stores/rtdb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { writable } from "svelte/store";
import { onValue, ref as dbRef } from "firebase/database";
import type { Database } from "firebase/database";

/**
* @param {Database} rtdb - Firebase Realtime Database instance.
* @param {string} path - Path to the individual database node.
* @param {T | undefined} startWith - Optional default data.
* @returns a store with realtime updates on individual database nodes.
*/
export function nodeStore<T = any>(
rtdb: Database,
path: string,
startWith?: T
) {
const dataRef = dbRef(rtdb, path);

const { subscribe } = writable<T | null>(startWith, (set) => {
const unsubscribe = onValue(dataRef, (snapshot) => {
set(snapshot.val() as T);
});

return unsubscribe;
});

return {
subscribe,
ref: dataRef,
};
}

/**
* @param {Database} rtdb - Firebase Realtime Database instance.
* @param {string} path - Path to the list of nodes.
* @param {T[]} startWith - Optional default data.
* @returns a store with realtime updates on lists of nodes.
*/
export function nodeListStore<T = any>(
rtdb: Database,
path: string,
startWith: T[] = []
) {
const listRef = dbRef(rtdb, path);

const { subscribe } = writable<T[]>(startWith, (set) => {
const unsubscribe = onValue(listRef, (snapshot) => {
const dataArr: T[] = [];
snapshot.forEach((childSnapshot) => {
const childData = childSnapshot.val();
dataArr.push({
nodeKey: childSnapshot.ref.key,
...(typeof childData === "object" ? childData : {}),
} as T);
});
set(dataArr);
});

return unsubscribe;
});

return {
subscribe,
ref: listRef,
};
}
3 changes: 2 additions & 1 deletion src/lib/stores/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { Firestore } from "firebase/firestore";
import type { Database } from "firebase/database";
import type { Auth } from "firebase/auth";
import { getContext, setContext } from "svelte";
import type { FirebaseStorage } from "firebase/storage";


export interface FirebaseSDKContext {
auth?: Auth;
firestore?: Firestore;
rtdb?: Database;
storage?: FirebaseStorage;
}

Expand Down
9 changes: 4 additions & 5 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<script lang="ts">
import FirebaseApp from '$lib/components/FirebaseApp.svelte';
import { db as firestore, auth, storage } from './firebase.js';
import FirebaseApp from "$lib/components/FirebaseApp.svelte";
import { db as firestore, auth, rtdb, storage } from "./firebase.js";
</script>

<FirebaseApp {auth} {firestore} {storage}>
<slot />
<FirebaseApp {auth} {firestore} {rtdb} {storage}>
<slot />
</FirebaseApp>
Loading

0 comments on commit dbc5f8b

Please sign in to comment.