feat: rest request sync with firestore

This commit is contained in:
Andrew Bastin
2021-08-15 10:45:26 +05:30
parent bb6d9a88ec
commit 7acde1c174
3 changed files with 145 additions and 1 deletions

View File

@@ -1,7 +1,15 @@
import firebase from "firebase/app"
import "firebase/firestore"
import "firebase/auth"
import { BehaviorSubject, Subject } from "rxjs"
import {
BehaviorSubject,
distinctUntilChanged,
filter,
map,
Subject,
Subscription,
} from "rxjs"
import { onBeforeUnmount, onMounted } from "@nuxtjs/composition-api"
export type HoppUser = firebase.User & {
provider?: string
@@ -219,3 +227,29 @@ export async function setProviderInfo(id: string, token: string) {
throw e
}
}
/**
* A Vue composable function that is called when the auth status
* is being updated to being logged in (fired multiple times),
* this is also called on component mount if the login
* was already resolved before mount.
*/
export function onLoggedIn(exec: (user: HoppUser) => void) {
let sub: Subscription | null = null
onMounted(() => {
sub = currentUser$
.pipe(
map((user) => !!user), // Get a logged in status (true or false)
distinctUntilChanged(), // Don't propagate unless the status updates
filter((x) => x) // Don't propagate unless it is logged in
)
.subscribe(() => {
exec(currentUser$.value!)
})
})
onBeforeUnmount(() => {
sub?.unsubscribe()
})
}

83
helpers/fb/request.ts Normal file
View File

@@ -0,0 +1,83 @@
import firebase from "firebase/app"
import "firebase/firestore"
import {
audit,
combineLatest,
distinctUntilChanged,
EMPTY,
from,
map,
Subscription,
} from "rxjs"
import {
HoppRESTRequest,
translateToNewRequest,
} from "../types/HoppRESTRequest"
import { currentUser$, HoppUser } from "./auth"
import { restRequest$ } from "~/newstore/RESTSession"
/**
* Writes a request to a user's firestore sync
*
* @param user The user to write to
* @param request The request to write to the request sync
*/
function writeCurrentRequest(user: HoppUser, request: HoppRESTRequest) {
return firebase
.firestore()
.collection("users")
.doc(user.uid)
.collection("requests")
.doc("rest")
.set(request)
}
/**
* Loads the synced request from the firestore sync
*
* @returns Fetched request object if exists else null
*/
export async function loadRequestFromSync(): Promise<HoppRESTRequest | null> {
const currentUser = currentUser$.value
if (!currentUser)
throw new Error("Cannot load request from sync without login")
const doc = await firebase
.firestore()
.collection("users")
.doc(currentUser.uid)
.collection("requests")
.doc("rest")
.get()
const data = doc.data()
if (!data) return null
else return translateToNewRequest(data)
}
/**
* Performs sync of the REST Request session with Firestore.
*
* @returns A subscription to the sync observable stream.
* Unsubscribe to stop syncing.
*/
export function startRequestSync(): Subscription {
const sub = combineLatest([
currentUser$,
restRequest$.pipe(distinctUntilChanged()),
])
.pipe(
map(([user, request]) =>
user ? from(writeCurrentRequest(user, request)) : EMPTY
),
audit((x) => x)
)
.subscribe(() => {
// NOTE: This subscription should be kept
console.log("synced request")
})
return sub
}

View File

@@ -77,6 +77,7 @@
import {
computed,
defineComponent,
onBeforeUnmount,
onMounted,
useContext,
watch,
@@ -84,6 +85,7 @@ import {
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
import { map } from "rxjs/operators"
import { Subscription } from "rxjs"
import { useSetting } from "~/newstore/settings"
import {
restRequest$,
@@ -101,6 +103,8 @@ import {
useStream,
useStreamSubscriber,
} from "~/helpers/utils/composables"
import { loadRequestFromSync, startRequestSync } from "~/helpers/fb/request"
import { onLoggedIn } from "~/helpers/fb/auth"
function bindRequestToURLParams() {
const {
@@ -159,11 +163,34 @@ function bindRequestToURLParams() {
})
}
function setupRequestSync() {
const { route } = useContext()
// Subscription to request sync
let sub: Subscription | null = null
// Load request on login resolve and start sync
onLoggedIn(async () => {
if (Object.keys(route.value.query).length === 0) {
const request = await loadRequestFromSync()
if (request) setRESTRequest(request)
}
sub = startRequestSync()
})
// Stop subscripton to stop syncing
onBeforeUnmount(() => {
sub?.unsubscribe()
})
}
export default defineComponent({
components: { Splitpanes, Pane },
setup() {
const { subscribeToStream } = useStreamSubscriber()
setupRequestSync()
bindRequestToURLParams()
subscribeToStream(restRequest$, (x) => {