bundling experiments (WIP)

Looking at ways to bundle custom widgets with Grist. WIP,
experimental, everything will need rewrite.
This commit is contained in:
Paul Fitzpatrick
2023-10-03 15:20:57 -04:00
parent 97a84ce6ee
commit fd1734de69
22 changed files with 734 additions and 155 deletions

View File

@@ -27,6 +27,8 @@ import {getOrgName, isTemplatesOrg, Organization, OrgError, UserAPI, UserAPIImpl
import {getUserPrefObs, getUserPrefsObs, markAsSeen, markAsUnSeen} from 'app/client/models/UserPrefs';
import {bundleChanges, Computed, Disposable, Observable, subscribe} from 'grainjs';
import isEqual from 'lodash/isEqual';
import { ICustomWidget } from 'app/common/CustomWidget';
import { AsyncCreate } from 'app/common/AsyncCreate';
const t = makeT('AppModel');
@@ -75,6 +77,8 @@ export interface TopAppModel {
* Reloads orgs and accounts for current user.
*/
fetchUsersAndOrgs(): Promise<void>;
getWidgets(): Promise<ICustomWidget[]>;
}
/**
@@ -143,6 +147,7 @@ export class TopAppModelImpl extends Disposable implements TopAppModel {
public readonly users = Observable.create<FullUser[]>(this, []);
public readonly plugins: LocalPlugin[] = [];
private readonly _gristConfig?: GristLoadConfig;
private readonly _widgets: AsyncCreate<ICustomWidget[]>;
constructor(
window: {gristConfig?: GristLoadConfig},
@@ -153,6 +158,7 @@ export class TopAppModelImpl extends Disposable implements TopAppModel {
this.isSingleOrg = Boolean(window.gristConfig && window.gristConfig.singleOrg);
this.productFlavor = getFlavor(window.gristConfig && window.gristConfig.org);
this._gristConfig = window.gristConfig;
this._widgets = new AsyncCreate<ICustomWidget[]>(() => this.api.getWidgets());
// 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).
@@ -175,6 +181,10 @@ export class TopAppModelImpl extends Disposable implements TopAppModel {
}
}
public async getWidgets(): Promise<ICustomWidget[]> {
return this._widgets.get();
}
public getUntrustedContentOrigin() {
if (G.window.isRunningUnderElectron) {
// when loaded within webviews it is safe to serve plugin's content from the same domain

View File

@@ -21,7 +21,7 @@ import {removeRule, RuleOwner} from 'app/client/models/RuleOwner';
import {LinkConfig} from 'app/client/ui/selectBy';
import {getWidgetTypes} from "app/client/ui/widgetTypesMap";
import {FilterColValues} from "app/common/ActiveDocAPI";
import {AccessLevel, ICustomWidget} from 'app/common/CustomWidget';
import {AccessLevel} from 'app/common/CustomWidget';
import {UserAction} from 'app/common/DocActions';
import {arrayRepeat} from 'app/common/gutil';
import {Sort} from 'app/common/SortSpec';
@@ -245,10 +245,19 @@ export interface CustomViewSectionDef {
* The url.
*/
url: modelUtil.KoSaveableObservable<string|null>;
/**
* A widgetId, if available. Preferred to url.
* For bundled custom widgets, it is important to refer
* to them by something other than url, since url will
* vary with deployment, and it should be possible to move
* documents between deployments if they have compatible
* widgets available.
*/
widgetId: modelUtil.KoSaveableObservable<string|null>;
/**
* Custom widget information.
*/
widgetDef: modelUtil.KoSaveableObservable<ICustomWidget|null>;
// widgetDef: modelUtil.KoSaveableObservable<ICustomWidget|null>;
/**
* Custom widget options.
*/
@@ -324,7 +333,7 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel):
const customViewDefaults = {
mode: 'url',
url: null,
widgetDef: null,
// widgetDef: null,
access: '',
pluginId: '',
sectionId: '',
@@ -336,7 +345,8 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel):
this.customDef = {
mode: customDefObj.prop('mode'),
url: customDefObj.prop('url'),
widgetDef: customDefObj.prop('widgetDef'),
widgetId: customDefObj.prop('widgetId'),
// widgetDef: customDefObj.prop('widgetDef'),
widgetOptions: customDefObj.prop('widgetOptions'),
columnsMapping: customDefObj.prop('columnsMapping'),
access: customDefObj.prop('access'),