Skip to content

Commit 4f166ed

Browse files
author
Tk2217
committed
Add firebase storage support
1 parent 7b00411 commit 4f166ed

File tree

3 files changed

+257
-0
lines changed

3 files changed

+257
-0
lines changed

src/File.svelte

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<script>
2+
export let path;
3+
export let log = false;
4+
export let traceId = "";
5+
export let startWith = undefined; // Why? Firestore returns null for docs that don't exist, predictible loading state.
6+
export let maxWait = 10000;
7+
export let once = false;
8+
9+
import { onDestroy, onMount, createEventDispatcher } from "svelte";
10+
import { uploadFileStore } from "./storage";
11+
12+
const opts = {
13+
startWith,
14+
traceId,
15+
log,
16+
maxWait,
17+
once
18+
}
19+
20+
let store = uploadFileStore(path, opts);
21+
22+
const dispatch = createEventDispatcher();
23+
24+
let unsub;
25+
26+
// Props changed
27+
$: {
28+
if (unsub) {
29+
// Unsub and create new store
30+
unsub();
31+
store = uploadFileStore(path, opts);
32+
dispatch("ref", { ref: store.ref });
33+
}
34+
35+
unsub = store.subscribe(url => {
36+
dispatch("url", {
37+
url
38+
});
39+
});
40+
}
41+
42+
onMount(() => dispatch("ref", { ref: store.ref }))
43+
onDestroy(() => unsub());
44+
</script>
45+
46+
<slot name="before" />
47+
48+
{#if $store}
49+
<slot url={$store} ref={store.ref} error={store.error} />
50+
{:else if store.loading}
51+
<slot name="loading" />
52+
{:else}
53+
<slot name="fallback" />
54+
{/if}
55+
56+
<slot name="after" />

src/Upload.svelte

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<script>
2+
export let path;
3+
export let log = false;
4+
export let traceId = "";
5+
export let startWith = undefined; // Why? Firestore returns null for docs that don't exist, predictible loading state.
6+
export let maxWait = 10000;
7+
export let once = false;
8+
9+
import { onDestroy, onMount, createEventDispatcher } from "svelte";
10+
import { fileStore } from "./storage";
11+
12+
const opts = {
13+
startWith,
14+
traceId,
15+
log,
16+
maxWait,
17+
once
18+
}
19+
20+
let store = fileStore(path, opts);
21+
22+
const dispatch = createEventDispatcher();
23+
24+
let unsub;
25+
26+
// Props changed
27+
$: {
28+
if (unsub) {
29+
// Unsub and create new store
30+
unsub();
31+
store = fileStore(path, opts);
32+
dispatch("ref", { ref: store.ref });
33+
}
34+
35+
unsub = store.subscribe(url => {
36+
dispatch("url", {
37+
url
38+
});
39+
});
40+
}
41+
42+
onMount(() => dispatch("ref", { ref: store.ref }))
43+
onDestroy(() => unsub());
44+
</script>
45+
46+
<slot name="before" />
47+
48+
{#if $store}
49+
<slot url={$store} ref={store.ref} error={store.error} />
50+
{:else if store.complete}
51+
<slot name="complete" />
52+
{:else}
53+
<slot name="fallback" />
54+
{/if}
55+
56+
<slot name="after" />

src/storage.js

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { writable } from 'svelte/store';
2+
import { assertApp } from './helpers';
3+
import { startTrace, stopTrace } from './perf';
4+
5+
// Svelte Store for Storage file
6+
export function fileStore(path, opts) {
7+
const storage = assertApp('storage');
8+
9+
const { startWith, log, traceId, maxWait, once } = { maxWait: 10000, ...opts };
10+
11+
const storageRef = storage.ref();
12+
const ref = typeof path === 'string' ? storageRef.child(path) : path;
13+
14+
// Performance trace
15+
const trace = traceId && startTrace(traceId);
16+
17+
// Internal state
18+
let _loading = typeof startWith !== undefined;
19+
let _error = null;
20+
let _teardown;
21+
let _waitForIt;
22+
23+
// State should never change without emitting a new value
24+
// Clears loading state on first call
25+
const next = (val, err) => {
26+
_loading = false;
27+
_waitForIt && clearTimeout(_waitForIt);
28+
_error = err || null;
29+
set(val);
30+
trace && stopTrace(trace);
31+
};
32+
33+
// Timout
34+
// Runs of first subscription
35+
const start = () => {
36+
37+
// Timout for fallback slot
38+
_waitForIt = maxWait && setTimeout(() => _loading && next(null, new Error(`Timeout at ${maxWait}. Using fallback slot.`) ), maxWait)
39+
40+
// Realtime firebase subscription
41+
_teardown = ref.on("value",
42+
snapshot => {
43+
// Emit next value
44+
snapshot.getDownloadURL().then((url) => {
45+
next(url);
46+
if (log) console.log(`URl: ${url}`);
47+
});
48+
49+
50+
if (log) console.log('Snapshot:', snapshot);
51+
52+
// Teardown after first emitted value if once
53+
once && _teardown();
54+
},
55+
56+
// Handle firebase thrown errors
57+
error => {
58+
console.error(error);
59+
next(null, error);
60+
}
61+
);
62+
63+
// Removes firebase listener when store completes
64+
return () => _teardown();
65+
};
66+
67+
// Svelte store
68+
const store = writable(startWith, start);
69+
const { subscribe, set } = store;
70+
71+
return {
72+
subscribe,
73+
firestore,
74+
ref,
75+
get loading() {
76+
return _loading;
77+
},
78+
get error() {
79+
return _error;
80+
}
81+
};
82+
}
83+
84+
export function uploadFileStore(path, opts) {
85+
const storage = assertApp('storage');
86+
87+
const { startWith, log, traceId } = { maxWait: 10000, ...opts };
88+
89+
const storageRef = storage.ref();
90+
const ref = typeof path === 'string' ? storageRef.child(path) : path;
91+
let snapshot;
92+
let task;
93+
94+
// Performance trace
95+
const trace = traceId && startTrace(traceId);
96+
97+
// Internal state
98+
let _loading = typeof startWith !== undefined;
99+
let _error = null;
100+
let _waitForIt;
101+
102+
// State should never change without emitting a new value
103+
// Clears loading state on first call
104+
const next = (val, err) => {
105+
_loading = false;
106+
_waitForIt && clearTimeout(_waitForIt);
107+
_error = err || null;
108+
set(val);
109+
trace && stopTrace(trace);
110+
};
111+
112+
// Timout
113+
// Runs of first subscription
114+
const start = () => {};
115+
116+
// Svelte store
117+
const store = writable(startWith, start);
118+
let { subscribe, set } = store;
119+
120+
const _set = set;
121+
set = (val) => {
122+
task = ref.put(val).then(snap => {
123+
snapshot = snap;
124+
ref.getDownloadURL().then((url) => {
125+
next(url);
126+
if (log) console.log(`URl: ${url}`);
127+
});
128+
});
129+
task.on('state_changed', snap => {
130+
snapshot = snap;
131+
});
132+
}
133+
134+
return {
135+
subscribe,
136+
firestore,
137+
ref,
138+
get loading() {
139+
return _loading;
140+
},
141+
get error() {
142+
return _error;
143+
}
144+
};
145+
}

0 commit comments

Comments
 (0)