Skip to content

Commit

Permalink
Merge pull request #115 from ItsAlexousd/feat/realtime-database
Browse files Browse the repository at this point in the history
Support for Firebase Realtime Database with Node and NodeList components
  • Loading branch information
codediodeio authored Dec 17, 2023
2 parents 3732913 + 837bbb2 commit d0f59a8
Show file tree
Hide file tree
Showing 22 changed files with 582 additions and 148 deletions.
308 changes: 209 additions & 99 deletions README.md

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions dist/components/FirebaseApp.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>import { setFirebaseContext } from "../stores/sdk.js";
export let firestore;
export let rtdb;
export let auth;
export let storage;
setFirebaseContext({ firestore, rtdb, auth, storage });
</script>

<slot />
25 changes: 25 additions & 0 deletions dist/components/FirebaseApp.svelte.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { SvelteComponent } from "svelte";
import type { Auth } from "firebase/auth";
import type { Firestore } from "firebase/firestore";
import type { Database } from "firebase/database";
import type { FirebaseStorage } from "firebase/storage";
declare const __propDef: {
props: {
firestore: Firestore;
rtdb: Database;
auth: Auth;
storage: FirebaseStorage;
};
events: {
[evt: string]: CustomEvent<any>;
};
slots: {
default: {};
};
};
export type FirebaseAppProps = typeof __propDef.props;
export type FirebaseAppEvents = typeof __propDef.events;
export type FirebaseAppSlots = typeof __propDef.slots;
export default class FirebaseApp extends SvelteComponent<FirebaseAppProps, FirebaseAppEvents, FirebaseAppSlots> {
}
export {};
15 changes: 15 additions & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Doc from "./components/Doc.svelte";
import User from "./components/User.svelte";
import Collection from "./components/Collection.svelte";
import Node from "./components/Node.svelte";
import NodeList from "./components/NodeList.svelte";
import FirebaseApp from "./components/FirebaseApp.svelte";
import SignedOut from "./components/SignedOut.svelte";
import SignedIn from "./components/SignedIn.svelte";
import { docStore } from "./stores/firestore.js";
import { collectionStore } from "./stores/firestore.js";
import { nodeStore } from './stores/rtdb.js';
import { nodeListStore } from './stores/rtdb.js';
import { userStore } from "./stores/auth.js";
import { getFirebaseContext } from "./stores/sdk.js";
export { Doc, User, Collection, Node, NodeList, FirebaseApp, SignedOut, SignedIn, docStore, collectionStore, nodeStore as refStore, nodeListStore as listStore, userStore, getFirebaseContext };
30 changes: 30 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Reexport your entry components here
import FirebaseApp from "./components/FirebaseApp.svelte";
import User from "./components/User.svelte";
import Collection from "./components/Collection.svelte";
import Doc from "./components/Doc.svelte";
import NodeList from "./components/NodeList.svelte";
import Node from "./components/Node.svelte";
import SignedIn from "./components/SignedIn.svelte";
import SignedOut from "./components/SignedOut.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";

export {
Doc,
User,
Collection,
Node,
NodeList,
FirebaseApp,
SignedOut,
SignedIn,
docStore,
collectionStore,
nodeStore as refStore,
nodeListStore as listStore,
userStore,
getFirebaseContext,
};
16 changes: 16 additions & 0 deletions dist/stores/sdk.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Firestore } from "firebase/firestore";
import type { Database } from "firebase/database";
import type { Auth } from "firebase/auth";
import type { FirebaseStorage } from "firebase/storage";
export interface FirebaseSDKContext {
auth?: Auth;
firestore?: Firestore;
rtdb?: Database;
storage?: FirebaseStorage;
}
export declare const contextKey = "firebase";
export declare function setFirebaseContext(sdks: FirebaseSDKContext): void;
/**
* Get the Firebase SDKs from Svelte context
*/
export declare function getFirebaseContext(): FirebaseSDKContext;
11 changes: 11 additions & 0 deletions dist/stores/sdk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { getContext, setContext } from "svelte";
export const contextKey = "firebase";
export function setFirebaseContext(sdks) {
setContext(contextKey, sdks);
}
/**
* Get the Firebase SDKs from Svelte context
*/
export function getFirebaseContext() {
return getContext(contextKey);
}
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 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>
4 changes: 2 additions & 2 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
<script lang="ts">
import { getFirebaseContext } from "$lib/stores/sdk.js";
const ctx = getFirebaseContext();
</script>

<h1>Welcome to SvelteFire</h1>

<ul>
<li><a href="/auth-test">Auth Test</a></li>
<li><a href="/firestore-test">Firestore Test</a></li>
<li><a href="/rtdb-test">Realtime Database Test</a></li>
<li><a href="/ssr-test">SSR Test</a></li>
<li><a href="/storage-test">Storage Test</a></li>
</ul>
<ul>
<li data-testid="auth">Auth Context: {!!ctx.auth}</li>
<li data-testid="firestore">Firestore Context: {!!ctx.firestore}</li>
<li data-testid="rtdb">Realtime Database Context: {!!ctx.rtdb}</li>
<li data-testid="storage">Storage Context: {!!ctx.storage}</li>
</ul>
Loading

0 comments on commit d0f59a8

Please sign in to comment.