Skip to content

Commit 38ea67e

Browse files
committed
- Created a download link component
- Created a storage list component
1 parent fc380cd commit 38ea67e

File tree

14 files changed

+232
-8
lines changed

14 files changed

+232
-8
lines changed

firebase.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
"firestore": {
77
"port": 8080
88
},
9+
"storage": {
10+
"port": 9199
11+
},
912
"hosting": {
1013
"port": 5000
1114
},
@@ -14,6 +17,9 @@
1417
},
1518
"singleProjectMode": true
1619
},
20+
"storage": {
21+
"rules": "storage.rules"
22+
},
1723
"hosting": {
1824
"public": "docs/dist",
1925
"ignore": [

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script lang="ts">
2+
import { downloadUrlStore } from '$lib/stores/storage.js';
3+
import { getFirebaseContext } from '$lib/stores/sdk.js';
4+
import type { FirebaseStorage, StorageReference } from 'firebase/storage';
5+
6+
export let ref: string | StorageReference;
7+
8+
const { storage } = getFirebaseContext();
9+
const store = downloadUrlStore(storage!, ref);
10+
11+
interface $$Slots {
12+
default: { link: string | null; ref: StorageReference | null; storage?: FirebaseStorage },
13+
loading: {},
14+
}
15+
</script>
16+
17+
{#if $store !== undefined}
18+
<slot link={$store} ref={store.reference} {storage}/>
19+
{:else}
20+
<slot name="loading" />
21+
{/if}
22+

src/lib/components/FirebaseApp.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
import { setFirebaseContext } from "$lib/stores/sdk.js";
33
import type { Auth } from "firebase/auth";
44
import type { Firestore } from "firebase/firestore";
5+
import type { FirebaseStorage } from "firebase/storage";
56
67
export let firestore: Firestore;
78
export let auth: Auth;
9+
export let storage: FirebaseStorage;
810
9-
setFirebaseContext({ firestore, auth });
11+
setFirebaseContext({ firestore, auth, storage });
1012
</script>
1113

1214
<slot />

src/lib/components/StorageList.svelte

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script lang="ts">
2+
import { storageListStore } from '$lib/stores/storage.js';
3+
import { getFirebaseContext } from '$lib/stores/sdk.js';
4+
import type { FirebaseStorage, ListResult, StorageReference } from 'firebase/storage';
5+
6+
export let ref: string | StorageReference;
7+
8+
const { storage } = getFirebaseContext();
9+
const listStore = storageListStore(storage!, ref);
10+
11+
interface $$Slots {
12+
default: { list: ListResult | null; ref: StorageReference | null; storage?: FirebaseStorage },
13+
loading: {},
14+
}
15+
</script>
16+
17+
{#if $listStore !== undefined}
18+
<slot list={$listStore} ref={listStore.reference} {storage} />
19+
{:else}
20+
<slot name="loading" />
21+
{/if}
22+

src/lib/stores/sdk.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import { writable } from "svelte/store";
22
import type { Firestore } from "firebase/firestore";
33
import type { Auth } from "firebase/auth";
44
import { getContext, setContext } from "svelte";
5+
import type { FirebaseStorage } from "firebase/storage";
56

67

78
export interface FirebaseSDKContext {
89
auth?: Auth;
910
firestore?: Firestore;
11+
storage?: FirebaseStorage;
1012
}
1113

1214
export const contextKey = "firebase";

src/lib/stores/storage.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { writable } from "svelte/store";
2+
import { getDownloadURL, list, ref } from "firebase/storage";
3+
4+
import type {
5+
StorageReference,
6+
FirebaseStorage,
7+
ListResult,
8+
} from "firebase/storage";
9+
10+
const defaultListResult: ListResult = {
11+
prefixes: [],
12+
items: [],
13+
};
14+
15+
interface StorageListStore {
16+
subscribe: (cb: (value: ListResult) => void) => void | (() => void);
17+
reference: StorageReference | null;
18+
}
19+
20+
/**
21+
* @param {FirebaseStorage} storage firebase storage instance
22+
* @param {string|StorageReference} reference file or storage item path or reference
23+
* @param {{prefixes:[], items:[]}} startWith optional default data
24+
* @returns a store with the list result
25+
*/
26+
export function storageListStore(
27+
storage: FirebaseStorage,
28+
reference: string | StorageReference,
29+
startWith: ListResult = defaultListResult
30+
): StorageListStore {
31+
32+
// Fallback for SSR
33+
if (!globalThis.window) {
34+
const { subscribe } = writable(startWith);
35+
return {
36+
subscribe,
37+
reference: null,
38+
};
39+
}
40+
41+
// Fallback for missing SDK
42+
if (!storage) {
43+
console.warn(
44+
"Cloud Storage is not initialized. Are you missing FirebaseApp as a parent component?"
45+
);
46+
const { subscribe } = writable(defaultListResult);
47+
return {
48+
subscribe,
49+
reference: null,
50+
};
51+
}
52+
53+
const storageRef = typeof reference === "string" ? ref(storage, reference) : reference;
54+
55+
const { subscribe } = writable(startWith, (set) => {
56+
list(storageRef).then((snapshot) => {
57+
set(snapshot);
58+
});
59+
});
60+
61+
return {
62+
subscribe,
63+
reference: storageRef,
64+
};
65+
}
66+
67+
interface DownloadUrlStore {
68+
subscribe: (cb: (value: string | null) => void) => void | (() => void);
69+
reference: StorageReference | null;
70+
}
71+
72+
/**
73+
* @param {FirebaseStorage} storage firebase storage instance
74+
* @param {string|StorageReference} reference file or storage item path or reference
75+
* @param {null} startWith optional default data
76+
* @returns a store with the list result
77+
*/
78+
export function downloadUrlStore(
79+
storage: FirebaseStorage,
80+
reference: string | StorageReference,
81+
startWith: string | null = null
82+
): DownloadUrlStore {
83+
84+
// Fallback for SSR
85+
if (!globalThis.window) {
86+
const { subscribe } = writable(startWith);
87+
return {
88+
subscribe,
89+
reference: null,
90+
};
91+
}
92+
93+
// Fallback for missing SDK
94+
if (!storage) {
95+
console.warn(
96+
"Cloud Storage is not initialized. Are you missing FirebaseApp as a parent component?"
97+
);
98+
const { subscribe } = writable(null);
99+
return {
100+
subscribe,
101+
reference: null,
102+
};
103+
}
104+
105+
const storageRef = typeof reference === "string" ? ref(storage, reference) : reference;
106+
107+
const { subscribe } = writable(startWith, (set) => {
108+
getDownloadURL(storageRef).then((snapshot) => {
109+
set(snapshot);
110+
});
111+
});
112+
113+
return {
114+
subscribe,
115+
reference: storageRef,
116+
};
117+
}
118+

src/routes/+layout.svelte

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
<script lang="ts">
22
import FirebaseApp from '$lib/components/FirebaseApp.svelte';
3-
import { db as firestore, auth } from './firebase.js';
3+
import { db as firestore, auth, storage } from './firebase.js';
44
55
</script>
66

7-
<FirebaseApp {auth} {firestore}>
8-
7+
<FirebaseApp {auth} {firestore} {storage}>
98
<slot />
109
</FirebaseApp>

src/routes/+page.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
<li><a href="/auth-test">Auth Test</a></li>
1313
<li><a href="/firestore-test">Firestore Test</a></li>
1414
<li><a href="/ssr-test">SSR Test</a></li>
15+
<li><a href="/storage-test">Storage Test</a></li>
1516
</ul>
1617
<ul>
1718
<li data-testid="auth">Auth Context: {!!ctx.auth}</li>
1819
<li data-testid="firestore">Firestore Context: {!!ctx.firestore}</li>
20+
<li data-testid="firestore">Storage Context: {!!ctx.storage}</li>
1921
</ul>

src/routes/auth-test/+page.svelte

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
import SignedIn from '$lib/components/SignedIn.svelte';
33
import SignedOut from '$lib/components/SignedOut.svelte';
44
import { signInAnonymously } from "firebase/auth";
5-
6-
75
</script>
86

97
<h1>Auth Test</h1>

0 commit comments

Comments
 (0)