Compare commits
2 Commits
1f22d5c41b
...
7acb0cbafa
| Author | SHA1 | Date | |
|---|---|---|---|
| 7acb0cbafa | |||
| 64985bb39e |
@@ -216,7 +216,7 @@ pub async fn event_file(
|
||||
MessageType::Video(c) => (c.source(), c.thumbnail_source()),
|
||||
_ => (None, None),
|
||||
};
|
||||
|
||||
|
||||
let source = match (query.thumbnail, source, thumb_source) {
|
||||
(false, Some(s), _) => s,
|
||||
(true, _, Some(s)) => s,
|
||||
|
||||
16
matrixgw_frontend/package-lock.json
generated
16
matrixgw_frontend/package-lock.json
generated
@@ -24,6 +24,7 @@
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-favicon": "^2.0.7",
|
||||
"react-json-view-lite": "^2.5.0",
|
||||
"react-router": "^7.9.5"
|
||||
},
|
||||
@@ -3691,6 +3692,21 @@
|
||||
"react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-favicon": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/react-favicon/-/react-favicon-2.0.7.tgz",
|
||||
"integrity": "sha512-Vqjk8VHnOu7vl7JnP13nPJ05DyFGObF655xFkbTUIbF4vqLx1Slbc56Hrbzg1leROAKHzElm3u4KUzaXO46A6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "19.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.0.tgz",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-favicon": "^2.0.7",
|
||||
"react-json-view-lite": "^2.5.0",
|
||||
"react-router": "^7.9.5"
|
||||
},
|
||||
|
||||
35
matrixgw_frontend/src/widgets/messages/AppIconModifier.tsx
Normal file
35
matrixgw_frontend/src/widgets/messages/AppIconModifier.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import Favicon from "react-favicon";
|
||||
import { WSState } from "./MatrixWS";
|
||||
|
||||
// Taken from https://github.com/element-hq/element-web/blob/0577e245dac944bd85eea07b93a9762a93062f62/src/favicon.ts
|
||||
function getInitialFavicon(): HTMLLinkElement[] {
|
||||
const icons: HTMLLinkElement[] = [];
|
||||
const links = window.document
|
||||
.getElementsByTagName("head")[0]
|
||||
.getElementsByTagName("link");
|
||||
for (const link of links) {
|
||||
if (
|
||||
link.hasAttribute("rel") &&
|
||||
/(^|\s)icon(\s|$)/i.test(link.getAttribute("rel")!)
|
||||
) {
|
||||
icons.push(link);
|
||||
}
|
||||
}
|
||||
return icons;
|
||||
}
|
||||
|
||||
let iconPath = getInitialFavicon()[0].getAttribute("href")!;
|
||||
|
||||
export function AppIconModifier(p: {
|
||||
numberUnread: number;
|
||||
state: string;
|
||||
}): React.ReactElement {
|
||||
const isError = p.state === WSState.Error || p.state === WSState.Closed;
|
||||
return (
|
||||
<Favicon
|
||||
url={iconPath}
|
||||
alertFillColor={isError ? "orange" : undefined}
|
||||
alertCount={isError ? "x" : p.numberUnread}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import type { WsMessage } from "../../api/WsApi";
|
||||
import { RoomEventsManager } from "../../utils/RoomEventsManager";
|
||||
import { AsyncWidget } from "../AsyncWidget";
|
||||
import { useUserInfo } from "../dashboard/BaseAuthenticatedPage";
|
||||
import { AppIconModifier } from "./AppIconModifier";
|
||||
import { MatrixWS } from "./MatrixWS";
|
||||
import { RoomSelector } from "./RoomSelector";
|
||||
import { RoomWidget } from "./RoomWidget";
|
||||
@@ -70,6 +71,11 @@ function _MainMessageWidget(p: {
|
||||
);
|
||||
}, [space, p.rooms]);
|
||||
|
||||
const unreadRooms = React.useMemo(
|
||||
() => p.rooms.filter((r) => r.number_unread_messages > 0).length,
|
||||
[p.rooms]
|
||||
);
|
||||
|
||||
const [_refreshCount, setRefreshCount] = React.useState(0);
|
||||
const [roomMgr, setRoomMgr] = React.useState<undefined | RoomEventsManager>();
|
||||
|
||||
@@ -84,6 +90,7 @@ function _MainMessageWidget(p: {
|
||||
setRoomMgr(mgr);
|
||||
};
|
||||
|
||||
const [wsState, setWsState] = React.useState("");
|
||||
const handleWsEvent = (m: WsMessage) => {
|
||||
// Process messages for current room
|
||||
if (roomMgr?.processWsMessage(m)) {
|
||||
@@ -114,9 +121,12 @@ function _MainMessageWidget(p: {
|
||||
<div style={{ display: "flex", height: "100%" }}>
|
||||
{/* Websocket */}
|
||||
<div style={{ position: "absolute", right: "0px", padding: "10px" }}>
|
||||
<MatrixWS onMessage={handleWsEvent} />
|
||||
<MatrixWS onMessage={handleWsEvent} onStateChange={setWsState} />
|
||||
</div>
|
||||
|
||||
{/** Application icon modifier */}
|
||||
<AppIconModifier numberUnread={unreadRooms} state={wsState} />
|
||||
|
||||
{/* Space selector */}
|
||||
<SpaceSelector {...p} selectedSpace={space} onChange={setSpace} />
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useSnackbar } from "../../hooks/contexts_provider/SnackbarProvider";
|
||||
import CircleIcon from "@mui/icons-material/Circle";
|
||||
import { Tooltip } from "@mui/material";
|
||||
|
||||
const State = {
|
||||
export const WSState = {
|
||||
Closed: "Closed",
|
||||
Connected: "Connected",
|
||||
Error: "Error",
|
||||
@@ -12,6 +12,7 @@ const State = {
|
||||
|
||||
export function MatrixWS(p: {
|
||||
onMessage: (msg: WsMessage) => void;
|
||||
onStateChange?: (state: string) => void;
|
||||
}): React.ReactElement {
|
||||
const snackbar = useSnackbar();
|
||||
|
||||
@@ -21,7 +22,13 @@ export function MatrixWS(p: {
|
||||
cbRef.current = p.onMessage;
|
||||
}, [p.onMessage]);
|
||||
|
||||
const [state, setState] = React.useState<string>(State.Closed);
|
||||
// Keep only the latest version of onStateChange
|
||||
const stateCbRef = React.useRef(p.onStateChange);
|
||||
React.useEffect(() => {
|
||||
stateCbRef.current = p.onStateChange;
|
||||
}, [p.onStateChange]);
|
||||
|
||||
const [state, setState] = React.useState<string>(WSState.Closed);
|
||||
const wsId = React.useRef<number | undefined>(undefined);
|
||||
const [connCount, setConnCount] = React.useState(0);
|
||||
|
||||
@@ -30,23 +37,35 @@ export function MatrixWS(p: {
|
||||
const ws = new WebSocket(WsApi.WsURL);
|
||||
wsId.current = id;
|
||||
|
||||
ws.onopen = () => setState(State.Connected);
|
||||
// Open
|
||||
ws.onopen = () => {
|
||||
if (wsId.current != id) return;
|
||||
|
||||
setState(WSState.Connected);
|
||||
stateCbRef.current?.(WSState.Connected);
|
||||
};
|
||||
|
||||
// Error
|
||||
ws.onerror = (e) => {
|
||||
if (wsId.current != id) return;
|
||||
|
||||
console.error(`WS Debug error!`, e);
|
||||
snackbar(`WebSocket error!`);
|
||||
setState(State.Error);
|
||||
setState(WSState.Error);
|
||||
stateCbRef.current?.(WSState.Error);
|
||||
|
||||
setTimeout(() => setConnCount(connCount + 1), 500);
|
||||
};
|
||||
|
||||
// Close
|
||||
ws.onclose = () => {
|
||||
if (wsId.current !== id) return;
|
||||
setState(State.Closed);
|
||||
setState(WSState.Closed);
|
||||
stateCbRef.current?.(WSState.Closed);
|
||||
wsId.current = undefined;
|
||||
};
|
||||
|
||||
// Message
|
||||
ws.onmessage = (msg) => {
|
||||
if (wsId.current !== id) return;
|
||||
|
||||
@@ -62,9 +81,9 @@ export function MatrixWS(p: {
|
||||
<Tooltip title={state}>
|
||||
<CircleIcon
|
||||
color={
|
||||
state === State.Connected
|
||||
state === WSState.Connected
|
||||
? "success"
|
||||
: state === State.Error
|
||||
: state === WSState.Error
|
||||
? "error"
|
||||
: undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user