import { isPlatformBrowser } from '@angular/common' ; import { Inject , Injectable , OnDestroy , PLATFORM_ID } from '@angular/core' ; import { BehaviorSubject , Observable , of , Subscription } from 'rxjs' ; import { catchError , take } from 'rxjs/operators' ; import { FormToastService } from '../../common/services/form-toast.service' ; import isMobileOrTablet from '../../helpers/is-mobile-or-tablet' ; import { Storage } from '../../services/storage' ; /**
 * Dismissed or active to make use of animations.
 */ export type AppPromptState = 'dismissed' | 'active' | 'expanded' ; /**
 * A supported mobile platform with our app, or non-mobile.
 */ export type MobilePlatform = 'android' | 'iphone' | 'non-mobile' ; const STORAGE_KEY = 'app-prompt:dismissed' ; let lastDateNow ; /**
 * navigates to the same screen in the app
 */ function navigateDeepIntoApp ( ) { ( window as any ) . location = 'mindsapp://' + ( window as any ) . location . href . split ( '//' ) [ 1 ] . split ( '/' ) . filter ( ( item , i ) => i !== 0 ) . join ( '/' ) ; } function watchIOS ( cb ) { if ( lastDateNow ) { if ( Date . now ( ) - lastDateNow > 50 ) { return ; } lastDateNow = undefined ; return cb ( ) ; } lastDateNow = Date . now ( ) ; setTimeout ( ( ) => watchIOS ( cb ) , 25 ) ; } function openIOS ( ) { watchIOS ( ( ) => { ( window as any ) . location = 'https://itunes.apple.com/us/app/minds-com/id961771928' ; setTimeout ( ( ) => window . location . reload ( ) , 1000 ) ; } ) ; navigateDeepIntoApp ( ) ; } /**
 * Service for interacting with the mobile app prompt.
 */ @ Injectable ( { providedIn : 'root' } ) export class AppPromptService implements OnDestroy { private subscriptions : Subscription [ ] = [ ] ; /**
     * Current state of modal
     */ public readonly state$ : BehaviorSubject < AppPromptState > = new BehaviorSubject < AppPromptState > ( 'dismissed' ) ; /**
     * Current user platform (defaults to non-mobile until after init)
     */ public readonly platform$ : BehaviorSubject < MobilePlatform > = new BehaviorSubject < MobilePlatform > ( 'non-mobile' ) ; constructor ( @ Inject ( PLATFORM_ID ) private platformId , private storage , private toaster ) { } ngOnDestroy ( ) { for ( const subscription of this . subscriptions ) { subscription . unsubscribe ( ) ; } } return { boolean } shouldShowPrompt ( ) { if ( ! this . hasAvailableApp ( ) ) return false ; if ( ! this . storage . get ( STORAGE_KEY ) ) return true ; if ( Date . now ( ) - Number ( this . storage . get ( STORAGE_KEY ) ) > 24 * 60 * 60 * 1000 ) { this . storage . destroy ( STORAGE_KEY ) ; return true ; } return false ; } returns { AppPromptService } public activate ( ) { this . state$ . next ( 'active' ) ; return this ; } returns { AppPromptService } public close ( ) { this . state$ . next ( 'dismissed' ) ; this . storage . set ( STORAGE_KEY , String ( Date . now ( ) ) ) ; return this ; } returns { boolean } public hasAvailableApp ( ) { if ( ! isPlatformBrowser ( this . platformId ) ) { return false ; } return isMobileOrTablet ( ) ; } returns { void } public setPlatform ( ) { if ( ! isPlatformBrowser ( this . platformId ) ) { return ; } if ( /iPad|iPhone|iPod/i . test ( navigator . userAgent ) ) { this . platform$ . next ( 'iphone' ) ; return ; } if ( /android/i . test ( navigator . userAgent ) ) { this . platform$ . next ( 'android' ) ; } } private openAppWithFallback ( fallback ) { if ( ! isPlatformBrowser ( this . platformId ) ) { return false ; } if ( this . platform$ . getValue ( ) === 'iphone' ) { this . watchIfAppWasntOpened ( ( ) => { fallback ( ) ; } ) ; } else { this . watchUserPresence ( ( ) => { this . activate ( ) ; } ) ; setTimeout ( ( ) => { fallback ( ) ; } , 1000 ) ; } navigateDeepIntoApp ( ) ; } private openAppStore ( ) { ( window as any ) . location = 'https://itunes.apple.com/us/app/minds-com/id961771928' ; } private lastDateNow : number ; returns { void } watchUserPresence ( cb ) { if ( this . state$ . getValue ( ) === 'dismissed' ) return ; if ( this . lastDateNow && Date . now ( ) - this . lastDateNow > 1000 ) { this . lastDateNow = undefined ; return cb ( ) ; } this . lastDateNow = Date . now ( ) ; setTimeout ( ( ) => this . watchUserPresence ( cb ) , 500 ) ; } onceCalled = false ; returns { void } watchIfAppWasntOpened ( cb ) { if ( this . state$ . getValue ( ) === 'dismissed' ) return ; if ( this . lastDateNow ) { if ( Date . now ( ) - this . lastDateNow > 50 ) { return ; } if ( ! this . onceCalled ) { this . onceCalled = true ; return ; } this . lastDateNow = undefined ; return cb ( ) ; } this . lastDateNow = Date . now ( ) ; setTimeout ( ( ) => this . watchIfAppWasntOpened ( cb ) , 25 ) ; } returns { void } public redirect ( ) { if ( isPlatformBrowser ( this . platformId ) ) { if ( this . platform$ . getValue ( ) === 'iphone' ) { return openIOS ( ) ; } this . subscriptions . push ( this . platform$ . pipe ( take ( 1 ) , catchError ( e => this . handleError ( e ) ) ) . subscribe ( ( platform ) => { try { switch ( platform ) { case 'iphone' : this . openAppWithFallback ( ( ) => { this . openAppStore ( ) ; setTimeout ( ( ) => window . location . reload ( ) , 1000 ) ; } ) ; break ; case 'android' : this . openAppWithFallback ( ( ) => this . expand ( ) ) ; break ; } } catch ( e ) { this . handleError ( e ) ; } } ) ) ; } } returns { void } public expand ( ) { this . state$ . next ( 'expanded' ) ; } param returns { Observable < null > } private handleError ( e ) { console . error ( e ) ; this . state$ . next ( 'dismissed' ) ; this . toaster . error ( 'Sorry, we were unable to open the mobile app or store right now' ) ; return of ( null ) ; } }