(core) deleting queue from single webhook

Summary: Using standard tost notification, message about webhook queue being overflown was added. message is permanent as long as queue is full. Message contains linkt to the webhook setings

Test Plan: two nbrowser test was added - one to check if message is show when queue is full, and second to check if message is dismiss when queue was cleaned.

Reviewers: georgegevoian

Reviewed By: georgegevoian

Subscribers: jarek

Differential Revision: https://phab.getgrist.com/D3929
This commit is contained in:
Jakub Serafin
2023-07-18 09:24:10 +02:00
parent 450472f74c
commit d894b60fd4
14 changed files with 475 additions and 151 deletions

View File

@@ -49,7 +49,7 @@ import {toKo} from 'grainjs';
// Re-export all the entity types available. The recommended usage is like this:
// import {ColumnRec, ViewFieldRec} from 'app/client/models/DocModel';
export type {ColumnRec, DocInfoRec, FilterRec, PageRec, TabBarRec, TableRec, ValidationRec,
ViewFieldRec, ViewRec, ViewSectionRec, CellRec};
ViewFieldRec, ViewRec, ViewSectionRec, CellRec};
/**
* Creates the type for a MetaRowModel containing a KoSaveableObservable for each field listed in

View File

@@ -117,20 +117,20 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
public readonly currentWorkspace = Computed.create(this, this.currentDoc, (use, doc) => doc && doc.workspace);
public readonly currentOrg = Computed.create(this, this.currentWorkspace, (use, ws) => ws && ws.org);
public readonly currentOrgName = Computed.create(this, this.currentOrg,
(use, org) => getOrgNameOrGuest(org, this.appModel.currentUser));
(use, org) => getOrgNameOrGuest(org, this.appModel.currentUser));
public readonly currentDocTitle = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.name : '');
public readonly isReadonly = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isReadonly : false);
public readonly isPrefork = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isPreFork : false);
public readonly isFork = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isFork : false);
public readonly isRecoveryMode = Computed.create(this, this.currentDoc,
(use, doc) => doc ? doc.isRecoveryMode : false);
(use, doc) => doc ? doc.isRecoveryMode : false);
public readonly userOverride = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.userOverride : null);
public readonly isBareFork = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isBareFork : false);
public readonly isSnapshot = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isSnapshot : false);
public readonly isTutorialTrunk = Computed.create(this, this.currentDoc,
(use, doc) => doc ? doc.isTutorialTrunk : false);
(use, doc) => doc ? doc.isTutorialTrunk : false);
public readonly isTutorialFork = Computed.create(this, this.currentDoc,
(use, doc) => doc ? doc.isTutorialFork : false);
(use, doc) => doc ? doc.isTutorialFork : false);
public readonly importSources: ImportSource[] = [];
@@ -169,7 +169,7 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
FlowRunner.create(this._openerHolder,
(flow: AsyncFlow) => this._openDoc(flow, urlId, urlOpenMode, state.params?.compare, linkParameters)
)
.resultPromise.catch(err => this._onOpenError(err));
.resultPromise.catch(err => this._onOpenError(err));
}
}
}));
@@ -266,7 +266,7 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
explanation: (
isDocOwner
? t("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. \
Recovery mode opens the document to be fully accessible to owners, and inaccessible to others. \
It also disables formulas. [{{error}}]", {error: err.message})
: isDenied
? t('Sorry, access to this document has been denied. [{{error}}]', {error: err.message})
@@ -301,7 +301,7 @@ It also disables formulas. [{{error}}]", {error: err.message})
comparisonUrlId: string | undefined,
linkParameters: Record<string, string> | undefined): Promise<void> {
console.log(`DocPageModel _openDoc starting for ${urlId} (mode ${urlOpenMode})` +
(comparisonUrlId ? ` (compare ${comparisonUrlId})` : ''));
(comparisonUrlId ? ` (compare ${comparisonUrlId})` : ''));
const gristDocModulePromise = loadGristDoc();
const docResponse = await retryOnNetworkError(flow, getDoc.bind(null, this._api, urlId));
@@ -375,7 +375,7 @@ It also disables formulas. [{{error}}]", {error: err.message})
await this._api.getDocAPI(urlId).compareDoc(comparisonUrlId, { detail: true }) : undefined;
const gristDoc = gdModule.GristDoc.create(flow, this._appObj, this.appModel, docComm, this, openDocResponse,
this.appModel.topAppModel.plugins, {comparison});
this.appModel.topAppModel.plugins, {comparison});
// Move ownership of docComm to GristDoc.
gristDoc.autoDispose(flow.release(docComm));
@@ -399,13 +399,13 @@ function addMenu(importSources: ImportSource[], gristDoc: GristDoc, isReadonly:
return [
menuItem(
(elem) => openPageWidgetPicker(elem, gristDoc, (val) => gristDoc.addNewPage(val).catch(reportError),
{isNewPage: true, buttonLabel: 'Add Page'}),
{isNewPage: true, buttonLabel: 'Add Page'}),
menuIcon("Page"), t("Add Page"), testId('dp-add-new-page'),
dom.cls('disabled', isReadonly)
),
menuItem(
(elem) => openPageWidgetPicker(elem, gristDoc, (val) => gristDoc.addWidgetToPage(val).catch(reportError),
{isNewPage: false, selectBy}),
{isNewPage: false, selectBy}),
menuIcon("Widget"), t("Add Widget to Page"), testId('dp-add-widget-to-page'),
// disable for readonly doc and all special views
dom.cls('disabled', (use) => typeof use(gristDoc.activeViewId) !== 'number' || isReadonly),

View File

@@ -1,14 +1,25 @@
import * as log from 'app/client/lib/log';
import {ConnectState, ConnectStateManager} from 'app/client/models/ConnectState';
import {isNarrowScreenObs, testId} from 'app/client/ui2018/cssVars';
import {delay} from 'app/common/delay';
import {isLongerThan} from 'app/common/gutil';
import {InactivityTimer} from 'app/common/InactivityTimer';
import {timeFormat} from 'app/common/timeFormat';
import {bundleChanges, Disposable, Holder, IDisposable, IDisposableOwner } from 'grainjs';
import {Computed, dom, DomElementArg, MutableObsArray, obsArray, Observable} from 'grainjs';
import {
bundleChanges,
Computed,
Disposable,
dom,
DomElementArg,
Holder,
IDisposable,
IDisposableOwner,
MutableObsArray,
obsArray,
Observable
} from 'grainjs';
import clamp = require('lodash/clamp');
import defaults = require('lodash/defaults');
import {isNarrowScreenObs, testId} from 'app/client/ui2018/cssVars';
// When rendering app errors, we'll only show the last few.
const maxAppErrors = 5;
@@ -79,9 +90,11 @@ export class Expirable extends Disposable {
/**
* Sets status to 'expiring', then calls dispose after a short delay.
*/
public async expire(): Promise<void> {
public async expire(withoutDelay: boolean = false): Promise<void> {
this.status.set('expiring');
await delay(Expirable.fadeDelay);
if(!withoutDelay) {
await delay(Expirable.fadeDelay);
}
if (!this.isDisposed()) {
this.dispose();
}
@@ -339,7 +352,7 @@ export class Notifier extends Disposable implements INotifier {
if (key) {
const prev = this._keyedItems.get(key);
if (prev) {
await prev.expire();
await prev.expire(true);
}
this._keyedItems.set(key, n);
n.onDispose(() => this.isDisposed() || this._keyedItems.delete(key));