From fb6a92eb182f5756bdb5d49292934d45ae9e6093 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 19:52:14 +0000 Subject: [PATCH 01/13] Update dependency vite-plugin-static-copy to v2 [SECURITY] --- package-lock.json | 25 ++++++++++++++++++++----- package.json | 2 +- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 70826ae7..92d91375 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,7 +101,7 @@ "typescript": "4.9.4", "vite": "5.4.19", "vite-plugin-pwa": "0.20.5", - "vite-plugin-static-copy": "1.0.4", + "vite-plugin-static-copy": "2.3.2", "vite-plugin-top-level-await": "1.4.4" }, "engines": { @@ -9308,6 +9308,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -11446,21 +11459,23 @@ } }, "node_modules/vite-plugin-static-copy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-1.0.4.tgz", - "integrity": "sha512-UtyOttgoeotSCwmBugsEZCZJZcIpjE9NGO7jlZ9OeedYpBueBdspD8waRZrjE+yQLH6qGNU2CvYB2FILviCQjg==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-2.3.2.tgz", + "integrity": "sha512-iwrrf+JupY4b9stBttRWzGHzZbeMjAHBhkrn67MNACXJVjEMRpCI10Q3AkxdBkl45IHaTfw/CNVevzQhP7yTwg==", "dev": true, + "license": "MIT", "dependencies": { "chokidar": "^3.5.3", "fast-glob": "^3.2.11", "fs-extra": "^11.1.0", + "p-map": "^7.0.3", "picocolors": "^1.0.0" }, "engines": { "node": "^18.0.0 || >=20.0.0" }, "peerDependencies": { - "vite": "^5.0.0" + "vite": "^5.0.0 || ^6.0.0" } }, "node_modules/vite-plugin-top-level-await": { diff --git a/package.json b/package.json index f1816cdd..3987dbfb 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "typescript": "4.9.4", "vite": "5.4.19", "vite-plugin-pwa": "0.20.5", - "vite-plugin-static-copy": "1.0.4", + "vite-plugin-static-copy": "2.3.2", "vite-plugin-top-level-await": "1.4.4" } } From c881b5995725246b4bba2b522b21c3b3abba8b7d Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Sun, 24 Aug 2025 18:03:20 +0530 Subject: [PATCH 02/13] =?UTF-8?q?Fix=20image=20overlap=20with=20=E2=80=9CM?= =?UTF-8?q?ark=20as=20read=E2=80=9D=20and=20typing=20indicator=20(#2457)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/components/message/content/style.css.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/message/content/style.css.ts b/src/app/components/message/content/style.css.ts index 93f3649c..bb5d8484 100644 --- a/src/app/components/message/content/style.css.ts +++ b/src/app/components/message/content/style.css.ts @@ -16,7 +16,6 @@ export const AbsoluteContainer = style([ position: 'absolute', top: 0, left: 0, - zIndex: 1, width: '100%', height: '100%', }, @@ -26,6 +25,7 @@ export const AbsoluteFooter = style([ DefaultReset, { position: 'absolute', + pointerEvents: 'none', bottom: config.space.S100, left: config.space.S100, right: config.space.S100, From 13cdcbcdb167cdf4f8bb124f922a078f36ebdad1 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Sun, 24 Aug 2025 18:04:21 +0530 Subject: [PATCH 03/13] New invite user to room dialog (#2460) * fix 0 displayed in invite with no timestamp * support displaying invite reason for receiver * show invite reason as compact message * remove unused import * revert: show invite reason as compact message * remove unused import * add new invite prompt --- .../invite-user-prompt/InviteUserPrompt.tsx | 291 ++++++++++++++++++ .../components/invite-user-prompt/index.ts | 1 + src/app/components/room-intro/RoomIntro.tsx | 16 +- src/app/features/lobby/HierarchyItemMenu.tsx | 45 ++- src/app/features/lobby/LobbyHeader.tsx | 17 +- src/app/features/room-nav/RoomNavItem.tsx | 17 +- src/app/features/room/RoomViewHeader.tsx | 17 +- src/app/pages/client/inbox/Invites.tsx | 52 ++-- src/app/pages/client/sidebar/SpaceTabs.tsx | 17 +- src/app/pages/client/space/Space.tsx | 17 +- 10 files changed, 434 insertions(+), 56 deletions(-) create mode 100644 src/app/components/invite-user-prompt/InviteUserPrompt.tsx create mode 100644 src/app/components/invite-user-prompt/index.ts diff --git a/src/app/components/invite-user-prompt/InviteUserPrompt.tsx b/src/app/components/invite-user-prompt/InviteUserPrompt.tsx new file mode 100644 index 00000000..82313c3e --- /dev/null +++ b/src/app/components/invite-user-prompt/InviteUserPrompt.tsx @@ -0,0 +1,291 @@ +import React, { + ChangeEventHandler, + FormEventHandler, + KeyboardEventHandler, + useCallback, + useMemo, + useRef, + useState, +} from 'react'; +import { + Overlay, + OverlayBackdrop, + OverlayCenter, + Box, + Header, + config, + Text, + IconButton, + Icon, + Icons, + Input, + Button, + Spinner, + color, + TextArea, + Dialog, + Menu, + toRem, + Scroll, + MenuItem, +} from 'folds'; +import { Room } from 'matrix-js-sdk'; +import { isKeyHotkey } from 'is-hotkey'; +import FocusTrap from 'focus-trap-react'; +import { stopPropagation } from '../../utils/keyboard'; +import { useDirectUsers } from '../../hooks/useDirectUsers'; +import { getMxIdLocalPart, getMxIdServer, isUserId } from '../../utils/matrix'; +import { Membership } from '../../../types/matrix/room'; +import { useAsyncSearch, UseAsyncSearchOptions } from '../../hooks/useAsyncSearch'; +import { highlightText, makeHighlightRegex } from '../../plugins/react-custom-html-parser'; +import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; +import { useMatrixClient } from '../../hooks/useMatrixClient'; +import { BreakWord } from '../../styles/Text.css'; +import { useAlive } from '../../hooks/useAlive'; + +const SEARCH_OPTIONS: UseAsyncSearchOptions = { + limit: 1000, + matchOptions: { + contain: true, + }, +}; +const getUserIdString = (userId: string) => getMxIdLocalPart(userId) ?? userId; + +type InviteUserProps = { + room: Room; + requestClose: () => void; +}; +export function InviteUserPrompt({ room, requestClose }: InviteUserProps) { + const mx = useMatrixClient(); + const alive = useAlive(); + + const inputRef = useRef(null); + const directUsers = useDirectUsers(); + const [validUserId, setValidUserId] = useState(); + + const filteredUsers = useMemo( + () => + directUsers.filter((userId) => { + const membership = room.getMember(userId)?.membership; + return membership !== Membership.Join; + }), + [directUsers, room] + ); + const [result, search, resetSearch] = useAsyncSearch( + filteredUsers, + getUserIdString, + SEARCH_OPTIONS + ); + const queryHighlighRegex = result?.query + ? makeHighlightRegex(result.query.split(' ')) + : undefined; + + const [inviteState, invite] = useAsyncCallback( + useCallback( + async (userId, reason) => { + await mx.invite(room.roomId, userId, reason); + }, + [mx, room] + ) + ); + + const inviting = inviteState.status === AsyncStatus.Loading; + + const handleReset = () => { + if (inputRef.current) inputRef.current.value = ''; + setValidUserId(undefined); + resetSearch(); + }; + + const handleSubmit: FormEventHandler = (evt) => { + evt.preventDefault(); + const target = evt.target as HTMLFormElement | undefined; + + if (inviting || !validUserId) return; + + const reasonInput = target?.reasonInput as HTMLTextAreaElement | undefined; + const reason = reasonInput?.value.trim(); + + invite(validUserId, reason || undefined).then(() => { + if (alive()) { + handleReset(); + if (reasonInput) reasonInput.value = ''; + } + }); + }; + + const handleSearchChange: ChangeEventHandler = (evt) => { + const value = evt.currentTarget.value.trim(); + if (isUserId(value)) { + setValidUserId(value); + } else { + setValidUserId(undefined); + const term = getMxIdLocalPart(value) ?? (value.startsWith('@') ? value.slice(1) : value); + if (term) { + search(term); + } else { + resetSearch(); + } + } + }; + + const handleUserId = (userId: string) => { + if (inputRef.current) { + inputRef.current.value = userId; + setValidUserId(userId); + resetSearch(); + inputRef.current.focus(); + } + }; + + const handleKeyDown: KeyboardEventHandler = (evt) => { + if (isKeyHotkey('escape', evt)) { + resetSearch(); + return; + } + if (isKeyHotkey('tab', evt) && result && result.items.length > 0) { + evt.preventDefault(); + const userId = result.items[0]; + handleUserId(userId); + } + }; + + return ( + }> + + inputRef.current, + clickOutsideDeactivates: true, + onDeactivate: requestClose, + escapeDeactivates: stopPropagation, + }} + > + + +
+ + + Invite + + + + + + + +
+ + + User ID +
+ + {result && result.items.length > 0 && ( + isKeyHotkey('arrowdown', evt), + isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt), + escapeDeactivates: stopPropagation, + }} + > + + + +
+ {result.items.map((userId) => { + const username = `${getMxIdLocalPart(userId)}`; + const userServer = getMxIdServer(userId); + + return ( + handleUserId(userId)} + after={ + + {userServer} + + } + disabled={inviting} + > + + + + {queryHighlighRegex + ? highlightText(queryHighlighRegex, [ + username ?? userId, + ]) + : username} + + + + + ); + })} +
+
+
+
+
+ )} +
+
+ + Reason (Optional) +