(core) Show usage banners in doc menu of free team sites

Summary:
Also fixes a minor CSS regression in UserManager where the
link to add a team member wasn't shown on a separate row.

Test Plan: Browser tests.

Reviewers: jarek

Reviewed By: jarek

Differential Revision: https://phab.getgrist.com/D3444
This commit is contained in:
George Gevoian
2022-05-25 23:47:26 -07:00
parent 2f3cf59fc3
commit 74ec9358da
16 changed files with 341 additions and 213 deletions

View File

@@ -20,7 +20,7 @@ import {OpenDocMode, UserOverride} from 'app/common/DocListAPI';
import {FilteredDocUsageSummary} from 'app/common/DocUsage';
import {IGristUrlState, parseUrlId, UrlIdParts} from 'app/common/gristUrls';
import {getReconnectTimeout} from 'app/common/gutil';
import {canEdit} from 'app/common/roles';
import {canEdit, isOwner} from 'app/common/roles';
import {Document, NEW_DOCUMENT_CODE, Organization, UserAPI, Workspace} from 'app/common/UserAPI';
import {Holder, Observable, subscribe} from 'grainjs';
import {Computed, Disposable, dom, DomArg, DomElementArg} from 'grainjs';
@@ -210,19 +210,19 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
public offerRecovery(err: Error) {
const isDenied = (err as any).code === 'ACL_DENY';
const isOwner = this.currentDoc.get()?.access === 'owners';
const isDocOwner = isOwner(this.currentDoc.get());
confirmModal(
"Error accessing document",
"Reload",
async () => window.location.reload(true),
isOwner ? `You can try reloading the document, or using recovery mode. ` +
isDocOwner ? `You can try reloading the document, or using recovery mode. ` +
`Recovery mode opens the document to be fully accessible to owners, and ` +
`inaccessible to others. It also disables formulas. ` +
`[${err.message}]` :
isDenied ? `Sorry, access to this document has been denied. [${err.message}]` :
`Document owners can attempt to recover the document. [${err.message}]`,
{ hideCancel: true,
extraButtons: (isOwner && !isDenied) ? bigBasicButton('Enter recovery mode', dom.on('click', async () => {
extraButtons: (isDocOwner && !isDenied) ? bigBasicButton('Enter recovery mode', dom.on('click', async () => {
await this._api.getDocAPI(this.currentDocId.get()!).recover(true);
window.location.reload(true);
}), testId('modal-recovery-mode')) : null,

View File

@@ -7,6 +7,7 @@ import {AppModel, reportError} from 'app/client/models/AppModel';
import {reportMessage, UserError} from 'app/client/models/errors';
import {urlState} from 'app/client/models/gristUrlState';
import {ownerName} from 'app/client/models/WorkspaceInfo';
import {OrgUsageSummary} from 'app/common/DocUsage';
import {IHomePage} from 'app/common/gristUrls';
import {isLongerThan} from 'app/common/gutil';
import {SortPref, UserOrgPrefs, ViewPref} from 'app/common/Prefs';
@@ -75,6 +76,8 @@ export interface HomeModel {
// user isn't allowed to create a doc.
newDocWorkspace: Observable<Workspace|null|"unsaved">;
currentOrgUsage: Observable<OrgUsageSummary|null>;
createWorkspace(name: string): Promise<void>;
renameWorkspace(id: number, name: string): Promise<void>;
deleteWorkspace(id: number, forever: boolean): Promise<void>;
@@ -155,6 +158,8 @@ export class HomeModelImpl extends Disposable implements HomeModel, ViewSettings
wss.every((ws) => ws.isSupportWorkspace || ws.docs.length === 0) &&
Boolean(use(this.newDocWorkspace))));
public readonly currentOrgUsage: Observable<OrgUsageSummary|null> = Observable.create(this, null);
private _userOrgPrefs = Observable.create<UserOrgPrefs|undefined>(this, this._app.currentOrg?.userOrgPrefs);
constructor(private _app: AppModel, clientScope: ClientScope) {
@@ -187,6 +192,8 @@ export class HomeModelImpl extends Disposable implements HomeModel, ViewSettings
clientScope);
const importSources = ImportSourceElement.fromArray(pluginManager.pluginsList);
this.importSources.set(importSources);
this._updateCurrentOrgUsage().catch(reportError);
}
// Accessor for the AppModel containing this HomeModel.
@@ -379,6 +386,14 @@ export class HomeModelImpl extends Disposable implements HomeModel, ViewSettings
await this._app.api.updateOrg('current', {userOrgPrefs: org.userOrgPrefs});
}
}
private async _updateCurrentOrgUsage() {
const currentOrg = this.app.currentOrg;
if (!roles.isOwner(currentOrg)) { return; }
const api = this.app.api;
this.currentOrgUsage.set(await api.getOrgUsageSummary(currentOrg.id));
}
}
// Check if active product allows just a single workspace.