import { Observable , Subscription , combineLatest , race , throwError , timer } from "rxjs" ; import { Store , select } from "@ngrx/store" ; import { concatMap , distinctUntilChanged , first , map , pairwise , startWith , tap } from "rxjs/operators" ; import { equals , noop } from "remeda" ; import { ACCOUNTS_ACTIONS , NAVIGATION_ACTIONS , NOTIFICATION_ACTIONS } from "src/web/browser-window/app/store/actions" ; import { AccountConfig } from "src/shared/model/account" ; import { AccountsSelectors , OptionsSelectors } from "src/web/browser-window/app/store/selectors" ; import { Component , ElementRef } from "@angular/core" ; import { CoreService } from "src/web/browser-window/app/_core/core.service" ; import { ElectronService } from "src/web/browser-window/app/_core/electron.service" ; import { ONE_SECOND_MS } from "src/shared/constants" ; import type { OnDestroy , OnInit } from "@angular/core" ; import { SETTINGS_OUTLET , SETTINGS_PATH } from "src/web/browser-window/app/app.constants" ; import { State } from "src/web/browser-window/app/store/reducers/accounts" ; import { WebAccount } from "src/web/browser-window/app/model" ; @ Component ( { selector : "electron-mail-accounts" , templateUrl : "./accounts.component.html" , styleUrls : [ "./accounts.component.scss" ] , preserveWhitespaces : true , } ) export class AccountsComponent implements OnInit , OnDestroy { readonly userDataDir = __METADATA__ . electronLocations . userDataDir ; readonly initialized$ : Observable < boolean | undefined > ; readonly layoutMode$ : Observable < "top" | "left" | "left-thin" > ; readonly hideControls$ : Observable < boolean > ; selectedAccount ? : WebAccount ; unreadSummary ? : number ; private accountsMap = new Map < WebAccount [ "accountConfig" ] [ "login" ] , WebAccount > ( ) ; private accounts$ : Observable < WebAccount [ ] > ; readonly logins$ : Observable < string [ ] > ; readonly loginsWithoutOrdering$ : Observable < string [ ] > ; private readonly subscription = new Subscription ( ) ; constructor ( private coreService , private api , private store , private readonly elementRef ) { this . initialized$ = this . store . pipe ( select ( AccountsSelectors . FEATURED . initialized ) ) ; this . layoutMode$ = this . store . pipe ( select ( OptionsSelectors . CONFIG . layoutMode ) ) ; this . hideControls$ = this . store . pipe ( select ( OptionsSelectors . CONFIG . hideControls ) ) ; this . accounts$ = this . store . pipe ( select ( AccountsSelectors . FEATURED . accounts ) ) ; this . logins$ = this . accounts$ . pipe ( map ( ( accounts ) => accounts . map ( ( account ) => account . accountConfig . login ) ) , distinctUntilChanged ( ) ) ; this . loginsWithoutOrdering$ = this . logins$ . pipe ( distinctUntilChanged ( ( prev , curr ) => equals ( [ ... prev ] . sort ( ) , [ ... curr ] . sort ( ) ) ) ) ; this . subscription . add ( this . accounts$ . subscribe ( ( accounts ) => { this . accountsMap = new Map ( accounts . reduce ( ( entries , account ) => { return entries . concat ( [ [ account . accountConfig . login , account ] ] ) ; } , [ ] ) ) ; } ) ) ; } getAccountByLogin ( login ) { const account = this . accountsMap . get ( login ) ; if ( ! account ) { throw new Error ( `No account found with "${ login }" login` ) ; } return account ; } ngOnInit ( ) { this . subscription . add ( combineLatest ( [ this . store . select ( AccountsSelectors . ACCOUNTS . loggedInAndUnreadSummary ) . pipe ( distinctUntilChanged ( ( prev , curr ) => equals ( prev , curr ) ) ) , this . store . pipe ( select ( OptionsSelectors . CONFIG . trayIconColor ) ) , this . store . pipe ( select ( OptionsSelectors . CONFIG . unreadBgColor ) ) , this . store . pipe ( select ( OptionsSelectors . CONFIG . unreadTextColor ) ) , this . store . pipe ( select ( OptionsSelectors . CONFIG . doNotRenderNotificationBadgeValue ) ) , ] ) . subscribe ( ( [ { hasLoggedOut , unread } ] ) => { this . unreadSummary = unread ; this . store . dispatch ( NOTIFICATION_ACTIONS . UpdateOverlayIcon ( { hasLoggedOut , unread } ) ) ; } ) ) ; this . subscription . add ( this . store . pipe ( select ( AccountsSelectors . FEATURED . selectedAccount ) ) . subscribe ( async ( selectedAccount ) => { if ( this . selectedAccount === selectedAccount ) { return ; } this . selectedAccount = selectedAccount ; if ( ! this . selectedAccount ) { await this . api . ipcMainClient ( ) ( "selectAccount" ) ( { reset : true } ) ; } } ) ) ; } activateAccountByLogin ( event , login ) { event . preventDefault ( ) ; this . store . dispatch ( ACCOUNTS_ACTIONS . Select ( { login } ) ) ; } openSettingsView ( ) { this . coreService . openSettingsView ( ) ; } openAddingAccountView ( ) { this . store . dispatch ( NAVIGATION_ACTIONS . Go ( { path : [ { outlets : { [ SETTINGS_OUTLET ] : `${ SETTINGS_PATH }/account-edit` } } ] , } ) ) ; } openAboutWindow ( ) { this . store . dispatch ( NAVIGATION_ACTIONS . OpenAboutWindow ( ) ) ; } openSettingsFolder ( ) { this . store . dispatch ( NAVIGATION_ACTIONS . OpenSettingsFolder ( ) ) ; } logout ( ) { this . coreService . logOut ( ) ; } quit ( ) { this . store . dispatch ( NAVIGATION_ACTIONS . Quit ( ) ) ; } cancelEvent ( event ) { event . preventDefault ( ) ; event . stopPropagation ( ) ; } accountUnloadRollback ( { accountUnloadRollbackUuid : uuid } ) { { const { nativeElement : targetNode } = this . elementRef ; const renderedAccountsTimeoutMs = ONE_SECOND_MS ; const resolveRenderedAccountsCount = ( ) : number => targetNode . querySelectorAll ( "electron-mail-account" ) . length ; race ( new Observable < ReturnType < typeof resolveRenderedAccountsCount > > ( ( subscribe ) => { const mutation = new MutationObserver ( ( ) => { subscribe . next ( resolveRenderedAccountsCount ( ) ) ; } ) ; mutation . observe ( targetNode , { childList : true , subtree : true } ) ; return ( ) : void => mutation . disconnect ( ) ; } ) . pipe ( startWith ( resolveRenderedAccountsCount ( ) ) , distinctUntilChanged ( ) , pairwise ( ) , first ( ) , tap ( ( [ prev , curr ] ) => { const countDiff = prev - curr ; if ( countDiff !== 1 ) { throw new Error ( `Only single account disabling expected to happen (actual count diff: ${ countDiff })` ) ; } this . store . dispatch ( ACCOUNTS_ACTIONS . UnloadRollback ( { uuid } ) ) ; } ) ) , timer ( renderedAccountsTimeoutMs ) . pipe ( concatMap ( ( ) => throwError ( ( ) => new Error ( `Failed to received the accounts count change in ${ renderedAccountsTimeoutMs }ms` ) ) ) ) ) . subscribe ( noop ) ; } } ngOnDestroy ( ) { this . subscription . unsubscribe ( ) ; } }