From 96df1401535ccea14eae18937ac4a1cf0503e46e Mon Sep 17 00:00:00 2001
From: Ajay Bura <32841439+ajbura@users.noreply.github.com>
Date: Mon, 9 Sep 2024 18:45:20 +1000
Subject: [PATCH] Improve-auth-media (#1933)

* fix set power level broken after sdk update

* add media authentication hook

* fix service worker types

* fix service worker not working in dev mode

* fix env mode check when registering sw
---
 package-lock.json                                 | 11 +----------
 package.json                                      |  1 -
 src/app/components/editor/Elements.tsx            |  5 ++---
 .../editor/autocomplete/EmoticonAutocomplete.tsx  |  5 ++---
 .../autocomplete/UserMentionAutocomplete.tsx      |  9 +++++----
 src/app/components/emoji-board/EmojiBoard.tsx     |  5 ++---
 src/app/components/event-readers/EventReaders.tsx |  5 ++---
 .../components/message/content/AudioContent.tsx   |  5 ++---
 .../components/message/content/FileContent.tsx    | 11 ++++-------
 .../components/message/content/ImageContent.tsx   |  5 ++---
 .../message/content/ThumbnailContent.tsx          |  5 ++---
 .../components/message/content/VideoContent.tsx   |  5 ++---
 src/app/components/room-card/RoomCard.tsx         |  5 ++---
 src/app/components/room-intro/RoomIntro.tsx       |  5 ++---
 src/app/components/url-preview/UrlPreviewCard.tsx |  5 ++---
 src/app/features/lobby/LobbyHeader.tsx            |  5 ++---
 src/app/features/lobby/LobbyHero.tsx              |  5 ++---
 src/app/features/lobby/RoomItem.tsx               |  5 ++---
 src/app/features/lobby/SpaceItem.tsx              |  5 ++---
 .../features/message-search/SearchResultGroup.tsx |  5 ++---
 src/app/features/room-nav/RoomNavItem.tsx         |  5 ++---
 src/app/features/room/MembersDrawer.tsx           |  5 ++---
 src/app/features/room/RoomInput.tsx               |  5 ++---
 src/app/features/room/RoomTimeline.tsx            |  5 ++---
 src/app/features/room/RoomViewHeader.tsx          |  5 ++---
 src/app/features/room/message/Message.tsx         |  5 ++---
 src/app/features/room/message/Reactions.tsx       |  5 ++---
 .../room/reaction-viewer/ReactionViewer.tsx       |  5 ++---
 src/app/hooks/useMediaAuthentication.ts           | 10 ++++++++++
 src/app/pages/client/ClientNonUIFeatures.tsx      |  6 +++---
 src/app/pages/client/inbox/Invites.tsx            |  5 ++---
 src/app/pages/client/inbox/Notifications.tsx      |  5 ++---
 src/app/pages/client/sidebar/SpaceTabs.tsx        |  8 +++-----
 src/app/pages/client/sidebar/UserTab.tsx          |  5 ++---
 src/client/action/room.js                         |  6 +-----
 src/index.tsx                                     | 15 +++++++++------
 src/sw.ts                                         |  8 ++++++--
 vite.config.js                                    |  4 ++++
 38 files changed, 100 insertions(+), 124 deletions(-)
 create mode 100644 src/app/hooks/useMediaAuthentication.ts

diff --git a/package-lock.json b/package-lock.json
index 1dcff435..9c4520ae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -79,7 +79,6 @@
         "@types/react-dom": "18.2.17",
         "@types/react-google-recaptcha": "2.1.8",
         "@types/sanitize-html": "2.9.0",
-        "@types/serviceworker": "0.0.95",
         "@types/ua-parser-js": "0.7.36",
         "@typescript-eslint/eslint-plugin": "5.46.1",
         "@typescript-eslint/parser": "5.46.1",
@@ -5036,13 +5035,6 @@
       "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
       "dev": true
     },
-    "node_modules/@types/serviceworker": {
-      "version": "0.0.95",
-      "resolved": "https://registry.npmjs.org/@types/serviceworker/-/serviceworker-0.0.95.tgz",
-      "integrity": "sha512-Zw7kLIehLvaXf/9RnxAUiYyHmYC5pfvIJD3b1uSPkZGzp+OVmXgmPzVW5fbhYHKcqkeGzsv89uGm+JmMCAPa8Q==",
-      "dev": true,
-      "license": "Apache-2.0"
-    },
     "node_modules/@types/trusted-types": {
       "version": "2.0.7",
       "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@@ -11514,7 +11506,6 @@
       "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.20.5.tgz",
       "integrity": "sha512-aweuI/6G6n4C5Inn0vwHumElU/UEpNuO+9iZzwPZGTCH87TeZ6YFMrEY6ZUBQdIHHlhTsbMDryFARcSuOdsz9Q==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "debug": "^4.3.6",
         "pretty-bytes": "^6.1.1",
@@ -12446,4 +12437,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index 09efccb7..ecc52c8b 100644
--- a/package.json
+++ b/package.json
@@ -90,7 +90,6 @@
     "@types/react-dom": "18.2.17",
     "@types/react-google-recaptcha": "2.1.8",
     "@types/sanitize-html": "2.9.0",
-    "@types/serviceworker": "0.0.95",
     "@types/ua-parser-js": "0.7.36",
     "@typescript-eslint/eslint-plugin": "5.46.1",
     "@typescript-eslint/parser": "5.46.1",
diff --git a/src/app/components/editor/Elements.tsx b/src/app/components/editor/Elements.tsx
index aee50ac8..a7438ecd 100644
--- a/src/app/components/editor/Elements.tsx
+++ b/src/app/components/editor/Elements.tsx
@@ -14,7 +14,7 @@ import { useMatrixClient } from '../../hooks/useMatrixClient';
 import { getBeginCommand } from './utils';
 import { BlockType } from './types';
 import { mxcUrlToHttp } from '../../utils/matrix';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 // Put this at the start and end of an inline component to work around this Chromium bug:
 // https://bugs.chromium.org/p/chromium/issues/detail?id=1249405
@@ -78,8 +78,7 @@ function RenderEmoticonElement({
   children,
 }: { element: EmoticonElement } & RenderElementProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const selected = useSelected();
   const focused = useFocused();
 
diff --git a/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx b/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx
index b77ae746..9722c795 100644
--- a/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx
+++ b/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx
@@ -19,7 +19,7 @@ import { IEmoji, emojis } from '../../../plugins/emoji';
 import { ExtendedPackImage, PackUsage } from '../../../plugins/custom-emoji';
 import { useKeyDown } from '../../../hooks/useKeyDown';
 import { mxcUrlToHttp } from '../../../utils/matrix';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 type EmoticonCompleteHandler = (key: string, shortcode: string) => void;
 
@@ -50,8 +50,7 @@ export function EmoticonAutocomplete({
   requestClose,
 }: EmoticonAutocompleteProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
 
   const imagePacks = useRelevantImagePacks(mx, PackUsage.Emoticon, imagePackRooms);
   const recentEmoji = useRecentEmoji(mx, 20);
diff --git a/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx
index 05d303a2..7794bcd1 100644
--- a/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx
+++ b/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx
@@ -18,7 +18,7 @@ import { useKeyDown } from '../../../hooks/useKeyDown';
 import { getMxIdLocalPart, getMxIdServer, validMxId } from '../../../utils/matrix';
 import { getMemberDisplayName, getMemberSearchStr } from '../../../utils/room';
 import { UserAvatar } from '../../user-avatar';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 type MentionAutoCompleteHandler = (userId: string, name: string) => void;
 
@@ -85,8 +85,7 @@ export function UserMentionAutocomplete({
   requestClose,
 }: UserMentionAutocompleteProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const roomId: string = room.roomId!;
   const roomAliasOrId = room.getCanonicalAlias() || roomId;
   const members = useRoomMembers(mx, roomId);
@@ -147,7 +146,9 @@ export function UserMentionAutocomplete({
       ) : (
         autoCompleteMembers.map((roomMember) => {
           const avatarMxcUrl = roomMember.getMxcAvatarUrl();
-          const avatarUrl = avatarMxcUrl ? mx.mxcUrlToHttp(avatarMxcUrl, 32, 32, 'crop', undefined, false, useAuthentication) : undefined;
+          const avatarUrl = avatarMxcUrl
+            ? mx.mxcUrlToHttp(avatarMxcUrl, 32, 32, 'crop', undefined, false, useAuthentication)
+            : undefined;
           return (
             <MenuItem
               key={roomMember.userId}
diff --git a/src/app/components/emoji-board/EmojiBoard.tsx b/src/app/components/emoji-board/EmojiBoard.tsx
index 46015acd..5a69df18 100644
--- a/src/app/components/emoji-board/EmojiBoard.tsx
+++ b/src/app/components/emoji-board/EmojiBoard.tsx
@@ -49,7 +49,7 @@ import { useDebounce } from '../../hooks/useDebounce';
 import { useThrottle } from '../../hooks/useThrottle';
 import { addRecentEmoji } from '../../plugins/recent-emoji';
 import { mobileOrTablet } from '../../utils/user-agent';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 const RECENT_GROUP_ID = 'recent_group';
 const SEARCH_GROUP_ID = 'search_group';
@@ -650,8 +650,7 @@ export function EmojiBoard({
 
   const setActiveGroupId = useSetAtom(activeGroupIdAtom);
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const emojiGroupLabels = useEmojiGroupLabels();
   const emojiGroupIcons = useEmojiGroupIcons();
   const imagePacks = useRelevantImagePacks(mx, usage, imagePackRooms);
diff --git a/src/app/components/event-readers/EventReaders.tsx b/src/app/components/event-readers/EventReaders.tsx
index a6163b8b..de1416b6 100644
--- a/src/app/components/event-readers/EventReaders.tsx
+++ b/src/app/components/event-readers/EventReaders.tsx
@@ -21,7 +21,7 @@ import * as css from './EventReaders.css';
 import { useMatrixClient } from '../../hooks/useMatrixClient';
 import { openProfileViewer } from '../../../client/action/navigation';
 import { UserAvatar } from '../user-avatar';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 export type EventReadersProps = {
   room: Room;
@@ -31,8 +31,7 @@ export type EventReadersProps = {
 export const EventReaders = as<'div', EventReadersProps>(
   ({ className, room, eventId, requestClose, ...props }, ref) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const latestEventReaders = useRoomEventReaders(room, eventId);
 
     const getName = (userId: string) =>
diff --git a/src/app/components/message/content/AudioContent.tsx b/src/app/components/message/content/AudioContent.tsx
index 38fa9515..b426654f 100644
--- a/src/app/components/message/content/AudioContent.tsx
+++ b/src/app/components/message/content/AudioContent.tsx
@@ -18,7 +18,7 @@ import {
 import { useThrottle } from '../../../hooks/useThrottle';
 import { secondsToMinutesAndSeconds } from '../../../utils/common';
 import { mxcUrlToHttp } from '../../../utils/matrix';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 const PLAY_TIME_THROTTLE_OPS = {
   wait: 500,
@@ -46,8 +46,7 @@ export function AudioContent({
   renderMediaControl,
 }: AudioContentProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
 
   const [srcState, loadSrc] = useAsyncCallback(
     useCallback(
diff --git a/src/app/components/message/content/FileContent.tsx b/src/app/components/message/content/FileContent.tsx
index 379d1456..62b8c56d 100644
--- a/src/app/components/message/content/FileContent.tsx
+++ b/src/app/components/message/content/FileContent.tsx
@@ -31,7 +31,7 @@ import {
 import * as css from './style.css';
 import { stopPropagation } from '../../../utils/keyboard';
 import { mxcUrlToHttp } from '../../../utils/matrix';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 const renderErrorButton = (retry: () => void, text: string) => (
   <TooltipProvider
@@ -77,8 +77,7 @@ type ReadTextFileProps = {
 };
 export function ReadTextFile({ body, mimeType, url, encInfo, renderViewer }: ReadTextFileProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const [textViewer, setTextViewer] = useState(false);
 
   const loadSrc = useCallback(
@@ -170,8 +169,7 @@ export type ReadPdfFileProps = {
 };
 export function ReadPdfFile({ body, mimeType, url, encInfo, renderViewer }: ReadPdfFileProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const [pdfViewer, setPdfViewer] = useState(false);
 
   const [pdfState, loadPdf] = useAsyncCallback(
@@ -246,8 +244,7 @@ export type DownloadFileProps = {
 };
 export function DownloadFile({ body, mimeType, url, info, encInfo }: DownloadFileProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
 
   const [downloadState, download] = useAsyncCallback(
     useCallback(async () => {
diff --git a/src/app/components/message/content/ImageContent.tsx b/src/app/components/message/content/ImageContent.tsx
index 798c60c5..d0a31c39 100644
--- a/src/app/components/message/content/ImageContent.tsx
+++ b/src/app/components/message/content/ImageContent.tsx
@@ -28,7 +28,7 @@ import { bytesToSize } from '../../../utils/common';
 import { FALLBACK_MIMETYPE } from '../../../utils/mimeTypes';
 import { stopPropagation } from '../../../utils/keyboard';
 import { mxcUrlToHttp } from '../../../utils/matrix';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 type RenderViewerProps = {
   src: string;
@@ -71,8 +71,7 @@ export const ImageContent = as<'div', ImageContentProps>(
     ref
   ) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const blurHash = info?.[MATRIX_BLUR_HASH_PROPERTY_NAME];
 
     const [load, setLoad] = useState(false);
diff --git a/src/app/components/message/content/ThumbnailContent.tsx b/src/app/components/message/content/ThumbnailContent.tsx
index 4324376d..b667e57e 100644
--- a/src/app/components/message/content/ThumbnailContent.tsx
+++ b/src/app/components/message/content/ThumbnailContent.tsx
@@ -4,7 +4,7 @@ import { useMatrixClient } from '../../../hooks/useMatrixClient';
 import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
 import { getFileSrcUrl } from './util';
 import { mxcUrlToHttp } from '../../../utils/matrix';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 export type ThumbnailContentProps = {
   info: IThumbnailContent;
@@ -12,8 +12,7 @@ export type ThumbnailContentProps = {
 };
 export function ThumbnailContent({ info, renderImage }: ThumbnailContentProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
 
   const [thumbSrcState, loadThumbSrc] = useAsyncCallback(
     useCallback(() => {
diff --git a/src/app/components/message/content/VideoContent.tsx b/src/app/components/message/content/VideoContent.tsx
index e83e4595..46f82fae 100644
--- a/src/app/components/message/content/VideoContent.tsx
+++ b/src/app/components/message/content/VideoContent.tsx
@@ -26,7 +26,7 @@ import { getFileSrcUrl } from './util';
 import { bytesToSize } from '../../../../util/common';
 import { millisecondsToMinutesAndSeconds } from '../../../utils/common';
 import { mxcUrlToHttp } from '../../../utils/matrix';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 type RenderVideoProps = {
   title: string;
@@ -63,8 +63,7 @@ export const VideoContent = as<'div', VideoContentProps>(
     ref
   ) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const blurHash = info.thumbnail_info?.[MATRIX_BLUR_HASH_PROPERTY_NAME];
 
     const [load, setLoad] = useState(false);
diff --git a/src/app/components/room-card/RoomCard.tsx b/src/app/components/room-card/RoomCard.tsx
index 1ca7813f..34a7e24b 100644
--- a/src/app/components/room-card/RoomCard.tsx
+++ b/src/app/components/room-card/RoomCard.tsx
@@ -32,7 +32,7 @@ import { useJoinedRoomId } from '../../hooks/useJoinedRoomId';
 import { useElementSizeObserver } from '../../hooks/useElementSizeObserver';
 import { getRoomAvatarUrl, getStateEvent } from '../../utils/room';
 import { useStateEventCallback } from '../../hooks/useStateEventCallback';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 type GridColumnCount = '1' | '2' | '3';
 const getGridColumnCount = (gridWidth: number): GridColumnCount => {
@@ -162,8 +162,7 @@ export const RoomCard = as<'div', RoomCardProps>(
     ref
   ) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const joinedRoomId = useJoinedRoomId(allRooms, roomIdOrAlias);
     const joinedRoom = mx.getRoom(joinedRoomId);
     const [topicEvent, setTopicEvent] = useState(() =>
diff --git a/src/app/components/room-intro/RoomIntro.tsx b/src/app/components/room-intro/RoomIntro.tsx
index 28be5daf..9e1a4f12 100644
--- a/src/app/components/room-intro/RoomIntro.tsx
+++ b/src/app/components/room-intro/RoomIntro.tsx
@@ -14,7 +14,7 @@ import { RoomAvatar } from '../room-avatar';
 import { nameInitials } from '../../utils/common';
 import { useRoomAvatar, useRoomName, useRoomTopic } from '../../hooks/useRoomMeta';
 import { mDirectAtom } from '../../state/mDirectList';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 export type RoomIntroProps = {
   room: Room;
@@ -22,8 +22,7 @@ export type RoomIntroProps = {
 
 export const RoomIntro = as<'div', RoomIntroProps>(({ room, ...props }, ref) => {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const { navigateRoom } = useRoomNavigate();
   const mDirects = useAtomValue(mDirectAtom);
 
diff --git a/src/app/components/url-preview/UrlPreviewCard.tsx b/src/app/components/url-preview/UrlPreviewCard.tsx
index c33b9850..2a51969a 100644
--- a/src/app/components/url-preview/UrlPreviewCard.tsx
+++ b/src/app/components/url-preview/UrlPreviewCard.tsx
@@ -11,15 +11,14 @@ import {
 import * as css from './UrlPreviewCard.css';
 import { tryDecodeURIComponent } from '../../utils/dom';
 import { mxcUrlToHttp } from '../../utils/matrix';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 const linkStyles = { color: color.Success.Main };
 
 export const UrlPreviewCard = as<'div', { url: string; ts: number }>(
   ({ url, ts, ...props }, ref) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const [previewStatus, loadPreview] = useAsyncCallback(
       useCallback(() => mx.getUrlPreview(url, ts), [url, ts, mx])
     );
diff --git a/src/app/features/lobby/LobbyHeader.tsx b/src/app/features/lobby/LobbyHeader.tsx
index ed5373b3..6ef0044b 100644
--- a/src/app/features/lobby/LobbyHeader.tsx
+++ b/src/app/features/lobby/LobbyHeader.tsx
@@ -34,7 +34,7 @@ import { stopPropagation } from '../../utils/keyboard';
 import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
 import { BackRouteHandler } from '../../components/BackRouteHandler';
 import { mxcUrlToHttp } from '../../utils/matrix';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 type LobbyMenuProps = {
   roomId: string;
@@ -124,8 +124,7 @@ type LobbyHeaderProps = {
 };
 export function LobbyHeader({ showProfile, powerLevels }: LobbyHeaderProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const space = useSpace();
   const setPeopleDrawer = useSetSetting(settingsAtom, 'isPeopleDrawer');
   const [menuAnchor, setMenuAnchor] = useState<RectCords>();
diff --git a/src/app/features/lobby/LobbyHero.tsx b/src/app/features/lobby/LobbyHero.tsx
index e652fd91..1cd0b8e2 100644
--- a/src/app/features/lobby/LobbyHero.tsx
+++ b/src/app/features/lobby/LobbyHero.tsx
@@ -12,12 +12,11 @@ import * as css from './LobbyHero.css';
 import { PageHero } from '../../components/page';
 import { onEnterOrSpace, stopPropagation } from '../../utils/keyboard';
 import { mxcUrlToHttp } from '../../utils/matrix';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 export function LobbyHero() {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const space = useSpace();
 
   const name = useRoomName(space);
diff --git a/src/app/features/lobby/RoomItem.tsx b/src/app/features/lobby/RoomItem.tsx
index 14852b3d..f8db3991 100644
--- a/src/app/features/lobby/RoomItem.tsx
+++ b/src/app/features/lobby/RoomItem.tsx
@@ -40,7 +40,7 @@ import { ErrorCode } from '../../cs-errorcode';
 import { getDirectRoomAvatarUrl, getRoomAvatarUrl } from '../../utils/room';
 import { ItemDraggableTarget, useDraggableItem } from './DnD';
 import { mxcUrlToHttp } from '../../utils/matrix';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 type RoomJoinButtonProps = {
   roomId: string;
@@ -336,8 +336,7 @@ export const RoomItemCard = as<'div', RoomItemCardProps>(
     ref
   ) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const { roomId, content } = item;
     const room = getRoom(roomId);
     const targetRef = useRef<HTMLDivElement>(null);
diff --git a/src/app/features/lobby/SpaceItem.tsx b/src/app/features/lobby/SpaceItem.tsx
index 3e3c3214..deaf9ba5 100644
--- a/src/app/features/lobby/SpaceItem.tsx
+++ b/src/app/features/lobby/SpaceItem.tsx
@@ -36,7 +36,7 @@ import { useDraggableItem } from './DnD';
 import { openCreateRoom, openSpaceAddExisting } from '../../../client/action/navigation';
 import { stopPropagation } from '../../utils/keyboard';
 import { mxcUrlToHttp } from '../../utils/matrix';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 function SpaceProfileLoading() {
   return (
@@ -410,8 +410,7 @@ export const SpaceItemCard = as<'div', SpaceItemCardProps>(
     ref
   ) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const { roomId, content } = item;
     const space = getRoom(roomId);
     const targetRef = useRef<HTMLDivElement>(null);
diff --git a/src/app/features/message-search/SearchResultGroup.tsx b/src/app/features/message-search/SearchResultGroup.tsx
index a3a67eda..29fce7bf 100644
--- a/src/app/features/message-search/SearchResultGroup.tsx
+++ b/src/app/features/message-search/SearchResultGroup.tsx
@@ -38,7 +38,7 @@ import { SequenceCard } from '../../components/sequence-card';
 import { UserAvatar } from '../../components/user-avatar';
 import { useMentionClickHandler } from '../../hooks/useMentionClickHandler';
 import { useSpoilerClickHandler } from '../../hooks/useSpoilerClickHandler';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 type SearchResultGroupProps = {
   room: Room;
@@ -57,8 +57,7 @@ export function SearchResultGroup({
   onOpen,
 }: SearchResultGroupProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const highlightRegex = useMemo(() => makeHighlightRegex(highlights), [highlights]);
 
   const mentionClickHandler = useMentionClickHandler(room.roomId);
diff --git a/src/app/features/room-nav/RoomNavItem.tsx b/src/app/features/room-nav/RoomNavItem.tsx
index c525ab2a..19d04f35 100644
--- a/src/app/features/room-nav/RoomNavItem.tsx
+++ b/src/app/features/room-nav/RoomNavItem.tsx
@@ -38,7 +38,7 @@ import { stopPropagation } from '../../utils/keyboard';
 import { getMatrixToRoom } from '../../plugins/matrix-to';
 import { getCanonicalAliasOrRoomId, isRoomAlias } from '../../utils/matrix';
 import { getViaServers } from '../../plugins/via-servers';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 type RoomNavItemMenuProps = {
   room: Room;
@@ -176,8 +176,7 @@ export function RoomNavItem({
   linkPath,
 }: RoomNavItemProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const [hover, setHover] = useState(false);
   const { hoverProps } = useHover({ onHoverChange: setHover });
   const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
diff --git a/src/app/features/room/MembersDrawer.tsx b/src/app/features/room/MembersDrawer.tsx
index 1b538ffc..a4305e45 100644
--- a/src/app/features/room/MembersDrawer.tsx
+++ b/src/app/features/room/MembersDrawer.tsx
@@ -55,7 +55,7 @@ import { ScrollTopContainer } from '../../components/scroll-top-container';
 import { UserAvatar } from '../../components/user-avatar';
 import { useRoomTypingMember } from '../../hooks/useRoomTypingMembers';
 import { stopPropagation } from '../../utils/keyboard';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 export const MembershipFilters = {
   filterJoined: (m: RoomMember) => m.membership === Membership.Join,
@@ -172,8 +172,7 @@ type MembersDrawerProps = {
 };
 export function MembersDrawer({ room, members }: MembersDrawerProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const scrollRef = useRef<HTMLDivElement>(null);
   const searchInputRef = useRef<HTMLInputElement>(null);
   const scrollTopAnchorRef = useRef<HTMLDivElement>(null);
diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx
index 03bc9655..8fcfbc97 100644
--- a/src/app/features/room/RoomInput.tsx
+++ b/src/app/features/room/RoomInput.tsx
@@ -108,7 +108,7 @@ import { mobileOrTablet } from '../../utils/user-agent';
 import { useElementSizeObserver } from '../../hooks/useElementSizeObserver';
 import { ReplyLayout, ThreadIndicator } from '../../components/message';
 import { roomToParentsAtom } from '../../state/room/roomToParents';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 interface RoomInputProps {
   editor: Editor;
@@ -119,8 +119,7 @@ interface RoomInputProps {
 export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
   ({ editor, fileDropContainerRef, roomId, room }, ref) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const [enterForNewline] = useSetting(settingsAtom, 'enterForNewline');
     const [isMarkdown] = useSetting(settingsAtom, 'isMarkdown');
     const commands = useCommands(mx, room);
diff --git a/src/app/features/room/RoomTimeline.tsx b/src/app/features/room/RoomTimeline.tsx
index c47cb35b..34f54868 100644
--- a/src/app/features/room/RoomTimeline.tsx
+++ b/src/app/features/room/RoomTimeline.tsx
@@ -122,7 +122,7 @@ import { roomToUnreadAtom } from '../../state/room/roomToUnread';
 import { useMentionClickHandler } from '../../hooks/useMentionClickHandler';
 import { useSpoilerClickHandler } from '../../hooks/useSpoilerClickHandler';
 import { useRoomNavigate } from '../../hooks/useRoomNavigate';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 const TimelineFloat = as<'div', css.TimelineFloatVariants>(
   ({ position, className, ...props }, ref) => (
@@ -438,8 +438,7 @@ const getRoomUnreadInfo = (room: Room, scrollTo = false) => {
 
 export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimelineProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const encryptedRoom = mx.isRoomEncrypted(room.roomId);
   const [messageLayout] = useSetting(settingsAtom, 'messageLayout');
   const [messageSpacing] = useSetting(settingsAtom, 'messageSpacing');
diff --git a/src/app/features/room/RoomViewHeader.tsx b/src/app/features/room/RoomViewHeader.tsx
index eaa90970..ae80deb6 100644
--- a/src/app/features/room/RoomViewHeader.tsx
+++ b/src/app/features/room/RoomViewHeader.tsx
@@ -53,7 +53,7 @@ import { stopPropagation } from '../../utils/keyboard';
 import { getMatrixToRoom } from '../../plugins/matrix-to';
 import { getViaServers } from '../../plugins/via-servers';
 import { BackRouteHandler } from '../../components/BackRouteHandler';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 type RoomMenuProps = {
   room: Room;
@@ -175,8 +175,7 @@ const RoomMenu = forwardRef<HTMLDivElement, RoomMenuProps>(({ room, requestClose
 export function RoomViewHeader() {
   const navigate = useNavigate();
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const screenSize = useScreenSizeContext();
   const room = useRoom();
   const space = useSpaceOptionally();
diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx
index 94a5825c..e9a2d797 100644
--- a/src/app/features/room/message/Message.tsx
+++ b/src/app/features/room/message/Message.tsx
@@ -67,7 +67,7 @@ import { copyToClipboard } from '../../../utils/dom';
 import { stopPropagation } from '../../../utils/keyboard';
 import { getMatrixToRoomEvent } from '../../../plugins/matrix-to';
 import { getViaServers } from '../../../plugins/via-servers';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 export type ReactionHandler = (keyOrMxc: string, shortcode: string) => void;
 
@@ -651,8 +651,7 @@ export const Message = as<'div', MessageProps>(
     ref
   ) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const senderId = mEvent.getSender() ?? '';
     const [hover, setHover] = useState(false);
     const { hoverProps } = useHover({ onHoverChange: setHover });
diff --git a/src/app/features/room/message/Reactions.tsx b/src/app/features/room/message/Reactions.tsx
index 258ab629..f0f308bb 100644
--- a/src/app/features/room/message/Reactions.tsx
+++ b/src/app/features/room/message/Reactions.tsx
@@ -22,7 +22,7 @@ import { useRelations } from '../../../hooks/useRelations';
 import * as css from './styles.css';
 import { ReactionViewer } from '../reaction-viewer';
 import { stopPropagation } from '../../../utils/keyboard';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 export type ReactionsProps = {
   room: Room;
@@ -34,8 +34,7 @@ export type ReactionsProps = {
 export const Reactions = as<'div', ReactionsProps>(
   ({ className, room, relations, mEventId, canSendReaction, onReactionToggle, ...props }, ref) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const [viewer, setViewer] = useState<boolean | string>(false);
     const myUserId = mx.getUserId();
     const reactions = useRelations(
diff --git a/src/app/features/room/reaction-viewer/ReactionViewer.tsx b/src/app/features/room/reaction-viewer/ReactionViewer.tsx
index 9e166a9e..d4b39845 100644
--- a/src/app/features/room/reaction-viewer/ReactionViewer.tsx
+++ b/src/app/features/room/reaction-viewer/ReactionViewer.tsx
@@ -25,7 +25,7 @@ import { useRelations } from '../../../hooks/useRelations';
 import { Reaction } from '../../../components/message';
 import { getHexcodeForEmoji, getShortcodeFor } from '../../../plugins/emoji';
 import { UserAvatar } from '../../../components/user-avatar';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 export type ReactionViewerProps = {
   room: Room;
@@ -36,8 +36,7 @@ export type ReactionViewerProps = {
 export const ReactionViewer = as<'div', ReactionViewerProps>(
   ({ className, room, initialKey, relations, requestClose, ...props }, ref) => {
     const mx = useMatrixClient();
-    const { versions } = useSpecVersions();
-    const useAuthentication = versions.includes('v1.11');
+    const useAuthentication = useMediaAuthentication();
     const reactions = useRelations(
       relations,
       useCallback((rel) => [...(rel.getSortedAnnotationsByKey() ?? [])], [])
diff --git a/src/app/hooks/useMediaAuthentication.ts b/src/app/hooks/useMediaAuthentication.ts
new file mode 100644
index 00000000..04b25e19
--- /dev/null
+++ b/src/app/hooks/useMediaAuthentication.ts
@@ -0,0 +1,10 @@
+import { useSpecVersions } from './useSpecVersions';
+
+export const useMediaAuthentication = (): boolean => {
+  const { versions } = useSpecVersions();
+
+  // Media authentication is introduced in spec version 1.11
+  const authenticatedMedia = versions.includes('v1.11');
+
+  return authenticatedMedia;
+};
diff --git a/src/app/pages/client/ClientNonUIFeatures.tsx b/src/app/pages/client/ClientNonUIFeatures.tsx
index 9f3f129b..8678e619 100644
--- a/src/app/pages/client/ClientNonUIFeatures.tsx
+++ b/src/app/pages/client/ClientNonUIFeatures.tsx
@@ -25,7 +25,7 @@ import { NotificationType, UnreadInfo } from '../../../types/matrix/room';
 import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
 import { useSelectedRoom } from '../../hooks/router/useSelectedRoom';
 import { useInboxNotificationsSelected } from '../../hooks/router/useInbox';
-import { useSpecVersions } from '../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
 
 function SystemEmojiFeature() {
   const [twitterEmoji] = useSetting(settingsAtom, 'twitterEmoji');
@@ -133,8 +133,7 @@ function MessageNotifications() {
   const notifRef = useRef<Notification>();
   const unreadCacheRef = useRef<Map<string, UnreadInfo>>(new Map());
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const [showNotifications] = useSetting(settingsAtom, 'showNotifications');
   const [notificationSound] = useSetting(settingsAtom, 'isNotificationSounds');
 
@@ -243,6 +242,7 @@ function MessageNotifications() {
     playSound,
     notify,
     selectedRoomId,
+    useAuthentication,
   ]);
 
   return (
diff --git a/src/app/pages/client/inbox/Invites.tsx b/src/app/pages/client/inbox/Invites.tsx
index 12ca65bb..8dcfa1c2 100644
--- a/src/app/pages/client/inbox/Invites.tsx
+++ b/src/app/pages/client/inbox/Invites.tsx
@@ -42,7 +42,7 @@ import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
 import { useRoomTopic } from '../../../hooks/useRoomMeta';
 import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
 import { BackRouteHandler } from '../../../components/BackRouteHandler';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 const COMPACT_CARD_WIDTH = 548;
 
@@ -55,8 +55,7 @@ type InviteCardProps = {
 };
 function InviteCard({ room, userId, direct, compact, onNavigate }: InviteCardProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const roomName = room.name || room.getCanonicalAlias() || room.roomId;
   const member = room.getMember(userId);
   const memberEvent = member?.events.member;
diff --git a/src/app/pages/client/inbox/Notifications.tsx b/src/app/pages/client/inbox/Notifications.tsx
index 95f9b9ff..64eabc99 100644
--- a/src/app/pages/client/inbox/Notifications.tsx
+++ b/src/app/pages/client/inbox/Notifications.tsx
@@ -81,7 +81,7 @@ import { useMentionClickHandler } from '../../../hooks/useMentionClickHandler';
 import { useSpoilerClickHandler } from '../../../hooks/useSpoilerClickHandler';
 import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
 import { BackRouteHandler } from '../../../components/BackRouteHandler';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 type RoomNotificationsGroup = {
   roomId: string;
@@ -192,8 +192,7 @@ function RoomNotificationsGroupComp({
   onOpen,
 }: RoomNotificationsGroupProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
   const mentionClickHandler = useMentionClickHandler(room.roomId);
   const spoilerClickHandler = useSpoilerClickHandler();
diff --git a/src/app/pages/client/sidebar/SpaceTabs.tsx b/src/app/pages/client/sidebar/SpaceTabs.tsx
index d63bb24d..343afae4 100644
--- a/src/app/pages/client/sidebar/SpaceTabs.tsx
+++ b/src/app/pages/client/sidebar/SpaceTabs.tsx
@@ -87,7 +87,7 @@ import { stopPropagation } from '../../../utils/keyboard';
 import { getMatrixToRoom } from '../../../plugins/matrix-to';
 import { getViaServers } from '../../../plugins/via-servers';
 import { getRoomAvatarUrl } from '../../../utils/room';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 type SpaceMenuProps = {
   room: Room;
@@ -381,8 +381,7 @@ function SpaceTab({
   onUnpin,
 }: SpaceTabProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const targetRef = useRef<HTMLDivElement>(null);
 
   const spaceDraggable: SidebarDraggable = useMemo(
@@ -528,8 +527,7 @@ function ClosedSpaceFolder({
   disabled,
 }: ClosedSpaceFolderProps) {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const handlerRef = useRef<HTMLDivElement>(null);
 
   const spaceDraggable: FolderDraggable = useMemo(() => ({ folder }), [folder]);
diff --git a/src/app/pages/client/sidebar/UserTab.tsx b/src/app/pages/client/sidebar/UserTab.tsx
index 09b3f251..5e6c82ab 100644
--- a/src/app/pages/client/sidebar/UserTab.tsx
+++ b/src/app/pages/client/sidebar/UserTab.tsx
@@ -7,7 +7,7 @@ import { UserAvatar } from '../../../components/user-avatar';
 import { useMatrixClient } from '../../../hooks/useMatrixClient';
 import { getMxIdLocalPart, mxcUrlToHttp } from '../../../utils/matrix';
 import { nameInitials } from '../../../utils/common';
-import { useSpecVersions } from '../../../hooks/useSpecVersions';
+import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
 
 type UserProfile = {
   avatar_url?: string;
@@ -15,8 +15,7 @@ type UserProfile = {
 };
 export function UserTab() {
   const mx = useMatrixClient();
-  const { versions } = useSpecVersions();
-  const useAuthentication = versions.includes('v1.11');
+  const useAuthentication = useMediaAuthentication();
   const userId = mx.getUserId()!;
 
   const [profile, setProfile] = useState<UserProfile>({});
diff --git a/src/client/action/room.js b/src/client/action/room.js
index cd4995b9..48ae7c47 100644
--- a/src/client/action/room.js
+++ b/src/client/action/room.js
@@ -244,11 +244,7 @@ async function unignore(mx, userIds) {
 }
 
 async function setPowerLevel(mx, roomId, userId, powerLevel) {
-  const room = mx.getRoom(roomId);
-
-  const powerlevelEvent = room.currentState.getStateEvents('m.room.power_levels')[0];
-
-  const result = await mx.setPowerLevel(roomId, userId, powerLevel, powerlevelEvent);
+  const result = await mx.setPowerLevel(roomId, userId, powerLevel);
   return result;
 }
 
diff --git a/src/index.tsx b/src/index.tsx
index 58ecc42a..805847f1 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -23,19 +23,22 @@ settings.applyTheme();
 
 // Register Service Worker
 if ('serviceWorker' in navigator) {
-  navigator.serviceWorker.register(
-    import.meta.env.MODE === 'production' ? `${trimTrailingSlash(import.meta.env.BASE_URL)}/sw.js` : '/dev-sw.js?dev-sw'
-  )
+  const swUrl =
+    import.meta.env.MODE === 'production'
+      ? `${trimTrailingSlash(import.meta.env.BASE_URL)}/sw.js`
+      : `/dev-sw.js?dev-sw`;
+
+  navigator.serviceWorker.register(swUrl);
   navigator.serviceWorker.addEventListener('message', (event) => {
     if (event.data?.type === 'token' && event.data?.responseKey) {
       // Get the token for SW.
-      const token = localStorage.getItem('cinny_access_token');
+      const token = localStorage.getItem('cinny_access_token') ?? undefined;
       event.source!.postMessage({
         responseKey: event.data.responseKey,
         token,
-      })
+      });
     }
-  })
+  });
 }
 
 const mountApp = () => {
diff --git a/src/sw.ts b/src/sw.ts
index 11953917..11f7f8b2 100644
--- a/src/sw.ts
+++ b/src/sw.ts
@@ -1,3 +1,8 @@
+/// <reference lib="WebWorker" />
+
+export type {};
+declare const self: ServiceWorkerGlobalScope;
+
 async function askForAccessToken(client: Client): Promise<string | undefined> {
   return new Promise((resolve) => {
     const responseKey = Math.random().toString(36);
@@ -32,11 +37,10 @@ self.addEventListener('fetch', (event: FetchEvent) => {
   }
   event.respondWith(
     (async (): Promise<Response> => {
-      const client = await clients.get(event.clientId);
+      const client = await self.clients.get(event.clientId);
       let token: string | undefined;
       if (client) token = await askForAccessToken(client);
 
-      // eslint-disable-next-line consistent-return
       return fetch(url, fetchConfig(token));
     })()
   );
diff --git a/vite.config.js b/vite.config.js
index e629c751..2771538c 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -77,6 +77,10 @@ export default defineConfig({
       injectManifest: {
         injectionPoint: undefined,
       },
+      devOptions: {
+        enabled: true,
+        type: 'module'
+      }
     }),
   ],
   optimizeDeps: {