Compare commits

..

1 Commits

Author SHA1 Message Date
Ajay Bura
b9dda2c553 post session info to service worker instead of asking from sw on each request 2026-02-12 10:10:38 +05:30
20 changed files with 53 additions and 73 deletions

View File

@@ -16,7 +16,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@v4.4.0
with:
node-version: 24.13.1
node-version: 20.12.2
cache: 'npm'
- name: Install dependencies
run: npm ci

View File

@@ -15,7 +15,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@v4.4.0
with:
node-version: 24.13.1
node-version: 20.12.2
cache: 'npm'
- name: Install dependencies
run: npm ci

View File

@@ -14,7 +14,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@v4.4.0
with:
node-version: 24.13.1
node-version: 20.12.2
cache: 'npm'
- name: Install dependencies
run: npm ci

View File

@@ -1,5 +1,5 @@
## Builder
FROM node:24.13.1-alpine AS builder
FROM node:20.12.2-alpine3.18 as builder
WORKDIR /src
@@ -11,7 +11,7 @@ RUN npm run build
## App
FROM nginx:1.29.5-alpine
FROM nginx:1.29.3-alpine
COPY --from=builder /src/dist /app
COPY --from=builder /src/docker-nginx.conf /etc/nginx/conf.d/default.conf

View File

@@ -83,7 +83,7 @@ mxFo+ioe/ABCufSmyqFye0psX3Sp
## Local development
> [!TIP]
> We recommend using a version manager as versions change very quickly. You will likely need to switch between multiple Node.js versions based on the needs of different projects you're working on. [NVM on windows](https://github.com/coreybutler/nvm-windows#installation--upgrades) on Windows and [nvm](https://github.com/nvm-sh/nvm) on Linux/macOS are pretty good choices. Recommended nodejs version is Krypton LTS (v24.13.1).
> We recommend using a version manager as versions change very quickly. You will likely need to switch between multiple Node.js versions based on the needs of different projects you're working on. [NVM on windows](https://github.com/coreybutler/nvm-windows#installation--upgrades) on Windows and [nvm](https://github.com/nvm-sh/nvm) on Linux/macOS are pretty good choices. Recommended nodejs version is Iron LTS (v20).
Execute the following commands to start a development server:
```sh

View File

@@ -1,10 +1,11 @@
{
"defaultHomeserver": 1,
"defaultHomeserver": 2,
"homeserverList": [
"converser.eu",
"envs.net",
"matrix.org",
"monero.social",
"mozilla.org",
"unredacted.org",
"xmr.se"
],
"allowCustomHomeservers": true,
@@ -14,7 +15,7 @@
"spaces": [
"#cinny-space:matrix.org",
"#community:matrix.org",
"#space:unredacted.org",
"#space:envs.net",
"#science-space:matrix.org",
"#libregaming-games:tchncs.de",
"#mathematics-on:matrix.org"
@@ -27,7 +28,7 @@
"#PrivSec.dev:arcticfoxes.net",
"#disroot:aria-net.org"
],
"servers": [ "matrix.org", "mozilla.org", "unredacted.org" ]
"servers": ["envs.net", "matrix.org", "monero.social", "mozilla.org"]
},
"hashRouter": {

37
package-lock.json generated
View File

@@ -32,7 +32,7 @@
"emojibase-data": "15.3.2",
"file-saver": "2.0.5",
"focus-trap-react": "10.0.2",
"folds": "2.5.0",
"folds": "2.4.0",
"html-dom-parser": "4.0.0",
"html-react-parser": "4.2.0",
"i18next": "23.12.2",
@@ -56,7 +56,7 @@
"react-google-recaptcha": "2.1.0",
"react-i18next": "15.0.0",
"react-range": "1.8.14",
"react-router-dom": "6.30.3",
"react-router-dom": "6.20.0",
"sanitize-html": "2.12.1",
"slate": "0.112.0",
"slate-dom": "0.112.2",
@@ -3699,10 +3699,9 @@
}
},
"node_modules/@remix-run/router": {
"version": "1.23.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
"integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
"license": "MIT",
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.13.0.tgz",
"integrity": "sha512-5dMOnVnefRsl4uRnAdoWjtVTdh8e6aZqgM4puy9nmEADH72ck+uXwzpJLEKE9Q6F8ZljNewLgmTfkxUrBdv4WA==",
"engines": {
"node": ">=14.0.0"
}
@@ -7158,9 +7157,9 @@
}
},
"node_modules/folds": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/folds/-/folds-2.5.0.tgz",
"integrity": "sha512-UJhvXAQ1XnZ9w10KJwSW+frvzzWE/zcF0dH3fDVCD70RFHAxwEi0UkkVS8CaZGxZF2Wvt3qTJyTS5LW3LwwUAw==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/folds/-/folds-2.4.0.tgz",
"integrity": "sha512-Q5xCmvU3SIM8etQ9qLF6Y5Jtv01c9JpG3QcnF+Z3nlbMvtktfE13Pj7p0XgSPBcA3OuoU0zXiRwiTlMcbU7KhA==",
"license": "Apache-2.0",
"peerDependencies": {
"@vanilla-extract/css": "1.9.2",
@@ -9606,12 +9605,11 @@
}
},
"node_modules/react-router": {
"version": "6.30.3",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
"integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
"license": "MIT",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.20.0.tgz",
"integrity": "sha512-pVvzsSsgUxxtuNfTHC4IxjATs10UaAtvLGVSA1tbUE4GDaOSU1Esu2xF5nWLz7KPiMuW8BJWuPFdlGYJ7/rW0w==",
"dependencies": {
"@remix-run/router": "1.23.2"
"@remix-run/router": "1.13.0"
},
"engines": {
"node": ">=14.0.0"
@@ -9621,13 +9619,12 @@
}
},
"node_modules/react-router-dom": {
"version": "6.30.3",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
"integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
"license": "MIT",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.20.0.tgz",
"integrity": "sha512-CbcKjEyiSVpA6UtCHOIYLUYn/UJfwzp55va4yEfpk7JBN3GPqWfHrdLkAvNCcpXr8QoihcDMuk0dzWZxtlB/mQ==",
"dependencies": {
"@remix-run/router": "1.23.2",
"react-router": "6.30.3"
"@remix-run/router": "1.13.0",
"react-router": "6.20.0"
},
"engines": {
"node": ">=14.0.0"

View File

@@ -43,7 +43,7 @@
"emojibase-data": "15.3.2",
"file-saver": "2.0.5",
"focus-trap-react": "10.0.2",
"folds": "2.5.0",
"folds": "2.4.0",
"html-dom-parser": "4.0.0",
"html-react-parser": "4.2.0",
"i18next": "23.12.2",
@@ -67,7 +67,7 @@
"react-google-recaptcha": "2.1.0",
"react-i18next": "15.0.0",
"react-range": "1.8.14",
"react-router-dom": "6.30.3",
"react-router-dom": "6.20.0",
"sanitize-html": "2.12.1",
"slate": "0.112.0",
"slate-dom": "0.112.2",

View File

@@ -88,8 +88,6 @@ export function EmoticonAutocomplete({
{autoCompleteEmoticon.map((emoticon) => {
const isCustomEmoji = 'url' in emoticon;
const key = isCustomEmoji ? emoticon.url : emoticon.unicode;
const customEmojiUrl = mxcUrlToHttp(mx, key, useAuthentication);
return (
<MenuItem
key={emoticon.shortcode + key}
@@ -100,11 +98,11 @@ export function EmoticonAutocomplete({
}
onClick={() => handleAutocomplete(key, emoticon.shortcode)}
before={
isCustomEmoji && customEmojiUrl ? (
isCustomEmoji ? (
<Box
shrink="No"
as="img"
src={customEmojiUrl}
src={mxcUrlToHttp(mx, key, useAuthentication) || key}
alt={emoticon.shortcode}
style={{ width: toRem(24), height: toRem(24), objectFit: 'contain' }}
/>

View File

@@ -202,7 +202,8 @@ function EmojiSidebar({ activeGroupAtom, packs, onScrollToGroup }: EmojiSidebarP
if (!label) label = isUserId(pack.id) ? 'Personal Pack' : mx.getRoom(pack.id)?.name;
const url =
mxcUrlToHttp(mx, pack.getAvatarUrl(usage) ?? '', useAuthentication) ?? undefined;
mxcUrlToHttp(mx, pack.getAvatarUrl(usage) ?? '', useAuthentication) ||
pack.meta.avatar;
return (
<ImageGroupIcon
@@ -265,7 +266,7 @@ function StickerSidebar({ activeGroupAtom, packs, onScrollToGroup }: StickerSide
if (!label) label = isUserId(pack.id) ? 'Personal Pack' : mx.getRoom(pack.id)?.name;
const url =
mxcUrlToHttp(mx, pack.getAvatarUrl(usage) ?? '', useAuthentication) ?? undefined;
mxcUrlToHttp(mx, pack.getAvatarUrl(usage) ?? '', useAuthentication) || pack.meta.avatar;
return (
<ImageGroupIcon

View File

@@ -68,7 +68,7 @@ export function CustomEmojiItem({ mx, useAuthentication, image }: CustomEmojiIte
loading="lazy"
className={css.CustomEmojiImg}
alt={image.body || image.shortcode}
src={mxcUrlToHttp(mx, image.url, useAuthentication) ?? ''}
src={mxcUrlToHttp(mx, image.url, useAuthentication) ?? image.url}
/>
</Box>
);
@@ -98,7 +98,7 @@ export function StickerItem({ mx, useAuthentication, image }: StickerItemProps)
loading="lazy"
className={css.StickerImg}
alt={image.body || image.shortcode}
src={mxcUrlToHttp(mx, image.url, useAuthentication) ?? ''}
src={mxcUrlToHttp(mx, image.url, useAuthentication) ?? image.url}
/>
</Box>
);

View File

@@ -27,8 +27,7 @@ export function FileDownloadButton({ filename, url, mimeType, encInfo }: FileDow
const [downloadState, download] = useAsyncCallback(
useCallback(async () => {
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication);
if (!mediaUrl) throw new Error('Invalid media URL');
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
const fileContent = encInfo
? await downloadEncryptedMedia(mediaUrl, (encBuf) => decryptFile(encBuf, mimeType, encInfo))
: await downloadMedia(mediaUrl);

View File

@@ -54,8 +54,7 @@ export function AudioContent({
const [srcState, loadSrc] = useAsyncCallback(
useCallback(async () => {
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication);
if (!mediaUrl) throw new Error('Invalid media URL');
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
const fileContent = encInfo
? await downloadEncryptedMedia(mediaUrl, (encBuf) => decryptFile(encBuf, mimeType, encInfo))
: await downloadMedia(mediaUrl);

View File

@@ -86,8 +86,7 @@ export function ReadTextFile({ body, mimeType, url, encInfo, renderViewer }: Rea
const [textState, loadText] = useAsyncCallback(
useCallback(async () => {
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication);
if (!mediaUrl) throw new Error('Invalid media URL');
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
const fileContent = encInfo
? await downloadEncryptedMedia(mediaUrl, (encBuf) => decryptFile(encBuf, mimeType, encInfo))
: await downloadMedia(mediaUrl);
@@ -177,8 +176,7 @@ export function ReadPdfFile({ body, mimeType, url, encInfo, renderViewer }: Read
const [pdfState, loadPdf] = useAsyncCallback(
useCallback(async () => {
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication);
if (!mediaUrl) throw new Error('Invalid media URL');
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
const fileContent = encInfo
? await downloadEncryptedMedia(mediaUrl, (encBuf) => decryptFile(encBuf, mimeType, encInfo))
: await downloadMedia(mediaUrl);
@@ -255,8 +253,7 @@ export function DownloadFile({ body, mimeType, url, info, encInfo }: DownloadFil
const [downloadState, download] = useAsyncCallback(
useCallback(async () => {
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication);
if (!mediaUrl) throw new Error('Invalid media URL');
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
const fileContent = encInfo
? await downloadEncryptedMedia(mediaUrl, (encBuf) => decryptFile(encBuf, mimeType, encInfo))
: await downloadMedia(mediaUrl);

View File

@@ -87,8 +87,7 @@ export const ImageContent = as<'div', ImageContentProps>(
const [srcState, loadSrc] = useAsyncCallback(
useCallback(async () => {
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication);
if (!mediaUrl) throw new Error('Invalid media URL');
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
if (encInfo) {
const fileContent = await downloadEncryptedMedia(mediaUrl, (encBuf) =>
decryptFile(encBuf, mimeType ?? FALLBACK_MIMETYPE, encInfo)

View File

@@ -23,8 +23,7 @@ export function ThumbnailContent({ info, renderImage }: ThumbnailContentProps) {
throw new Error('Failed to load thumbnail');
}
const mediaUrl = mxcUrlToHttp(mx, thumbMxcUrl, useAuthentication);
if (!mediaUrl) throw new Error('Invalid media URL');
const mediaUrl = mxcUrlToHttp(mx, thumbMxcUrl, useAuthentication) ?? thumbMxcUrl;
if (encInfo) {
const fileContent = await downloadEncryptedMedia(mediaUrl, (encBuf) =>
decryptFile(encBuf, thumbInfo.mimetype ?? FALLBACK_MIMETYPE, encInfo)

View File

@@ -81,8 +81,7 @@ export const VideoContent = as<'div', VideoContentProps>(
const [srcState, loadSrc] = useAsyncCallback(
useCallback(async () => {
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication);
if (!mediaUrl) throw new Error('Invalid media URL');
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
const fileContent = encInfo
? await downloadEncryptedMedia(mediaUrl, (encBuf) =>
decryptFile(encBuf, mimeType, encInfo)

View File

@@ -471,7 +471,6 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
const permissions = useRoomPermissions(creators, powerLevels);
const canRedact = permissions.action('redact', mx.getSafeUserId());
const canDeleteOwn = permissions.event(MessageEvent.RoomRedaction, mx.getSafeUserId());
const canSendReaction = permissions.event(MessageEvent.Reaction, mx.getSafeUserId());
const canPinEvent = permissions.stateEvent(StateEvent.RoomPinnedEvents, mx.getSafeUserId());
const [editId, setEditId] = useState<string>();
@@ -1048,7 +1047,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
collapse={collapse}
highlight={highlighted}
edit={editId === mEventId}
canDelete={canRedact || (canDeleteOwn && mEvent.getSender() === mx.getUserId())}
canDelete={canRedact || mEvent.getSender() === mx.getUserId()}
canSendReaction={canSendReaction}
canPinEvent={canPinEvent}
imagePackRooms={imagePackRooms}
@@ -1130,7 +1129,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
collapse={collapse}
highlight={highlighted}
edit={editId === mEventId}
canDelete={canRedact || (canDeleteOwn && mEvent.getSender() === mx.getUserId())}
canDelete={canRedact || mEvent.getSender() === mx.getUserId()}
canSendReaction={canSendReaction}
canPinEvent={canPinEvent}
imagePackRooms={imagePackRooms}
@@ -1248,7 +1247,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
messageLayout={messageLayout}
collapse={collapse}
highlight={highlighted}
canDelete={canRedact || (canDeleteOwn && mEvent.getSender() === mx.getUserId())}
canDelete={canRedact || mEvent.getSender() === mx.getUserId()}
canSendReaction={canSendReaction}
canPinEvent={canPinEvent}
imagePackRooms={imagePackRooms}

View File

@@ -1,6 +1,6 @@
import { MatrixClient, ReceiptType } from 'matrix-js-sdk';
export async function markAsRead(mx: MatrixClient, roomId: string, privateReceipt?: boolean) {
export async function markAsRead(mx: MatrixClient, roomId: string, privateReceipt: boolean) {
const room = mx.getRoom(roomId);
if (!room) return;
@@ -19,15 +19,8 @@ export async function markAsRead(mx: MatrixClient, roomId: string, privateReceip
const latestEvent = getLatestValidEvent();
if (latestEvent === null) return;
const latestEventId = latestEvent.getId();
if (!latestEventId) return;
// Set both the read receipt AND the fully_read marker
// The fully_read marker is what persists your read position across sessions
await mx.setRoomReadMarkers(
roomId,
latestEventId, // m.fully_read marker
latestEvent, // m.read receipt event
privateReceipt ? { receiptType: ReceiptType.ReadPrivate } : undefined
await mx.sendReadReceipt(
latestEvent,
privateReceipt ? ReceiptType.ReadPrivate : ReceiptType.Read
);
}

View File

@@ -160,8 +160,7 @@ export const getOrphanParents = (roomToParents: RoomToParents, roomId: string):
};
export const isMutedRule = (rule: IPushRule) =>
// Check for empty actions (new spec) or dont_notify (deprecated)
(rule.actions.length === 0 || rule.actions[0] === 'dont_notify') && rule.conditions?.[0]?.kind === 'event_match';
rule.actions[0] === 'dont_notify' && rule.conditions?.[0]?.kind === 'event_match';
export const findMutedRule = (overrideRules: IPushRule[], roomId: string) =>
overrideRules.find((rule) => rule.rule_id === roomId && isMutedRule(rule));