fix: presence MaxListeners leak + self-host fonts to fix tracking prevention
Presence: subscribe on MatrixClient (mx) instead of individual User objects. User EventEmitters have a 10-listener default; the same user appears in many mounted components simultaneously (member list, avatars, presence rings) and was accumulating 11+ listeners per event, causing MaxListenersExceededWarning on User.presence, User.currentlyActive, and User.lastPresenceTs. Fonts: download JetBrains Mono and Fira Code latin subsets to public/fonts/ and replace Google Fonts CDN links (two external origins) with a local CSS file. Windows WebView2 tracking prevention was blocking googleapis.com and gstatic.com, logging 18+ "blocked" warnings per session. VT323 remains on Google Fonts as it's decorative and not a default option. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -30,9 +30,10 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600&family=JetBrains+Mono:ital,wght@0,400;0,600;0,700;1,400&family=VT323&display=swap"
|
||||
href="https://fonts.googleapis.com/css2?family=VT323&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="stylesheet" href="/fonts/custom-fonts.css" />
|
||||
<link id="favicon" rel="shortcut icon" href="./public/favicon.ico" />
|
||||
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang=en>
|
||||
<meta charset=utf-8>
|
||||
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
|
||||
<title>Error 404 (Not Found)!!1</title>
|
||||
<style>
|
||||
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
|
||||
</style>
|
||||
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
|
||||
<p><b>404.</b> <ins>That’s an error.</ins>
|
||||
<p>The requested URL <code>/s/jetbrainsmono/v18/tDbY2o-flEEny0FZhsfKu5WU4xD-IQ.woff2</code> was not found on this server. <ins>That’s all we know.</ins>
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang=en>
|
||||
<meta charset=utf-8>
|
||||
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
|
||||
<title>Error 404 (Not Found)!!1</title>
|
||||
<style>
|
||||
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
|
||||
</style>
|
||||
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
|
||||
<p><b>404.</b> <ins>That’s an error.</ins>
|
||||
<p>The requested URL <code>/s/jetbrainsmono/v18/tDbY2o-flEEny0FZhsfKu5WU4xD-IQ.woff2</code> was not found on this server. <ins>That’s all we know.</ins>
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang=en>
|
||||
<meta charset=utf-8>
|
||||
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
|
||||
<title>Error 404 (Not Found)!!1</title>
|
||||
<style>
|
||||
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
|
||||
</style>
|
||||
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
|
||||
<p><b>404.</b> <ins>That’s an error.</ins>
|
||||
<p>The requested URL <code>/s/jetbrainsmono/v18/tDbY2o-flEEny0FZhsfKu5WU4xD-IQ.woff2</code> was not found on this server. <ins>That’s all we know.</ins>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
/* Self-hosted fonts — avoids tracking prevention in desktop WebView2 */
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/JetBrainsMono-italic-400.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/JetBrainsMono-normal-400.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('/fonts/JetBrainsMono-normal-700.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Fira Code';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/FiraCode-400.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Fira Code';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url('/fonts/FiraCode-600.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@@ -29,20 +29,24 @@ export const useUserPresence = (userId: string): UserPresence | undefined => {
|
||||
const [presence, setPresence] = useState(() => (user ? getUserPresence(user) : undefined));
|
||||
|
||||
useEffect(() => {
|
||||
// Subscribe on mx (MatrixClient) rather than on individual User objects.
|
||||
// User objects have a default 10-listener limit; the same user can appear
|
||||
// in many components simultaneously (avatars, member list, etc.) and
|
||||
// per-user subscription causes MaxListenersExceededWarning at 11+.
|
||||
const updatePresence: UserEventHandlerMap[UserEvent.Presence] = (event, u) => {
|
||||
if (u.userId === user?.userId) {
|
||||
setPresence(getUserPresence(user));
|
||||
setPresence(getUserPresence(u));
|
||||
}
|
||||
};
|
||||
user?.on(UserEvent.Presence, updatePresence);
|
||||
user?.on(UserEvent.CurrentlyActive, updatePresence);
|
||||
user?.on(UserEvent.LastPresenceTs, updatePresence);
|
||||
mx.on(UserEvent.Presence, updatePresence);
|
||||
mx.on(UserEvent.CurrentlyActive, updatePresence);
|
||||
mx.on(UserEvent.LastPresenceTs, updatePresence);
|
||||
return () => {
|
||||
user?.removeListener(UserEvent.Presence, updatePresence);
|
||||
user?.removeListener(UserEvent.CurrentlyActive, updatePresence);
|
||||
user?.removeListener(UserEvent.LastPresenceTs, updatePresence);
|
||||
mx.removeListener(UserEvent.Presence, updatePresence);
|
||||
mx.removeListener(UserEvent.CurrentlyActive, updatePresence);
|
||||
mx.removeListener(UserEvent.LastPresenceTs, updatePresence);
|
||||
};
|
||||
}, [user]);
|
||||
}, [mx, user]);
|
||||
|
||||
return presence;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user