From d679e68501aef1d731555311091a515ff5b8362d Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Mon, 9 Mar 2026 17:34:44 +1100 Subject: [PATCH 01/11] Fix recent emoji does not persist (#2722) Fix recent emoji are not getting saved Refactor recent emoji retrieval to ensure structured cloning and proper type checking. The sdk was not updating account data because we are mutating the original and it compare and early return if found same. --- src/app/plugins/recent-emoji.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/plugins/recent-emoji.ts b/src/app/plugins/recent-emoji.ts index 3634538fb..811ed9d5a 100644 --- a/src/app/plugins/recent-emoji.ts +++ b/src/app/plugins/recent-emoji.ts @@ -27,7 +27,11 @@ export const getRecentEmojis = (mx: MatrixClient, limit?: number): IEmoji[] => { export function addRecentEmoji(mx: MatrixClient, unicode: string) { const recentEmojiEvent = getAccountData(mx, AccountDataEvent.ElementRecentEmoji); - const recentEmoji = recentEmojiEvent?.getContent().recent_emoji ?? []; + const recentEmojiContent = recentEmojiEvent?.getContent(); + const recentEmoji = + recentEmojiContent && Array.isArray(recentEmojiContent.recent_emoji) + ? structuredClone(recentEmojiContent.recent_emoji) + : []; const emojiIndex = recentEmoji.findIndex(([u]) => u === unicode); let entry: [EmojiUnicode, EmojiUsageCount]; From 2eb5a9a616a7de6f0637dcfa2660629ff6d57433 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:17:15 +1100 Subject: [PATCH 02/11] Fix crash with bad location uri (#2746) fix crash with bad location uri --- .../components/message/MsgTypeRenderers.tsx | 2 ++ src/app/utils/common.ts | 22 +++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/app/components/message/MsgTypeRenderers.tsx b/src/app/components/message/MsgTypeRenderers.tsx index a40ecae1e..abbf354de 100644 --- a/src/app/components/message/MsgTypeRenderers.tsx +++ b/src/app/components/message/MsgTypeRenderers.tsx @@ -389,6 +389,8 @@ export function MLocation({ content }: MLocationProps) { const geoUri = content.geo_uri; if (typeof geoUri !== 'string') return ; const location = parseGeoUri(geoUri); + if (!location) return ; + return ( {geoUri} diff --git a/src/app/utils/common.ts b/src/app/utils/common.ts index 678f1b6ef..6bda28021 100644 --- a/src/app/utils/common.ts +++ b/src/app/utils/common.ts @@ -87,13 +87,21 @@ export const scaleYDimension = (x: number, scaledX: number, y: number): number = }; export const parseGeoUri = (location: string) => { - const [, data] = location.split(':'); - const [cords] = data.split(';'); - const [latitude, longitude] = cords.split(','); - return { - latitude, - longitude, - }; + try { + const [, data] = location.split(':'); + const [cords] = data.split(';'); + const [latitude, longitude] = cords.split(','); + + if (typeof latitude === 'string' && typeof longitude === 'string') { + return { + latitude, + longitude, + }; + } + return undefined; + } catch { + return undefined; + } }; const START_SLASHES_REG = /^\/+/g; From 4449e7c6e820efe7a50b5964fd77714a1bd93d28 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:39:58 +1100 Subject: [PATCH 03/11] Show call support error and disable join button (#2748) * allow user to end call if error when loading * show call support missing error if livekit server is not provided * prevent joining from nav item double click if no livekit support --- src/app/cs-api.ts | 10 +++ src/app/features/call-status/CallControl.tsx | 53 +++++++++-- src/app/features/call-status/CallStatus.tsx | 2 +- src/app/features/call/CallView.tsx | 32 ++++++- src/app/features/room-nav/RoomNavItem.tsx | 8 ++ src/app/hooks/useLivekitSupport.ts | 16 ++++ src/app/pages/client/AutoDiscovery.tsx | 32 +++++++ src/app/pages/client/ClientRoot.tsx | 95 +++++++++++--------- src/app/plugins/call/CallEmbed.ts | 34 +++++-- 9 files changed, 220 insertions(+), 62 deletions(-) create mode 100644 src/app/hooks/useLivekitSupport.ts create mode 100644 src/app/pages/client/AutoDiscovery.tsx diff --git a/src/app/cs-api.ts b/src/app/cs-api.ts index b9aac06ab..95a131a85 100644 --- a/src/app/cs-api.ts +++ b/src/app/cs-api.ts @@ -20,6 +20,16 @@ export type AutoDiscoveryInfo = Record & { 'm.identity_server'?: { base_url: string; }; + 'org.matrix.msc2965.authentication'?: { + account?: string; + issuer?: string; + }; + 'org.matrix.msc4143.rtc_foci'?: [ + { + livekit_service_url: string; + type: 'livekit'; + } + ]; }; export const autoDiscovery = async ( diff --git a/src/app/features/call-status/CallControl.tsx b/src/app/features/call-status/CallControl.tsx index 2f2bac7fb..6416fda52 100644 --- a/src/app/features/call-status/CallControl.tsx +++ b/src/app/features/call-status/CallControl.tsx @@ -1,14 +1,17 @@ import { Box, Chip, Icon, IconButton, Icons, Spinner, Text, Tooltip, TooltipProvider } from 'folds'; import React, { useCallback } from 'react'; +import { useSetAtom } from 'jotai'; import { StatusDivider } from './components'; import { CallEmbed, useCallControlState } from '../../plugins/call'; import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; +import { callEmbedAtom } from '../../state/callEmbed'; type MicrophoneButtonProps = { enabled: boolean; onToggle: () => Promise; + disabled?: boolean; }; -function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) { +function MicrophoneButton({ enabled, onToggle, disabled }: MicrophoneButtonProps) { return ( onToggle()} outlined + disabled={disabled} > @@ -38,8 +42,9 @@ function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) { type SoundButtonProps = { enabled: boolean; onToggle: () => void; + disabled?: boolean; }; -function SoundButton({ enabled, onToggle }: SoundButtonProps) { +function SoundButton({ enabled, onToggle, disabled }: SoundButtonProps) { return ( onToggle()} outlined + disabled={disabled} > Promise; + disabled?: boolean; }; -function VideoButton({ enabled, onToggle }: VideoButtonProps) { +function VideoButton({ enabled, onToggle, disabled }: VideoButtonProps) { return ( onToggle()} outlined + disabled={disabled} > void; + disabled?: boolean; }; -function ScreenShareButton({ enabled, onToggle }: ScreenShareButtonProps) { +function ScreenShareButton({ enabled, onToggle, disabled }: ScreenShareButtonProps) { return ( @@ -136,8 +146,17 @@ function ScreenShareButton({ enabled, onToggle }: ScreenShareButtonProps) { ); } -export function CallControl({ callEmbed, compact }: { callEmbed: CallEmbed; compact: boolean }) { +export function CallControl({ + callEmbed, + compact, + callJoined, +}: { + callEmbed: CallEmbed; + compact: boolean; + callJoined: boolean; +}) { const { microphone, video, sound, screenshare } = useCallControlState(callEmbed.control); + const setCallEmbed = useSetAtom(callEmbedAtom); const [hangupState, hangup] = useAsyncCallback( useCallback(() => callEmbed.hangup(), [callEmbed]) @@ -145,20 +164,38 @@ export function CallControl({ callEmbed, compact }: { callEmbed: CallEmbed; comp const exiting = hangupState.status === AsyncStatus.Loading || hangupState.status === AsyncStatus.Success; + const handleHangup = () => { + if (!callJoined) { + setCallEmbed(undefined); + return; + } + hangup(); + }; + return ( callEmbed.control.toggleMicrophone()} + disabled={!callJoined} + /> + callEmbed.control.toggleSound()} + disabled={!callJoined} /> - callEmbed.control.toggleSound()} /> {!compact && } - callEmbed.control.toggleVideo()} /> + callEmbed.control.toggleVideo()} + disabled={!callJoined} + /> {!compact && ( callEmbed.control.toggleScreenshare()} + disabled={!callJoined} /> )} @@ -176,7 +213,7 @@ export function CallControl({ callEmbed, compact }: { callEmbed: CallEmbed; comp } disabled={exiting} outlined - onClick={hangup} + onClick={handleHangup} > {!compact && ( diff --git a/src/app/features/call-status/CallStatus.tsx b/src/app/features/call-status/CallStatus.tsx index 5d2182c2c..1d30d1b40 100644 --- a/src/app/features/call-status/CallStatus.tsx +++ b/src/app/features/call-status/CallStatus.tsx @@ -74,7 +74,7 @@ export function CallStatus({ callEmbed }: CallStatusProps) { )} - + ); diff --git a/src/app/features/call/CallView.tsx b/src/app/features/call/CallView.tsx index 0cddd2be2..7a8c28a5e 100644 --- a/src/app/features/call/CallView.tsx +++ b/src/app/features/call/CallView.tsx @@ -13,10 +13,29 @@ import { useCallMembers, useCallSession } from '../../hooks/useCall'; import { CallMemberRenderer } from './CallMemberCard'; import * as css from './styles.css'; import { CallControls } from './CallControls'; +import { useLivekitSupport } from '../../hooks/useLivekitSupport'; -function JoinMessage({ hasParticipant }: { hasParticipant?: boolean }) { +function LivekitServerMissingMessage() { + return ( + + Your homeserver does not support calling. But you can still join call started by others. + + ); +} + +function JoinMessage({ + hasParticipant, + livekitSupported, +}: { + hasParticipant?: boolean; + livekitSupported?: boolean; +}) { if (hasParticipant) return null; + if (livekitSupported === false) { + return ; + } + return ( Voice chat’s empty — Be the first to hop in! @@ -43,12 +62,13 @@ function AlreadyInCallMessage() { function CallPrescreen() { const mx = useMatrixClient(); const room = useRoom(); + const livekitSupported = useLivekitSupport(); const powerLevels = usePowerLevelsContext(); const creators = useRoomCreators(room); const permissions = useRoomPermissions(creators, powerLevels); - const canJoin = permissions.event(StateEvent.GroupCallMemberPrefix, mx.getSafeUserId()); + const hasPermission = permissions.event(StateEvent.GroupCallMemberPrefix, mx.getSafeUserId()); const callSession = useCallSession(room); const callMembers = useCallMembers(room, callSession); @@ -57,6 +77,8 @@ function CallPrescreen() { const callEmbed = useCallEmbed(); const inOtherCall = callEmbed && callEmbed.roomId !== room.roomId; + const canJoin = hasPermission && (livekitSupported || hasParticipant); + return ( @@ -77,7 +99,11 @@ function CallPrescreen() {
{!inOtherCall && - (canJoin ? : )} + (hasPermission ? ( + + ) : ( + + ))} {inOtherCall && }
diff --git a/src/app/features/room-nav/RoomNavItem.tsx b/src/app/features/room-nav/RoomNavItem.tsx index 9c5af9388..b317b13ab 100644 --- a/src/app/features/room-nav/RoomNavItem.tsx +++ b/src/app/features/room-nav/RoomNavItem.tsx @@ -57,6 +57,8 @@ import { useCallMembers, useCallSession } from '../../hooks/useCall'; import { useCallEmbed, useCallStart } from '../../hooks/useCallEmbed'; import { callChatAtom } from '../../state/callEmbed'; import { useCallPreferencesAtom } from '../../state/hooks/callPreferences'; +import { useAutoDiscoveryInfo } from '../../hooks/useAutoDiscoveryInfo'; +import { livekitSupport } from '../../hooks/useLivekitSupport'; type RoomNavItemMenuProps = { room: Room; @@ -282,8 +284,14 @@ export function RoomNavItem({ const startCall = useCallStart(direct); const callEmbed = useCallEmbed(); const callPref = useAtomValue(useCallPreferencesAtom()); + const autoDiscoveryInfo = useAutoDiscoveryInfo(); const handleStartCall: MouseEventHandler = (evt) => { + // Do not join if no livekit support or call is not started by others + if (!livekitSupport(autoDiscoveryInfo) && callMembers.length === 0) { + return; + } + // Do not join if already in call if (callEmbed) { return; diff --git a/src/app/hooks/useLivekitSupport.ts b/src/app/hooks/useLivekitSupport.ts new file mode 100644 index 000000000..3cb2c1d88 --- /dev/null +++ b/src/app/hooks/useLivekitSupport.ts @@ -0,0 +1,16 @@ +import { AutoDiscoveryInfo } from '../cs-api'; +import { useAutoDiscoveryInfo } from './useAutoDiscoveryInfo'; + +export const livekitSupport = (autoDiscoveryInfo: AutoDiscoveryInfo): boolean => { + const rtcFoci = autoDiscoveryInfo['org.matrix.msc4143.rtc_foci']; + + return ( + Array.isArray(rtcFoci) && rtcFoci.some((info) => typeof info.livekit_service_url === 'string') + ); +}; + +export const useLivekitSupport = (): boolean => { + const autoDiscoveryInfo = useAutoDiscoveryInfo(); + + return livekitSupport(autoDiscoveryInfo); +}; diff --git a/src/app/pages/client/AutoDiscovery.tsx b/src/app/pages/client/AutoDiscovery.tsx new file mode 100644 index 000000000..76423477f --- /dev/null +++ b/src/app/pages/client/AutoDiscovery.tsx @@ -0,0 +1,32 @@ +import React, { ReactNode, useCallback, useMemo } from 'react'; +import { AutoDiscoveryInfoProvider } from '../../hooks/useAutoDiscoveryInfo'; +import { AsyncStatus, useAsyncCallbackValue } from '../../hooks/useAsyncCallback'; +import { autoDiscovery, AutoDiscoveryInfo } from '../../cs-api'; +import { getMxIdServer } from '../../utils/matrix'; + +type AutoDiscoveryProps = { + userId: string; + baseUrl: string; + children: ReactNode; +}; +export function AutoDiscovery({ userId, baseUrl, children }: AutoDiscoveryProps) { + const [state] = useAsyncCallbackValue( + useCallback(async () => { + const server = getMxIdServer(userId); + return autoDiscovery(fetch, server ?? userId); + }, [userId]) + ); + + const [, info] = state.status === AsyncStatus.Success ? state.data : []; + + const fallback: AutoDiscoveryInfo = useMemo( + () => ({ + 'm.homeserver': { + base_url: baseUrl, + }, + }), + [baseUrl] + ); + + return {children}; +} diff --git a/src/app/pages/client/ClientRoot.tsx b/src/app/pages/client/ClientRoot.tsx index e1a5dc0c0..93f0526e3 100644 --- a/src/app/pages/client/ClientRoot.tsx +++ b/src/app/pages/client/ClientRoot.tsx @@ -35,6 +35,7 @@ import { stopPropagation } from '../../utils/keyboard'; import { SyncStatus } from './SyncStatus'; import { AuthMetadataProvider } from '../../hooks/useAuthMetadata'; import { getFallbackSession } from '../../state/sessions'; +import { AutoDiscovery } from './AutoDiscovery'; function ClientRootLoading() { return ( @@ -143,7 +144,7 @@ type ClientRootProps = { }; export function ClientRoot({ children }: ClientRootProps) { const [loading, setLoading] = useState(true); - const { baseUrl } = getFallbackSession() ?? {}; + const { baseUrl, userId } = getFallbackSession() ?? {}; const [loadState, loadMatrix] = useAsyncCallback( useCallback(() => { @@ -183,47 +184,55 @@ export function ClientRoot({ children }: ClientRootProps) { ); return ( - - {mx && } - {loading && } - {(loadState.status === AsyncStatus.Error || startState.status === AsyncStatus.Error) && ( - - - - - {loadState.status === AsyncStatus.Error && ( - {`Failed to load. ${loadState.error.message}`} - )} - {startState.status === AsyncStatus.Error && ( - {`Failed to start. ${startState.error.message}`} - )} - - - - - - )} - {loading || !mx ? ( - - ) : ( - - - {(serverConfigs) => ( - - - - {children} - - - - )} - - - )} - + + + {mx && } + {loading && } + {(loadState.status === AsyncStatus.Error || startState.status === AsyncStatus.Error) && ( + + + + + {loadState.status === AsyncStatus.Error && ( + {`Failed to load. ${loadState.error.message}`} + )} + {startState.status === AsyncStatus.Error && ( + {`Failed to start. ${startState.error.message}`} + )} + + + + + + )} + {loading || !mx ? ( + + ) : ( + + + {(serverConfigs) => ( + + + + {children} + + + + )} + + + )} + + ); } diff --git a/src/app/plugins/call/CallEmbed.ts b/src/app/plugins/call/CallEmbed.ts index aeb28e360..870466769 100644 --- a/src/app/plugins/call/CallEmbed.ts +++ b/src/app/plugins/call/CallEmbed.ts @@ -146,7 +146,7 @@ export class CallEmbed { let initialMediaEvent = true; this.disposables.push( - this.listenEvent(ElementWidgetActions.DeviceMute, (evt) => { + this.listenAction(ElementWidgetActions.DeviceMute, (evt) => { if (initialMediaEvent) { initialMediaEvent = false; this.control.applyState(); @@ -177,18 +177,27 @@ export class CallEmbed { return this.call.transport.send(ElementWidgetActions.HangupCall, {}); } - public listenEvent(type: string, callback: (event: CustomEvent) => void) { - this.call.on(`action:${type}`, callback); - return () => { - this.call.off(`action:${type}`, callback); - }; + public onPreparing(callback: () => void) { + return this.listenEvent('preparing', callback); + } + + public onPreparingError(callback: (error: any) => void) { + return this.listenEvent('error:preparing', callback); + } + + public onReady(callback: () => void) { + return this.listenEvent('ready', callback); + } + + public onCapabilitiesNotified(callback: () => void) { + return this.listenEvent('capabilitiesNotified', callback); } private start() { // Room widgets get locked to the room they were added in this.call.setViewedRoomId(this.roomId); this.disposables.push( - this.listenEvent(ElementWidgetActions.JoinCall, this.onCallJoined.bind(this)) + this.listenAction(ElementWidgetActions.JoinCall, this.onCallJoined.bind(this)) ); // Populate the map of "read up to" events for this widget with the current event in every room. @@ -375,4 +384,15 @@ export class CallEmbed { } } } + + public listenAction(type: string, callback: (event: CustomEvent) => void) { + return this.listenEvent(`action:${type}`, callback); + } + + public listenEvent(type: string, callback: (event: T) => void) { + this.call.on(type, callback); + return () => { + this.call.off(type, callback); + }; + } } From 296249de3245dd597b5711ff4a81eb568ebf9a83 Mon Sep 17 00:00:00 2001 From: Krishan <33421343+kfiven@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:26:25 +1100 Subject: [PATCH 04/11] chore: enable semantic check on PR title (#2447) * Enable semantic check on PR title * Update semantic pull request action version * Update PR trigger types in workflow configuration Removed 'reopened' and 'synchronize' types from pull request triggers. --- .github/workflows/pr-title.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/pr-title.yml diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 000000000..a52ee8e6d --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,15 @@ +name: Check PR title + +on: + pull_request_target: + types: + - opened + - edited + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 37e0c2aaac26ed77ec0fac2c97d03dfa08362172 Mon Sep 17 00:00:00 2001 From: Krishan <33421343+kfiven@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:26:55 +1100 Subject: [PATCH 05/11] chore(deps): continue action if login fails (#2758) chore(action): continue action if login fails --- .github/workflows/docker-pr.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docker-pr.yml b/.github/workflows/docker-pr.yml index 822b8f3fc..960aeb8f6 100644 --- a/.github/workflows/docker-pr.yml +++ b/.github/workflows/docker-pr.yml @@ -30,6 +30,7 @@ jobs: with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} + continue-on-error: true - name: Login to the Github Container registry #Do not update this action from a outside PR if: github.event.pull_request.head.repo.fork == false @@ -38,6 +39,7 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true - name: Extract metadata (tags, labels) for Docker, GHCR id: meta From 0cbfbab5ad4dec16c0c927bef6232662c6725aee Mon Sep 17 00:00:00 2001 From: Krishan <33421343+kfiven@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:57:20 +1100 Subject: [PATCH 06/11] chore: add semantic commits to renovate configuration (#2760) * Add semantic commits to Renovate configuration * Fix formatting issue in renovate.json --- .github/renovate.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/renovate.json b/.github/renovate.json index 62b0cf2a9..2c6c653e0 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,6 +1,10 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["config:recommended", ":dependencyDashboardApproval"], + "extends": [ + "config:recommended", + ":dependencyDashboardApproval", + ":semanticCommits" + ], "labels": ["Dependencies"], "packageRules": [ { From 0d1566977afc7d27795fff2ceebb7c46261e7395 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Tue, 10 Mar 2026 22:45:26 +1100 Subject: [PATCH 07/11] fix: call ui imorovements (#2749) * fix member button tooltip in call room header * hide sticker button in room input based on chat window width * render camera on off data instead of duplicate join messages * hide duplicate call member changes instead of rendering as video status * fix prescreen message spacing --- src/app/features/call/CallView.tsx | 4 ++-- src/app/features/call/styles.css.ts | 4 ++++ src/app/features/room/RoomInput.tsx | 2 +- src/app/features/room/RoomTimeline.tsx | 8 +++++++- src/app/features/room/RoomViewHeader.tsx | 6 +++++- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/app/features/call/CallView.tsx b/src/app/features/call/CallView.tsx index 7a8c28a5e..7c7bec6cc 100644 --- a/src/app/features/call/CallView.tsx +++ b/src/app/features/call/CallView.tsx @@ -97,7 +97,7 @@ function CallPrescreen() { )} -
+ {!inOtherCall && (hasPermission ? ( @@ -105,7 +105,7 @@ function CallPrescreen() { ))} {inOtherCall && } -
+
diff --git a/src/app/features/call/styles.css.ts b/src/app/features/call/styles.css.ts index 249edb08a..2b9f28ad6 100644 --- a/src/app/features/call/styles.css.ts +++ b/src/app/features/call/styles.css.ts @@ -22,3 +22,7 @@ export const CallMemberCard = style({ export const CallControlContainer = style({ padding: config.space.S400, }); + +export const PrescreenMessage = style({ + padding: config.space.S200, +}); diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index ae46d2d09..f88ccf938 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -221,7 +221,7 @@ export const RoomInput = forwardRef( const isComposing = useComposingCheck(); useElementSizeObserver( - useCallback(() => document.body, []), + useCallback(() => fileDropContainerRef.current, [fileDropContainerRef]), useCallback((width) => setHideStickerBtn(width < 500), []) ); diff --git a/src/app/features/room/RoomTimeline.tsx b/src/app/features/room/RoomTimeline.tsx index 640430540..39d7e50a6 100644 --- a/src/app/features/room/RoomTimeline.tsx +++ b/src/app/features/room/RoomTimeline.tsx @@ -1475,7 +1475,13 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli const senderId = mEvent.getSender() ?? ''; const senderName = getMemberDisplayName(room, senderId) || getMxIdLocalPart(senderId); - const callJoined = mEvent.getContent().application; + const content = mEvent.getContent(); + const prevContent = mEvent.getPrevContent(); + + const callJoined = content.application; + if (callJoined && 'application' in prevContent) { + return null; + } const timeJSX = (
Twitter diff --git a/src/app/pages/client/WelcomePage.tsx b/src/app/pages/client/WelcomePage.tsx index 79630990e..8b0bc3c6f 100644 --- a/src/app/pages/client/WelcomePage.tsx +++ b/src/app/pages/client/WelcomePage.tsx @@ -24,7 +24,7 @@ export function WelcomePage() { target="_blank" rel="noreferrer noopener" > - v4.10.5 + v4.11.0 } From 919fe8381bbb69d371244cc67d40375948034ecc Mon Sep 17 00:00:00 2001 From: Krishan <33421343+kfiven@users.noreply.github.com> Date: Wed, 11 Mar 2026 23:06:53 +1100 Subject: [PATCH 10/11] chore(deps): Update slate deps to 0.123.0 (#2764) Update slate deps to latest --- package-lock.json | 61 ++++++++++++++++------------------------------- package.json | 8 +++---- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/package-lock.json b/package-lock.json index a482aa058..b1ac60dbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,10 +59,10 @@ "react-range": "1.8.14", "react-router-dom": "6.30.3", "sanitize-html": "2.12.1", - "slate": "0.112.0", - "slate-dom": "0.112.2", - "slate-history": "0.110.3", - "slate-react": "0.112.1", + "slate": "0.123.0", + "slate-dom": "0.123.0", + "slate-history": "0.113.1", + "slate-react": "0.123.0", "ua-parser-js": "1.0.35" }, "devDependencies": { @@ -10291,20 +10291,15 @@ } }, "node_modules/slate": { - "version": "0.112.0", - "resolved": "https://registry.npmjs.org/slate/-/slate-0.112.0.tgz", - "integrity": "sha512-PRnfFgDA3tSop4OH47zu4M1R4Uuhm/AmASu29Qp7sGghVFb713kPBKEnSf1op7Lx/nCHkRlCa3ThfHtCBy+5Yw==", - "license": "MIT", - "dependencies": { - "immer": "^10.0.3", - "is-plain-object": "^5.0.0", - "tiny-warning": "^1.0.3" - } + "version": "0.123.0", + "resolved": "https://registry.npmjs.org/slate/-/slate-0.123.0.tgz", + "integrity": "sha512-Oon3HR/QzJQBjuOUJT1jGGlp8Ff7t3Bkr/rJ2lDqxNT4H+cBnXpEVQ/si6hn1ZCHhD2xY/2N91PQoH/rD7kxTg==", + "license": "MIT" }, "node_modules/slate-dom": { - "version": "0.112.2", - "resolved": "https://registry.npmjs.org/slate-dom/-/slate-dom-0.112.2.tgz", - "integrity": "sha512-cozITMlpcBxrov854reM6+TooiHiqpfM/nZPrnjpN1wSiDsAQmYbWUyftC+jlwcpFj80vywfDHzlG6hXIc5h6A==", + "version": "0.123.0", + "resolved": "https://registry.npmjs.org/slate-dom/-/slate-dom-0.123.0.tgz", + "integrity": "sha512-OUinp4tvSrAlt64JL9y20Xin08jgnnj1gJmIuPdGvU5MELKXRNZh17a7EKKNOS6OZPAE8Dk9NI1MAIS/Qz0YBw==", "license": "MIT", "dependencies": { "@juggle/resize-observer": "^3.4.0", @@ -10316,13 +10311,13 @@ "tiny-invariant": "1.3.1" }, "peerDependencies": { - "slate": ">=0.99.0" + "slate": ">=0.121.0" } }, "node_modules/slate-history": { - "version": "0.110.3", - "resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.110.3.tgz", - "integrity": "sha512-sgdff4Usdflmw5ZUbhDkxFwCBQ2qlDKMMkF93w66KdV48vHOgN2BmLrf+2H8SdX8PYIpP/cTB0w8qWC2GwhDVA==", + "version": "0.113.1", + "resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.113.1.tgz", + "integrity": "sha512-J9NSJ+UG2GxoW0lw5mloaKcN0JI0x2IA5M5FxyGiInpn+QEutxT1WK7S/JneZCMFJBoHs1uu7S7e6pxQjubHmQ==", "license": "MIT", "dependencies": { "is-plain-object": "^5.0.0" @@ -10332,15 +10327,14 @@ } }, "node_modules/slate-react": { - "version": "0.112.1", - "resolved": "https://registry.npmjs.org/slate-react/-/slate-react-0.112.1.tgz", - "integrity": "sha512-V9b+waxPweXqAkSQmKQ1afG4Me6nVQACPpxQtHPIX02N7MXa5f5WilYv+bKt7vKKw+IZC2F0Gjzhv5BekVgP/A==", + "version": "0.123.0", + "resolved": "https://registry.npmjs.org/slate-react/-/slate-react-0.123.0.tgz", + "integrity": "sha512-nQwXL1FEacrY9ZFmatRhoBnsySNUX2x6qB77V3oNHd7wWxBJWuzz4GMrBXcVoRE8Gac7Angf8xaNGzb6zcPlHg==", "license": "MIT", "dependencies": { "@juggle/resize-observer": "^3.4.0", "direction": "^1.0.4", "is-hotkey": "^0.2.0", - "is-plain-object": "^5.0.0", "lodash": "^4.17.21", "scroll-into-view-if-needed": "^3.1.0", "tiny-invariant": "1.3.1" @@ -10348,18 +10342,8 @@ "peerDependencies": { "react": ">=18.2.0", "react-dom": ">=18.2.0", - "slate": ">=0.99.0", - "slate-dom": ">=0.110.2" - } - }, - "node_modules/slate/node_modules/immer": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", - "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" + "slate": ">=0.121.0", + "slate-dom": ">=0.119.1" } }, "node_modules/smob": { @@ -10729,11 +10713,6 @@ "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==", "license": "MIT" }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, "node_modules/tinyglobby": { "version": "0.2.10", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", diff --git a/package.json b/package.json index 9cc02e190..4549a8046 100644 --- a/package.json +++ b/package.json @@ -71,10 +71,10 @@ "react-range": "1.8.14", "react-router-dom": "6.30.3", "sanitize-html": "2.12.1", - "slate": "0.112.0", - "slate-dom": "0.112.2", - "slate-history": "0.110.3", - "slate-react": "0.112.1", + "slate": "0.123.0", + "slate-dom": "0.123.0", + "slate-history": "0.113.1", + "slate-react": "0.123.0", "ua-parser-js": "1.0.35" }, "devDependencies": { From 6a05ff58406755d5613e48dc3cf4f39f1033764a Mon Sep 17 00:00:00 2001 From: Krishan <33421343+kfiven@users.noreply.github.com> Date: Wed, 11 Mar 2026 23:07:37 +1100 Subject: [PATCH 11/11] chore(release): v4.11.1 [skip ci] (#2765) * chore(release): 4.11.0 [skip ci] * chore(release): 4.11.1 [skip ci] --- package-lock.json | 4 ++-- package.json | 2 +- src/app/features/settings/about/About.tsx | 2 +- src/app/pages/auth/AuthFooter.tsx | 2 +- src/app/pages/client/WelcomePage.tsx | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index b1ac60dbd..399f03a92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cinny", - "version": "4.11.0", + "version": "4.11.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cinny", - "version": "4.11.0", + "version": "4.11.1", "license": "AGPL-3.0-only", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "1.1.6", diff --git a/package.json b/package.json index 4549a8046..ba2cd751e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cinny", - "version": "4.11.0", + "version": "4.11.1", "description": "Yet another matrix client", "main": "index.js", "type": "module", diff --git a/src/app/features/settings/about/About.tsx b/src/app/features/settings/about/About.tsx index 3f8ac163a..f536d12ef 100644 --- a/src/app/features/settings/about/About.tsx +++ b/src/app/features/settings/about/About.tsx @@ -46,7 +46,7 @@ export function About({ requestClose }: AboutProps) { Cinny - v4.11.0 + v4.11.1 Yet another matrix client. diff --git a/src/app/pages/auth/AuthFooter.tsx b/src/app/pages/auth/AuthFooter.tsx index d4c58058b..7e509d1ee 100644 --- a/src/app/pages/auth/AuthFooter.tsx +++ b/src/app/pages/auth/AuthFooter.tsx @@ -15,7 +15,7 @@ export function AuthFooter() { target="_blank" rel="noreferrer" > - v4.11.0 + v4.11.1 Twitter diff --git a/src/app/pages/client/WelcomePage.tsx b/src/app/pages/client/WelcomePage.tsx index 8b0bc3c6f..b3ec2eb44 100644 --- a/src/app/pages/client/WelcomePage.tsx +++ b/src/app/pages/client/WelcomePage.tsx @@ -24,7 +24,7 @@ export function WelcomePage() { target="_blank" rel="noreferrer noopener" > - v4.11.0 + v4.11.1 }