Implement dialog for resending local echo transactions
This commit is contained in:
parent
14b0def143
commit
c5574219bb
11 changed files with 289 additions and 9 deletions
|
@ -76,6 +76,7 @@
|
||||||
@import "./views/dialogs/_RoomSettingsDialogBridges.scss";
|
@import "./views/dialogs/_RoomSettingsDialogBridges.scss";
|
||||||
@import "./views/dialogs/_RoomUpgradeDialog.scss";
|
@import "./views/dialogs/_RoomUpgradeDialog.scss";
|
||||||
@import "./views/dialogs/_RoomUpgradeWarningDialog.scss";
|
@import "./views/dialogs/_RoomUpgradeWarningDialog.scss";
|
||||||
|
@import "./views/dialogs/_ServerOfflineDialog.scss";
|
||||||
@import "./views/dialogs/_SetEmailDialog.scss";
|
@import "./views/dialogs/_SetEmailDialog.scss";
|
||||||
@import "./views/dialogs/_SetMxIdDialog.scss";
|
@import "./views/dialogs/_SetMxIdDialog.scss";
|
||||||
@import "./views/dialogs/_SetPasswordDialog.scss";
|
@import "./views/dialogs/_SetPasswordDialog.scss";
|
||||||
|
@ -216,6 +217,7 @@
|
||||||
@import "./views/settings/tabs/user/_SecurityUserSettingsTab.scss";
|
@import "./views/settings/tabs/user/_SecurityUserSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/user/_VoiceUserSettingsTab.scss";
|
@import "./views/settings/tabs/user/_VoiceUserSettingsTab.scss";
|
||||||
@import "./views/terms/_InlineTermsAgreement.scss";
|
@import "./views/terms/_InlineTermsAgreement.scss";
|
||||||
|
@import "./views/toasts/_NonUrgentEchoFailureToast.scss";
|
||||||
@import "./views/verification/_VerificationShowSas.scss";
|
@import "./views/verification/_VerificationShowSas.scss";
|
||||||
@import "./views/voip/_CallContainer.scss";
|
@import "./views/voip/_CallContainer.scss";
|
||||||
@import "./views/voip/_CallView.scss";
|
@import "./views/voip/_CallView.scss";
|
||||||
|
|
72
res/css/views/dialogs/_ServerOfflineDialog.scss
Normal file
72
res/css/views/dialogs/_ServerOfflineDialog.scss
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_ServerOfflineDialog {
|
||||||
|
.mx_ServerOfflineDialog_content {
|
||||||
|
padding-right: 85px;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border-color: $primary-fg-color;
|
||||||
|
opacity: 0.1;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
li:nth-child(n + 2) {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ServerOfflineDialog_content_context {
|
||||||
|
.mx_ServerOfflineDialog_content_context_timestamp {
|
||||||
|
display: inline-block;
|
||||||
|
width: 115px;
|
||||||
|
color: $muted-fg-color;
|
||||||
|
line-height: 24px; // same as avatar
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ServerOfflineDialog_content_context_timeline {
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(100% - 155px); // 115px timestamp width + 40px right margin
|
||||||
|
|
||||||
|
.mx_ServerOfflineDialog_content_context_timeline_header {
|
||||||
|
span {
|
||||||
|
margin-left: 8px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ServerOfflineDialog_content_context_txn {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
.mx_ServerOfflineDialog_content_context_txn_desc {
|
||||||
|
width: calc(100% - 100px); // 100px is an arbitrary margin for the button
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton {
|
||||||
|
float: right;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
res/css/views/toasts/_NonUrgentEchoFailureToast.scss
Normal file
37
res/css/views/toasts/_NonUrgentEchoFailureToast.scss
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_NonUrgentEchoFailureToast {
|
||||||
|
.mx_NonUrgentEchoFailureToast_icon {
|
||||||
|
display: inline-block;
|
||||||
|
width: $font-18px;
|
||||||
|
height: $font-18px;
|
||||||
|
mask-position: center;
|
||||||
|
mask-size: contain;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
background-color: #fff; // we know that non-urgent toasts are always styled the same
|
||||||
|
mask-image: url('$(res)/img/element-icons/cloud-off.svg');
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span { // includes the i18n block
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
3
res/img/element-icons/cloud-off.svg
Normal file
3
res/img/element-icons/cloud-off.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.53033 0.46967C1.23744 0.176777 0.762563 0.176777 0.46967 0.46967C0.176777 0.762563 0.176777 1.23744 0.46967 1.53033L4.3982 5.45886C3.81109 6.13809 3.38896 7.01315 3.21555 7.99387C1.96379 8.20624 1 9.465 1 10.981C1 12.6455 2.16209 14 3.59014 14H12.9393L16.4697 17.5303C16.7626 17.8232 17.2374 17.8232 17.5303 17.5303C17.8232 17.2374 17.8232 16.7626 17.5303 16.4697L1.53033 0.46967ZM17 10.9817C16.998 11.8303 16.6946 12.5985 16.2081 13.1475L7.07635 4.01569C7.18805 4.00529 7.30083 4 7.41451 4C8.75982 4 9.99711 4.71787 10.8072 5.94503C11.0993 5.85476 11.4011 5.80939 11.7058 5.80939C13.0303 5.80939 14.2138 6.65743 14.8199 8.00337C16.0519 8.23522 17 9.48685 17 10.9817Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 839 B |
122
src/components/views/dialogs/ServerOfflineDialog.tsx
Normal file
122
src/components/views/dialogs/ServerOfflineDialog.tsx
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import BaseDialog from './BaseDialog';
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
import { EchoStore } from "../../../stores/local-echo/EchoStore";
|
||||||
|
import { formatTime } from "../../../DateUtils";
|
||||||
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
import { RoomEchoContext } from "../../../stores/local-echo/RoomEchoContext";
|
||||||
|
import RoomAvatar from "../avatars/RoomAvatar";
|
||||||
|
import { TransactionStatus } from "../../../stores/local-echo/EchoTransaction";
|
||||||
|
import Spinner from "../elements/Spinner";
|
||||||
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
onFinished: (bool) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ServerOfflineDialog extends React.PureComponent<IProps> {
|
||||||
|
public componentDidMount() {
|
||||||
|
EchoStore.instance.on(UPDATE_EVENT, this.onEchosUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
EchoStore.instance.off(UPDATE_EVENT, this.onEchosUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onEchosUpdated = () => {
|
||||||
|
this.forceUpdate(); // no state to worry about
|
||||||
|
};
|
||||||
|
|
||||||
|
private renderTimeline(): React.ReactElement[] {
|
||||||
|
return EchoStore.instance.contexts.map((c, i) => {
|
||||||
|
if (!c.firstFailedTime) return null; // not useful
|
||||||
|
if (!(c instanceof RoomEchoContext)) throw new Error("Cannot render unknown context: " + c);
|
||||||
|
const header = (
|
||||||
|
<div className="mx_ServerOfflineDialog_content_context_timeline_header">
|
||||||
|
<RoomAvatar width={24} height={24} room={c.room} />
|
||||||
|
<span>{c.room.name}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const entries = c.transactions
|
||||||
|
.filter(t => t.status === TransactionStatus.DoneError || t.didPreviouslyFail)
|
||||||
|
.map((t, j) => {
|
||||||
|
let button = <Spinner w={19} h={19} />;
|
||||||
|
if (t.status === TransactionStatus.DoneError) {
|
||||||
|
button = (
|
||||||
|
<AccessibleButton kind="link" onClick={() => t.run()}>{_t("Resend")}</AccessibleButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="mx_ServerOfflineDialog_content_context_txn" key={`txn-${j}`}>
|
||||||
|
<span className="mx_ServerOfflineDialog_content_context_txn_desc">
|
||||||
|
{t.auditName}
|
||||||
|
</span>
|
||||||
|
{button}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div className="mx_ServerOfflineDialog_content_context" key={`context-${i}`}>
|
||||||
|
<div className="mx_ServerOfflineDialog_content_context_timestamp">
|
||||||
|
{formatTime(c.firstFailedTime, SettingsStore.getValue("showTwelveHourTimestamps"))}
|
||||||
|
</div>
|
||||||
|
<div className="mx_ServerOfflineDialog_content_context_timeline">
|
||||||
|
{header}
|
||||||
|
{entries}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
let timeline = this.renderTimeline().filter(c => !!c); // remove nulls for next check
|
||||||
|
if (timeline.length === 0) {
|
||||||
|
timeline = [<div key={1}>{_t("You're all caught up.")}</div>];
|
||||||
|
}
|
||||||
|
|
||||||
|
return <BaseDialog title={_t("Server isn't responding")}
|
||||||
|
className='mx_ServerOfflineDialog'
|
||||||
|
contentId='mx_Dialog_content'
|
||||||
|
onFinished={this.props.onFinished}
|
||||||
|
hasCancel={true}
|
||||||
|
>
|
||||||
|
<div className="mx_ServerOfflineDialog_content">
|
||||||
|
<p>{_t(
|
||||||
|
"Your server isn't responding to some of your requests for some reason. " +
|
||||||
|
"Below are some possible reasons why this happened.",
|
||||||
|
)}</p>
|
||||||
|
<ul>
|
||||||
|
<li>{_t("The server took too long to respond.")}</li>
|
||||||
|
<li>{_t("Your firewall or anti-virus is blocking the request.")}</li>
|
||||||
|
<li>{_t("A browser extension is preventing the request.")}</li>
|
||||||
|
<li>{_t("The server is offline.")}</li>
|
||||||
|
<li>{_t("The server has denied your request.")}</li>
|
||||||
|
<li>{_t("Your area is experiencing difficulties connecting to the internet.")}</li>
|
||||||
|
<li>{_t("A connection error occurred while trying to contact the server.")}</li>
|
||||||
|
<li>{_t("The server is not configured to indicate what the problem is (CORS).")}</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2>{_t("Recent changes that have not yet been received")}</h2>
|
||||||
|
{timeline}
|
||||||
|
</div>
|
||||||
|
</BaseDialog>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,13 +16,23 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
import Modal from "../../../Modal";
|
||||||
|
import ServerOfflineDialog from "../dialogs/ServerOfflineDialog";
|
||||||
|
|
||||||
export default class NonUrgentEchoFailureToast extends React.PureComponent {
|
export default class NonUrgentEchoFailureToast extends React.PureComponent {
|
||||||
render() {
|
private openDialog = () => {
|
||||||
|
Modal.createTrackedDialog('Local Echo Server Error', '', ServerOfflineDialog, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
public render() {
|
||||||
return (
|
return (
|
||||||
<div className="mx_NonUrgentEchoFailureToast">
|
<div className="mx_NonUrgentEchoFailureToast">
|
||||||
{_t("Your server isn't responding to some <a>requests</a>", {}, {
|
<span className="mx_NonUrgentEchoFailureToast_icon" />
|
||||||
'a': (sub) => <a>{sub}</a>
|
{_t("Your server isn't responding to some <a>requests</a>.", {}, {
|
||||||
|
'a': (sub) => (
|
||||||
|
<AccessibleButton kind="link" onClick={this.openDialog}>{sub}</AccessibleButton>
|
||||||
|
),
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -612,7 +612,7 @@
|
||||||
"Headphones": "Headphones",
|
"Headphones": "Headphones",
|
||||||
"Folder": "Folder",
|
"Folder": "Folder",
|
||||||
"Pin": "Pin",
|
"Pin": "Pin",
|
||||||
"Your server isn't responding to some <a>requests</a>": "Your server isn't responding to some <a>requests</a>",
|
"Your server isn't responding to some <a>requests</a>.": "Your server isn't responding to some <a>requests</a>.",
|
||||||
"From %(deviceName)s (%(deviceId)s)": "From %(deviceName)s (%(deviceId)s)",
|
"From %(deviceName)s (%(deviceId)s)": "From %(deviceName)s (%(deviceId)s)",
|
||||||
"Decline (%(counter)s)": "Decline (%(counter)s)",
|
"Decline (%(counter)s)": "Decline (%(counter)s)",
|
||||||
"Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:",
|
"Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:",
|
||||||
|
@ -1745,6 +1745,19 @@
|
||||||
"Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.",
|
"Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.",
|
||||||
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please <a>report a bug</a>.": "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please <a>report a bug</a>.",
|
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please <a>report a bug</a>.": "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please <a>report a bug</a>.",
|
||||||
"You'll upgrade this room from <oldVersion /> to <newVersion />.": "You'll upgrade this room from <oldVersion /> to <newVersion />.",
|
"You'll upgrade this room from <oldVersion /> to <newVersion />.": "You'll upgrade this room from <oldVersion /> to <newVersion />.",
|
||||||
|
"Resend": "Resend",
|
||||||
|
"You're all caught up.": "You're all caught up.",
|
||||||
|
"Server isn't responding": "Server isn't responding",
|
||||||
|
"Your server isn't responding to some of your requests for some reason. Below are some possible reasons why this happened.": "Your server isn't responding to some of your requests for some reason. Below are some possible reasons why this happened.",
|
||||||
|
"The server took too long to respond.": "The server took too long to respond.",
|
||||||
|
"Your firewall or anti-virus is blocking the request.": "Your firewall or anti-virus is blocking the request.",
|
||||||
|
"A browser extension is preventing the request.": "A browser extension is preventing the request.",
|
||||||
|
"The server is offline.": "The server is offline.",
|
||||||
|
"The server has denied your request.": "The server has denied your request.",
|
||||||
|
"Your area is experiencing difficulties connecting to the internet.": "Your area is experiencing difficulties connecting to the internet.",
|
||||||
|
"A connection error occurred while trying to contact the server.": "A connection error occurred while trying to contact the server.",
|
||||||
|
"The server is not configured to indicate what the problem is (CORS).": "The server is not configured to indicate what the problem is (CORS).",
|
||||||
|
"Recent changes that have not yet been received": "Recent changes that have not yet been received",
|
||||||
"Sign out and remove encryption keys?": "Sign out and remove encryption keys?",
|
"Sign out and remove encryption keys?": "Sign out and remove encryption keys?",
|
||||||
"Clear Storage and Sign Out": "Clear Storage and Sign Out",
|
"Clear Storage and Sign Out": "Clear Storage and Sign Out",
|
||||||
"Send Logs": "Send Logs",
|
"Send Logs": "Send Logs",
|
||||||
|
@ -1852,7 +1865,6 @@
|
||||||
"Reject invitation": "Reject invitation",
|
"Reject invitation": "Reject invitation",
|
||||||
"Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?",
|
"Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?",
|
||||||
"Unable to reject invite": "Unable to reject invite",
|
"Unable to reject invite": "Unable to reject invite",
|
||||||
"Resend": "Resend",
|
|
||||||
"Resend edit": "Resend edit",
|
"Resend edit": "Resend edit",
|
||||||
"Resend %(unsentCount)s reaction(s)": "Resend %(unsentCount)s reaction(s)",
|
"Resend %(unsentCount)s reaction(s)": "Resend %(unsentCount)s reaction(s)",
|
||||||
"Resend removal": "Resend removal",
|
"Resend removal": "Resend removal",
|
||||||
|
|
|
@ -59,13 +59,18 @@ export abstract class CachedEcho<C extends EchoContext, K, V> extends EventEmitt
|
||||||
|
|
||||||
private decacheKey(key: K) {
|
private decacheKey(key: K) {
|
||||||
if (this.cache.has(key)) {
|
if (this.cache.has(key)) {
|
||||||
this.cache.get(key).txn.cancel(); // should be safe to call
|
this.context.disownTransaction(this.cache.get(key).txn);
|
||||||
this.cache.delete(key);
|
this.cache.delete(key);
|
||||||
this.emit(PROPERTY_UPDATED, key);
|
this.emit(PROPERTY_UPDATED, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected markEchoReceived(key: K) {
|
protected markEchoReceived(key: K) {
|
||||||
|
if (this.cache.has(key)) {
|
||||||
|
const txn = this.cache.get(key).txn;
|
||||||
|
this.context.disownTransaction(txn);
|
||||||
|
txn.cancel();
|
||||||
|
}
|
||||||
this.decacheKey(key);
|
this.decacheKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +84,6 @@ export abstract class CachedEcho<C extends EchoContext, K, V> extends EventEmitt
|
||||||
this.cacheVal(key, targetVal, txn); // set the cache now as it won't be updated by the .when() ladder below.
|
this.cacheVal(key, targetVal, txn); // set the cache now as it won't be updated by the .when() ladder below.
|
||||||
|
|
||||||
txn.when(TransactionStatus.Pending, () => this.cacheVal(key, targetVal, txn))
|
txn.when(TransactionStatus.Pending, () => this.cacheVal(key, targetVal, txn))
|
||||||
.when(TransactionStatus.DoneError, () => this.decacheKey(key))
|
|
||||||
.when(TransactionStatus.DoneError, () => revertFn());
|
.when(TransactionStatus.DoneError, () => revertFn());
|
||||||
|
|
||||||
txn.run();
|
txn.run();
|
||||||
|
|
|
@ -28,7 +28,6 @@ export enum ContextTransactionState {
|
||||||
export abstract class EchoContext extends Whenable<ContextTransactionState> implements IDestroyable {
|
export abstract class EchoContext extends Whenable<ContextTransactionState> implements IDestroyable {
|
||||||
private _transactions: EchoTransaction[] = [];
|
private _transactions: EchoTransaction[] = [];
|
||||||
private _state = ContextTransactionState.NotStarted;
|
private _state = ContextTransactionState.NotStarted;
|
||||||
public readonly startTime: Date = new Date();
|
|
||||||
|
|
||||||
public get transactions(): EchoTransaction[] {
|
public get transactions(): EchoTransaction[] {
|
||||||
return arrayFastClone(this._transactions);
|
return arrayFastClone(this._transactions);
|
||||||
|
@ -38,6 +37,19 @@ export abstract class EchoContext extends Whenable<ContextTransactionState> impl
|
||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get firstFailedTime(): Date {
|
||||||
|
const failedTxn = this.transactions.find(t => t.didPreviouslyFail || t.status === TransactionStatus.DoneError);
|
||||||
|
if (failedTxn) return failedTxn.startTime;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public disownTransaction(txn: EchoTransaction) {
|
||||||
|
const idx = this._transactions.indexOf(txn);
|
||||||
|
if (idx >= 0) this._transactions.splice(idx, 1);
|
||||||
|
txn.destroy();
|
||||||
|
this.checkTransactions();
|
||||||
|
}
|
||||||
|
|
||||||
public beginTransaction(auditName: string, runFn: RunFn): EchoTransaction {
|
public beginTransaction(auditName: string, runFn: RunFn): EchoTransaction {
|
||||||
const txn = new EchoTransaction(auditName, runFn);
|
const txn = new EchoTransaction(auditName, runFn);
|
||||||
this._transactions.push(txn);
|
this._transactions.push(txn);
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { RoomEchoContext } from "./RoomEchoContext";
|
||||||
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
|
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
|
||||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||||
import { ActionPayload } from "../../dispatcher/payloads";
|
import { ActionPayload } from "../../dispatcher/payloads";
|
||||||
import { ContextTransactionState } from "./EchoContext";
|
import { ContextTransactionState, EchoContext } from "./EchoContext";
|
||||||
import NonUrgentToastStore, { ToastReference } from "../NonUrgentToastStore";
|
import NonUrgentToastStore, { ToastReference } from "../NonUrgentToastStore";
|
||||||
import NonUrgentEchoFailureToast from "../../components/views/toasts/NonUrgentEchoFailureToast";
|
import NonUrgentEchoFailureToast from "../../components/views/toasts/NonUrgentEchoFailureToast";
|
||||||
|
|
||||||
|
@ -50,6 +50,10 @@ export class EchoStore extends AsyncStoreWithClient<IState> {
|
||||||
return EchoStore._instance;
|
return EchoStore._instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get contexts(): EchoContext[] {
|
||||||
|
return Array.from(this.caches.values()).map(e => e.context);
|
||||||
|
}
|
||||||
|
|
||||||
public getOrCreateEchoForRoom(room: Room): RoomCachedEcho {
|
public getOrCreateEchoForRoom(room: Room): RoomCachedEcho {
|
||||||
if (this.caches.has(roomContextKey(room))) {
|
if (this.caches.has(roomContextKey(room))) {
|
||||||
return this.caches.get(roomContextKey(room)) as RoomCachedEcho;
|
return this.caches.get(roomContextKey(room)) as RoomCachedEcho;
|
||||||
|
|
|
@ -28,6 +28,8 @@ export class EchoTransaction extends Whenable<TransactionStatus> {
|
||||||
private _status = TransactionStatus.Pending;
|
private _status = TransactionStatus.Pending;
|
||||||
private didFail = false;
|
private didFail = false;
|
||||||
|
|
||||||
|
public readonly startTime = new Date();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public readonly auditName,
|
public readonly auditName,
|
||||||
public runFn: RunFn,
|
public runFn: RunFn,
|
||||||
|
|
Loading…
Reference in a new issue