mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Adding import from google drive to the home screen
Summary: Importing from google drive from home screen (also for anonymous users) Test Plan: Browser tests Reviewers: dsagal, paulfitz Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2943
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import {get as getBrowserGlobals} from 'app/client/lib/browserGlobals';
|
||||
import {reportError, setErrorNotifier} from 'app/client/models/errors';
|
||||
import {urlState} from 'app/client/models/gristUrlState';
|
||||
import {Notifier} from 'app/client/models/NotifyModel';
|
||||
@@ -5,12 +6,14 @@ import {getFlavor, ProductFlavor} from 'app/client/ui/CustomThemes';
|
||||
import {Features} from 'app/common/Features';
|
||||
import {GristLoadConfig} from 'app/common/gristUrls';
|
||||
import {FullUser} from 'app/common/LoginSessionAPI';
|
||||
import {LocalPlugin} from 'app/common/plugin';
|
||||
import {getOrgName, Organization, OrgError, UserAPI, UserAPIImpl} from 'app/common/UserAPI';
|
||||
import {Computed, Disposable, Observable, subscribe} from 'grainjs';
|
||||
|
||||
export {reportError} from 'app/client/models/errors';
|
||||
|
||||
export type PageType = "doc" | "home" | "billing" | "welcome";
|
||||
const G = getBrowserGlobals('document', 'window');
|
||||
|
||||
// TopAppModel is the part of the app model that persists across org and user switches.
|
||||
export interface TopAppModel {
|
||||
@@ -20,6 +23,7 @@ export interface TopAppModel {
|
||||
currentSubdomain: Observable<string|undefined>;
|
||||
|
||||
notifier: Notifier;
|
||||
plugins: LocalPlugin[];
|
||||
|
||||
// Everything else gets fully rebuilt when the org/user changes. This is to ensure that
|
||||
// different parts of the code aren't using different users/orgs while the switch is pending.
|
||||
@@ -30,6 +34,11 @@ export interface TopAppModel {
|
||||
|
||||
// Rebuilds the AppModel and consequently the AppUI, without changing the user or the org.
|
||||
reload(): void;
|
||||
|
||||
/**
|
||||
* Returns the UntrustedContentOrigin use settings. Throws if not defined.
|
||||
*/
|
||||
getUntrustedContentOrigin(): string;
|
||||
}
|
||||
|
||||
// AppModel is specific to the currently loaded organization and active user. It gets rebuilt when
|
||||
@@ -59,6 +68,8 @@ export class TopAppModelImpl extends Disposable implements TopAppModel {
|
||||
public readonly currentSubdomain = Computed.create(this, urlState().state, (use, s) => s.org);
|
||||
public readonly notifier = Notifier.create(this);
|
||||
public readonly appObs = Observable.create<AppModel|null>(this, null);
|
||||
public readonly plugins: LocalPlugin[] = [];
|
||||
private readonly _gristConfig?: GristLoadConfig;
|
||||
|
||||
constructor(
|
||||
window: {gristConfig?: GristLoadConfig},
|
||||
@@ -68,10 +79,12 @@ export class TopAppModelImpl extends Disposable implements TopAppModel {
|
||||
setErrorNotifier(this.notifier);
|
||||
this.isSingleOrg = Boolean(window.gristConfig && window.gristConfig.singleOrg);
|
||||
this.productFlavor = getFlavor(window.gristConfig && window.gristConfig.org);
|
||||
this._gristConfig = window.gristConfig;
|
||||
|
||||
// Initially, and on any change to subdomain, call initialize() to get the full Organization
|
||||
// and the FullUser to use for it (the user may change when switching orgs).
|
||||
this.autoDispose(subscribe(this.currentSubdomain, (use) => this.initialize()));
|
||||
this.plugins = this._gristConfig?.plugins || [];
|
||||
}
|
||||
|
||||
public initialize(): void {
|
||||
@@ -87,6 +100,23 @@ export class TopAppModelImpl extends Disposable implements TopAppModel {
|
||||
}
|
||||
}
|
||||
|
||||
public getUntrustedContentOrigin() {
|
||||
if (G.window.isRunningUnderElectron) {
|
||||
// when loaded within webviews it is safe to serve plugin's content from the same domain
|
||||
return "";
|
||||
}
|
||||
|
||||
const origin = this._gristConfig?.pluginUrl;
|
||||
if (!origin) {
|
||||
throw new Error("Missing untrustedContentOrigin configuration");
|
||||
}
|
||||
if (origin.match(/:[0-9]+$/)) {
|
||||
// Port number already specified, no need to add.
|
||||
return origin;
|
||||
}
|
||||
return origin + ":" + G.window.location.port;
|
||||
}
|
||||
|
||||
private async _doInitialize() {
|
||||
this.appObs.set(null);
|
||||
try {
|
||||
|
||||
@@ -10,8 +10,8 @@ import {cssLeftPanel, cssScrollPane} from 'app/client/ui/LeftPanelCommon';
|
||||
import {buildPagesDom} from 'app/client/ui/Pages';
|
||||
import {openPageWidgetPicker} from 'app/client/ui/PageWidgetPicker';
|
||||
import {tools} from 'app/client/ui/Tools';
|
||||
import {testId} from 'app/client/ui2018/cssVars';
|
||||
import {bigBasicButton} from 'app/client/ui2018/buttons';
|
||||
import {testId} from 'app/client/ui2018/cssVars';
|
||||
import {menu, menuDivider, menuIcon, menuItem, menuText} from 'app/client/ui2018/menus';
|
||||
import {confirmModal} from 'app/client/ui2018/modals';
|
||||
import {AsyncFlow, CancelledError, FlowRunner} from 'app/common/AsyncFlow';
|
||||
@@ -21,8 +21,8 @@ import {IGristUrlState, parseUrlId, UrlIdParts} from 'app/common/gristUrls';
|
||||
import {getReconnectTimeout} from 'app/common/gutil';
|
||||
import {canEdit} from 'app/common/roles';
|
||||
import {Document, NEW_DOCUMENT_CODE, Organization, UserAPI, Workspace} from 'app/common/UserAPI';
|
||||
import {Computed, Disposable, dom, DomArg, DomElementArg} from 'grainjs';
|
||||
import {Holder, Observable, subscribe} from 'grainjs';
|
||||
import {Computed, Disposable, dom, DomArg, DomElementArg} from 'grainjs';
|
||||
|
||||
// tslint:disable:no-console
|
||||
|
||||
@@ -271,7 +271,7 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
|
||||
await this._api.getDocAPI(urlId).compareDoc(comparisonUrlId, { detail: true }) : undefined;
|
||||
|
||||
const gristDoc = gdModule.GristDoc.create(flow, this._appObj, docComm, this, openDocResponse,
|
||||
{comparison});
|
||||
this.appModel.topAppModel.plugins, {comparison});
|
||||
|
||||
// Move ownership of docComm to GristDoc.
|
||||
gristDoc.autoDispose(flow.release(docComm));
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import {ClientScope} from 'app/client/components/ClientScope';
|
||||
import {guessTimezone} from 'app/client/lib/guessTimezone';
|
||||
import {HomePluginManager} from 'app/client/lib/HomePluginManager';
|
||||
import {ImportSourceElement} from 'app/client/lib/ImportSourceElement';
|
||||
import {localStorageObs} from 'app/client/lib/localStorageObs';
|
||||
import {AppModel, reportError} from 'app/client/models/AppModel';
|
||||
import {UserError} from 'app/client/models/errors';
|
||||
@@ -10,9 +13,9 @@ import {SortPref, UserOrgPrefs, ViewPref} from 'app/common/Prefs';
|
||||
import * as roles from 'app/common/roles';
|
||||
import {Document, Workspace} from 'app/common/UserAPI';
|
||||
import {bundleChanges, Computed, Disposable, Observable, subscribe} from 'grainjs';
|
||||
import * as moment from 'moment';
|
||||
import flatten = require('lodash/flatten');
|
||||
import sortBy = require('lodash/sortBy');
|
||||
import * as moment from 'moment';
|
||||
|
||||
const DELAY_BEFORE_SPINNER_MS = 500;
|
||||
|
||||
@@ -62,6 +65,7 @@ export interface HomeModel {
|
||||
|
||||
currentSort: Observable<SortPref>;
|
||||
currentView: Observable<ViewPref>;
|
||||
importSources: Observable<ImportSourceElement[]>;
|
||||
|
||||
// The workspace for new docs, or "unsaved" to only allow unsaved-doc creation, or null if the
|
||||
// user isn't allowed to create a doc.
|
||||
@@ -96,6 +100,7 @@ export class HomeModelImpl extends Disposable implements HomeModel, ViewSettings
|
||||
public readonly singleWorkspace = Observable.create(this, true);
|
||||
public readonly trashWorkspaces = Observable.create<Workspace[]>(this, []);
|
||||
public readonly templateWorkspaces = Observable.create<Workspace[]>(this, []);
|
||||
public readonly importSources = Observable.create<ImportSourceElement[]>(this, []);
|
||||
|
||||
// Get the workspace details for the workspace with id of currentWSId.
|
||||
public readonly currentWS = Computed.create(this, (use) =>
|
||||
@@ -133,7 +138,7 @@ export class HomeModelImpl extends Disposable implements HomeModel, ViewSettings
|
||||
|
||||
private _userOrgPrefs = Observable.create<UserOrgPrefs|undefined>(this, this._app.currentOrg?.userOrgPrefs);
|
||||
|
||||
constructor(private _app: AppModel) {
|
||||
constructor(private _app: AppModel, clientScope: ClientScope) {
|
||||
super();
|
||||
|
||||
if (!this.app.currentValidUser) {
|
||||
@@ -155,6 +160,14 @@ export class HomeModelImpl extends Disposable implements HomeModel, ViewSettings
|
||||
|
||||
this.autoDispose(subscribe(this.currentPage, this.currentWSId, (use) =>
|
||||
this._updateWorkspaces().catch(reportError)));
|
||||
|
||||
// Defer home plugin initialization
|
||||
const pluginManager = new HomePluginManager(
|
||||
_app.topAppModel.plugins,
|
||||
_app.topAppModel.getUntrustedContentOrigin()!,
|
||||
clientScope);
|
||||
const importSources = ImportSourceElement.fromArray(pluginManager.pluginsList);
|
||||
this.importSources.set(importSources);
|
||||
}
|
||||
|
||||
// Accessor for the AppModel containing this HomeModel.
|
||||
|
||||
Reference in New Issue
Block a user