Compare commits
9 Commits
fix-257
...
improve-sw
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9dda2c553 | ||
|
|
073a9f5786 | ||
|
|
655c1c9aff | ||
|
|
17d4bceb42 | ||
|
|
0f61f2f328 | ||
|
|
c88cb4bca9 | ||
|
|
46c02b89de | ||
|
|
e13d97aa98 | ||
|
|
958ae8945d |
4
.github/workflows/prod-deploy.yml
vendored
4
.github/workflows/prod-deploy.yml
vendored
@@ -72,12 +72,12 @@ jobs:
|
|||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.11.1
|
uses: docker/setup-buildx-action@v3.11.1
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3.5.0
|
uses: docker/login-action@v3.6.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Login to the Container registry
|
- name: Login to the Container registry
|
||||||
uses: docker/login-action@v3.5.0
|
uses: docker/login-action@v3.6.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ RUN npm run build
|
|||||||
|
|
||||||
|
|
||||||
## App
|
## App
|
||||||
FROM nginx:1.29.1-alpine
|
FROM nginx:1.29.3-alpine
|
||||||
|
|
||||||
COPY --from=builder /src/dist /app
|
COPY --from=builder /src/dist /app
|
||||||
COPY --from=builder /src/docker-nginx.conf /etc/nginx/conf.d/default.conf
|
COPY --from=builder /src/docker-nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|||||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.10.0",
|
"version": "4.10.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.10.0",
|
"version": "4.10.2",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atlaskit/pragmatic-drag-and-drop": "1.1.6",
|
"@atlaskit/pragmatic-drag-and-drop": "1.1.6",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"emojibase-data": "15.3.2",
|
"emojibase-data": "15.3.2",
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
"focus-trap-react": "10.0.2",
|
"focus-trap-react": "10.0.2",
|
||||||
"folds": "2.3.0",
|
"folds": "2.4.0",
|
||||||
"html-dom-parser": "4.0.0",
|
"html-dom-parser": "4.0.0",
|
||||||
"html-react-parser": "4.2.0",
|
"html-react-parser": "4.2.0",
|
||||||
"i18next": "23.12.2",
|
"i18next": "23.12.2",
|
||||||
@@ -7157,9 +7157,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/folds": {
|
"node_modules/folds": {
|
||||||
"version": "2.3.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/folds/-/folds-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/folds/-/folds-2.4.0.tgz",
|
||||||
"integrity": "sha512-1KoM21jrg5daxvKrmSY0V04wa946KlNT0z6h017Rsnw2fdtNC6J0f34Ce5GF46Tzi00gZ/7SvCDXMzW/7e5s0w==",
|
"integrity": "sha512-Q5xCmvU3SIM8etQ9qLF6Y5Jtv01c9JpG3QcnF+Z3nlbMvtktfE13Pj7p0XgSPBcA3OuoU0zXiRwiTlMcbU7KhA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vanilla-extract/css": "1.9.2",
|
"@vanilla-extract/css": "1.9.2",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.10.0",
|
"version": "4.10.2",
|
||||||
"description": "Yet another matrix client",
|
"description": "Yet another matrix client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
"emojibase-data": "15.3.2",
|
"emojibase-data": "15.3.2",
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
"focus-trap-react": "10.0.2",
|
"focus-trap-react": "10.0.2",
|
||||||
"folds": "2.3.0",
|
"folds": "2.4.0",
|
||||||
"html-dom-parser": "4.0.0",
|
"html-dom-parser": "4.0.0",
|
||||||
"html-react-parser": "4.2.0",
|
"html-react-parser": "4.2.0",
|
||||||
"i18next": "23.12.2",
|
"i18next": "23.12.2",
|
||||||
|
|||||||
@@ -212,9 +212,10 @@ export const getMentions = (mx: MatrixClient, roomId: string, editor: Editor): M
|
|||||||
if (node.type === BlockType.CodeBlock) return;
|
if (node.type === BlockType.CodeBlock) return;
|
||||||
|
|
||||||
if (node.type === BlockType.Mention) {
|
if (node.type === BlockType.Mention) {
|
||||||
if (node.id === getCanonicalAliasOrRoomId(mx, roomId)) {
|
if (node.name === '@room') {
|
||||||
mentionData.room = true;
|
mentionData.room = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUserId(node.id) && node.id !== mx.getUserId()) {
|
if (isUserId(node.id) && node.id !== mx.getUserId()) {
|
||||||
mentionData.users.add(node.id);
|
mentionData.users.add(node.id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { Page, PageContent, PageHeader } from '../../../components/page';
|
|||||||
import { useRoom } from '../../../hooks/useRoom';
|
import { useRoom } from '../../../hooks/useRoom';
|
||||||
import { useRoomMembers } from '../../../hooks/useRoomMembers';
|
import { useRoomMembers } from '../../../hooks/useRoomMembers';
|
||||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||||
import { usePowerLevels } from '../../../hooks/usePowerLevels';
|
import { useGetMemberPowerLevel, usePowerLevels } from '../../../hooks/usePowerLevels';
|
||||||
import { VirtualTile } from '../../../components/virtualizer';
|
import { VirtualTile } from '../../../components/virtualizer';
|
||||||
import { MemberTile } from '../../../components/member-tile';
|
import { MemberTile } from '../../../components/member-tile';
|
||||||
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
|
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
|
||||||
@@ -87,12 +87,13 @@ export function Members({ requestClose }: MembersProps) {
|
|||||||
const powerLevels = usePowerLevels(room);
|
const powerLevels = usePowerLevels(room);
|
||||||
const creators = useRoomCreators(room);
|
const creators = useRoomCreators(room);
|
||||||
const getPowerTag = useGetMemberPowerTag(room, creators, powerLevels);
|
const getPowerTag = useGetMemberPowerTag(room, creators, powerLevels);
|
||||||
|
const getPowerLevel = useGetMemberPowerLevel(powerLevels);
|
||||||
|
|
||||||
const [membershipFilterIndex, setMembershipFilterIndex] = useState(0);
|
const [membershipFilterIndex, setMembershipFilterIndex] = useState(0);
|
||||||
const [sortFilterIndex, setSortFilterIndex] = useSetting(settingsAtom, 'memberSortFilterIndex');
|
const [sortFilterIndex, setSortFilterIndex] = useSetting(settingsAtom, 'memberSortFilterIndex');
|
||||||
const membershipFilter = useMembershipFilter(membershipFilterIndex, useMembershipFilterMenu());
|
const membershipFilter = useMembershipFilter(membershipFilterIndex, useMembershipFilterMenu());
|
||||||
const memberSort = useMemberSort(sortFilterIndex, useMemberSortMenu());
|
const memberSort = useMemberSort(sortFilterIndex, useMemberSortMenu());
|
||||||
const memberPowerSort = useMemberPowerSort(creators);
|
const memberPowerSort = useMemberPowerSort(creators, getPowerLevel);
|
||||||
|
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
|
|||||||
onClick={() => setAdvance(!advance)}
|
onClick={() => setAdvance(!advance)}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Text size="T200">Advance Options</Text>
|
<Text size="T200">Advanced Options</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export function CreateSpaceForm({ defaultKind, space, onCreate }: CreateSpaceFor
|
|||||||
onClick={() => setAdvance(!advance)}
|
onClick={() => setAdvance(!advance)}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Text size="T200">Advance Options</Text>
|
<Text size="T200">Advanced Options</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export function General({ requestClose }: GeneralProps) {
|
|||||||
<RoomLocalAddresses permissions={permissions} />
|
<RoomLocalAddresses permissions={permissions} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">Advance Options</Text>
|
<Text size="L400">Advanced Options</Text>
|
||||||
<RoomUpgrade permissions={permissions} requestClose={requestClose} />
|
<RoomUpgrade permissions={permissions} requestClose={requestClose} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ import { useRoomTypingMember } from '../../hooks/useRoomTypingMembers';
|
|||||||
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
||||||
import { useMembershipFilter, useMembershipFilterMenu } from '../../hooks/useMemberFilter';
|
import { useMembershipFilter, useMembershipFilterMenu } from '../../hooks/useMemberFilter';
|
||||||
import { useMemberPowerSort, useMemberSort, useMemberSortMenu } from '../../hooks/useMemberSort';
|
import { useMemberPowerSort, useMemberSort, useMemberSortMenu } from '../../hooks/useMemberSort';
|
||||||
import { usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
import { useGetMemberPowerLevel, usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
||||||
import { MembershipFilterMenu } from '../../components/MembershipFilterMenu';
|
import { MembershipFilterMenu } from '../../components/MembershipFilterMenu';
|
||||||
import { MemberSortMenu } from '../../components/MemberSortMenu';
|
import { MemberSortMenu } from '../../components/MemberSortMenu';
|
||||||
import { useOpenUserRoomProfile, useUserRoomProfileState } from '../../state/hooks/userRoomProfile';
|
import { useOpenUserRoomProfile, useUserRoomProfileState } from '../../state/hooks/userRoomProfile';
|
||||||
@@ -185,6 +185,7 @@ export function MembersDrawer({ room, members }: MembersDrawerProps) {
|
|||||||
const powerLevels = usePowerLevelsContext();
|
const powerLevels = usePowerLevelsContext();
|
||||||
const creators = useRoomCreators(room);
|
const creators = useRoomCreators(room);
|
||||||
const getPowerTag = useGetMemberPowerTag(room, creators, powerLevels);
|
const getPowerTag = useGetMemberPowerTag(room, creators, powerLevels);
|
||||||
|
const getPowerLevel = useGetMemberPowerLevel(powerLevels);
|
||||||
|
|
||||||
const fetchingMembers = members.length < room.getJoinedMemberCount();
|
const fetchingMembers = members.length < room.getJoinedMemberCount();
|
||||||
const openUserRoomProfile = useOpenUserRoomProfile();
|
const openUserRoomProfile = useOpenUserRoomProfile();
|
||||||
@@ -198,7 +199,7 @@ export function MembersDrawer({ room, members }: MembersDrawerProps) {
|
|||||||
|
|
||||||
const membershipFilter = useMembershipFilter(membershipFilterIndex, membershipFilterMenu);
|
const membershipFilter = useMembershipFilter(membershipFilterIndex, membershipFilterMenu);
|
||||||
const memberSort = useMemberSort(sortFilterIndex, sortFilterMenu);
|
const memberSort = useMemberSort(sortFilterIndex, sortFilterMenu);
|
||||||
const memberPowerSort = useMemberPowerSort(creators);
|
const memberPowerSort = useMemberPowerSort(creators, getPowerLevel);
|
||||||
|
|
||||||
const typingMembers = useRoomTypingMember(room.roomId);
|
const typingMembers = useRoomTypingMember(room.roomId);
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export function About({ requestClose }: AboutProps) {
|
|||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Box gap="100" alignItems="End">
|
<Box gap="100" alignItems="End">
|
||||||
<Text size="H3">Cinny</Text>
|
<Text size="H3">Cinny</Text>
|
||||||
<Text size="T200">v4.10.0</Text>
|
<Text size="T200">v4.10.2</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Text>Yet another matrix client.</Text>
|
<Text>Yet another matrix client.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export function General({ requestClose }: GeneralProps) {
|
|||||||
<RoomLocalAddresses permissions={permissions} />
|
<RoomLocalAddresses permissions={permissions} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">Advance Options</Text>
|
<Text size="L400">Advanced Options</Text>
|
||||||
<RoomUpgrade permissions={permissions} requestClose={requestClose} />
|
<RoomUpgrade permissions={permissions} requestClose={requestClose} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -47,7 +47,10 @@ export const useMemberSort = (index: number, memberSort: MemberSortItem[]): Memb
|
|||||||
return item;
|
return item;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useMemberPowerSort = (creators: Set<string>): MemberSortFn => {
|
export const useMemberPowerSort = (
|
||||||
|
creators: Set<string>,
|
||||||
|
getPowerLevel: (userId: string) => number
|
||||||
|
): MemberSortFn => {
|
||||||
const sort: MemberSortFn = useCallback(
|
const sort: MemberSortFn = useCallback(
|
||||||
(a, b) => {
|
(a, b) => {
|
||||||
if (creators.has(a.userId) && creators.has(b.userId)) {
|
if (creators.has(a.userId) && creators.has(b.userId)) {
|
||||||
@@ -56,7 +59,7 @@ export const useMemberPowerSort = (creators: Set<string>): MemberSortFn => {
|
|||||||
if (creators.has(a.userId)) return -1;
|
if (creators.has(a.userId)) return -1;
|
||||||
if (creators.has(b.userId)) return 1;
|
if (creators.has(b.userId)) return 1;
|
||||||
|
|
||||||
return b.powerLevel - a.powerLevel;
|
return getPowerLevel(b.userId) - getPowerLevel(a.userId);
|
||||||
},
|
},
|
||||||
[creators]
|
[creators]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ import { Create } from './client/create';
|
|||||||
import { CreateSpaceModalRenderer } from '../features/create-space';
|
import { CreateSpaceModalRenderer } from '../features/create-space';
|
||||||
import { SearchModalRenderer } from '../features/search';
|
import { SearchModalRenderer } from '../features/search';
|
||||||
import { getFallbackSession } from '../state/sessions';
|
import { getFallbackSession } from '../state/sessions';
|
||||||
|
import { pushSessionToSW } from '../../sw-session';
|
||||||
|
|
||||||
export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => {
|
export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => {
|
||||||
const { hashRouter } = clientConfig;
|
const { hashRouter } = clientConfig;
|
||||||
@@ -106,7 +107,8 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
|
|||||||
|
|
||||||
<Route
|
<Route
|
||||||
loader={() => {
|
loader={() => {
|
||||||
if (!getFallbackSession()) {
|
const session = getFallbackSession();
|
||||||
|
if (!session) {
|
||||||
const afterLoginPath = getAppPathFromHref(
|
const afterLoginPath = getAppPathFromHref(
|
||||||
getOriginBaseUrl(hashRouter),
|
getOriginBaseUrl(hashRouter),
|
||||||
window.location.href
|
window.location.href
|
||||||
@@ -114,6 +116,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
|
|||||||
if (afterLoginPath) setAfterLoginRedirectPath(afterLoginPath);
|
if (afterLoginPath) setAfterLoginRedirectPath(afterLoginPath);
|
||||||
return redirect(getLoginPath());
|
return redirect(getLoginPath());
|
||||||
}
|
}
|
||||||
|
pushSessionToSW(session.baseUrl, session.accessToken);
|
||||||
return null;
|
return null;
|
||||||
}}
|
}}
|
||||||
element={
|
element={
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export function AuthFooter() {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
v4.10.0
|
v4.10.2
|
||||||
</Text>
|
</Text>
|
||||||
<Text as="a" size="T300" href="https://twitter.com/cinnyapp" target="_blank" rel="noreferrer">
|
<Text as="a" size="T300" href="https://twitter.com/cinnyapp" target="_blank" rel="noreferrer">
|
||||||
Twitter
|
Twitter
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function WelcomePage() {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
>
|
>
|
||||||
v4.10.0
|
v4.10.2
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from
|
|||||||
|
|
||||||
import { cryptoCallbacks } from './secretStorageKeys';
|
import { cryptoCallbacks } from './secretStorageKeys';
|
||||||
import { clearNavToActivePathStore } from '../app/state/navToActivePath';
|
import { clearNavToActivePathStore } from '../app/state/navToActivePath';
|
||||||
|
import { pushSessionToSW } from '../sw-session';
|
||||||
|
|
||||||
type Session = {
|
type Session = {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
@@ -53,6 +54,7 @@ export const clearCacheAndReload = async (mx: MatrixClient) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const logoutClient = async (mx: MatrixClient) => {
|
export const logoutClient = async (mx: MatrixClient) => {
|
||||||
|
pushSessionToSW();
|
||||||
mx.stopClient();
|
mx.stopClient();
|
||||||
try {
|
try {
|
||||||
await mx.logout();
|
await mx.logout();
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import App from './app/pages/App';
|
|||||||
|
|
||||||
// import i18n (needs to be bundled ;))
|
// import i18n (needs to be bundled ;))
|
||||||
import './app/i18n';
|
import './app/i18n';
|
||||||
|
import { pushSessionToSW } from './sw-session';
|
||||||
|
import { getFallbackSession } from './app/state/sessions';
|
||||||
|
|
||||||
document.body.classList.add(configClass, varsClass);
|
document.body.classList.add(configClass, varsClass);
|
||||||
|
|
||||||
@@ -25,16 +27,9 @@ if ('serviceWorker' in navigator) {
|
|||||||
? `${trimTrailingSlash(import.meta.env.BASE_URL)}/sw.js`
|
? `${trimTrailingSlash(import.meta.env.BASE_URL)}/sw.js`
|
||||||
: `/dev-sw.js?dev-sw`;
|
: `/dev-sw.js?dev-sw`;
|
||||||
|
|
||||||
navigator.serviceWorker.register(swUrl);
|
navigator.serviceWorker.register(swUrl).then(() => {
|
||||||
navigator.serviceWorker.addEventListener('message', (event) => {
|
const session = getFallbackSession();
|
||||||
if (event.data?.type === 'token' && event.data?.responseKey) {
|
pushSessionToSW(session?.baseUrl, session?.accessToken);
|
||||||
// Get the token for SW.
|
|
||||||
const token = localStorage.getItem('cinny_access_token') ?? undefined;
|
|
||||||
event.source!.postMessage({
|
|
||||||
responseKey: event.data.responseKey,
|
|
||||||
token,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
src/sw-session.ts
Normal file
10
src/sw-session.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export function pushSessionToSW(baseUrl?: string, accessToken?: string) {
|
||||||
|
if (!('serviceWorker' in navigator)) return;
|
||||||
|
if (!navigator.serviceWorker.controller) return;
|
||||||
|
|
||||||
|
navigator.serviceWorker.controller.postMessage({
|
||||||
|
type: 'setSession',
|
||||||
|
accessToken,
|
||||||
|
baseUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
94
src/sw.ts
94
src/sw.ts
@@ -3,22 +3,64 @@
|
|||||||
export type {};
|
export type {};
|
||||||
declare const self: ServiceWorkerGlobalScope;
|
declare const self: ServiceWorkerGlobalScope;
|
||||||
|
|
||||||
async function askForAccessToken(client: Client): Promise<string | undefined> {
|
self.addEventListener('install', () => {
|
||||||
return new Promise((resolve) => {
|
self.skipWaiting();
|
||||||
const responseKey = Math.random().toString(36);
|
});
|
||||||
const listener = (event: ExtendableMessageEvent) => {
|
|
||||||
if (event.data.responseKey !== responseKey) return;
|
self.addEventListener('activate', (event: ExtendableEvent) => {
|
||||||
resolve(event.data.token);
|
event.waitUntil(self.clients.claim());
|
||||||
self.removeEventListener('message', listener);
|
});
|
||||||
};
|
|
||||||
self.addEventListener('message', listener);
|
type SessionInfo = {
|
||||||
client.postMessage({ responseKey, type: 'token' });
|
accessToken: string;
|
||||||
|
baseUrl: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store session per client (tab)
|
||||||
|
*/
|
||||||
|
const sessions = new Map<string, SessionInfo>();
|
||||||
|
|
||||||
|
async function cleanupDeadClients() {
|
||||||
|
const activeClients = await self.clients.matchAll();
|
||||||
|
const activeIds = new Set(activeClients.map((c) => c.id));
|
||||||
|
|
||||||
|
Array.from(sessions.keys()).forEach((id) => {
|
||||||
|
if (!activeIds.has(id)) {
|
||||||
|
sessions.delete(id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchConfig(token?: string): RequestInit | undefined {
|
/**
|
||||||
if (!token) return undefined;
|
* Receive session updates from clients
|
||||||
|
*/
|
||||||
|
self.addEventListener('message', (event: ExtendableMessageEvent) => {
|
||||||
|
const client = event.source as Client | null;
|
||||||
|
if (!client) return;
|
||||||
|
|
||||||
|
const { type, accessToken, baseUrl } = event.data || {};
|
||||||
|
|
||||||
|
if (type !== 'setSession') return;
|
||||||
|
|
||||||
|
cleanupDeadClients();
|
||||||
|
|
||||||
|
if (typeof accessToken === 'string' && typeof baseUrl === 'string') {
|
||||||
|
sessions.set(client.id, { accessToken, baseUrl });
|
||||||
|
} else {
|
||||||
|
// Logout or invalid session
|
||||||
|
sessions.delete(client.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function validMediaRequest(url: string, baseUrl: string): boolean {
|
||||||
|
const downloadUrl = new URL('/_matrix/client/v1/media/download', baseUrl);
|
||||||
|
const thumbnailUrl = new URL('/_matrix/client/v1/media/thumbnail', baseUrl);
|
||||||
|
|
||||||
|
return url.startsWith(downloadUrl.href) || url.startsWith(thumbnailUrl.href);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchConfig(token: string): RequestInit {
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
@@ -27,26 +69,16 @@ function fetchConfig(token?: string): RequestInit | undefined {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addEventListener('activate', (event: ExtendableEvent) => {
|
|
||||||
event.waitUntil(clients.claim());
|
|
||||||
});
|
|
||||||
|
|
||||||
self.addEventListener('fetch', (event: FetchEvent) => {
|
self.addEventListener('fetch', (event: FetchEvent) => {
|
||||||
const { url, method } = event.request;
|
const { url, method } = event.request;
|
||||||
if (method !== 'GET') return;
|
|
||||||
if (
|
|
||||||
!url.includes('/_matrix/client/v1/media/download') &&
|
|
||||||
!url.includes('/_matrix/client/v1/media/thumbnail')
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.respondWith(
|
|
||||||
(async (): Promise<Response> => {
|
|
||||||
const client = await self.clients.get(event.clientId);
|
|
||||||
let token: string | undefined;
|
|
||||||
if (client) token = await askForAccessToken(client);
|
|
||||||
|
|
||||||
return fetch(url, fetchConfig(token));
|
if (method !== 'GET') return;
|
||||||
})()
|
if (!event.clientId) return;
|
||||||
);
|
|
||||||
|
const session = sessions.get(event.clientId);
|
||||||
|
if (!session) return;
|
||||||
|
|
||||||
|
if (!validMediaRequest(url, session.baseUrl)) return;
|
||||||
|
|
||||||
|
event.respondWith(fetch(url, fetchConfig(session.accessToken)));
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user