tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SyncedTabsDeckComponent.sys.mjs (5878B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import { SyncedTabsDeckStore } from "resource:///modules/syncedtabs/SyncedTabsDeckStore.sys.mjs";
      6 import { SyncedTabsDeckView } from "resource:///modules/syncedtabs/SyncedTabsDeckView.sys.mjs";
      7 import { SyncedTabsListStore } from "resource:///modules/syncedtabs/SyncedTabsListStore.sys.mjs";
      8 import { TabListComponent } from "resource:///modules/syncedtabs/TabListComponent.sys.mjs";
      9 import { TabListView } from "resource:///modules/syncedtabs/TabListView.sys.mjs";
     10 import { getChromeWindow } from "resource:///modules/syncedtabs/util.sys.mjs";
     11 import { UIState } from "resource://services-sync/UIState.sys.mjs";
     12 
     13 /* SyncedTabsDeckComponent
     14 * This component instantiates views and storage objects as well as defines
     15 * behaviors that will be passed down to the views. This helps keep the views
     16 * isolated and easier to test.
     17 */
     18 
     19 export function SyncedTabsDeckComponent({
     20  window,
     21  SyncedTabs,
     22  deckStore,
     23  listStore,
     24  listComponent,
     25  DeckView,
     26  getChromeWindowMock,
     27 }) {
     28  this._window = window;
     29  this._SyncedTabs = SyncedTabs;
     30  this._DeckView = DeckView || SyncedTabsDeckView;
     31  // used to stub during tests
     32  this._getChromeWindow = getChromeWindowMock || getChromeWindow;
     33 
     34  this._deckStore = deckStore || new SyncedTabsDeckStore();
     35  this._syncedTabsListStore = listStore || new SyncedTabsListStore(SyncedTabs);
     36  this.tabListComponent =
     37    listComponent ||
     38    new TabListComponent({
     39      window: this._window,
     40      store: this._syncedTabsListStore,
     41      View: TabListView,
     42      SyncedTabs,
     43      clipboardHelper: Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
     44        Ci.nsIClipboardHelper
     45      ),
     46      getChromeWindow: this._getChromeWindow,
     47    });
     48 }
     49 
     50 SyncedTabsDeckComponent.prototype = {
     51  PANELS: {
     52    TABS_CONTAINER: "tabs-container",
     53    TABS_FETCHING: "tabs-fetching",
     54    LOGIN_FAILED: "reauth",
     55    NOT_AUTHED_INFO: "notAuthedInfo",
     56    SYNC_DISABLED: "syncDisabled",
     57    SINGLE_DEVICE_INFO: "singleDeviceInfo",
     58    TABS_DISABLED: "tabs-disabled",
     59    UNVERIFIED: "unverified",
     60  },
     61 
     62  get container() {
     63    return this._deckView ? this._deckView.container : null;
     64  },
     65 
     66  init() {
     67    Services.obs.addObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED);
     68    Services.obs.addObserver(this, UIState.ON_UPDATE);
     69 
     70    // Add app locale change support for HTML sidebar
     71    Services.obs.addObserver(this, "intl:app-locales-changed");
     72    this.updateDir();
     73 
     74    // Go ahead and trigger sync
     75    this._SyncedTabs.syncTabs().catch(console.error);
     76 
     77    this._deckView = new this._DeckView(this._window, this.tabListComponent, {
     78      onConnectDeviceClick: event => this.openConnectDevice(event),
     79      onSyncPrefClick: event => this.openSyncPrefs(event),
     80    });
     81 
     82    this._deckStore.on("change", state => this._deckView.render(state));
     83    // Trigger the initial rendering of the deck view
     84    // Object.values only in nightly
     85    this._deckStore.setPanels(
     86      Object.keys(this.PANELS).map(k => this.PANELS[k])
     87    );
     88    // Set the initial panel to display
     89    this.updatePanel();
     90    this._recordPanelToggle(true);
     91  },
     92 
     93  uninit() {
     94    Services.obs.removeObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED);
     95    Services.obs.removeObserver(this, UIState.ON_UPDATE);
     96    Services.obs.removeObserver(this, "intl:app-locales-changed");
     97    this._deckView.destroy();
     98    this._recordPanelToggle(false);
     99  },
    100 
    101  async _recordPanelToggle(opened) {
    102    const state = UIState.get();
    103    const { status } = state;
    104    Glean.syncedTabs.sidebarToggle.record({
    105      opened,
    106      synced_tabs_loaded: status === UIState.STATUS_SIGNED_IN,
    107      version: "old",
    108    });
    109  },
    110 
    111  observe(subject, topic) {
    112    switch (topic) {
    113      case this._SyncedTabs.TOPIC_TABS_CHANGED:
    114        this._syncedTabsListStore.getData();
    115        this.updatePanel();
    116        break;
    117      case UIState.ON_UPDATE:
    118        this.updatePanel();
    119        break;
    120      case "intl:app-locales-changed":
    121        this.updateDir();
    122        break;
    123      default:
    124        break;
    125    }
    126  },
    127 
    128  async getPanelStatus() {
    129    try {
    130      const state = UIState.get();
    131      const { status } = state;
    132      if (status == UIState.STATUS_NOT_CONFIGURED) {
    133        return this.PANELS.NOT_AUTHED_INFO;
    134      } else if (status == UIState.STATUS_LOGIN_FAILED) {
    135        return this.PANELS.LOGIN_FAILED;
    136      } else if (status == UIState.STATUS_NOT_VERIFIED) {
    137        return this.PANELS.UNVERIFIED;
    138      } else if (!state.syncEnabled) {
    139        return this.PANELS.SYNC_DISABLED;
    140      } else if (!this._SyncedTabs.isConfiguredToSyncTabs) {
    141        return this.PANELS.TABS_DISABLED;
    142      } else if (!this._SyncedTabs.hasSyncedThisSession) {
    143        return this.PANELS.TABS_FETCHING;
    144      }
    145      const clients = await this._SyncedTabs.getTabClients();
    146      if (clients.length) {
    147        return this.PANELS.TABS_CONTAINER;
    148      }
    149      return this.PANELS.SINGLE_DEVICE_INFO;
    150    } catch (err) {
    151      console.error(err);
    152      return this.PANELS.NOT_AUTHED_INFO;
    153    }
    154  },
    155 
    156  updateDir() {
    157    // If the HTML document doesn't exist, we can't update the window
    158    if (!this._window.document) {
    159      return;
    160    }
    161 
    162    if (Services.locale.isAppLocaleRTL) {
    163      this._window.document.body.dir = "rtl";
    164    } else {
    165      this._window.document.body.dir = "ltr";
    166    }
    167  },
    168 
    169  updatePanel() {
    170    // return promise for tests
    171    return this.getPanelStatus()
    172      .then(panelId => this._deckStore.selectPanel(panelId))
    173      .catch(console.error);
    174  },
    175 
    176  openSyncPrefs() {
    177    this._getChromeWindow(this._window).gSync.openPrefs("tabs-sidebar");
    178  },
    179 
    180  openConnectDevice() {
    181    this._getChromeWindow(this._window).gSync.openConnectAnotherDevice(
    182      "tabs-sidebar"
    183    );
    184  },
    185 };