/**
 *    _____ __
 *   / ___// /_  _____
 *   \__ \/ / / / / _ \
 *  ___/ / / /_/ /  __/
 * /____/_/\__, /\___/
 *       /____/
 *       Copyright 2018 Parsa Ghadimi. All Rights Reserved.
 *       Licence: MIT License
 */ import firebase from "firebase/app" ; import { FS_CONFIG } from "./config" ; import * as types from "./types" ; import * as util from "./util" ; import "firebase/auth" ; import "firebase/firestore" ; import "firebase/storage" ; export interface DB { queryLatest ( ) : Promise < types . PresentationInfo [ ] > ; queryProfile ( uid ) : Promise < types . PresentationInfo [ ] > ; getPresentation ( id ) : Promise < types . Presentation > ; getThumbnailLink ( p ) : Promise < string > ; create ( ) : Promise < string > ; uploadThumbnail ( p , blob ) : Promise < void > ; update ( id , p ) : Promise < void > ; login ( ) : void ; logout ( ) : void ; onAuthStateChanged ( cb ) ; } export const db = Object . create ( null ) ; declare const exports ; export function enableFS ( ) { const tmp = new FirestoreDB ( FS_CONFIG ) ; exports . db = tmp ; } export function enableMock ( ) { } class FirestoreDB implements DB { private auth : firebase . auth . Auth ; private db : firebase . firestore . Firestore ; private collectionRef : firebase . firestore . CollectionReference ; private storageRef : firebase . storage . Reference ; constructor ( config ) { firebase . initializeApp ( config ) ; this . auth = firebase . auth ( ) ; this . db = firebase . firestore ( ) ; this . db . settings ( { timestampsInSnapshots : true } ) ; this . collectionRef = this . db . collection ( "presentations" ) ; this . storageRef = firebase . storage ( ) . ref ( ) ; } async queryLatest ( ) { const query = this . collectionRef . orderBy ( "created" , "desc" ) . limit ( 20 ) ; const snapshots = await query . get ( ) ; const out = [ ] ; snapshots . forEach ( snap => { const id = snap . id ; const data = snap . data ( ) as types . Presentation ; out . push ( { id , data , thumbnail : null } ) ; } ) ; return out ; } async queryProfile ( uid ) { const query = this . collectionRef . where ( "owner.uid" , "==" , uid ) ; const snapshots = await query . get ( ) ; const out = [ ] ; snapshots . forEach ( snap => { const id = snap . id ; const data = snap . data ( ) as types . Presentation ; out . push ( { id , data , thumbnail : null } ) ; } ) ; return out ; } async getPresentation ( id ) { const docRef = this . collectionRef . doc ( id ) ; const snap = await docRef . get ( ) ; if ( snap . exists ) { return snap . data ( ) as types . Presentation ; } else { throw Error ( `Presentation does not exist ${ id }` ) ; } } getThumbnailLink ( p ) { const path = thumbnailPath ( p . data . owner . uid , p . id ) ; const thumbRef = this . storageRef . child ( path ) ; return thumbRef . getDownloadURL ( ) ; } async create ( ) { const u = this . auth . currentUser ; const stepId = util . randomString ( ) ; const presentation = { owner : { displayName : u . displayName , photoURL : u . photoURL , uid : u . uid } , steps : { [ stepId ] : util . emptyStep ( ) } , created : firebase . firestore . FieldValue . serverTimestamp ( ) , updated : firebase . firestore . FieldValue . serverTimestamp ( ) , order : [ stepId ] } ; const doc = await this . collectionRef . add ( presentation ) ; return doc . id ; } async uploadThumbnail ( p , blob ) { const userId = p . data . owner . uid ; const path = thumbnailPath ( userId , p . id ) ; const ref = this . storageRef . child ( path ) ; await ref . put ( blob ) ; } async update ( id , p ) { if ( ! ownsDoc ( this . auth . currentUser , p ) ) { throw new Error ( "Not owned by this user." ) ; } const docRef = this . collectionRef . doc ( id ) ; const newProps = { steps : p . steps , order : p . order , updated : firebase . firestore . FieldValue . serverTimestamp ( ) } ; await docRef . update ( newProps ) ; } login ( ) { const provider = new firebase . auth . GoogleAuthProvider ( ) ; return firebase . auth ( ) . signInWithPopup ( provider ) ; } logout ( ) { return firebase . auth ( ) . signOut ( ) ; } onAuthStateChanged ( cb ) { firebase . auth ( ) . onAuthStateChanged ( cb ) ; } } export function thumbnailPath ( userId , presentationId ) { return `/data/${ userId }/${ presentationId }/thumb.png` ; } export function ownsDoc ( u , p ) { return u . uid === p . owner . uid ; }