Merge branch 'refs/heads/develop' into florianduros/tooltip-update
This commit is contained in:
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { DecryptionError } from "matrix-js-sdk/src/crypto/algorithms";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { Error as ErrorEvent } from "@matrix-org/analytics-events/types/typescript/Error";
|
||||
import { DecryptionFailureCode } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import { PosthogAnalytics } from "./PosthogAnalytics";
|
||||
|
||||
@@ -25,17 +25,15 @@ export class DecryptionFailure {
|
||||
|
||||
public constructor(
|
||||
public readonly failedEventId: string,
|
||||
public readonly errorCode: string,
|
||||
public readonly errorCode: DecryptionFailureCode,
|
||||
) {
|
||||
this.ts = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
type ErrorCode = "OlmKeysNotSentError" | "OlmIndexError" | "UnknownError" | "OlmUnspecifiedError";
|
||||
|
||||
type ErrorCode = ErrorEvent["name"];
|
||||
type TrackingFn = (count: number, trackedErrCode: ErrorCode, rawError: string) => void;
|
||||
|
||||
export type ErrCodeMapFn = (errcode: string) => ErrorCode;
|
||||
export type ErrCodeMapFn = (errcode: DecryptionFailureCode) => ErrorCode;
|
||||
|
||||
export class DecryptionFailureTracker {
|
||||
private static internalInstance = new DecryptionFailureTracker(
|
||||
@@ -52,12 +50,14 @@ export class DecryptionFailureTracker {
|
||||
(errorCode) => {
|
||||
// Map JS-SDK error codes to tracker codes for aggregation
|
||||
switch (errorCode) {
|
||||
case "MEGOLM_UNKNOWN_INBOUND_SESSION_ID":
|
||||
case DecryptionFailureCode.MEGOLM_UNKNOWN_INBOUND_SESSION_ID:
|
||||
return "OlmKeysNotSentError";
|
||||
case "OLM_UNKNOWN_MESSAGE_INDEX":
|
||||
case DecryptionFailureCode.OLM_UNKNOWN_MESSAGE_INDEX:
|
||||
return "OlmIndexError";
|
||||
case undefined:
|
||||
return "OlmUnspecifiedError";
|
||||
case DecryptionFailureCode.HISTORICAL_MESSAGE_NO_KEY_BACKUP:
|
||||
case DecryptionFailureCode.HISTORICAL_MESSAGE_BACKUP_UNCONFIGURED:
|
||||
case DecryptionFailureCode.HISTORICAL_MESSAGE_WORKING_BACKUP:
|
||||
return "HistoricalMessage";
|
||||
default:
|
||||
return "UnknownError";
|
||||
}
|
||||
@@ -76,11 +76,11 @@ export class DecryptionFailureTracker {
|
||||
// accumulated in `failureCounts`.
|
||||
public visibleFailures: Map<string, DecryptionFailure> = new Map();
|
||||
|
||||
// A histogram of the number of failures that will be tracked at the next tracking
|
||||
// interval, split by failure error code.
|
||||
public failureCounts: Record<string, number> = {
|
||||
// [errorCode]: 42
|
||||
};
|
||||
/**
|
||||
* A histogram of the number of failures that will be tracked at the next tracking
|
||||
* interval, split by failure error code.
|
||||
*/
|
||||
private failureCounts: Map<DecryptionFailureCode, number> = new Map();
|
||||
|
||||
// Event IDs of failures that were tracked previously
|
||||
public trackedEvents: Set<string> = new Set();
|
||||
@@ -108,10 +108,10 @@ export class DecryptionFailureTracker {
|
||||
*
|
||||
* @param {function} fn The tracking function, which will be called when failures
|
||||
* are tracked. The function should have a signature `(count, trackedErrorCode) => {...}`,
|
||||
* where `count` is the number of failures and `errorCode` matches the `.code` of
|
||||
* provided DecryptionError errors (by default, unless `errorCodeMapFn` is specified.
|
||||
* @param {function?} errorCodeMapFn The function used to map error codes to the
|
||||
* trackedErrorCode. If not provided, the `.code` of errors will be used.
|
||||
* where `count` is the number of failures and `errorCode` matches the output of `errorCodeMapFn`.
|
||||
*
|
||||
* @param {function} errorCodeMapFn The function used to map decryption failure reason codes to the
|
||||
* `trackedErrorCode`.
|
||||
*/
|
||||
private constructor(
|
||||
private readonly fn: TrackingFn,
|
||||
@@ -138,13 +138,15 @@ export class DecryptionFailureTracker {
|
||||
// localStorage.setItem('mx-decryption-failure-event-ids', JSON.stringify([...this.trackedEvents]));
|
||||
// }
|
||||
|
||||
public eventDecrypted(e: MatrixEvent, err: DecryptionError): void {
|
||||
// for now we only track megolm decrytion failures
|
||||
public eventDecrypted(e: MatrixEvent): void {
|
||||
// for now we only track megolm decryption failures
|
||||
if (e.getWireContent().algorithm != "m.megolm.v1.aes-sha2") {
|
||||
return;
|
||||
}
|
||||
if (err) {
|
||||
this.addDecryptionFailure(new DecryptionFailure(e.getId()!, err.code));
|
||||
|
||||
const errCode = e.decryptionFailureReason;
|
||||
if (errCode !== null) {
|
||||
this.addDecryptionFailure(new DecryptionFailure(e.getId()!, errCode));
|
||||
} else {
|
||||
// Could be an event in the failures, remove it
|
||||
this.removeDecryptionFailuresForEvent(e);
|
||||
@@ -205,7 +207,7 @@ export class DecryptionFailureTracker {
|
||||
this.failures = new Map();
|
||||
this.visibleEvents = new Set();
|
||||
this.visibleFailures = new Map();
|
||||
this.failureCounts = {};
|
||||
this.failureCounts = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,7 +238,7 @@ export class DecryptionFailureTracker {
|
||||
private aggregateFailures(failures: Set<DecryptionFailure>): void {
|
||||
for (const failure of failures) {
|
||||
const errorCode = failure.errorCode;
|
||||
this.failureCounts[errorCode] = (this.failureCounts[errorCode] || 0) + 1;
|
||||
this.failureCounts.set(errorCode, (this.failureCounts.get(errorCode) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,12 +247,12 @@ export class DecryptionFailureTracker {
|
||||
* function with the number of failures that should be tracked.
|
||||
*/
|
||||
public trackFailures(): void {
|
||||
for (const errorCode of Object.keys(this.failureCounts)) {
|
||||
if (this.failureCounts[errorCode] > 0) {
|
||||
for (const [errorCode, count] of this.failureCounts.entries()) {
|
||||
if (count > 0) {
|
||||
const trackedErrorCode = this.errorCodeMapFn(errorCode);
|
||||
|
||||
this.fn(this.failureCounts[errorCode], trackedErrorCode, errorCode);
|
||||
this.failureCounts[errorCode] = 0;
|
||||
this.fn(count, trackedErrorCode, errorCode);
|
||||
this.failureCounts.set(errorCode, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import { defer, IDeferred, QueryDict } from "matrix-js-sdk/src/utils";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { throttle } from "lodash";
|
||||
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
||||
import { DecryptionError } from "matrix-js-sdk/src/crypto/algorithms";
|
||||
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
|
||||
|
||||
// what-input helps improve keyboard accessibility
|
||||
@@ -1597,7 +1596,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
|
||||
// When logging out, stop tracking failures and destroy state
|
||||
cli.on(HttpApiEvent.SessionLoggedOut, () => dft.stop());
|
||||
cli.on(MatrixEventEvent.Decrypted, (e, err) => dft.eventDecrypted(e, err as DecryptionError));
|
||||
cli.on(MatrixEventEvent.Decrypted, (e) => dft.eventDecrypted(e));
|
||||
|
||||
cli.on(ClientEvent.Room, (room) => {
|
||||
if (cli.isCryptoEnabled()) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import { UPDATE_EVENT } from "../AsyncStore";
|
||||
import { IPreview } from "./previews/IPreview";
|
||||
import { VoiceBroadcastInfoEventType } from "../../voice-broadcast";
|
||||
import { VoiceBroadcastPreview } from "./previews/VoiceBroadcastPreview";
|
||||
import shouldHideEvent from "../../shouldHideEvent";
|
||||
|
||||
// Emitted event for when a room's preview has changed. First argument will the room for which
|
||||
// the change happened.
|
||||
@@ -184,21 +185,6 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
||||
return previewDef?.previewer.getTextFor(event, undefined, true) ?? "";
|
||||
}
|
||||
|
||||
private shouldSkipPreview(event: MatrixEvent, previousEvent?: MatrixEvent): boolean {
|
||||
if (event.isRelation(RelationType.Replace)) {
|
||||
if (previousEvent !== undefined) {
|
||||
// Ignore edits if they don't apply to the latest event in the room to keep the preview on the latest event
|
||||
const room = this.matrixClient?.getRoom(event.getRoomId()!);
|
||||
const relatedEvent = room?.findEventById(event.relationEventId!);
|
||||
if (relatedEvent !== previousEvent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async generatePreview(room: Room, tagId?: TagID): Promise<void> {
|
||||
const events = [...room.getLiveTimeline().getEvents()];
|
||||
|
||||
@@ -221,8 +207,6 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
||||
this.previews.set(room.roomId, map);
|
||||
}
|
||||
|
||||
const previousEventInAny = map.get(TAG_ANY)?.event;
|
||||
|
||||
// Set the tags so we know what to generate
|
||||
if (!map.has(TAG_ANY)) map.set(TAG_ANY, null);
|
||||
if (tagId && !map.has(tagId)) map.set(tagId, null);
|
||||
@@ -237,7 +221,8 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
||||
const event = events[i];
|
||||
|
||||
await this.matrixClient?.decryptEventIfNeeded(event);
|
||||
|
||||
const shouldHide = shouldHideEvent(event);
|
||||
if (shouldHide) continue;
|
||||
const previewDef = PREVIEWS[event.getType()];
|
||||
if (!previewDef) continue;
|
||||
if (previewDef.isState && isNullOrUndefined(event.getStateKey())) continue;
|
||||
@@ -245,16 +230,11 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
||||
const anyPreviewText = previewDef.previewer.getTextFor(event);
|
||||
if (!anyPreviewText) continue; // not previewable for some reason
|
||||
|
||||
if (!this.shouldSkipPreview(event, previousEventInAny)) {
|
||||
changed = changed || anyPreviewText !== map.get(TAG_ANY)?.text;
|
||||
map.set(TAG_ANY, mkMessagePreview(anyPreviewText, event));
|
||||
}
|
||||
changed = changed || anyPreviewText !== map.get(TAG_ANY)?.text;
|
||||
map.set(TAG_ANY, mkMessagePreview(anyPreviewText, event));
|
||||
|
||||
const tagsToGenerate = Array.from(map.keys()).filter((t) => t !== TAG_ANY); // we did the any tag above
|
||||
for (const genTagId of tagsToGenerate) {
|
||||
const previousEventInTag = map.get(genTagId)?.event;
|
||||
if (this.shouldSkipPreview(event, previousEventInTag)) continue;
|
||||
|
||||
const realTagId = genTagId === TAG_ANY ? undefined : genTagId;
|
||||
const preview = previewDef.previewer.getTextFor(event, realTagId);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user