Merge remote-tracking branch 'upstream/main' into feature_sfu
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
commit
3d0906a145
16 changed files with 315 additions and 148 deletions
|
|
@ -28,6 +28,7 @@ import {
|
|||
SignupTracker,
|
||||
MuteCameraTracker,
|
||||
MuteMicrophoneTracker,
|
||||
UndecryptableToDeviceEventTracker,
|
||||
} from "./PosthogEvents";
|
||||
import { Config } from "./config/Config";
|
||||
import { getUrlParams } from "./UrlParams";
|
||||
|
|
@ -415,4 +416,5 @@ export class PosthogAnalytics {
|
|||
public eventLogin = new LoginTracker();
|
||||
public eventMuteMicrophone = new MuteMicrophoneTracker();
|
||||
public eventMuteCamera = new MuteCameraTracker();
|
||||
public eventUndecryptableToDevice = new UndecryptableToDeviceEventTracker();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,3 +149,17 @@ export class MuteCameraTracker {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
interface UndecryptableToDeviceEvent {
|
||||
eventName: "UndecryptableToDeviceEvent";
|
||||
callId: string;
|
||||
}
|
||||
|
||||
export class UndecryptableToDeviceEventTracker {
|
||||
track(callId: string) {
|
||||
PosthogAnalytics.instance.trackEvent<UndecryptableToDeviceEvent>({
|
||||
eventName: "UndecryptableToDeviceEvent",
|
||||
callId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,6 +216,12 @@ export function fullAliasFromRoomName(
|
|||
return `#${roomAliasLocalpartFromRoomName(roomName)}:${client.getDomain()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* XXX: What is this trying to do? It looks like it's getting the localpart from
|
||||
* a room alias, but why is it splitting on hyphens and then putting spaces in??
|
||||
* @param roomId
|
||||
* @returns
|
||||
*/
|
||||
export function roomNameFromRoomId(roomId: string): string {
|
||||
return roomId
|
||||
.match(/([^:]+):.*$/)[1]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { Resizable } from "re-resizable";
|
||||
import React, {
|
||||
useEffect,
|
||||
|
|
@ -34,6 +35,7 @@ import { CallEvent } from "matrix-js-sdk/src/webrtc/call";
|
|||
|
||||
import styles from "./GroupCallInspector.module.css";
|
||||
import { SelectInput } from "../input/SelectInput";
|
||||
import { PosthogAnalytics } from "../PosthogAnalytics";
|
||||
|
||||
interface InspectorContextState {
|
||||
eventsByUserId?: { [userId: string]: SequenceDiagramMatrixEvent[] };
|
||||
|
|
@ -108,6 +110,19 @@ function formatTimestamp(timestamp: number | Date) {
|
|||
return dateFormatter.format(timestamp);
|
||||
}
|
||||
|
||||
function formatType(event: SequenceDiagramMatrixEvent): string {
|
||||
if (event.content.msgtype === "m.bad.encrypted") return "Undecryptable";
|
||||
return event.type;
|
||||
}
|
||||
|
||||
function lineForEvent(event: SequenceDiagramMatrixEvent): string {
|
||||
return `${getUserName(event.from)} ${
|
||||
event.ignored ? "-x" : "->>"
|
||||
} ${getUserName(event.to)}: ${formatTimestamp(event.timestamp)} ${formatType(
|
||||
event
|
||||
)} ${formatContent(event.type, event.content)}`;
|
||||
}
|
||||
|
||||
export const InspectorContext =
|
||||
createContext<
|
||||
[
|
||||
|
|
@ -187,21 +202,7 @@ export function SequenceDiagramViewer({
|
|||
participant ${getUserName(localUserId)}
|
||||
participant Room
|
||||
participant ${selectedUserId ? getUserName(selectedUserId) : "unknown"}
|
||||
${
|
||||
events
|
||||
? events
|
||||
.map(
|
||||
({ to, from, timestamp, type, content, ignored }) =>
|
||||
`${getUserName(from)} ${ignored ? "-x" : "->>"} ${getUserName(
|
||||
to
|
||||
)}: ${formatTimestamp(timestamp)} ${type} ${formatContent(
|
||||
type,
|
||||
content
|
||||
)}`
|
||||
)
|
||||
.join("\n ")
|
||||
: ""
|
||||
}
|
||||
${events ? events.map(lineForEvent).join("\n ") : ""}
|
||||
`;
|
||||
|
||||
mermaid.mermaidAPI.render("mermaid", graphDefinition, (svgCode: string) => {
|
||||
|
|
@ -389,12 +390,23 @@ function useGroupCallState(
|
|||
function onSendVoipEvent(event: Record<string, unknown>) {
|
||||
dispatch({ type: CallEvent.SendVoipEvent, rawEvent: event });
|
||||
}
|
||||
|
||||
function onUndecryptableToDevice(event: MatrixEvent) {
|
||||
dispatch({ type: ClientEvent.ReceivedVoipEvent, event });
|
||||
|
||||
Sentry.captureMessage("Undecryptable to-device Event");
|
||||
PosthogAnalytics.instance.eventUndecryptableToDevice.track(
|
||||
groupCall.groupCallId
|
||||
);
|
||||
}
|
||||
|
||||
client.on(RoomStateEvent.Events, onUpdateRoomState);
|
||||
//groupCall.on("calls_changed", onCallsChanged);
|
||||
groupCall.on(CallEvent.SendVoipEvent, onSendVoipEvent);
|
||||
//client.on("state", onCallsChanged);
|
||||
//client.on("hangup", onCallHangup);
|
||||
client.on(ClientEvent.ReceivedVoipEvent, onReceivedVoipEvent);
|
||||
client.on(ClientEvent.UndecryptableToDeviceEvent, onUndecryptableToDevice);
|
||||
|
||||
onUpdateRoomState();
|
||||
|
||||
|
|
@ -405,6 +417,10 @@ function useGroupCallState(
|
|||
//client.removeListener("state", onCallsChanged);
|
||||
//client.removeListener("hangup", onCallHangup);
|
||||
client.removeListener(ClientEvent.ReceivedVoipEvent, onReceivedVoipEvent);
|
||||
client.removeListener(
|
||||
ClientEvent.UndecryptableToDeviceEvent,
|
||||
onUndecryptableToDevice
|
||||
);
|
||||
};
|
||||
}, [client, groupCall]);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
import React, { useCallback, useState, useRef } from "react";
|
||||
import classNames from "classnames";
|
||||
import { useSpring, animated } from "@react-spring/web";
|
||||
import { logger } from "@sentry/utils";
|
||||
|
||||
import styles from "./PTTButton.module.css";
|
||||
import { ReactComponent as MicIcon } from "../icons/Mic.svg";
|
||||
|
|
@ -68,11 +69,23 @@ export const PTTButton: React.FC<Props> = ({
|
|||
enqueueNetworkWaiting(true, 100);
|
||||
startTalking();
|
||||
}, [enqueueNetworkWaiting, startTalking, buttonHeld]);
|
||||
|
||||
const unhold = useCallback(() => {
|
||||
if (!buttonHeld) return;
|
||||
setButtonHeld(false);
|
||||
setNetworkWaiting(false);
|
||||
stopTalking();
|
||||
}, [setNetworkWaiting, stopTalking]);
|
||||
}, [setNetworkWaiting, stopTalking, buttonHeld]);
|
||||
|
||||
const onMouseUp = useCallback(() => {
|
||||
logger.info("Mouse up event: unholding PTT button");
|
||||
unhold();
|
||||
}, [unhold]);
|
||||
|
||||
const onBlur = useCallback(() => {
|
||||
logger.info("Blur event: unholding PTT button");
|
||||
unhold();
|
||||
}, [unhold]);
|
||||
|
||||
const onButtonMouseDown = useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
|
|
@ -85,7 +98,7 @@ export const PTTButton: React.FC<Props> = ({
|
|||
// These listeners go on the window so even if the user's cursor / finger
|
||||
// leaves the button while holding it, the button stays pushed until
|
||||
// they stop clicking / tapping.
|
||||
useEventTarget(window, "mouseup", unhold);
|
||||
useEventTarget(window, "mouseup", onMouseUp);
|
||||
useEventTarget(
|
||||
window,
|
||||
"touchend",
|
||||
|
|
@ -103,6 +116,8 @@ export const PTTButton: React.FC<Props> = ({
|
|||
}
|
||||
if (!touchFound) return;
|
||||
|
||||
logger.info("Touch event ended: unholding PTT button");
|
||||
|
||||
e.preventDefault();
|
||||
unhold();
|
||||
setActiveTouchId(null);
|
||||
|
|
@ -163,6 +178,8 @@ export const PTTButton: React.FC<Props> = ({
|
|||
|
||||
e.preventDefault();
|
||||
|
||||
logger.info("Keyup event for spacebar: unholding PTT button");
|
||||
|
||||
unhold();
|
||||
}
|
||||
},
|
||||
|
|
@ -171,7 +188,7 @@ export const PTTButton: React.FC<Props> = ({
|
|||
);
|
||||
|
||||
// TODO: We will need to disable this for a global PTT hotkey to work
|
||||
useEventTarget(window, "blur", unhold);
|
||||
useEventTarget(window, "blur", onBlur);
|
||||
|
||||
const prefersReducedMotion = usePrefersReducedMotion();
|
||||
const { shadow } = useSpring({
|
||||
|
|
|
|||
|
|
@ -210,36 +210,36 @@ export const PTTCallView: React.FC<Props> = ({
|
|||
</Header>
|
||||
)}
|
||||
<div className={styles.center}>
|
||||
{showControls && (
|
||||
<>
|
||||
<div className={styles.participants}>
|
||||
<p>
|
||||
{t("{{count}} people connected", {
|
||||
count: participatingMembers.length,
|
||||
})}
|
||||
</p>
|
||||
<Facepile
|
||||
size={facepileSize}
|
||||
max={8}
|
||||
className={styles.facepile}
|
||||
client={client}
|
||||
members={participatingMembers}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
<OverflowMenu
|
||||
inCall
|
||||
roomIdOrAlias={roomIdOrAlias}
|
||||
groupCall={groupCall}
|
||||
showInvite={false}
|
||||
feedbackModalState={feedbackModalState}
|
||||
feedbackModalProps={feedbackModalProps}
|
||||
/>
|
||||
{!isEmbedded && <HangupButton onPress={onLeave} />}
|
||||
<InviteButton onPress={() => inviteModalState.open()} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{/* Always render this because the window will become shorter when the on-screen
|
||||
keyboard appears, so if we don't render it, the dialog will unmount. */}
|
||||
<div style={{ display: showControls ? "block" : "none" }}>
|
||||
<div className={styles.participants}>
|
||||
<p>
|
||||
{t("{{count}} people connected", {
|
||||
count: participatingMembers.length,
|
||||
})}
|
||||
</p>
|
||||
<Facepile
|
||||
size={facepileSize}
|
||||
max={8}
|
||||
className={styles.facepile}
|
||||
client={client}
|
||||
members={participatingMembers}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
<OverflowMenu
|
||||
inCall
|
||||
roomIdOrAlias={roomIdOrAlias}
|
||||
groupCall={groupCall}
|
||||
showInvite={false}
|
||||
feedbackModalState={feedbackModalState}
|
||||
feedbackModalProps={feedbackModalProps}
|
||||
/>
|
||||
{!isEmbedded && <HangupButton onPress={onLeave} />}
|
||||
<InviteButton onPress={() => inviteModalState.open()} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.pttButtonContainer}>
|
||||
{showControls &&
|
||||
|
|
|
|||
|
|
@ -50,9 +50,9 @@ export const RoomPage: FC = () => {
|
|||
const [isRegistering, setIsRegistering] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// If we're not already authed and we've been given a display name as
|
||||
// If we've finished loading, are not already authed and we've been given a display name as
|
||||
// a URL param, automatically register a passwordless user
|
||||
if (!isAuthenticated && displayName) {
|
||||
if (!loading && !isAuthenticated && displayName) {
|
||||
setIsRegistering(true);
|
||||
registerPasswordlessUser(displayName).finally(() => {
|
||||
setIsRegistering(false);
|
||||
|
|
@ -63,6 +63,7 @@ export const RoomPage: FC = () => {
|
|||
displayName,
|
||||
setIsRegistering,
|
||||
registerPasswordlessUser,
|
||||
loading,
|
||||
]);
|
||||
|
||||
const groupCallView = useCallback(
|
||||
|
|
|
|||
|
|
@ -52,12 +52,22 @@ export const useLoadGroupCall = (
|
|||
|
||||
const fetchOrCreateRoom = async (): Promise<Room> => {
|
||||
try {
|
||||
const room = await client.joinRoom(roomIdOrAlias, { viaServers });
|
||||
// We lowercase the localpart when we create the room, so we must lowercase
|
||||
// it here too (we just do the whole alias). We can't do the same to room IDs
|
||||
// though.
|
||||
const sanitisedIdOrAlias =
|
||||
roomIdOrAlias[0] === "#"
|
||||
? roomIdOrAlias.toLowerCase()
|
||||
: roomIdOrAlias;
|
||||
|
||||
const room = await client.joinRoom(sanitisedIdOrAlias, {
|
||||
viaServers,
|
||||
});
|
||||
logger.info(
|
||||
`Joined ${roomIdOrAlias}, waiting room to be ready for group calls`
|
||||
`Joined ${sanitisedIdOrAlias}, waiting room to be ready for group calls`
|
||||
);
|
||||
await client.waitUntilRoomReadyForGroupCalls(room.roomId);
|
||||
logger.info(`${roomIdOrAlias}, is ready for group calls`);
|
||||
logger.info(`${sanitisedIdOrAlias}, is ready for group calls`);
|
||||
return room;
|
||||
} catch (error) {
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -113,12 +113,14 @@ export const usePTT = (
|
|||
},
|
||||
setState,
|
||||
] = useState(() => {
|
||||
// slightly concerningly, this can end up null as we seem to sometimes get
|
||||
// here before the room state contains our own member event
|
||||
const roomMember = groupCall.room.getMember(client.getUserId());
|
||||
|
||||
const activeSpeakerFeed = getActiveSpeakerFeed(userMediaFeeds, groupCall);
|
||||
|
||||
return {
|
||||
isAdmin: roomMember.powerLevel >= 100,
|
||||
isAdmin: roomMember ? roomMember.powerLevel >= 100 : false,
|
||||
talkOverEnabled: false,
|
||||
pttButtonHeld: false,
|
||||
activeSpeakerUserId: activeSpeakerFeed ? activeSpeakerFeed.userId : null,
|
||||
|
|
|
|||
|
|
@ -1,21 +1,4 @@
|
|||
/*
|
||||
Copyright 2022 New Vector Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
/*
|
||||
Copyright 2017 OpenMarket Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 The New Vector Ltd
|
||||
|
|
@ -54,15 +37,23 @@ limitations under the License.
|
|||
// actually timestamps. We then purge the remaining logs. We also do this
|
||||
// purge on startup to prevent logs from accumulating.
|
||||
|
||||
import EventEmitter from "events";
|
||||
import { throttle } from "lodash";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||
|
||||
// the frequency with which we flush to indexeddb
|
||||
const FLUSH_RATE_MS = 30 * 1000;
|
||||
|
||||
// the length of log data we keep in indexeddb (and include in the reports)
|
||||
const MAX_LOG_SIZE = 1024 * 1024 * 5; // 5 MB
|
||||
|
||||
// Shortest amount of time between flushes. We are just appending to an
|
||||
// IndexedDB table so we don't expect flushing to be that expensive, but
|
||||
// we can batch the writes a little.
|
||||
const MAX_FLUSH_INTERVAL_MS = 2 * 1000;
|
||||
|
||||
enum ConsoleLoggerEvent {
|
||||
Log = "log",
|
||||
}
|
||||
|
||||
type LogFunction = (
|
||||
...args: (Error | DOMException | object | string)[]
|
||||
) => void;
|
||||
|
|
@ -76,7 +67,7 @@ interface LogEntry {
|
|||
index?: number;
|
||||
}
|
||||
|
||||
export class ConsoleLogger {
|
||||
export class ConsoleLogger extends EventEmitter {
|
||||
private logs = "";
|
||||
private originalFunctions: { [key in LogFunctionName]?: LogFunction } = {};
|
||||
|
||||
|
|
@ -99,13 +90,6 @@ export class ConsoleLogger {
|
|||
});
|
||||
}
|
||||
|
||||
public bypassRageshake(
|
||||
fnName: LogFunctionName,
|
||||
...args: (Error | DOMException | object | string)[]
|
||||
): void {
|
||||
this.originalFunctions[fnName](...args);
|
||||
}
|
||||
|
||||
public log(
|
||||
level: string,
|
||||
...args: (Error | DOMException | object | string)[]
|
||||
|
|
@ -137,23 +121,27 @@ export class ConsoleLogger {
|
|||
// Using + really is the quickest way in JS
|
||||
// http://jsperf.com/concat-vs-plus-vs-join
|
||||
this.logs += line;
|
||||
|
||||
this.emit(ConsoleLoggerEvent.Log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve log lines to flush to disk.
|
||||
* @param {boolean} keepLogs True to not delete logs after flushing.
|
||||
* @return {string} \n delimited log lines to flush.
|
||||
* Returns the log lines to flush to disk and empties the internal log buffer
|
||||
* @return {string} \n delimited log lines
|
||||
*/
|
||||
public flush(keepLogs?: boolean): string {
|
||||
// The ConsoleLogger doesn't care how these end up on disk, it just
|
||||
// flushes them to the caller.
|
||||
if (keepLogs) {
|
||||
return this.logs;
|
||||
}
|
||||
public popLogs(): string {
|
||||
const logsToFlush = this.logs;
|
||||
this.logs = "";
|
||||
return logsToFlush;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns lines currently in the log buffer without removing them
|
||||
* @return {string} \n delimited log lines
|
||||
*/
|
||||
public peekLogs(): string {
|
||||
return this.logs;
|
||||
}
|
||||
}
|
||||
|
||||
// A class which stores log lines in an IndexedDB instance.
|
||||
|
|
@ -164,8 +152,14 @@ export class IndexedDBLogStore {
|
|||
private flushAgainPromise: Promise<void> = null;
|
||||
private id: string;
|
||||
|
||||
constructor(private indexedDB: IDBFactory, private logger: ConsoleLogger) {
|
||||
constructor(
|
||||
private indexedDB: IDBFactory,
|
||||
private loggerInstance: ConsoleLogger
|
||||
) {
|
||||
this.id = "instance-" + randomString(16);
|
||||
|
||||
loggerInstance.on(ConsoleLoggerEvent.Log, this.onLoggerLog);
|
||||
window.addEventListener("beforeunload", this.flush);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -174,30 +168,31 @@ export class IndexedDBLogStore {
|
|||
public connect(): Promise<void> {
|
||||
const req = this.indexedDB.open("logs");
|
||||
return new Promise((resolve, reject) => {
|
||||
req.onsuccess = (event: Event) => {
|
||||
// @ts-ignore
|
||||
this.db = event.target.result;
|
||||
// Periodically flush logs to local storage / indexeddb
|
||||
setInterval(this.flush.bind(this), FLUSH_RATE_MS);
|
||||
req.onsuccess = () => {
|
||||
this.db = req.result;
|
||||
|
||||
resolve();
|
||||
};
|
||||
|
||||
req.onerror = (event) => {
|
||||
const err =
|
||||
// @ts-ignore
|
||||
"Failed to open log database: " + event.target.error.name;
|
||||
req.onerror = () => {
|
||||
const err = "Failed to open log database: " + req.error.name;
|
||||
logger.error(err);
|
||||
reject(new Error(err));
|
||||
};
|
||||
|
||||
// First time: Setup the object store
|
||||
req.onupgradeneeded = (event) => {
|
||||
// @ts-ignore
|
||||
const db = event.target.result;
|
||||
req.onupgradeneeded = () => {
|
||||
const db = req.result;
|
||||
// This is the log entries themselves. Each entry is a chunk of
|
||||
// logs (ie multiple lines). 'id' is the instance ID (so logs with
|
||||
// the same instance ID are all from the same session) and 'index'
|
||||
// is a sequence number for the chunk. The log lines live in the
|
||||
// 'lines' key, which is a chunk of many newline-separated log lines.
|
||||
const logObjStore = db.createObjectStore("logs", {
|
||||
keyPath: ["id", "index"],
|
||||
});
|
||||
// Keys in the database look like: [ "instance-148938490", 0 ]
|
||||
// (The instance ID plus the ID of each log chunk).
|
||||
// Later on we need to query everything based on an instance id.
|
||||
// In order to do this, we need to set up indexes "id".
|
||||
logObjStore.createIndex("id", "id", { unique: false });
|
||||
|
|
@ -206,6 +201,9 @@ export class IndexedDBLogStore {
|
|||
this.generateLogEntry(new Date() + " ::: Log database was created.")
|
||||
);
|
||||
|
||||
// This records the last time each instance ID generated a log message, such
|
||||
// that the logs from each session can be collated in the order they last logged
|
||||
// something.
|
||||
const lastModifiedStore = db.createObjectStore("logslastmod", {
|
||||
keyPath: "id",
|
||||
});
|
||||
|
|
@ -214,6 +212,26 @@ export class IndexedDBLogStore {
|
|||
});
|
||||
}
|
||||
|
||||
private onLoggerLog = () => {
|
||||
if (!this.db) return;
|
||||
|
||||
this.throttledFlush();
|
||||
};
|
||||
|
||||
// Throttled function to flush logs. We use throttle rather
|
||||
// than debounce as we want logs to be written regularly, otherwise
|
||||
// if there's a constant stream of logging, we'd never write anything.
|
||||
private throttledFlush = throttle(
|
||||
() => {
|
||||
this.flush();
|
||||
},
|
||||
MAX_FLUSH_INTERVAL_MS,
|
||||
{
|
||||
leading: false,
|
||||
trailing: true,
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Flush logs to disk.
|
||||
*
|
||||
|
|
@ -233,7 +251,7 @@ export class IndexedDBLogStore {
|
|||
*
|
||||
* @return {Promise} Resolved when the logs have been flushed.
|
||||
*/
|
||||
public flush(): Promise<void> {
|
||||
public flush = (): Promise<void> => {
|
||||
// check if a flush() operation is ongoing
|
||||
if (this.flushPromise) {
|
||||
if (this.flushAgainPromise) {
|
||||
|
|
@ -258,20 +276,19 @@ export class IndexedDBLogStore {
|
|||
reject(new Error("No connected database"));
|
||||
return;
|
||||
}
|
||||
const lines = this.logger.flush();
|
||||
const lines = this.loggerInstance.popLogs();
|
||||
if (lines.length === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const txn = this.db.transaction(["logs", "logslastmod"], "readwrite");
|
||||
const objStore = txn.objectStore("logs");
|
||||
txn.oncomplete = (event) => {
|
||||
txn.oncomplete = () => {
|
||||
resolve();
|
||||
};
|
||||
txn.onerror = (event) => {
|
||||
logger.error("Failed to flush logs : ", event);
|
||||
// @ts-ignore
|
||||
reject(new Error("Failed to write logs: " + event.target.errorCode));
|
||||
reject(new Error("Failed to write logs: " + txn.error.message));
|
||||
};
|
||||
objStore.add(this.generateLogEntry(lines));
|
||||
const lastModStore = txn.objectStore("logslastmod");
|
||||
|
|
@ -280,7 +297,7 @@ export class IndexedDBLogStore {
|
|||
this.flushPromise = null;
|
||||
});
|
||||
return this.flushPromise;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Consume the most recent logs and return them. Older logs which are not
|
||||
|
|
@ -307,13 +324,11 @@ export class IndexedDBLogStore {
|
|||
.index("id")
|
||||
.openCursor(IDBKeyRange.only(id), "prev");
|
||||
let lines = "";
|
||||
query.onerror = (event) => {
|
||||
// @ts-ignore
|
||||
reject(new Error("Query failed: " + event.target.errorCode));
|
||||
query.onerror = () => {
|
||||
reject(new Error("Query failed: " + query.error.message));
|
||||
};
|
||||
query.onsuccess = (event) => {
|
||||
// @ts-ignore
|
||||
const cursor = event.target.result;
|
||||
query.onsuccess = () => {
|
||||
const cursor = query.result;
|
||||
if (!cursor) {
|
||||
resolve(lines);
|
||||
return; // end of results
|
||||
|
|
@ -355,9 +370,8 @@ export class IndexedDBLogStore {
|
|||
const o = txn.objectStore("logs");
|
||||
// only load the key path, not the data which may be huge
|
||||
const query = o.index("id").openKeyCursor(IDBKeyRange.only(id));
|
||||
query.onsuccess = (event) => {
|
||||
// @ts-ignore
|
||||
const cursor = event.target.result;
|
||||
query.onsuccess = () => {
|
||||
const cursor = query.result;
|
||||
if (!cursor) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -367,12 +381,10 @@ export class IndexedDBLogStore {
|
|||
txn.oncomplete = () => {
|
||||
resolve();
|
||||
};
|
||||
txn.onerror = (event) => {
|
||||
txn.onerror = () => {
|
||||
reject(
|
||||
new Error(
|
||||
"Failed to delete logs for " +
|
||||
// @ts-ignore
|
||||
`'${id}' : ${event.target.errorCode}`
|
||||
"Failed to delete logs for " + `'${id}' : ${txn.error.message}`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
|
@ -456,14 +468,12 @@ function selectQuery<T>(
|
|||
const query = store.openCursor(keyRange);
|
||||
return new Promise((resolve, reject) => {
|
||||
const results = [];
|
||||
query.onerror = (event) => {
|
||||
// @ts-ignore
|
||||
reject(new Error("Query failed: " + event.target.errorCode));
|
||||
query.onerror = () => {
|
||||
reject(new Error("Query failed: " + query.error.message));
|
||||
};
|
||||
// collect results
|
||||
query.onsuccess = (event) => {
|
||||
// @ts-ignore
|
||||
const cursor = event.target.result;
|
||||
query.onsuccess = () => {
|
||||
const cursor = query.result;
|
||||
if (!cursor) {
|
||||
resolve(results);
|
||||
return; // end of results
|
||||
|
|
@ -479,8 +489,6 @@ declare global {
|
|||
// eslint-disable-next-line no-var, camelcase
|
||||
var mx_rage_logger: ConsoleLogger;
|
||||
// eslint-disable-next-line no-var, camelcase
|
||||
var mx_rage_initPromise: Promise<void>;
|
||||
// eslint-disable-next-line no-var, camelcase
|
||||
var mx_rage_initStoragePromise: Promise<void>;
|
||||
}
|
||||
|
||||
|
|
@ -491,19 +499,11 @@ declare global {
|
|||
* be set up immediately for the logs.
|
||||
* @return {Promise} Resolves when set up.
|
||||
*/
|
||||
export function init(setUpPersistence = true): Promise<void> {
|
||||
if (global.mx_rage_initPromise) {
|
||||
return global.mx_rage_initPromise;
|
||||
}
|
||||
export function init(): Promise<void> {
|
||||
global.mx_rage_logger = new ConsoleLogger();
|
||||
global.mx_rage_logger.monkeyPatch(window.console);
|
||||
|
||||
if (setUpPersistence) {
|
||||
return tryInitStorage();
|
||||
}
|
||||
|
||||
global.mx_rage_initPromise = Promise.resolve();
|
||||
return global.mx_rage_initPromise;
|
||||
return tryInitStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -573,7 +573,7 @@ export async function getLogsForReport(): Promise<LogEntry[]> {
|
|||
} else {
|
||||
return [
|
||||
{
|
||||
lines: global.mx_rage_logger.flush(true),
|
||||
lines: global.mx_rage_logger.peekLogs(),
|
||||
id: "-",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue