import { createLogger } from 'common/logger' ; import { UserProfile , BillingInfo , UserPublicProfile , UserLocalSettingsIded , UserLocalSettings } from 'common/models' ; import Identify from 'common/models/Identify' ; import RepoError from './RepoError' ; import Collections from 'common/database/collections' ; import DBProvider , { getIdentify , serverOnly , documentSnapshot , UnsubscribeSnapshot , DocumentSnapshotCallback , DocumentSnapshot } from './dbProvider' ; const logger = createLogger ( '[UserRepo]' , false ) ; type UserProfileIded = Identify < UserProfile > ; export class UserRepo { constructor ( protected readonly db ) { } private get collection ( ) { return this . db . collection ( Collections . Users ) ; } private get serverCollection ( ) { return serverOnly ( this . db ) . collection ( Collections . Users ) ; } getUserById ( uid ) ; getUserById ( uid , cb ) ; getUserById ( uid , cb = null ) { if ( ! uid ) { throw RepoError . InvalidArg ( 'uid' ) ; } return documentSnapshot ( this . db , this . collection . doc ( uid ) , cb ) ; } async getPublicUserInfo ( uid ) { if ( ! uid ) { throw RepoError . InvalidArg ( 'uid' ) ; } const res = await this . db . doc ( Collections . userPublicInfoDoc ( uid ) ) . get ( ) ; return res . exists ? res . data ( ) as UserPublicProfile : null ; } async getUserByEmail ( email ) { if ( ! email ) { throw RepoError . InvalidArg ( 'email' ) ; } const users = await this . collection . where ( 'email' , '==' , email ) . limit ( 1 ) . get ( ) ; if ( users . empty ) { return null ; } return getIdentify < UserProfileIded > ( users . docs [ 0 ] ) ; } async ensureAndUpdate ( uid , profile ) { if ( ! uid ) { throw RepoError . InvalidArg ( 'uid' ) ; } const profileDoc = this . serverCollection . doc ( uid ) ; if ( profile ) { delete profile . billing ; await profileDoc . set ( profile , { merge : true } ) ; } const profileRes = await profileDoc . get ( ) ; return getIdentify < UserProfileIded > ( profileRes ) ; } async updatePublicProfile ( uid , info ) { const publicProfileDoc = serverOnly ( this . db ) . doc ( Collections . userPublicInfoDoc ( uid ) ) ; await publicProfileDoc . set ( info , { merge : true } ) ; } async deleteBilling ( uid ) { if ( ! uid ) { throw RepoError . InvalidArg ( 'uid' ) ; } try { const doc = this . serverCollection . doc ( uid ) ; await doc . set ( { billing : null } , { merge : true } ) ; const res = await doc . get ( ) ; return getIdentify < UserProfileIded > ( res ) ; } catch ( err ) { throw RepoError . InvalidArg ( `An error during deleting billing: ${ err . message }` ) ; } } async updateBilling ( uid , billing ) { if ( ! uid ) { throw RepoError . InvalidArg ( 'uid' ) ; } const docRef = this . serverCollection . doc ( uid ) ; logger . log ( 'UpdateBilling for user ID: ' , uid , 'with new fields:' , billing ) ; logger . log ( 'With new fields:' , billing ) ; await serverOnly ( this . db ) . runTransaction ( async ( t ) => { const doc = await t . get ( docRef ) ; if ( ! doc . exists ) { throw 'Document does not exist' ; } let currentBilling = ( doc . data ( ) as UserProfile ) . billing ; if ( ! currentBilling ) { currentBilling = { } as BillingInfo ; } if ( billing . stripeUid && ! currentBilling . stripeUid ) { currentBilling . stripeUid = billing . stripeUid ; } if ( billing . trialPeriondEnd && ! currentBilling . trialPeriondEnd ) { currentBilling . trialPeriondEnd = billing . trialPeriondEnd ; } if ( billing . defaultSource ) { currentBilling . defaultSource = billing . defaultSource ; } if ( billing . last4 !== undefined ) { currentBilling . last4 = billing . last4 ; } if ( billing . cancelAt !== undefined ) { currentBilling . cancelAt = billing . cancelAt ; } if ( billing . subscriptionId !== undefined ) { currentBilling . subscriptionId = billing . subscriptionId ; } if ( billing . planId !== undefined ) { currentBilling . planId = billing . planId ; } if ( billing . plan !== undefined ) { currentBilling . plan = billing . plan ; } if ( billing . planCategory !== undefined ) { currentBilling . planCategory = billing . planCategory ; } if ( billing . maxClients !== undefined ) { currentBilling . maxClients = billing . maxClients ; } if ( billing . paymentFailed != null ) { currentBilling . paymentFailed = billing . paymentFailed ; } if ( billing . nextPaymentAttempt !== undefined ) { currentBilling . nextPaymentAttempt = billing . nextPaymentAttempt ; } return t . set ( docRef , { billing : currentBilling } , { merge : true } ) ; } ) ; return await this . getUserById ( uid ) ; } async getAllLocalSettings ( uid ) { const colRef = this . db . collection ( Collections . userLocalSettings ( uid ) ) ; const items = await colRef . get ( ) ; const docs = items . docs ; return docs . map ( d => getIdentify < UserLocalSettingsIded > ( d ) ) ; } async findLocalSetting ( deviceId ) { const snap = await serverOnly ( this . db ) . collectionGroup ( Collections . LocalSettings ) . where ( 'deviceId' , '==' , deviceId ) . get ( ) ; const results = snap . docs . map ( d => { const settings = getIdentify < UserLocalSettingsIded & { userId : string ; } > ( d ) ; settings . userId = d . ref . parent . parent . id ; return settings ; } ) ; return results ; } async getLocalSettings ( uid , deviceId ) { try { const docRef = this . db . doc ( Collections . userLocalSettings ( uid , deviceId ) ) ; const doc = await docRef . get ( ) ; return getIdentify < UserLocalSettingsIded > ( doc ) ; } catch ( err ) { logger . error ( 'getLocalSettings: ' , err ) ; } } async updateLocalSettings ( uid , deviceId , settings ) { try { const docRef = this . db . doc ( Collections . userLocalSettings ( uid , deviceId ) ) ; settings . deviceId = deviceId ; await docRef . set ( settings , { merge : true } ) ; } catch ( err ) { logger . error ( 'updateLocalSettings: ' , err ) ; } } } export default UserRepo ;