diff --git a/app/client/models/UserManagerModel.ts b/app/client/models/UserManagerModel.ts index 36dc8acc..847f4c4f 100644 --- a/app/client/models/UserManagerModel.ts +++ b/app/client/models/UserManagerModel.ts @@ -6,6 +6,7 @@ import {ShareAnnotations, ShareAnnotator} from 'app/common/ShareAnnotator'; import {normalizeEmail} from 'app/common/emails'; import {GristLoadConfig} from 'app/common/gristUrls'; import * as roles from 'app/common/roles'; +import {getGristConfig} from 'app/common/urlUtils'; import {ANONYMOUS_USER_EMAIL, Document, EVERYONE_EMAIL, FullUser, getRealAccess, Organization, PermissionData, PermissionDelta, UserAPI, Workspace} from 'app/common/UserAPI'; import {computed, Computed, Disposable, obsArray, ObsArray, observable, Observable} from 'grainjs'; @@ -179,7 +180,8 @@ export class UserManagerModelImpl extends Disposable implements UserManagerModel super(); if (this._options.appModel) { const product = this._options.appModel.currentProduct; - this._shareAnnotator = new ShareAnnotator(product, initData); + const {supportEmail} = getGristConfig(); + this._shareAnnotator = new ShareAnnotator(product, initData, {supportEmail}); } this.annotate(); } diff --git a/app/common/ShareAnnotator.ts b/app/common/ShareAnnotator.ts index 720b344f..cf5fc329 100644 --- a/app/common/ShareAnnotator.ts +++ b/app/common/ShareAnnotator.ts @@ -28,14 +28,23 @@ export interface ShareAnnotations { users: Map; // Annotations keyed by normalized user email. } +export interface ShareAnnotatorOptions { + supportEmail?: string; // Known email address of the support user (e.g. support@getgrist.com). +} + /** * Helper for annotating users mentioned in a proposed change of shares, given the * current shares in place. */ export class ShareAnnotator { private _features = this._product?.features ?? {}; + private _supportEmail = this._options.supportEmail; - constructor(private _product: Product|null, private _state: PermissionData) { + constructor( + private _product: Product|null, + private _state: PermissionData, + private _options: ShareAnnotatorOptions = {} + ) { } public updateState(state: PermissionData) { @@ -84,7 +93,10 @@ export class ShareAnnotator { .map(([k, ]) => normalizeEmail(k))); for (const email of tweaks) { const annotation = annotations.users.get(email) || makeAnnotation({ - email, isMember: false, access: '', + email, + isMember: false, + isSupport: Boolean(email.trim() !== '' && email === this._supportEmail), + access: '', }); annotations.users.set(email, annotation); } diff --git a/app/common/gristUrls.ts b/app/common/gristUrls.ts index 49450133..19e3199f 100644 --- a/app/common/gristUrls.ts +++ b/app/common/gristUrls.ts @@ -493,7 +493,7 @@ export interface GristLoadConfig { // In single-org mode, this is the single well-known org. Suppress any org selection UI. singleOrg?: string; - + // Url for support for the browser client to use. helpCenterUrl?: string; @@ -578,6 +578,9 @@ export interface GristLoadConfig { // TODO: remove when comments will be released. featureComments?: boolean; + + // Email address of the support user. + supportEmail?: string; } export const HideableUiElements = StringUnion("helpCenter", "billing", "templates", "multiSite", "multiAccounts"); diff --git a/app/server/lib/sendAppPage.ts b/app/server/lib/sendAppPage.ts index 3a1bd0d0..28a300b2 100644 --- a/app/server/lib/sendAppPage.ts +++ b/app/server/lib/sendAppPage.ts @@ -1,6 +1,7 @@ import {getPageTitleSuffix, GristLoadConfig, HideableUiElements, IHideableUiElement} from 'app/common/gristUrls'; import {getTagManagerSnippet} from 'app/common/tagManager'; import {Document} from 'app/common/UserAPI'; +import {SUPPORT_EMAIL} from 'app/gen-server/lib/HomeDBManager'; import {isAnonymousUser, RequestWithLogin} from 'app/server/lib/Authorizer'; import {RequestWithOrg} from 'app/server/lib/extractOrg'; import {GristServer} from 'app/server/lib/GristServer'; @@ -63,6 +64,7 @@ export function makeGristConfig(homeUrl: string|null, extra: Partial