2020-07-21 22:55:28 +00:00
|
|
|
import {LogMessage, ServerLogUIEvents} from "tc-shared/ui/frames/log/Definitions";
|
|
|
|
import {VariadicTranslatable} from "tc-shared/ui/react-elements/i18n";
|
|
|
|
import {Registry} from "tc-shared/events";
|
|
|
|
import {useEffect, useRef, useState} from "react";
|
|
|
|
import * as React from "react";
|
|
|
|
import {findLogDispatcher} from "tc-shared/ui/frames/log/DispatcherLog";
|
|
|
|
|
|
|
|
const cssStyle = require("./Renderer.scss");
|
|
|
|
|
|
|
|
const LogFallbackDispatcher = (_unused, __unused, eventType) => (
|
|
|
|
<div className={cssStyle.errorMessage}>
|
|
|
|
<VariadicTranslatable text={"Missing log entry builder for {0}"}>
|
|
|
|
<>{eventType}</>
|
|
|
|
</VariadicTranslatable>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
|
|
|
|
const LogEntryRenderer = React.memo((props: { entry: LogMessage, handlerId: string }) => {
|
|
|
|
const dispatcher = findLogDispatcher(props.entry.type as any) || LogFallbackDispatcher;
|
|
|
|
const rendered = dispatcher(props.entry.data, props.handlerId, props.entry.type);
|
|
|
|
|
|
|
|
if(!rendered) /* hide message */
|
|
|
|
return null;
|
|
|
|
|
|
|
|
const date = new Date(props.entry.timestamp);
|
|
|
|
return (
|
|
|
|
<div className={cssStyle.logEntry}>
|
|
|
|
<div className={cssStyle.timestamp}><{
|
|
|
|
("0" + date.getHours()).substr(-2) + ":" +
|
|
|
|
("0" + date.getMinutes()).substr(-2) + ":" +
|
|
|
|
("0" + date.getSeconds()).substr(-2)
|
|
|
|
}></div>
|
|
|
|
{rendered}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
export const ServerLogRenderer = (props: { events: Registry<ServerLogUIEvents>, handlerId: string }) => {
|
|
|
|
const [ logs, setLogs ] = useState<LogMessage[] | "loading">(() => {
|
|
|
|
props.events.fire_async("query_log");
|
|
|
|
return "loading";
|
|
|
|
});
|
|
|
|
|
|
|
|
const [ revision, setRevision ] = useState(0);
|
|
|
|
|
|
|
|
const refContainer = useRef<HTMLDivElement>();
|
|
|
|
const scrollOffset = useRef<number | "bottom">("bottom");
|
|
|
|
|
|
|
|
props.events.reactUse("notify_log", event => {
|
|
|
|
const logs = event.log.slice(0);
|
|
|
|
logs.splice(0, Math.max(0, logs.length - 100));
|
|
|
|
logs.sort((a, b) => a.timestamp - b.timestamp);
|
|
|
|
setLogs(logs);
|
|
|
|
});
|
|
|
|
|
|
|
|
props.events.reactUse("notify_log_add", event => {
|
2020-09-17 21:06:02 +00:00
|
|
|
if(logs === "loading") {
|
2020-07-21 22:55:28 +00:00
|
|
|
return;
|
2020-09-17 21:06:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(__build.mode === "debug") {
|
|
|
|
const index = logs.findIndex(e => e.uniqueId === event.event.uniqueId);
|
|
|
|
if(index !== -1) {
|
|
|
|
debugger;
|
|
|
|
}
|
|
|
|
}
|
2020-07-21 22:55:28 +00:00
|
|
|
|
|
|
|
logs.push(event.event);
|
|
|
|
logs.splice(0, Math.max(0, logs.length - 100));
|
|
|
|
logs.sort((a, b) => a.timestamp - b.timestamp);
|
|
|
|
setRevision(revision + 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const id = requestAnimationFrame(() => {
|
|
|
|
if(!refContainer.current)
|
|
|
|
return;
|
|
|
|
|
|
|
|
refContainer.current.scrollTop = scrollOffset.current === "bottom" ? refContainer.current.scrollHeight : scrollOffset.current;
|
|
|
|
});
|
|
|
|
return () => cancelAnimationFrame(id);
|
|
|
|
});
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={cssStyle.logContainer} ref={refContainer} onScroll={event => {
|
|
|
|
const target = event.target as HTMLDivElement;
|
|
|
|
|
|
|
|
const top = target.scrollTop;
|
|
|
|
const total = target.scrollHeight - target.clientHeight;
|
|
|
|
const shouldFollow = top + 50 > total;
|
|
|
|
|
|
|
|
scrollOffset.current = shouldFollow ? "bottom" : top;
|
|
|
|
}}>
|
|
|
|
{logs === "loading" ? null : logs.map(e => <LogEntryRenderer key={e.uniqueId} entry={e} handlerId={props.handlerId} />)}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|