feat: rest request sync with firestore
This commit is contained in:
@@ -1,7 +1,15 @@
|
|||||||
import firebase from "firebase/app"
|
import firebase from "firebase/app"
|
||||||
import "firebase/firestore"
|
import "firebase/firestore"
|
||||||
import "firebase/auth"
|
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 & {
|
export type HoppUser = firebase.User & {
|
||||||
provider?: string
|
provider?: string
|
||||||
@@ -219,3 +227,29 @@ export async function setProviderInfo(id: string, token: string) {
|
|||||||
throw e
|
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
83
helpers/fb/request.ts
Normal 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
|
||||||
|
}
|
||||||
@@ -77,6 +77,7 @@
|
|||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
defineComponent,
|
defineComponent,
|
||||||
|
onBeforeUnmount,
|
||||||
onMounted,
|
onMounted,
|
||||||
useContext,
|
useContext,
|
||||||
watch,
|
watch,
|
||||||
@@ -84,6 +85,7 @@ import {
|
|||||||
import { Splitpanes, Pane } from "splitpanes"
|
import { Splitpanes, Pane } from "splitpanes"
|
||||||
import "splitpanes/dist/splitpanes.css"
|
import "splitpanes/dist/splitpanes.css"
|
||||||
import { map } from "rxjs/operators"
|
import { map } from "rxjs/operators"
|
||||||
|
import { Subscription } from "rxjs"
|
||||||
import { useSetting } from "~/newstore/settings"
|
import { useSetting } from "~/newstore/settings"
|
||||||
import {
|
import {
|
||||||
restRequest$,
|
restRequest$,
|
||||||
@@ -101,6 +103,8 @@ import {
|
|||||||
useStream,
|
useStream,
|
||||||
useStreamSubscriber,
|
useStreamSubscriber,
|
||||||
} from "~/helpers/utils/composables"
|
} from "~/helpers/utils/composables"
|
||||||
|
import { loadRequestFromSync, startRequestSync } from "~/helpers/fb/request"
|
||||||
|
import { onLoggedIn } from "~/helpers/fb/auth"
|
||||||
|
|
||||||
function bindRequestToURLParams() {
|
function bindRequestToURLParams() {
|
||||||
const {
|
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({
|
export default defineComponent({
|
||||||
components: { Splitpanes, Pane },
|
components: { Splitpanes, Pane },
|
||||||
setup() {
|
setup() {
|
||||||
const { subscribeToStream } = useStreamSubscriber()
|
const { subscribeToStream } = useStreamSubscriber()
|
||||||
|
|
||||||
|
setupRequestSync()
|
||||||
bindRequestToURLParams()
|
bindRequestToURLParams()
|
||||||
|
|
||||||
subscribeToStream(restRequest$, (x) => {
|
subscribeToStream(restRequest$, (x) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user