Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
RootViewModel.ts 5.61 KiB
import { ViewModel, Client, Navigation, createRouter, Platform } from "hydrogen-view-sdk";
import { IChatterboxConfig } from "../types/IChatterboxConfig";
import { ChatterboxViewModel } from "./ChatterboxViewModel";
import "hydrogen-view-sdk/style.css";
import { AccountSetupViewModel } from "./AccountSetupViewModel";
import { MessageFromParent } from "../observables/MessageFromParent";

type Options = { platform: typeof Platform, navigation: typeof Navigation, urlCreator: ReturnType<typeof createRouter>, startMinimized: boolean };

export class RootViewModel extends ViewModel {
    private _config: IChatterboxConfig;
    private _client: typeof Client;
    private _chatterBoxViewModel?: ChatterboxViewModel;
    private _accountSetupViewModel?: AccountSetupViewModel;
    private _activeSection?: string;
    private _messageFromParent: MessageFromParent = new MessageFromParent();
    private _startMinimized: boolean;
    private _isWatchingNotificationCount: boolean;

    constructor(config: IChatterboxConfig, options: Options) {
        super(options);
        this._startMinimized = options.startMinimized;
        this._config = config;
        this._client = new Client(this.platform);
        this._setupNavigation();
        this._messageFromParent.on("maximize", () => this.start());
        // Chatterbox can be minimized via the start button on the parent page!
        this._messageFromParent.on("minimize", () => this.navigation.push("minimize"));
    }

    private _setupNavigation() {
        this.navigation.observe("account-setup").subscribe(() => this._showAccountSetup());
        this.navigation.observe("timeline").subscribe((loginPromise) => this._showTimeline(loginPromise));
        this.navigation.observe("minimize").subscribe(() => this.minimizeChatterbox());
    }

    async start() {
        const sessionAlreadyExists = await this.attemptStartWithExistingSession();
        if (sessionAlreadyExists) {
            if (!this._isWatchingNotificationCount) {
                this._watchNotificationCount();
            }
            if (this._startMinimized) {
                // when CB is maximized, this function is run again
                // don't start in minimized state then
                this._startMinimized = false;
                return;
            }
            this.navigation.push("timeline");
            return;
        }
        this.navigation.push("account-setup");
    }

    private async _showTimeline(loginPromise: Promise<void>) {
        this._activeSection = "timeline";
        if (!this._chatterBoxViewModel) {
            this._chatterBoxViewModel = this.track(new ChatterboxViewModel(
                this.childOptions({
                    client: this._client,
                    config: this._config,
                    state: this._state,
                    loginPromise,
                })
            ));
            await this._chatterBoxViewModel.load();
            if (!this._isWatchingNotificationCount) {
                // for when chatterbox is loaded initially
                this._watchNotificationCount();
            }
        }
        this.emitChange("activeSection");
    }

    private _showAccountSetup() {
        this._activeSection = "account-setup";
        this._accountSetupViewModel = this.track(new AccountSetupViewModel(
            this.childOptions({
                client: this._client,
                config: this._config,
                state: this._state,
            })
        ));
        this.emitChange("activeSection");
    }

    /**
     * Try to start Hydrogen based on an existing hydrogen session.
     * If multiple sessions exist, this method chooses the most recent one.
     */
    async attemptStartWithExistingSession(): Promise<boolean> {
        const sessionIds = await this.platform.sessionInfoStorage.getAll();
        const session = sessionIds.pop();
        if (session) {
            const { id } = session;
            await this._client.startWithExistingSession(id);
            return true;
        }
        return false;
    }

    private _watchNotificationCount() {
        const [room] = this._client.session.rooms.values();
        let previousCount = room.notificationCount;
        (window as any).sendNotificationCount(previousCount);
        const subscription = {
            onUpdate(_: unknown, room) {
                const newCount = room.notificationCount;
                if (newCount !== previousCount) {
                    if (!room.isUnread && newCount !== 0) {
                        /*
                        when chatterbox is maximized and there are previous unread messages,
                        this condition is hit but we still want to send the notification count so that 
                        the badge zeroes out.
                        */
                        room.clearUnread();
                        return;
                    }
                    (window as any).sendNotificationCount(newCount);
                    previousCount = newCount;
                }
            },
        };
        this.track(this._client.session.rooms.subscribe(subscription));
        this._isWatchingNotificationCount = true;
    }
    
    minimizeChatterbox() {
        this._chatterBoxViewModel = this.disposeTracked(this._chatterBoxViewModel);
        this._accountSetupViewModel = this.disposeTracked(this._chatterBoxViewModel);
        this._activeSection = "";
        this.emitChange("chatterboxViewModel");
    }

    get chatterboxViewModel() {
        return this._chatterBoxViewModel;
    }

    get accountSetupViewModel() {
        return this._accountSetupViewModel;
    }

    get activeSection() {
        return this._activeSection;
    }
}