diff --git a/matrixgw_backend/src/controllers/matrix/matrix_event_controller.rs b/matrixgw_backend/src/controllers/matrix/matrix_event_controller.rs
index da2a1ee..efbe9e0 100644
--- a/matrixgw_backend/src/controllers/matrix/matrix_event_controller.rs
+++ b/matrixgw_backend/src/controllers/matrix/matrix_event_controller.rs
@@ -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,
diff --git a/matrixgw_frontend/package-lock.json b/matrixgw_frontend/package-lock.json
index 8da93cc..254aaa2 100644
--- a/matrixgw_frontend/package-lock.json
+++ b/matrixgw_frontend/package-lock.json
@@ -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",
diff --git a/matrixgw_frontend/package.json b/matrixgw_frontend/package.json
index 7beadb3..fb5008d 100644
--- a/matrixgw_frontend/package.json
+++ b/matrixgw_frontend/package.json
@@ -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"
},
diff --git a/matrixgw_frontend/src/widgets/messages/AppIconModifier.tsx b/matrixgw_frontend/src/widgets/messages/AppIconModifier.tsx
new file mode 100644
index 0000000..4d10089
--- /dev/null
+++ b/matrixgw_frontend/src/widgets/messages/AppIconModifier.tsx
@@ -0,0 +1,26 @@
+import Favicon from "react-favicon";
+
+// 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;
+}): React.ReactElement {
+ return ;
+}
diff --git a/matrixgw_frontend/src/widgets/messages/MainMessagesWidget.tsx b/matrixgw_frontend/src/widgets/messages/MainMessagesWidget.tsx
index f6ac333..8d87f46 100644
--- a/matrixgw_frontend/src/widgets/messages/MainMessagesWidget.tsx
+++ b/matrixgw_frontend/src/widgets/messages/MainMessagesWidget.tsx
@@ -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();
@@ -117,6 +123,9 @@ function _MainMessageWidget(p: {
+ {/** Application icon modifier */}
+
+
{/* Space selector */}