diff --git a/.eslintignore b/.eslintignore index e1b0ceb50ca..67047c46832 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,3 +7,4 @@ test/end-to-end-tests/lib/ src/component-index.js # Auto-generated file src/modules.ts +src/components/structures/scripts/freshworks.js \ No newline at end of file diff --git a/.github/workflows/static_analysis.yaml b/.github/workflows/static_analysis.yaml index b7c02c3f2e9..95db239aee3 100644 --- a/.github/workflows/static_analysis.yaml +++ b/.github/workflows/static_analysis.yaml @@ -1,5 +1,7 @@ +## VERJI WE MUST REVISIT THIS FILE MANUALLY IN PIPELINE / UNSURE WHAT IS NECESSARY TO KEEP name: Static Analysis on: + workflow_dispatch: pull_request: {} push: branches: [develop, master] @@ -24,7 +26,6 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 with: cache: "yarn" @@ -38,6 +39,8 @@ jobs: i18n_lint: name: "i18n Check" + # VERJI skip i18n by setting false + if: ${{false}} uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main permissions: pull-requests: read @@ -53,6 +56,8 @@ jobs: welcome_to_element rethemendex_lint: + # VERJI skip Rethemendex check by setting false + if: ${{false}} name: "Rethemendex Check" runs-on: ubuntu-24.04 steps: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 35803a60f1a..62f18889801 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,3 +1,4 @@ +## VERJI NEEDS TO MANUALLY CHECK THIS ALONG WITH RUNNING ACTION name: Tests on: pull_request: {} diff --git a/.github/workflows/verji-release-drafter.yml b/.github/workflows/verji-release-drafter.yml new file mode 100644 index 00000000000..585834d6014 --- /dev/null +++ b/.github/workflows/verji-release-drafter.yml @@ -0,0 +1,9 @@ +name: Verji Release Drafter +on: + push: + branches: [verji-staging] + workflow_dispatch: {} +concurrency: ${{ github.workflow }} +jobs: + draft: + uses: verji/matrix-js-sdk/.github/workflows/release-drafter-workflow.yml@verji-develop diff --git a/jest.config.ts b/jest.config.ts index 326f2040d97..1a6bd4bc57d 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -19,6 +19,7 @@ const config: Config = { globalSetup: "/test/globalSetup.ts", setupFiles: ["jest-canvas-mock", "web-streams-polyfill/polyfill"], setupFilesAfterEnv: ["/test/setupTests.ts"], + moduleFileExtensions: ["js", "jsx", "json", "ts", "tsx"], moduleNameMapper: { "\\.(css|scss|pcss)$": "/__mocks__/cssMock.js", "\\.(gif|png|ttf|woff2)$": "/__mocks__/imageMock.js", diff --git a/package.json b/package.json index 6d082f95865..77ac5f2f808 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "@formatjs/intl-segmenter": "^11.5.7", "@matrix-org/analytics-events": "^0.29.0", "@matrix-org/emojibase-bindings": "^1.3.3", - "@matrix-org/react-sdk-module-api": "^2.4.0", + "@matrix-org/react-sdk-module-api": "https://github.com/verji/matrix-react-sdk-module-api.git#verji-main", "@matrix-org/spec": "^1.7.0", "@sentry/browser": "^8.0.0", "@vector-im/compound-design-tokens": "^2.0.1", @@ -142,6 +142,7 @@ "react-focus-lock": "^2.5.1", "react-transition-group": "^4.4.1", "rfc4648": "^1.4.0", + "rss-parser": "^3.12.0", "sanitize-filename": "^1.6.3", "sanitize-html": "2.13.1", "tar-js": "^0.3.0", @@ -205,9 +206,10 @@ "@types/react-beautiful-dnd": "^13.0.0", "@types/react-dom": "18.3.1", "@types/react-transition-group": "^4.4.0", - "@types/sanitize-html": "2.13.0", - "@types/semver": "^7.5.8", - "@types/tar-js": "^0.3.5", + "@types/sanitize-html": "2.11.0", + "@types/sdp-transform": "^2.4.6", + "@types/seedrandom": "3.0.4", + "@types/tar-js": "^0.3.2", "@types/ua-parser-js": "^0.7.36", "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.0.0", @@ -272,9 +274,7 @@ "prettier": "3.4.2", "process": "^0.11.10", "raw-loader": "^4.0.2", - "rimraf": "^6.0.0", - "semver": "^7.5.2", - "source-map-loader": "^5.0.0", + "rimraf": "^5.0.0", "stylelint": "^16.1.0", "stylelint-config-standard": "^36.0.0", "stylelint-scss": "^6.0.0", diff --git a/res/css/_components.pcss b/res/css/_components.pcss index e9a53cd43cc..76df6b9915d 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -1,4 +1,10 @@ /* autogenerated by rethemendex.sh */ + +/* ----- Verji start ----- */ +@import "./views/dialogs/_InviteNewMembersDialog.scss"; +@import "./views/dialogs/_ConfirmInviteExternalUsersDialog.scss"; +/* ----- Verji end ----- */ + @import "./_animations.pcss"; @import "./_common.pcss"; @import "./_font-sizes.pcss"; diff --git a/res/css/structures/_HomePage.pcss b/res/css/structures/_HomePage.pcss index 2c7e665e713..f27549e1599 100644 --- a/res/css/structures/_HomePage.pcss +++ b/res/css/structures/_HomePage.pcss @@ -13,6 +13,7 @@ Please see LICENSE files in the repository root for full details. height: 100%; margin-left: auto; margin-right: auto; + max-height: 100%; } .mx_HomePage_default { diff --git a/res/css/structures/_LeftPanel.pcss b/res/css/structures/_LeftPanel.pcss index d6d23bbcf28..f005ca1b5db 100644 --- a/res/css/structures/_LeftPanel.pcss +++ b/res/css/structures/_LeftPanel.pcss @@ -140,6 +140,39 @@ Please see LICENSE files in the repository root for full details. } } + .mx_LeftPanel_omButton, + .mx_LeftPanel_newsButton { + width: 32px; + height: 32px; + border-radius: 8px; + background-color: $panel-actions; + position: relative; + margin-left: 8px; + margin-top: 2px; + margin-bottom: 2px; + + &::before { + content: ""; + position: absolute; + top: 8px; + left: 8px; + width: 16px; + height: 16px; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background-color: $secondary-content; + } + + &:hover { + background-color: $tertiary-content; + + &::before { + background-color: $background; + } + } + } + .mx_LeftPanel_exploreButton, .mx_LeftPanel_recentsButton { width: 32px; @@ -171,6 +204,15 @@ Please see LICENSE files in the repository root for full details. } } + //Verji start + .mx_LeftPanel_newsButton::before { + mask-image: url("$(res)/img/verji/news.svg"); + } + .mx_LeftPanel_omButton::before { + mask-image: url("$(res)/img/verji/shield.svg"); + } + //Verji end + .mx_LeftPanel_exploreButton::before { mask-image: url("$(res)/img/element-icons/roomlist/explore.svg"); } diff --git a/res/css/structures/_MiscHeader.scss b/res/css/structures/_MiscHeader.scss new file mode 100644 index 00000000000..a932e6cd78e --- /dev/null +++ b/res/css/structures/_MiscHeader.scss @@ -0,0 +1,61 @@ +/* + ROSBERG FILE +*/ + +.mx_MiscHeaderButtons { + display: flex; + &::before { + content: unset; + } +} + +.mx_MiscHeaderButtons::before { + content: ""; + //background-color: $header-divider-color; + opacity: 0.5; + margin: 6px 8px; + border-radius: 1px; + width: 1px; +} + +.mx_MiscHeader_miscButton { + cursor: pointer; + flex: 0 0 auto; + margin-left: 1px; + margin-right: 1px; + height: 32px; + width: 32px; + position: relative; + border-radius: 100%; + + &::before { + content: ""; + position: absolute; + top: 4px; // center with parent of 32px + left: 4px; // center with parent of 32px + height: 24px; + width: 24px; + background-color: $icon-button-color; + mask-repeat: no-repeat; + mask-size: contain; + } + + &:hover { + //background: rgba($accent-color, 0.1); + + &::before { + //background-color: $accent-color; + } + } +} + +.mx_MiscHeader_miscButton_highlight { + &::before { + //background-color: $accent-color !important; + } +} + +.mx_MiscHeader_roomSupportButton::before { + mask-image: url("$(res)/img/element-icons/settings/help.svg"); + mask-position: center; +} diff --git a/res/css/structures/_RightPanel.pcss b/res/css/structures/_RightPanel.pcss index 0f796dbc96d..1abd109ad9b 100644 --- a/res/css/structures/_RightPanel.pcss +++ b/res/css/structures/_RightPanel.pcss @@ -39,6 +39,10 @@ Please see LICENSE files in the repository root for full details. mask-image: url("@vector-im/compound-design-tokens/icons/info-solid.svg"); mask-position: center; } +.mx_RightPanel_supportButton::before { + mask-image: url("$(res)/img/element-icons/settings/help.svg"); + mask-position: center; +} .mx_RightPanel_pinnedMessagesButton { &::before { diff --git a/res/css/structures/_RoomView.pcss b/res/css/structures/_RoomView.pcss index 65ea555ce18..3d10ac14382 100644 --- a/res/css/structures/_RoomView.pcss +++ b/res/css/structures/_RoomView.pcss @@ -154,6 +154,32 @@ Please see LICENSE files in the repository root for full details. margin-bottom: 80px; /* visually center the content (intentional offset) */ } +// Verji start +@keyframes fade1 { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +.mx_RoomView_News1 { + animation: 0.2s ease-out 0s 0.2 fade1; +} + +@keyframes fade2 { + 0% { + transform: translateX(70px); + } + 100% { + transform: translateX(0); + } +} +.mx_RoomView_News2 { + animation: 0.2s ease 0s 0.2 fade2; +} +// Verji end + .mx_RoomView_MessageList { list-style-type: none; padding: var(--RoomView_MessageList-padding); /* mx_ProfileResizer depends on this value */ diff --git a/res/css/structures/_UserMenu.pcss b/res/css/structures/_UserMenu.pcss index d24a6e4ac7a..a7135e22618 100644 --- a/res/css/structures/_UserMenu.pcss +++ b/res/css/structures/_UserMenu.pcss @@ -185,8 +185,91 @@ Please see LICENSE files in the repository root for full details. .mx_UserMenu_iconSignOut::before { mask-image: url("@vector-im/compound-design-tokens/icons/leave.svg"); } - .mx_UserMenu_iconQr::before { mask-image: url("@vector-im/compound-design-tokens/icons/qr-code.svg"); } + /* Verji start */ + .mx_UserMenu_iconMembers::before { + mask-image: url("$(res)/img/element-icons/room/members.svg"); + } + + .mx_UserMenu_iconInvite::before { + mask-image: url("$(res)/img/element-icons/room/invite.svg"); + } + + .mx_UserMenu_oidcmanage::before { + mask-image: url("$(res)/img/verji/address-card.svg"); + } + + .mx_UserMenu_portal::before { + mask-image: url("$(res)/img/verji/house-user.svg"); + } + + .mx_UserMenu_signing::before { + mask-image: url("$(res)/img/verji/signing.svg"); + } + /* Verji end */ +} + +.mx_UserMenu_CustomStatusSection { + margin: 0 12px 8px; + + .mx_UserMenu_CustomStatusSection_field { + position: relative; + display: flex; + + &.mx_UserMenu_CustomStatusSection_field_hasQuery { + .mx_UserMenu_CustomStatusSection_clear { + display: block; + } + } + + > .mx_UserMenu_CustomStatusSection_input { + border: 1px solid $accent; + border-radius: 8px; + width: 100%; + + &:focus + .mx_UserMenu_CustomStatusSection_clear { + display: block; + } + } + + > .mx_UserMenu_CustomStatusSection_clear { + display: none; + + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + + width: 16px; + height: 16px; + margin-right: 8px; + background-color: $quinary-content; + border-radius: 50%; + + &::before { + content: ""; + position: absolute; + width: inherit; + height: inherit; + mask-image: url("$(res)/img/feather-customised/x.svg"); + mask-position: center; + mask-size: 12px; + mask-repeat: no-repeat; + background-color: $secondary-content; + } + } + } + + > p { + font-size: $font-12px; + line-height: $font-15px; + color: $secondary-content; + margin: 4px 0; + } + + .mx_AccessibleButton_kind_primary_outline { + display: block; + } } diff --git a/res/css/views/dialogs/_ConfirmInviteExternalUsersDialog.scss b/res/css/views/dialogs/_ConfirmInviteExternalUsersDialog.scss new file mode 100644 index 00000000000..ebef647adbd --- /dev/null +++ b/res/css/views/dialogs/_ConfirmInviteExternalUsersDialog.scss @@ -0,0 +1,41 @@ +/* ROSBERG style module */ +/* NOTE: This code (and any other Rosberg .pcss/.scss files) has to be imported in here: res\css\_components.pcss */ + +.mx_ConfirmInviteExternalUsersDialog { + &.mx_BaseDialog { + width: 480px; + } +} + +.mx_Dialog_content { + margin-bottom: 24px; +} + +.vmx_all_users { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; + gap: 20px; +} + +.mx_ConfirmInviteExternalUsersDialog_user { + display: flex; + flex-direction: column; + flex: 1; + padding: 8px; + border: 1px solid #ccc; + border-radius: 4px; + + :first-child { + margin-top: 0; + } + + &_email, + &_project, + &_phoneNr { + margin-top: 4px; + font-style: italic; + white-space: nowrap; + } +} diff --git a/res/css/views/dialogs/_InviteNewMembersDialog.scss b/res/css/views/dialogs/_InviteNewMembersDialog.scss new file mode 100644 index 00000000000..49fd04be651 --- /dev/null +++ b/res/css/views/dialogs/_InviteNewMembersDialog.scss @@ -0,0 +1,210 @@ +/* ROSBERG style module */ +/* NOTE: This code (and any other Rosberg .pcss files) has to be imported in here: res\css\_components.pcss */ + +.vmx_main_dialog { + @at-root .mx_Dialog & { + height: auto !important; + } +} + +.vmx_no_external_user_found { + display: flex; + flex-direction: row; + position: relative; + justify-content: space-between; + padding-bottom: 12px; + margin-bottom: 12px; + + padding: 8px; + border: 1px solid #dec802; + border-radius: 4px; + + .vmx_no_external_user_found_warning_icon { + margin-right: 20px; + } + + .vmx_no_external_user_found_close_btn { + right: 10px; + display: none; + scale: 0.8; + } + &:hover { + .vmx_no_external_user_found_close_btn { + display: block !important; + } + } +} + +.vmx_invite_external_user { + display: flex; + flex-wrap: wrap; + position: relative; + justify-content: space-between; + padding-bottom: 12px; + margin-bottom: 12px; + // border-bottom: 1px solid var(--quinary-content, #6F7882) + + padding: 8px; + border: 1px solid #ccc; + border-radius: 4px; + // margin-bottom: 8px; + + .vmx_invite_external_user_close_btn { + right: 10px; + display: none; + scale: 0.8; + } + &:hover { + .vmx_invite_external_user_close_btn { + display: block !important; + } + } +} + +.vmx_invite_external_user_tenant_info { + display: flex; + flex-direction: row; + width: 100%; + align-items: center; + :last-child { + margin-left: 20px; + scale: 0.8; + } +} + +.vmx_invite_external_user_field { + flex: 1 1 0; + /* min-width: 300px; */ + padding: 1rem; + box-sizing: border-box; +} + +@media (max-width: 960px) { + .vmx_invite_external_user_field { + flex-basis: 100%; + } +} + +.vmx_phone_nr_invalid { + border-color: red !important; +} + +.vmx_phone_input_flag > :first-child { + margin-left: 10px; +} + +.vmx_input { + margin: 0 !important; +} + +.vmx_infoText { + margin-bottom: 40px !important; +} + +.vmx_input_readonly { + background-color: var(--quinary-content, #6f7882); + color: var(--primary-content, #ffffff); +} + +.vmx_dialog_footer { + display: flex; + flex-direction: row; + align-items: center; + justify-content: end; + margin-top: 40px; + padding: 0 !important; + width: 100%; +} + +.vmx_divider { + height: 1em; + display: block; +} + +.vmx_InviteDialog_roomTile { + cursor: pointer; + padding: 5px 10px; + + &:hover { + background-color: $header-panel-bg-color; + border-radius: 4px; + } + + * { + vertical-align: middle; + } + + .vmx_InviteDialog_roomTile_avatarStack { + display: inline-block; + position: relative; + width: 36px; + height: 36px; + + & > * { + position: absolute; + top: 0; + left: 0; + } + } + + .vmx_InviteDialog_roomTile_selected { + width: 36px; + height: 36px; + border-radius: 36px; + background-color: $username-variant1-color; + display: inline-block; + position: relative; + + &::before { + content: ""; + width: 24px; + height: 24px; + grid-column: 1; + grid-row: 1; + mask-image: url("$(res)/img/feather-customised/check.svg"); + mask-size: 100%; + mask-repeat: no-repeat; + position: absolute; + top: 6px; // 50% + left: 6px; // 50% + background-color: #ffffff; // this is fine without a var because it's for both themes + } + } + + .vmx_InviteDialog_roomTile_nameStack { + display: inline-block; + overflow: hidden; + } + + .vmx_InviteDialog_roomTile_name { + font-weight: 600; + font-size: $font-14px; + color: $primary-content; + margin-left: 7px; + } + + .vmx_InviteDialog_roomTile_userId { + font-size: $font-12px; + color: $muted-fg-color; + margin-left: 7px; + } + + .vmx_InviteDialog_roomTile_name, + .vmx_InviteDialog_roomTile_userId { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .vmx_InviteDialog_roomTile_time { + text-align: right; + font-size: $font-12px; + color: $muted-fg-color; + float: right; + line-height: 3.6rem; // Height of the avatar to keep the time vertically aligned + } + + .vmx_InviteDialog_roomTile_highlight { + font-weight: 900; + } +} diff --git a/res/css/views/rooms/_NotificationBadge.pcss b/res/css/views/rooms/_NotificationBadge.pcss index 1e708fc8473..060ba62ce87 100644 --- a/res/css/views/rooms/_NotificationBadge.pcss +++ b/res/css/views/rooms/_NotificationBadge.pcss @@ -25,6 +25,12 @@ Please see LICENSE files in the repository root for full details. align-items: center; justify-content: center; + //Verji start + &.mx_NotificationBadge_green { + background-color: $accent; + } + // Verji end + /* These are the 3 background types */ &.mx_NotificationBadge_dot { diff --git a/res/img/verji/address-card.svg b/res/img/verji/address-card.svg new file mode 100644 index 00000000000..2258c1f3aa1 --- /dev/null +++ b/res/img/verji/address-card.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/verji/house-user.svg b/res/img/verji/house-user.svg new file mode 100644 index 00000000000..42b75783e86 --- /dev/null +++ b/res/img/verji/house-user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/verji/news.svg b/res/img/verji/news.svg new file mode 100644 index 00000000000..1db2488e24a --- /dev/null +++ b/res/img/verji/news.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/verji/shield.svg b/res/img/verji/shield.svg new file mode 100644 index 00000000000..3d9a8fcb7d9 --- /dev/null +++ b/res/img/verji/shield.svg @@ -0,0 +1,22 @@ + + + + + + diff --git a/res/img/verji/signing.svg b/res/img/verji/signing.svg new file mode 100644 index 00000000000..67cf21788fc --- /dev/null +++ b/res/img/verji/signing.svg @@ -0,0 +1,42 @@ + + + + + + + + diff --git a/src/LegacyCallHandler.tsx b/src/LegacyCallHandler.tsx index b804ca0084d..6a8ee1d05bd 100644 --- a/src/LegacyCallHandler.tsx +++ b/src/LegacyCallHandler.tsx @@ -902,8 +902,10 @@ export default class LegacyCallHandler extends EventEmitter { await this.placeMatrixCall(roomId, type, transferee); } else { - // > 2 || useFor1To1Calls - await this.placeJitsiCall(roomId, type); + // > 2 + if (SettingsStore.getValue(UIFeature.MultipleCallsInRoom)) { + await this.placeJitsiCall(roomId, type); + } } } diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index 89faa9e0e9a..2a142d43bb1 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -846,7 +846,8 @@ async function persistCredentials(credentials: IMatrixClientCreds): Promise = {}; let secretStorageKeyInfo: Record = {}; let secretStorageBeingAccessed = false; +let dehydrationCache: { + key?: Uint8Array; + keyInfo?: SecretStorage.SecretStorageKeyDescription; +} = {}; /** * This can be used by other components to check if secret storage access is in * progress, so that we can e.g. avoid intermittently showing toasts during @@ -104,7 +108,15 @@ async function getSecretStorageKey({ return [keyId, secretStorageKeys[keyId]]; } - const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup.getSecretStorageKey(); + if (dehydrationCache.key) { + if (await MatrixClientPeg.safeGet().checkSecretStorageKey(dehydrationCache.key, keyInfo)) { + cacheSecretStorageKey(keyId, keyInfo, dehydrationCache.key); + return [keyId, dehydrationCache.key]; + } + } + + // const keyFromCustomisations = SecurityCustomisations.getSecretStorageKey?.(); + const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup?.getSecretStorageKey(); if (keyFromCustomisations) { logger.log("getSecretStorageKey: Using secret storage key from CryptoSetupExtension"); cacheSecretStorageKey(keyId, keyInfo, keyFromCustomisations); @@ -148,6 +160,57 @@ async function getSecretStorageKey({ return [keyId, key]; } +export async function getDehydrationKey( + keyInfo: SecretStorage.SecretStorageKeyDescription, + checkFunc: (data: Uint8Array) => void, +): Promise { + // const keyFromCustomisations = SecurityCustomisations.getSecretStorageKey?.(); + const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup?.getSecretStorageKey(); + if (keyFromCustomisations) { + logger.log("CryptoSetupExtension: Using key from extension (dehydration)"); + return keyFromCustomisations; + } + + const inputToKey = makeInputToKey(keyInfo); + const { finished } = Modal.createDialog( + AccessSecretStorageDialog, + /* props= */ + { + keyInfo, + checkPrivateKey: async (input: KeyParams): Promise => { + const key = await inputToKey(input); + try { + checkFunc(key); + return true; + } catch (e) { + return false; + } + }, + }, + /* className= */ undefined, + /* isPriorityModal= */ false, + /* isStaticModal= */ false, + /* options= */ { + onBeforeClose: async (reason): Promise => { + if (reason === "backgroundClick") { + return confirmToDismiss(); + } + return true; + }, + }, + ); + const [input] = await finished; + if (!input) { + throw new AccessCancelledError(); + } + const key = await inputToKey(input); + + // need to copy the key because rehydration (unpickling) will clobber it + dehydrationCache = { key: new Uint8Array(key), keyInfo }; + + return key; +} + function cacheSecretStorageKey( keyId: string, keyInfo: SecretStorage.SecretStorageKeyDescription, @@ -291,8 +354,9 @@ async function doAccessSecretStorage(func: () => Promise, opts: AccessSecr await func(); logger.debug("accessSecretStorage: operation complete"); } catch (e) { - ModuleRunner.instance.extensions.cryptoSetup.catchAccessSecretStorageError(e as Error); - logger.error("accessSecretStorage: error during operation", e); + // SecurityCustomisations.catchAccessSecretStorageError?.(e as Error); + ModuleRunner.instance.extensions.cryptoSetup?.catchAccessSecretStorageError(e as Error); + logger.error(e); // Re-throw so that higher level logic can abort as needed throw e; } diff --git a/src/VerjiLocalSearch.ts b/src/VerjiLocalSearch.ts new file mode 100644 index 00000000000..58c1c0fe172 --- /dev/null +++ b/src/VerjiLocalSearch.ts @@ -0,0 +1,424 @@ +/* + Copyright 2024 Verji Tech AS. All rights reserved. + Unauthorized copying or distribution of this file, via any medium, is strictly prohibited. +*/ + +import { + EventTimeline, + MatrixClient, + MatrixEvent, + Room, + RoomMember, + SearchResult, + ISearchResults, + ISearchResponse, + // ISearchResult, + // IEventWithRoomId +} from "matrix-js-sdk/src/matrix"; +import { EventContext } from "matrix-js-sdk/src/models/event-context"; // eslint-disable-line + +interface WordHighlight { + word: string; + highlight: boolean; +} + +export interface SearchTerm { + searchTypeAdvanced: boolean; + searchTypeNormal: boolean; + searchExpression?: RegExp | null; + regExpHighlightMap?: { [key: string]: boolean }; + fullText?: string; + words: WordHighlight[]; + regExpHighlights: any[]; + isEmptySearch?: boolean; +} + +interface Member { + userId: string; +} + +interface MemberObj { + [key: string]: Member; +} + +export interface SearchResultItem { + result: MatrixEvent; + context: EventContext; +} + +/** + * Searches all events locally based on the provided search term and room ID. + * + * @param {MatrixClient} client - The Matrix client instance. + * @param {string} term - The search term. + * @param {string | undefined} roomId - The ID of the room to search in. + * @returns {Promise} A promise that resolves to the search results. + * @throws {Error} If the Matrix client is not initialized or the room is not found. + */ +export default async function searchAllEventsLocally( + client: MatrixClient, + term: string, + roomId: string | undefined, +): Promise { + const searchResults: ISearchResults = { + results: [], + highlights: [], + count: 0, + }; + + if (!client) { + throw new Error("Matrix client is not initialized"); + } + + const room: Room | null = client.getRoom(roomId); + if (!room) { + throw new Error("Room not found"); + } + + const members = room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getMembers(); + const termObj: SearchTerm = makeSearchTermObject(term.trim()); + + if (termObj.isEmptySearch) { + return searchResults; + } + + let matchingMembers: Member[] = []; + if (Array.isArray(members) && members.length) { + matchingMembers = members.filter((member: RoomMember) => isMemberMatch(member, termObj)); + } + + const memberObj: MemberObj = {}; + for (let i = 0; i < matchingMembers.length; i++) { + memberObj[matchingMembers[i].userId] = matchingMembers[i]; + } + + await loadFullHistory(client, room); + + // Search and return intermediary form of matches + const matches = findAllMatches(termObj, room, memberObj); + + // search context is reversed there ☝️, so fix + //matches.forEach(m => m.context = reverseEventContext(m.context)); + + // Process the matches to produce the equivalent result from a client.search() call + const searchResponse = getClientSearchResponse(searchResults, matches); + + // mimic the original code + const results = client.processRoomEventsSearch(searchResults, searchResponse); + + return results; +} + +/** + * Loads the full history of events for a given room. + * + * @param client - The Matrix client instance. + * @param room - The room for which to load the history. + * @returns A promise that resolves when the full history is loaded. + * @throws {Error} If the Matrix client is not initialized. + */ +async function loadFullHistory(client: MatrixClient | null, room: Room): Promise { + let hasMoreEvents = true; + do { + try { + // get the first neighbour of the live timeline on every iteration + // as each time we paginate, two timelines could have overlapped and connected, and the new + // pagination token ends up on the first one. + const timeline: EventTimeline | null = getFirstLiveTimelineNeighbour(room); + if (!timeline) { + throw new Error("Timeline not found"); + } + if (client && timeline) { + hasMoreEvents = await client.paginateEventTimeline(timeline, { limit: 100, backwards: true }); + } else { + throw new Error("Matrix client is not initialized"); + } + } catch (err: any) { + // deal with rate-limit error + if (err.name === "M_LIMIT_EXCEEDED") { + const waitTime = err.data.retry_after_ms; + await new Promise((r) => setTimeout(r, waitTime)); + } else { + throw err; + } + } + } while (hasMoreEvents); +} + +/** + * Retrieves the first live timeline neighbour of a given room. + * A live timeline neighbour is a timeline that is adjacent to the current timeline in the backwards direction. + * + * @param room - The room for which to retrieve the first live timeline neighbour. + * @returns The first live timeline neighbour if found, otherwise null. + */ +function getFirstLiveTimelineNeighbour(room: Room): EventTimeline | null { + const liveTimeline = room.getLiveTimeline(); + let timeline = liveTimeline; + while (timeline) { + const neighbour = timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS); + if (!neighbour) { + return timeline; + } + timeline = neighbour; + } + return null; +} + +/** + * Finds all matches in a room based on the given search term object and matching members. + * + * @param {SearchTerm} termObj - The search term object. + * @param {Room} room - The room to search in. + * @param {MemberObj} matchingMembers - The matching members. + * @returns {SearchResultItem[]} An array of search result items. + */ +export function findAllMatches(termObj: SearchTerm, room: Room, matchingMembers: MemberObj): SearchResultItem[] { + const matches: SearchResultItem[] = []; + let searchHit: SearchResultItem | null = null; + let prevEvent: MatrixEvent | null = null; + let timeline: EventTimeline | null = room.getLiveTimeline(); + + const iterationCallback = (roomEvent: MatrixEvent): void => { + if (searchHit !== null) { + searchHit.context.addEvents([roomEvent], false); + } + searchHit = null; + + if (roomEvent.getType() === "m.room.message" && !roomEvent.isRedacted()) { + if (eventMatchesSearchTerms(termObj, roomEvent, matchingMembers)) { + const evCtx = new EventContext(roomEvent); + if (prevEvent !== null) { + evCtx.addEvents([prevEvent], true); + } + + const resObj: SearchResultItem = { result: roomEvent, context: evCtx }; + matches.push(resObj); + searchHit = resObj; + } + + prevEvent = roomEvent; + } + }; + + // This code iterates over a timeline, retrieves events from the timeline, and invokes a callback function for each event in reverse order. + while (timeline) { + const events = timeline.getEvents(); + for (let i = events.length - 1; i >= 0; i--) { + iterationCallback(events[i]); + } + timeline = timeline.getNeighbouringTimeline(EventTimeline.FORWARDS); + } + + return matches; +} + +/** + * Checks if a room member matches the given search term. + * @param member - The room member to check. + * @param termObj - The search term object. + * @returns True if the member matches the search term, false otherwise. + */ +export function isMemberMatch(member: RoomMember, termObj: SearchTerm): boolean { + const memberName = member.name.toLowerCase(); + if (termObj.searchTypeAdvanced === true) { + const expResults = termObj.searchExpression && memberName.match(termObj.searchExpression); + if (expResults && expResults.length > 0) { + for (let i = 0; i < expResults.length; i++) { + if (termObj.regExpHighlightMap && !termObj.regExpHighlightMap[expResults[i]]) { + termObj.regExpHighlightMap[expResults[i]] = true; + termObj.regExpHighlights.push(expResults[i]); + } + } + return true; + } + return false; + } + + if (termObj.fullText && memberName.indexOf(termObj.fullText) > -1) { + return true; + } + + for (let i = 0; i < termObj.words.length; i++) { + const word = termObj.words[i].word; + if (memberName.indexOf(word) === -1) { + return false; + } + } + + return true; +} + +/** + * Checks if an event matches the given search terms. + * @param searchTermObj - The search term object containing the search criteria. + * @param evt - The Matrix event to be checked. + * @param matchingMembers - The object containing matching members. + * @returns True if the event matches the search terms, false otherwise. + */ +export function eventMatchesSearchTerms( + searchTermObj: SearchTerm, + evt: MatrixEvent, + matchingMembers: MemberObj, +): boolean { + const content = evt.getContent(); + const sender = evt.getSender(); + const loweredEventContent = content.body.toLowerCase(); + + const evtDate = evt.getDate(); + const dateIso = evtDate && evtDate.toISOString(); + const dateLocale = evtDate && evtDate.toLocaleString(); + + // if (matchingMembers[sender?.userId] !== undefined) { + if (sender && matchingMembers[sender] !== undefined) { + return true; + } + + if (searchTermObj.searchTypeAdvanced === true) { + const expressionResults = loweredEventContent.match(searchTermObj.searchExpression); + if (expressionResults && expressionResults.length > 0) { + for (let i = 0; i < expressionResults.length; i++) { + if (searchTermObj.regExpHighlightMap && !searchTermObj.regExpHighlightMap[expressionResults[i]]) { + searchTermObj.regExpHighlightMap[expressionResults[i]] = true; + searchTermObj.regExpHighlights.push(expressionResults[i]); + } + } + return true; + } + + let dateIsoExprResults; + let dateLocaleExprResults; + if (dateIso && dateLocale && searchTermObj.searchExpression instanceof RegExp) { + dateIsoExprResults = dateIso.match(searchTermObj.searchExpression); + dateLocaleExprResults = dateLocale.match(searchTermObj.searchExpression); + } + + if ( + (dateIsoExprResults && dateIsoExprResults.length > 0) || + (dateLocaleExprResults && dateLocaleExprResults.length > 0) + ) { + return true; + } + + return false; + } + + if (loweredEventContent.indexOf(searchTermObj.fullText) > -1) { + return true; + } + + if ( + (dateIso && searchTermObj.fullText && dateIso.indexOf(searchTermObj.fullText) > -1) || + (dateLocale && searchTermObj.fullText && dateLocale.indexOf(searchTermObj.fullText) > -1) + ) { + return true; + } + + if (searchTermObj.words.length > 0) { + for (let i = 0; i < searchTermObj.words.length; i++) { + const word = searchTermObj.words[i]; + if (loweredEventContent.indexOf(word) === -1) { + return false; + } + } + return true; + } + + return false; +} + +/** + * Creates a search term object based on the provided search term. + * @param searchTerm - The search term to create the object from. + * @returns The created search term object. + */ +export function makeSearchTermObject(searchTerm: string): SearchTerm { + let term = searchTerm.toLowerCase(); + if (term.indexOf("rx:") === 0) { + term = searchTerm.substring(3).trim(); + return { + searchTypeAdvanced: true, + searchTypeNormal: false, + searchExpression: new RegExp(term), + words: [], + regExpHighlights: [], + regExpHighlightMap: {}, + isEmptySearch: term.length === 0, + }; + } + + const words = term + .split(" ") + .filter((w) => w) + .map(function (w) { + return { word: w, highlight: false }; + }); + + return { + searchTypeAdvanced: false, + searchTypeNormal: true, + fullText: term, + words: words, + regExpHighlights: [], + isEmptySearch: term.length === 0, + }; +} + +/** + * Reverses the order of events in the given event context. + * + * @param {EventContext} context - The event context to reverse. + * @returns {EventContext} The reversed event context. + */ +export function reverseEventContext(eventContext: EventContext): EventContext { + const contextTimeline = eventContext.getTimeline(); + const ourEventIndex = eventContext.getOurEventIndex(); + const ourEvent = eventContext.getEvent(); + const reversedContext = new EventContext(contextTimeline[ourEventIndex]); + let afterOurEvent = false; + + for (let i = 0; i < contextTimeline.length; i++) { + const event = contextTimeline[i]; + if (event.getId() === ourEvent.getId()) { + afterOurEvent = true; + continue; + } + if (afterOurEvent) { + reversedContext.addEvents([event], true); + } else { + reversedContext.addEvents([event], false); + } + } + + return reversedContext; +} + +/** + * Transform the matches by projecting them into a ISearchResponse + * + * @param searchResults - The search results object to be updated. + * @param matches - An array of matches. + * @param termObj - The search term object. + * @returns The updated searchResults object. + */ +function getClientSearchResponse(searchResults: ISearchResults, matches: SearchResultItem[]): ISearchResponse { + const response: ISearchResponse = { + search_categories: { + room_events: { + count: 0, + highlights: [], + results: [], + }, + }, + }; + + response.search_categories.room_events.count = matches.length; + for (let i = 0; i < matches.length; i++) { + const reversedContext = reverseEventContext(matches[i].context); + + const sr = new SearchResult(0, reversedContext); + searchResults.results.push(sr); + } + + return response; +} diff --git a/src/VerjiMergeNotes-December2024.md b/src/VerjiMergeNotes-December2024.md new file mode 100644 index 00000000000..c5ce96fd035 --- /dev/null +++ b/src/VerjiMergeNotes-December2024.md @@ -0,0 +1,144 @@ +# Verji Merge Notes - December 2024 +I found it necessary to include a note file, to document the process where I am attempting to: +- Update our Element-web fork (big update) + - This includes the huge change where matrix-react-sdk was absorbed by element-web + - Ensure Verji-customisations are included + +## Documenting & Planning + +After spending some time looking into the available documentation about how Element managed and conducted the absorbtion of matrix-react-sdk into element-web. +See: [Task #2125 - Discover/Understand Elements Process of Merging matrix-react-sdk Into element-web](https://dev.azure.com/rosbergas/VerjiKanban/_boards/board/t/VerjiKanban%20Team/Issues?workitem=2125) +[test](https://vg.no) + +As we have some different challanges than element, namely make sure our own customisations, which is a layer on top of matrix-react-sdk, and a layer on top of element-web. +Our Verji-customisations, have been tracked in forks of their Upstream counterparts, and in our custom branches. + +In the Task: [#2148 Explore Merge Strategies to get our Verji-Element Up to date](https://dev.azure.com/rosbergas/VerjiKanban/_boards/board/t/VerjiKanban%20Team/Issues?workitem=2148) +I start exploring different possibilities, on how to achive our desired goal. + +We decided to go for a "hybrid" of the initial 2 suggetions: +- Two step rocket? + - Absorb our fork matrix-react-sdk into our fork element-web + - Then handle the upgrade / sync to upstream? +- New Fork? + - Start from working new fresh fork + - Manually add customisations? + +- Hybrid + - Start from working new fresh fork + - merge in our customisations from our previous matrix-react-sdk fork on top + +## The Process +As mention we pursued an hybrid approach, New Forks + merge matrix-react-sdk fork on top. + +### 1. New Forks +I created new fresh forks on all the repos involved and added suffix (v2) +- [element-web-v2](https://github.com/verji/element-web-v2/tree/verji-develop) +- [matrix-js-sdk-v2](https://github.com/verji/matrix-js-sdk-v2) + +To verify that the forks were stable and working, spun up the client locally. Everything worked like a charm. + +### 2. Prepping for Merger of our matrix-react-sdk +I cloned the repositores into a new workspace/work folder and created a new branch to be able to safely abort if anyhing went wrong + +1. `mkdir fresh-fork` +2. `cd fresh-fork` +3. `git clone https://github.com/verji/element-web-v2` +4. `git clone https://github.com/verji/matrix-js-sdk-v2` +5. `https://github.com/verji/matrix-react-sdk` +6. `cd element-web` +7. `checkout -b verji-merge-react-sdk` +8. `git remote add -f matrix-react-sdk ../matrix-react-sdk` + +### 3. The Merger +Once the project had been set up, and prepared, the next step was to merge in our fork for matrix-react-sdk + +- `git merge matrix-react-sdk/verji-develop --allow-unrelated-histories --no-commit` + +Instantly I can tell that this merger is much more managable than previous attempts +Looks like most of our customisations are detected, and easily manually solveable. + +Some cases may turn out to require a bit more attention. In cases were components or functionality have been removed, heavily altered or similar. But this is something that we would have to solve regardless. + +I'll attempt to document the files and areas which may need more attention. + +## Config & .git Files(workflows) +There are large changes in most .git-workflows and action files. +So I think we most likely will have to re-visit these - as we've done previously. To make sure the automatic actions are running to our liking. +We might have to disable checks, and automations that doesen't make sense for us, and incorporate the workflows we wish to keep and or customise. + +## Deleted Files +### Deleted Components +Deleted components from Upstream, but existing in our version. +I will denote each file with: +- ✅ - Keep file (I'll keep file which has custom Verji-Code / feature flags and document FF'appears in file.) +- ❌ - File deleted (If we don't have any customisations in them, I'll delete) +- RoomContextMenu.tsx ✅ + - UIFeature.RoomSummaryFilesOption + - SettingsStore.getValue(UIFeature.ShowAddWidgetsInRoomInfo) +- Tooltip.tsx ❌ +- LegacyRoomHeader.tsx ✅ + - CustomComponentLifecycle.LegacyRoomHeader +- SearchBar.tsx ✅ + - SettingsStore.getValue(UIFeature.SearchInAllRooms) +- EmailAddresses.tsx ✅ + - SettingsStore.getValue(UIFeature.EmailAddressShowRemoveButton) + - SettingsStore.getValue(UIFeature.EmailAddressShowAddButton) +- PhoneNumbers.tsx ✅ + - UIFeature.PhoneNumerShowRemoveButton + - SettingsStore.getValue(UIFeature.PhoneNumerShowAddButton) +- GeneralUserSettingsTab.tsx✅ + - SettingsStore.getValue(UIFeature.UserSettingsExternalAccount) + - SettingsStore.getValue(UIFeature.UserSettingsChangePassword) + - SettingsStore.getValue(UIFeature.UserSettingsSetIdServer) + - SettingsStore.getValue(UIFeature.UserSettingsDiscovery) + - SettingsStore.getValue(UIFeature.UserSettingsIntegrationManager) +### Deleted Test Files +Deleted or moved tests from Upstream, but existing in our version. +I will denote each file with: +- ✅ - Keep file (I'll keep file which has custom Verji-tests) +- ❌ - File deleted (If we don't have any custom verji tests in them, I'll delete) + +- DeviceListener-test.ts ✅ +- MatrixClientPeg-test.ts ✅ +- LeftPanel-test.tsx ✅ +- LoggedInView-test.tsx ✅ +- MatrixChat-test.tsx ✅ +- MessagePanel-test.tsx ✅ +- RoomView-test.tsx ✅ +- ViewSource-test.tsx ❌ +- Login-test.tsx ✅ +- MessageContextMenu-test.tsx ✅ +- RoomContextMenu-test.tsx ✅ +- SpaceContextMenu-test.tsx ✅ +- CreateRoomDialog-test.tsx ✅ +- ExportDialog-test.tsx ✅ +- InviteDialog-test.tsx ✅ +- RoomSettingsDialog-test.tsx ✅ +- UserSettingsDialog-test.tsx ✅ +- PowerSelector-test.tsx ✅ +- RoomSummaryCard-test.tsx ✅ +- UserInfo-test.tsx ✅ +- LegacyRoomHeader-test.tsx ✅ +- MessageComposer-test.tsx ✅ +- RoomKnocksBar-test.tsx ✅ +- RoomList-test.tsx ✅ +- RoomListHeader-test.tsx ✅ +- RoomPreviewBar-test.tsx ✅ +- SendMessageComposer-test.tsx ❌ +- WysiwygComposer-test.tsx ❌ +- CrossSigningPanel-test.tsx ✅ +- SecureBacupPanel-test.tsx ✅ +- ThemeChoicePanel-test.tsx ✅ +- PhoneNumbers-test.tsx ✅ +- PeopleRoomSettingsTab-test.tsx❌ +- GeneralUserSettingsTab-test.tsx ✅ +- PreferenceUserSettingsTab-test.tsx✅ +- SecurityUserSettingsTab-test.tsx✅ +- SidebarUserSettingsTab-test.tsx❌ +- VoiceUserSettingsTab-test.tsx ✅ +- SpacePanel-test.tsx ✅ +- MockModule.ts ✅ +- ModuleRunner-test.ts ✅ +- StopGapWidget-test.ts ❌ +- StopGapWidgetDriver-test.ts ❌ diff --git a/src/components/structures/HomePage.tsx b/src/components/structures/HomePage.tsx index 31deb381ddd..afa59155812 100644 --- a/src/components/structures/HomePage.tsx +++ b/src/components/structures/HomePage.tsx @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. */ import * as React from "react"; -import { useContext, useState } from "react"; +import { useState } from "react"; import AutoHideScrollbar from "./AutoHideScrollbar"; import { getHomePageUrl } from "../../utils/pages"; @@ -20,10 +20,13 @@ import { OwnProfileStore } from "../../stores/OwnProfileStore"; import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton"; import { UPDATE_EVENT } from "../../stores/AsyncStore"; import { useEventEmitter } from "../../hooks/useEventEmitter"; -import MatrixClientContext, { useMatrixClientContext } from "../../contexts/MatrixClientContext"; +import { useMatrixClientContext } from "../../contexts/MatrixClientContext"; import MiniAvatarUploader, { AVATAR_SIZE } from "../views/elements/MiniAvatarUploader"; import PosthogTrackers from "../../PosthogTrackers"; import EmbeddedPage from "./EmbeddedPage"; +import SettingsStore from "../../settings/SettingsStore"; +import { UIFeature } from "../../settings/UIFeature"; +import { MatrixClientPeg } from "../../MatrixClientPeg"; const onClickSendDm = (ev: ButtonEvent): void => { PosthogTrackers.trackInteraction("WebHomeCreateChatButton", ev); @@ -55,7 +58,8 @@ const getOwnProfile = ( }); const UserWelcomeTop: React.FC = () => { - const cli = useContext(MatrixClientContext); + // const cli = useContext(MatrixClientContext); + const cli = MatrixClientPeg.safeGet(); const userId = cli.getUserId()!; const [ownProfile, setOwnProfile] = useState(getOwnProfile(userId)); useEventEmitter(OwnProfileStore.instance, UPDATE_EVENT, () => { @@ -86,6 +90,38 @@ const UserWelcomeTop: React.FC = () => { ); }; +// //Buttons on homepage can be enabled (true), or disabled (false) setting the UIFeature.HomePageButtons in settings.tsx +// const ShowButtons = () => { +// return ( +//
+// +// {_tDom("onboarding|send_dm")} +// +// +// {_tDom("onboarding|explore_rooms")} +// +// +// {_tDom("onboarding|create_room")} +// +//
+// ) +// } + +//Buttons on homepage can be enabled (true), or disabled (false) setting the UIFeature.HomePageButtons in settings.tsx +const showButtons = ( +
+ + {_tDom("onboarding|send_dm")} + + + {_tDom("onboarding|explore_rooms")} + + + {_tDom("onboarding|create_room")} + +
+); + const HomePage: React.FC = ({ justRegistered = false }) => { const cli = useMatrixClientContext(); const config = SdkConfig.get(); @@ -115,17 +151,8 @@ const HomePage: React.FC = ({ justRegistered = false }) => {
{introSection} -
- - {_tDom("onboarding|send_dm")} - - - {_tDom("onboarding|explore_rooms")} - - - {_tDom("onboarding|create_room")} - -
+ {/* {SettingsStore.getValue(UIFeature.HomePageButtons) && } */} + {SettingsStore.getValue(UIFeature.HomePageButtons) && showButtons}
); diff --git a/src/components/structures/LeftPanel.tsx b/src/components/structures/LeftPanel.tsx index 49d0f570a5c..2cf716eaf2e 100644 --- a/src/components/structures/LeftPanel.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -9,6 +9,10 @@ Please see LICENSE files in the repository root for full details. import * as React from "react"; import { createRef } from "react"; import classNames from "classnames"; +import { + CustomComponentLifecycle, + CustomComponentOpts, +} from "@matrix-org/react-sdk-module-api/lib/lifecycles/CustomComponentLifecycle"; import dis from "../../dispatcher/dispatcher"; import { _t } from "../../languageHandler"; @@ -31,12 +35,14 @@ import IndicatorScrollbar from "./IndicatorScrollbar"; import RoomBreadcrumbs from "../views/rooms/RoomBreadcrumbs"; import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts"; import { shouldShowComponent } from "../../customisations/helpers/UIComponents"; -import { UIComponent } from "../../settings/UIFeature"; +import { UIComponent, UIFeature } from "../../settings/UIFeature"; import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton"; import PosthogTrackers from "../../PosthogTrackers"; import PageType from "../../PageTypes"; import { UserOnboardingButton } from "../views/user-onboarding/UserOnboardingButton"; import { Landmark, LandmarkNavigation } from "../../accessibility/LandmarkNavigation"; +import SettingsStore from "../../settings/SettingsStore"; +import { ModuleRunner } from "../../modules/ModuleRunner"; interface IProps { isMinimized: boolean; @@ -344,7 +350,11 @@ export default class LeftPanel extends React.Component { } let rightButton: JSX.Element | undefined; - if (this.state.activeSpace === MetaSpace.Home && shouldShowComponent(UIComponent.ExploreRooms)) { + if ( + SettingsStore.getValue(UIFeature.ShowExploreRoomsButton) && + this.state.activeSpace === MetaSpace.Home && + shouldShowComponent(UIComponent.ExploreRooms) + ) { rightButton = ( { ); } + const customNewsOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke( + CustomComponentLifecycle.NewsAndOperatingMessages, + customNewsOpts as CustomComponentOpts, + ); return (
{ {dialPadButton} + {rightButton}
); diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 019f9cd1a84..60214abb3bb 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -19,6 +19,10 @@ import { } from "matrix-js-sdk/src/matrix"; import { MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import classNames from "classnames"; +import { + CustomComponentLifecycle, + CustomComponentOpts, +} from "@matrix-org/react-sdk-module-api/lib/lifecycles/CustomComponentLifecycle"; import { isOnlyCtrlOrCmdKeyEvent, Key } from "../../Keyboard"; import PageTypes from "../../PageTypes"; @@ -66,6 +70,7 @@ import { monitorSyncedPushRules } from "../../utils/pushRules/monitorSyncedPushR import { ConfigOptions } from "../../SdkConfig"; import { MatrixClientContextProvider } from "./MatrixClientContextProvider"; import { Landmark, LandmarkNavigation } from "../../accessibility/LandmarkNavigation"; +import { ModuleRunner } from "../../modules/ModuleRunner"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -190,6 +195,7 @@ class LoggedInView extends React.Component { OwnProfileStore.instance.on(UPDATE_EVENT, this.refreshBackgroundImage); this.loadResizerPreferences(); this.refreshBackgroundImage(); + this.attachFreshworksWidget(); //Verji } private onTimezoneUpdate = async (): Promise => { @@ -231,6 +237,20 @@ class LoggedInView extends React.Component { this.resizer?.detach(); } + // Verji start + private attachFreshworksWidget(): void { + const head = document.querySelector("head"); + const script = document.createElement("script"); + const scriptExt = document.createElement("script"); + script.setAttribute("src", "./scripts/freshworks.js"); + scriptExt.setAttribute("src", "https://euc-widget.freshworks.com/widgets/80000004505.js"); + scriptExt.async = true; + scriptExt.defer = true; + head?.appendChild(script); + head?.appendChild(scriptExt); + } + // Verji end + private onCallState = (): void => { const activeCalls = LegacyCallHandler.instance.getAllActiveCalls(); if (activeCalls === this.state.activeCalls) return; @@ -702,43 +722,58 @@ class LoggedInView extends React.Component { return ; }); + const customLoggedInViewOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke( + CustomComponentLifecycle.LoggedInView, + customLoggedInViewOpts as CustomComponentOpts, + ); + const customSpacePanelOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke(CustomComponentLifecycle.SpacePanel, customSpacePanelOpts as CustomComponentOpts); + const customLeftPanelOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke(CustomComponentLifecycle.LeftPanel, customLeftPanelOpts as CustomComponentOpts); return ( - -
- -
-
- -
- - - -
- + + +
+ +
+
+ +
+ + + + + +
+ + + +
+ +
{pageElement}
- -
{pageElement}
-
- - - {audioFeedArraysForCalls} - + + + {audioFeedArraysForCalls} + + ); } } diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index ee120c430ae..14655cd9ab3 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -418,8 +418,9 @@ export default class MatrixChat extends React.PureComponent { // if the user has previously set up cross-signing, verify this device so we can fetch the // private keys. + // if (SecurityCustomisations.SHOW_ENCRYPTION_SETUP_UI === false) { const cryptoExtension = ModuleRunner.instance.extensions.cryptoSetup; - if (cryptoExtension.SHOW_ENCRYPTION_SETUP_UI == false) { + if (cryptoExtension !== undefined && cryptoExtension.SHOW_ENCRYPTION_SETUP_UI == false) { this.onLoggedIn(); } else { this.setStateForNewView({ view: Views.COMPLETE_SECURITY }); diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 772d5698a30..38ff568d7d5 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -37,6 +37,10 @@ import { CallState, MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import { debounce, throttle } from "lodash"; import { CryptoEvent } from "matrix-js-sdk/src/crypto-api"; import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle"; +import { + CustomComponentLifecycle, + CustomComponentOpts, +} from "@matrix-org/react-sdk-module-api/lib/lifecycles/CustomComponentLifecycle"; import shouldHideEvent from "../../shouldHideEvent"; import { _t } from "../../languageHandler"; @@ -59,6 +63,7 @@ import { TimelineRenderingType, MainSplitContentType } from "../../contexts/Room import { E2EStatus, shieldStatusForRoom } from "../../utils/ShieldUtils"; import { Action } from "../../dispatcher/actions"; import { IMatrixClientCreds } from "../../MatrixClientPeg"; +import LegacyRoomHeader, { ISearchInfo } from "../views/rooms/LegacyRoomHeader"; import ScrollPanel from "./ScrollPanel"; import TimelinePanel from "./TimelinePanel"; import ErrorBoundary from "../views/elements/ErrorBoundary"; @@ -128,6 +133,9 @@ import { onView3pidInvite } from "../../stores/right-panel/action-handlers"; import RoomSearchAuxPanel from "../views/rooms/RoomSearchAuxPanel"; import { PinnedMessageBanner } from "../views/rooms/PinnedMessageBanner"; import { ScopedRoomContextProvider, useScopedRoomContext } from "../../contexts/ScopedRoomContext"; +import { ModuleRunner } from "../../modules/ModuleRunner"; +// import eventSearch from "../../Searching"; +import searchAllEventsLocally from "../../VerjiLocalSearch"; // VERJI const DEBUG = false; const PREVENT_MULTIPLE_JITSI_WITHIN = 30_000; @@ -306,11 +314,33 @@ function LocalRoomView(props: LocalRoomViewProps): ReactElement { /> ); } - + const customRoomHeaderOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke(CustomComponentLifecycle.RoomHeader, customRoomHeaderOpts as CustomComponentOpts); return (
- + + {SettingsStore.getValue("feature_new_room_decoration_ui") ? ( + + ) : ( + + )} +
@@ -342,10 +372,33 @@ interface ILocalRoomCreateLoaderProps { */ function LocalRoomCreateLoader(props: ILocalRoomCreateLoaderProps): ReactElement { const text = _t("room|creating_room_text", { names: props.names }); + const customRoomHeaderOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke(CustomComponentLifecycle.RoomHeader, customRoomHeaderOpts as CustomComponentOpts); return (
- + + {SettingsStore.getValue("feature_new_room_decoration_ui") ? ( + + ) : ( + + )} +
@@ -1698,7 +1751,16 @@ export class RoomView extends React.Component { const roomId = scope === SearchScope.Room ? this.getRoomId() : undefined; debuglog("sending search request"); const abortController = new AbortController(); - const promise = eventSearch(this.context.client!, term, roomId, abortController.signal); + + // VERJI START + let promise: Promise; + // currently, we use the local search for all events. edit this 'if' statement to change that. + if (scope === SearchScope.Room || scope === SearchScope.All) { + promise = searchAllEventsLocally(this.context.client!, term, roomId); + } else { + promise = eventSearch(this.context.client!, term, roomId, abortController.signal); + } + // VERJI END this.setState({ timelineRenderingType: TimelineRenderingType.Search, @@ -2506,6 +2568,9 @@ export class RoomView extends React.Component { const showChatEffects = SettingsStore.getValue("showChatEffects"); + const customAppsDrawerOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke(CustomComponentLifecycle.AppsDrawer, customAppsDrawerOpts as CustomComponentOpts); + let mainSplitBody: JSX.Element | undefined; let mainSplitContentClassName: string | undefined; // Decide what to show in the main split @@ -2536,13 +2601,15 @@ export class RoomView extends React.Component { mainSplitContentClassName = "mx_MainSplit_maximisedWidget"; mainSplitBody = ( <> - + + + {previewBar} ); @@ -2575,36 +2642,98 @@ export class RoomView extends React.Component { analyticsRoomType = this.state.mainSplitContentType === MainSplitContentType.Call ? "video_room" : "maximised_widget"; } + let excludedRightPanelPhaseButtons = [RightPanelPhases.Timeline]; + // VERJI MERGE - MAY CONTAIN ISSUES - CHANGES MADE IN ROOMVIEW + let onAppsClick: (() => void) | null = this.onAppsClick; + let onForgetClick: (() => void) | null = this.onForgetClick; + let onSearchClick: (() => void) | null = this.onSearchClick; + let onInviteClick: (() => void) | null = null; + let viewingCall = false; + // Simplify the header for other main split types + switch (this.state.mainSplitContentType) { + case MainSplitContentType.MaximisedWidget: + excludedRightPanelPhaseButtons = []; + onAppsClick = null; + onForgetClick = null; + onSearchClick = null; + break; + case MainSplitContentType.Call: + excludedRightPanelPhaseButtons = []; + onAppsClick = null; + onForgetClick = null; + onSearchClick = null; + if (this.state.room.canInvite(this.context.client.getSafeUserId())) { + onInviteClick = this.onInviteClick; + } + viewingCall = true; + } + + const myMember = this.state.room!.getMember(this.context.client!.getSafeUserId()); + const showForgetButton = + !this.context.client.isGuest() && + (([KnownMembership.Leave, KnownMembership.Ban] as Array).includes(myMembership) || + myMember?.isKicked()); + + const CustomRoomView = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke(CustomComponentLifecycle.RoomView, CustomRoomView as CustomComponentOpts); + const customRoomHeaderOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke(CustomComponentLifecycle.RoomHeader, customRoomHeaderOpts as CustomComponentOpts); + // VERJI MERGE MAY CONTAIN ISSUES - CHANGES MADE IN WRAPPED COMPONENT return ( - -
- {showChatEffects && this.roomView.current && ( - - )} - - -
+ +
+ {showChatEffects && this.roomView.current && ( + + )} + + - - {mainSplitBody} -
- - -
- +
+ + {SettingsStore.getValue("feature_new_room_decoration_ui") ? ( + + ) : ( + + )} + + {mainSplitBody} +
+
+
+
+
+ ); } } diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index bf0ddb1fe2a..49d15683285 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -26,7 +26,7 @@ import { useStateArray } from "../../hooks/useStateArray"; import { _t } from "../../languageHandler"; import PosthogTrackers from "../../PosthogTrackers"; import { inviteMultipleToRoom, showRoomInviteDialog } from "../../RoomInvite"; -import { UIComponent } from "../../settings/UIFeature"; +import { UIComponent, UIFeature } from "../../settings/UIFeature"; import { UPDATE_EVENT } from "../../stores/AsyncStore"; import RightPanelStore from "../../stores/right-panel/RightPanelStore"; import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases"; @@ -66,6 +66,7 @@ import MainSplit from "./MainSplit"; import RightPanel from "./RightPanel"; import SpaceHierarchy, { showRoom } from "./SpaceHierarchy"; import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks"; +import SettingsStore from "../../settings/SettingsStore"; interface IProps { space: Room; @@ -165,7 +166,7 @@ const SpaceLandingAddButton: React.FC<{ space: Room }> = ({ space }) => { showAddExistingRooms(space); }} /> - {canCreateSpace && ( + {canCreateSpace && SettingsStore.getValue(UIFeature.ShowCreateSpaceButton) && ( = ({ space }) => {
- - {inviteButton} + {SettingsStore.getValue(UIFeature.ShowMembersListForSpaces) && ( + + )} + {SettingsStore.getValue(UIFeature.ShowAddMoreButtonForSpaces) && inviteButton} {settingsButton}
- + {SettingsStore.getValue(UIFeature.ShowSpaceLandingPageDetails) && ( + <> + + + )}
); }; diff --git a/src/components/structures/ViewSource.tsx b/src/components/structures/ViewSource.tsx index 3aba3be472f..2f2f5effa1f 100644 --- a/src/components/structures/ViewSource.tsx +++ b/src/components/structures/ViewSource.tsx @@ -13,8 +13,8 @@ import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import SyntaxHighlight from "../views/elements/SyntaxHighlight"; import { _t } from "../../languageHandler"; import MatrixClientContext from "../../contexts/MatrixClientContext"; -import { canEditContent } from "../../utils/EventUtils"; -import { MatrixClientPeg } from "../../MatrixClientPeg"; +// import { canEditContent } from "../../utils/EventUtils"; //Verji +// import { MatrixClientPeg } from "../../MatrixClientPeg"; //Verji import BaseDialog from "../views/dialogs/BaseDialog"; import { DevtoolsContext } from "../views/dialogs/devtools/BaseTool"; import { StateEventEditor } from "../views/dialogs/devtools/RoomState"; @@ -135,11 +135,12 @@ export default class ViewSource extends React.Component { ); } - private canSendStateEvent(mxEvent: MatrixEvent): boolean { - const cli = MatrixClientPeg.safeGet(); - const room = cli.getRoom(mxEvent.getRoomId()); - return !!room?.currentState.mayClientSendStateEvent(mxEvent.getType(), cli); - } + // VERJI comment method out, (not in use as: canEdit also commented out) + // private canSendStateEvent(mxEvent: MatrixEvent): boolean { + // const cli = MatrixClientPeg.safeGet(); + // const room = cli.getRoom(mxEvent.getRoomId()); + // return !!room?.currentState.mayClientSendStateEvent(mxEvent.getType(), cli); + // } public render(): React.ReactNode { const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit @@ -147,9 +148,9 @@ export default class ViewSource extends React.Component { const isEditing = this.state.isEditing; const roomId = mxEvent.getRoomId()!; const eventId = mxEvent.getId()!; - const canEdit = mxEvent.isState() - ? this.canSendStateEvent(mxEvent) - : canEditContent(MatrixClientPeg.safeGet(), this.props.mxEvent); + // const canEdit = mxEvent.isState() //Verji + // ? this.canSendStateEvent(mxEvent) + // : canEditContent(MatrixClientPeg.safeGet(), this.props.mxEvent); return (
@@ -168,11 +169,17 @@ export default class ViewSource extends React.Component { )}
{isEditing ? this.editSourceContent() : this.viewSourceContent()} - {!isEditing && canEdit && ( + {/* NOTE: Verji - Removed the Edit button as we have no use for it at the moment. */} + {/* {!isEditing && canEdit && (
- +
- )} + )} */} + +
+ +
+ {/* Verji end */}
); } diff --git a/src/components/structures/auth/Login.tsx b/src/components/structures/auth/Login.tsx index 0a14450e63a..bb9fec42d85 100644 --- a/src/components/structures/auth/Login.tsx +++ b/src/components/structures/auth/Login.tsx @@ -540,24 +540,31 @@ export default class LoginComponent extends React.PureComponent } return ( - - - -

- {_t("action|sign_in")} - {loader} -

- {errorTextSection} - {serverDeadSection} - + {SettingsStore.getValue(UIFeature.EnableLoginPage) && ( + <> + + + +

+ {_t("action|sign_in")} + {loader} +

+ {errorTextSection} + {serverDeadSection} + - {this.renderLoginComponentForFlows()} - {footer} -
-
+ {this.renderLoginComponentForFlows()} + {footer} +
+
+ + )} + ; +
); } } diff --git a/src/components/structures/auth/SetupEncryptionBody.tsx b/src/components/structures/auth/SetupEncryptionBody.tsx index 32528fc7e39..3bd3744e077 100644 --- a/src/components/structures/auth/SetupEncryptionBody.tsx +++ b/src/components/structures/auth/SetupEncryptionBody.tsx @@ -19,6 +19,8 @@ import { SetupEncryptionStore, Phase } from "../../../stores/SetupEncryptionStor import EncryptionPanel from "../../views/right_panel/EncryptionPanel"; import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleButton"; import Spinner from "../../views/elements/Spinner"; +import { UIFeature } from "../../../settings/UIFeature"; +import SettingsStore from "../../../settings/SettingsStore"; function keyHasPassphrase(keyInfo: SecretStorageKeyDescription): boolean { return Boolean(keyInfo.passphrase && keyInfo.passphrase.salt && keyInfo.passphrase.iterations); @@ -198,19 +200,23 @@ export default class SetupEncryptionBody extends React.Component {verifyButton} {useRecoveryKeyButton}
-
- {_t("encryption|reset_all_button", undefined, { - a: (sub) => ( - - {sub} - - ), - })} -
+ {SettingsStore.getValue(UIFeature.SetupEncryptionResetButton) && ( + <> +
+ {_t("encryption|reset_all_button", undefined, { + a: (sub) => ( + + {sub} + + ), + })} +
+ + )}
); } diff --git a/src/components/structures/grouper/CreationGrouper.tsx b/src/components/structures/grouper/CreationGrouper.tsx index 84982066c36..dc54f367299 100644 --- a/src/components/structures/grouper/CreationGrouper.tsx +++ b/src/components/structures/grouper/CreationGrouper.tsx @@ -18,6 +18,8 @@ import DateSeparator from "../../views/messages/DateSeparator"; import NewRoomIntro from "../../views/rooms/NewRoomIntro"; import GenericEventListSummary from "../../views/elements/GenericEventListSummary"; import { SeparatorKind } from "../../views/messages/TimelineSeparator"; +import SettingsStore from "../../../settings/SettingsStore"; +import { UIFeature } from "../../../settings/UIFeature"; // Wrap initial room creation events into a GenericEventListSummary // Grouping only events sent by the same user that sent the `m.room.create` and only until @@ -127,7 +129,9 @@ export class CreationGrouper extends BaseGrouper { summaryText = _t("timeline|creation_summary_room", { creator }); } - ret.push(); + { + SettingsStore.getValue(UIFeature.EnableNewRoomIntro) && ret.push(); + } ret.push( private checkPermissions = (): void => { const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); + const roomState = room?.getLiveTimeline().getState(EventTimeline.FORWARDS); // Verji // We explicitly decline to show the redact option on ACL events as it has a potential // to obliterate the room - https://github.com/matrix-org/synapse/issues/4042 // Similarly for encryption events, since redacting them "breaks everything" + // Verji start, adds more events to prevent redact + let redactable = true; + if ( + this.props.mxEvent?.event?.type?.includes(".avatar") || + this.props.mxEvent?.event?.type?.includes(".topic") || + this.props.mxEvent.getType() === EventType.RoomMember || + this.props.mxEvent.getType() === EventType.RoomJoinRules || + this.props.mxEvent.getType() === EventType.RoomPowerLevels || + this.props.mxEvent.getType() === EventType.RoomHistoryVisibility || + this.props.mxEvent.getType() === EventType.RoomGuestAccess || + this.props.mxEvent.getType() === EventType.RoomName || + this.props.mxEvent.getType() === EventType.RoomTopic + ) { + redactable = false; + } const canRedact = + //!!roomState?.maySendRedactionForEvent(this.props.mxEvent, cli.getSafeUserId()) && !!room?.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.getSafeUserId()) && this.props.mxEvent.getType() !== EventType.RoomServerAcl && - this.props.mxEvent.getType() !== EventType.RoomEncryption; - + this.props.mxEvent.getType() !== EventType.RoomEncryption && + redactable; + //Verji end + // VERJI MERGE - May have to revisit pin-button mechanics (Our version differs a bit) const canPin = PinningUtils.canPin(cli, this.props.mxEvent) || PinningUtils.canUnpin(cli, this.props.mxEvent); this.setState({ canRedact, canPin }); }; private canEndPoll(mxEvent: MatrixEvent): boolean { + // ROSBERG isMyEvent to overide verji strict canRedact rules - in case where ender of the poll is the owner of the poll + const isMyEvent = mxEvent.sender?.userId === MatrixClientPeg.safeGet().getSafeUserId(); return ( M_POLL_START.matches(mxEvent.getType()) && - this.state.canRedact && + (this.state.canRedact || isMyEvent) && // Verji - evaluates to true if you are admin OR the event is yours !isPollEnded(mxEvent, MatrixClientPeg.safeGet()) ); } @@ -199,13 +226,14 @@ export default class MessageContextMenu extends React.Component }); }; - private onReportEventClick = (): void => { - dis.dispatch({ - action: Action.OpenReportEventDialog, - event: this.props.mxEvent, - }); - this.closeMenu(); - }; + // VERJI method no longer in use, as we commented out reportEventButton + // private onReportEventClick = (): void => { + // dis.dispatch({ + // action: Action.OpenReportEventDialog, + // event: this.props.mxEvent, + // }); + // this.closeMenu(); + // }; private onViewSourceClick = (): void => { Modal.createDialog( @@ -253,14 +281,15 @@ export default class MessageContextMenu extends React.Component this.closeMenu(); }; - private onShareClick = (e: ButtonEvent): void => { - e.preventDefault(); - Modal.createDialog(ShareDialog, { - target: this.props.mxEvent, - permalinkCreator: this.props.permalinkCreator, - }); - this.closeMenu(); - }; + // VERJI commented out as share has been removed + // private onShareClick = (e: ButtonEvent): void => { + // e.preventDefault(); + // Modal.createDialog(ShareDialog, { + // target: this.props.mxEvent, + // permalinkCreator: this.props.permalinkCreator, + // }); + // this.closeMenu(); + // }; private onCopyLinkClick = (e: ButtonEvent): void => { e.preventDefault(); // So that we don't open the permalink @@ -350,7 +379,7 @@ export default class MessageContextMenu extends React.Component public render(): React.ReactNode { const cli = MatrixClientPeg.safeGet(); - const me = cli.getUserId(); + // const me = cli.getUserId(); //Verji const { mxEvent, rightClick, link, eventTileOps, reactions, collapseReplyChain, ...other } = this.props; delete other.getRelationsForEvent; delete other.permalinkCreator; @@ -358,7 +387,7 @@ export default class MessageContextMenu extends React.Component const eventStatus = mxEvent.status; const unsentReactionsCount = this.getUnsentReactions().length; const contentActionable = isContentActionable(mxEvent); - const permalink = this.props.permalinkCreator?.forEvent(this.props.mxEvent.getId()!); + // const permalink = this.props.permalinkCreator?.forEvent(this.props.mxEvent.getId()!); //Verji // status is SENT before remote-echo, null after const isSent = !eventStatus || eventStatus === EventStatus.SENT; const { timelineRenderingType, canReact, canSendMessages } = this.context; @@ -440,25 +469,27 @@ export default class MessageContextMenu extends React.Component ); } - let permalinkButton: JSX.Element | undefined; - if (permalink) { - permalinkButton = ( - - ); - } + // Verji start + // let permalinkButton: JSX.Element | undefined; + // if (permalink) { + // permalinkButton = ( + // + // ); + // } + // Verji end let endPollButton: JSX.Element | undefined; if (this.canEndPoll(mxEvent)) { @@ -518,16 +549,18 @@ export default class MessageContextMenu extends React.Component ); } - let reportEventButton: JSX.Element | undefined; - if (mxEvent.getSender() !== me) { - reportEventButton = ( - - ); - } + // Verji start + // let reportEventButton: JSX.Element | undefined; + // if (mxEvent.getSender() !== me) { + // reportEventButton = ( + // + // ); + // } + // Verji end let copyLinkButton: JSX.Element | undefined; if (link) { @@ -658,8 +691,8 @@ export default class MessageContextMenu extends React.Component {openInMapSiteButton} {endPollButton} {forwardButton} - {permalinkButton} - {reportEventButton} + {/*Verji removed {permalinkButton} */} + {/*Verji removed {reportEventButton} */} {externalURLButton} {jumpToRelatedEventButton} {unhidePreviewButton} @@ -684,21 +717,29 @@ export default class MessageContextMenu extends React.Component ); } + const CustomMessageContextMenu = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke( + CustomComponentLifecycle.MessageContextMenu, + CustomMessageContextMenu as CustomComponentOpts, + ); + return ( - - - {nativeItemsList} - {quickItemsList} - {commonItemsList} - {redactItemList} - - {reactionPicker} - + + + + {nativeItemsList} + {quickItemsList} + {commonItemsList} + {redactItemList} + + {reactionPicker} + + ); } } diff --git a/src/components/views/context_menus/RoomContextMenu.tsx b/src/components/views/context_menus/RoomContextMenu.tsx new file mode 100644 index 00000000000..30df3ccd28d --- /dev/null +++ b/src/components/views/context_menus/RoomContextMenu.tsx @@ -0,0 +1,401 @@ +/* +Copyright 2021 - 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React, { useContext } from "react"; +import { Room, RoomMemberEvent } from "matrix-js-sdk/src/matrix"; +import { KnownMembership } from "matrix-js-sdk/src/types"; + +import { IProps as IContextMenuProps } from "../../structures/ContextMenu"; +import IconizedContextMenu, { + IconizedContextMenuCheckbox, + IconizedContextMenuOption, + IconizedContextMenuOptionList, +} from "./IconizedContextMenu"; +import { _t } from "../../../languageHandler"; +import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import { ButtonEvent } from "../elements/AccessibleButton"; +import { DefaultTagID, TagID } from "../../../stores/room-list/models"; +import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore"; +import dis from "../../../dispatcher/dispatcher"; +import { EchoChamber } from "../../../stores/local-echo/EchoChamber"; +import { RoomNotifState } from "../../../RoomNotifs"; +import Modal from "../../../Modal"; +import ExportDialog from "../dialogs/ExportDialog"; +import { useFeatureEnabled } from "../../../hooks/useSettings"; +import { usePinnedEvents } from "../right_panel/PinnedMessagesCard"; +import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases"; +import { RoomSettingsTab } from "../dialogs/RoomSettingsDialog"; +import { useEventEmitterState } from "../../../hooks/useEventEmitter"; +import RightPanelStore from "../../../stores/right-panel/RightPanelStore"; +import DMRoomMap from "../../../utils/DMRoomMap"; +import { Action } from "../../../dispatcher/actions"; +import PosthogTrackers from "../../../PosthogTrackers"; +import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; +import { getKeyBindingsManager } from "../../../KeyBindingsManager"; +import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; +import SettingsStore from "../../../settings/SettingsStore"; +import { SdkContextClass } from "../../../contexts/SDKContext"; +import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; +import { UIComponent, UIFeature } from "../../../settings/UIFeature"; +import { DeveloperToolsOption } from "./DeveloperToolsOption"; +import { tagRoom } from "../../../utils/room/tagRoom"; + +interface IProps extends IContextMenuProps { + room: Room; +} + +/** + * Room context menu accessible via the room header. + * @deprecated will be removed as part of `feature_new_room_decoration_ui` + */ +const RoomContextMenu: React.FC = ({ room, onFinished, ...props }) => { + const cli = useContext(MatrixClientContext); + const roomTags = useEventEmitterState(RoomListStore.instance, LISTS_UPDATE_EVENT, () => + RoomListStore.instance.getTagsForRoom(room), + ); + + let leaveOption: JSX.Element | undefined; + if (roomTags.includes(DefaultTagID.Archived)) { + const onForgetRoomClick = (ev: ButtonEvent): void => { + ev.preventDefault(); + ev.stopPropagation(); + + dis.dispatch({ + action: "forget_room", + room_id: room.roomId, + }); + onFinished(); + }; + + leaveOption = ( + + ); + } else { + const onLeaveRoomClick = (ev: ButtonEvent): void => { + ev.preventDefault(); + ev.stopPropagation(); + + dis.dispatch({ + action: "leave_room", + room_id: room.roomId, + }); + onFinished(); + + PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuLeaveItem", ev); + }; + + leaveOption = ( + + ); + } + + const isDm = DMRoomMap.shared().getUserIdForRoomId(room.roomId); + const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms"); + const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms"); + const isVideoRoom = + videoRoomsEnabled && (room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom())); + const canInvite = useEventEmitterState(cli, RoomMemberEvent.PowerLevel, () => room.canInvite(cli.getUserId()!)); + let inviteOption: JSX.Element | undefined; + if (canInvite && !isDm && shouldShowComponent(UIComponent.InviteUsers)) { + const onInviteClick = (ev: ButtonEvent): void => { + ev.preventDefault(); + ev.stopPropagation(); + + dis.dispatch({ + action: "view_invite", + roomId: room.roomId, + }); + onFinished(); + + PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuInviteItem", ev); + }; + + inviteOption = ( + + ); + } + + let favouriteOption: JSX.Element | undefined; + let lowPriorityOption: JSX.Element | undefined; + let notificationOption: JSX.Element | undefined; + if (room.getMyMembership() === KnownMembership.Join) { + const isFavorite = roomTags.includes(DefaultTagID.Favourite); + favouriteOption = ( + { + onTagRoom(e, DefaultTagID.Favourite); + PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuFavouriteToggle", e); + }} + active={isFavorite} + label={isFavorite ? _t("room|context_menu|unfavourite") : _t("room|context_menu|favourite")} + iconClassName="mx_RoomTile_iconStar" + /> + ); + + const isLowPriority = roomTags.includes(DefaultTagID.LowPriority); + lowPriorityOption = ( + onTagRoom(e, DefaultTagID.LowPriority)} + active={isLowPriority} + label={_t("common|low_priority")} + iconClassName="mx_RoomTile_iconArrowDown" + /> + ); + + const echoChamber = EchoChamber.forRoom(room); + let notificationLabel: string | undefined; + let iconClassName: string | undefined; + switch (echoChamber.notificationVolume) { + case RoomNotifState.AllMessages: + notificationLabel = _t("notifications|default"); + iconClassName = "mx_RoomTile_iconNotificationsDefault"; + break; + case RoomNotifState.AllMessagesLoud: + notificationLabel = _t("notifications|all_messages"); + iconClassName = "mx_RoomTile_iconNotificationsAllMessages"; + break; + case RoomNotifState.MentionsOnly: + notificationLabel = _t("room|context_menu|mentions_only"); + iconClassName = "mx_RoomTile_iconNotificationsMentionsKeywords"; + break; + case RoomNotifState.Mute: + notificationLabel = _t("common|mute"); + iconClassName = "mx_RoomTile_iconNotificationsNone"; + break; + } + + notificationOption = ( + { + ev.preventDefault(); + ev.stopPropagation(); + + dis.dispatch({ + action: "open_room_settings", + room_id: room.roomId, + initial_tab_id: RoomSettingsTab.Notifications, + }); + onFinished(); + + PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuNotificationsItem", ev); + }} + label={_t("notifications|enable_prompt_toast_title")} + iconClassName={iconClassName} + > + {notificationLabel} + + ); + } + + let peopleOption: JSX.Element | undefined; + let copyLinkOption: JSX.Element | undefined; + if (!isDm) { + peopleOption = ( + { + ev.preventDefault(); + ev.stopPropagation(); + + ensureViewingRoom(ev); + RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, false); + onFinished(); + PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuPeopleItem", ev); + }} + label={_t("common|people")} + iconClassName="mx_RoomTile_iconPeople" + > + {room.getJoinedMemberCount()} + + ); + + copyLinkOption = ( + { + ev.preventDefault(); + ev.stopPropagation(); + + dis.dispatch({ + action: "copy_room", + room_id: room.roomId, + }); + onFinished(); + }} + label={_t("room|context_menu|copy_link")} + iconClassName="mx_RoomTile_iconCopyLink" + /> + ); + } + + let filesOption: JSX.Element | undefined; + if (!isVideoRoom && SettingsStore.getValue(UIFeature.RoomSummaryFilesOption)) { + filesOption = ( + { + ev.preventDefault(); + ev.stopPropagation(); + + ensureViewingRoom(ev); + RightPanelStore.instance.pushCard({ phase: RightPanelPhases.FilePanel }, false); + onFinished(); + }} + label={_t("right_panel|files_button")} + iconClassName="mx_RoomTile_iconFiles" + /> + ); + } + + const pinningEnabled = useFeatureEnabled("feature_pinning"); + const pinCount = usePinnedEvents(pinningEnabled ? room : undefined)?.length; + + let pinsOption: JSX.Element | undefined; + if (pinningEnabled && !isVideoRoom) { + pinsOption = ( + { + ev.preventDefault(); + ev.stopPropagation(); + + ensureViewingRoom(ev); + RightPanelStore.instance.pushCard({ phase: RightPanelPhases.PinnedMessages }, false); + onFinished(); + }} + label={_t("right_panel|pinned_messages_button")} + iconClassName="mx_RoomTile_iconPins" + > + {pinCount > 0 && {pinCount}} + + ); + } + + let widgetsOption: JSX.Element | undefined; + if (!isVideoRoom && SettingsStore.getValue(UIFeature.ShowAddWidgetsInRoomInfo)) { + widgetsOption = ( + { + ev.preventDefault(); + ev.stopPropagation(); + + ensureViewingRoom(ev); + RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomSummary }, false); + onFinished(); + }} + label={_t("right_panel|widgets_section")} + iconClassName="mx_RoomTile_iconWidgets" + /> + ); + } + + let exportChatOption: JSX.Element | undefined; + if (!isVideoRoom) { + exportChatOption = ( + { + ev.preventDefault(); + ev.stopPropagation(); + + Modal.createDialog(ExportDialog, { room }); + onFinished(); + }} + label={_t("right_panel|export_chat_button")} + iconClassName="mx_RoomTile_iconExport" + /> + ); + } + + const onTagRoom = (ev: ButtonEvent, tagId: TagID): void => { + ev.preventDefault(); + ev.stopPropagation(); + + tagRoom(room, tagId); + + const action = getKeyBindingsManager().getAccessibilityAction(ev as React.KeyboardEvent); + switch (action) { + case KeyBindingAction.Enter: + // Implements https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-12 + onFinished(); + break; + } + }; + + const ensureViewingRoom = (ev: ButtonEvent): void => { + if (SdkContextClass.instance.roomViewStore.getRoomId() === room.roomId) return; + dis.dispatch( + { + action: Action.ViewRoom, + room_id: room.roomId, + metricsTrigger: "RoomList", + metricsViaKeyboard: ev.type !== "click", + }, + true, + ); + }; + + return ( + + + {inviteOption} + {notificationOption} + {favouriteOption} + {peopleOption} + {filesOption} + {pinsOption} + {widgetsOption} + {lowPriorityOption} + {copyLinkOption} + + { + ev.preventDefault(); + ev.stopPropagation(); + + dis.dispatch({ + action: "open_room_settings", + room_id: room.roomId, + }); + onFinished(); + PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuSettingsItem", ev); + }} + label={_t("common|settings")} + iconClassName="mx_RoomTile_iconSettings" + /> + + {exportChatOption} + + {SettingsStore.getValue("developerMode") && ( + + )} + + {leaveOption} + + + ); +}; + +export default RoomContextMenu; diff --git a/src/components/views/context_menus/SpaceContextMenu.tsx b/src/components/views/context_menus/SpaceContextMenu.tsx index 2d407214daf..2f443900a2f 100644 --- a/src/components/views/context_menus/SpaceContextMenu.tsx +++ b/src/components/views/context_menus/SpaceContextMenu.tsx @@ -29,7 +29,7 @@ import SettingsStore from "../../../settings/SettingsStore"; import { useFeatureEnabled } from "../../../hooks/useSettings"; import { Action } from "../../../dispatcher/actions"; import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; -import { UIComponent } from "../../../settings/UIFeature"; +import { UIComponent, UIFeature } from "../../../settings/UIFeature"; import PosthogTrackers from "../../../PosthogTrackers"; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; @@ -185,7 +185,7 @@ const SpaceContextMenu: React.FC = ({ space, hideHeader, onFinished, ... )} - {canAddSubSpaces && ( + {SettingsStore.getValue(UIFeature.AddSubSpace) && canAddSubSpaces && ( = ({ space, hideHeader, onFinished, ... onClick={onHomeClick} /> {inviteOption} - + {SettingsStore.getValue(UIFeature.ShowSpaceLandingPageDetails) && ( + + )} = ({ space, hideHeader, onFinished, ... /> {devtoolsOption} {settingsOption} - {leaveOption} + {SettingsStore.getValue(UIFeature.ShowLeaveSpaceInContextMenu) && leaveOption} {newRoomSection} diff --git a/src/components/views/context_menus/WidgetContextMenu.tsx b/src/components/views/context_menus/WidgetContextMenu.tsx index 46052de9ff2..3321ef4f3ed 100644 --- a/src/components/views/context_menus/WidgetContextMenu.tsx +++ b/src/components/views/context_menus/WidgetContextMenu.tsx @@ -30,6 +30,7 @@ import { getConfigLivestreamUrl, startJitsiAudioLivestream } from "../../../Live import { ModuleRunner } from "../../../modules/ModuleRunner"; import { ElementWidget } from "../../../stores/widgets/StopGapWidget"; import { useScopedRoomContext } from "../../../contexts/ScopedRoomContext.tsx"; +import { UIFeature } from "../../../settings/UIFeature"; interface IProps extends Omit, "children"> { app: IWidget; @@ -264,7 +265,7 @@ export const WidgetContextMenu: React.FC = ({ {streamAudioStreamButton} {editButton} {revokeButton} - {deleteButton} + {SettingsStore.getValue(UIFeature.WidgetContextDeleteButton) && deleteButton} {snapshotButton} {moveLeftButton} {moveRightButton} diff --git a/src/components/views/dialogs/BugReportDialog.tsx b/src/components/views/dialogs/BugReportDialog.tsx index 373f30d3aeb..67006e0f5ef 100644 --- a/src/components/views/dialogs/BugReportDialog.tsx +++ b/src/components/views/dialogs/BugReportDialog.tsx @@ -25,6 +25,8 @@ import { sendSentryReport } from "../../../sentry"; import defaultDispatcher from "../../../dispatcher/dispatcher"; import { Action } from "../../../dispatcher/actions"; import { getBrowserSupport } from "../../../SupportedBrowser"; +import SettingsStore from "../../../settings/SettingsStore"; +import { UIFeature } from "../../../settings/UIFeature"; interface IProps { onFinished: (success: boolean) => void; @@ -223,21 +225,22 @@ export default class BugReportDialog extends React.Component {

{_t("bug_reporting|description")}

- {_t( - "bug_reporting|before_submitting", - {}, - { - a: (sub) => ( - - {sub} - - ), - }, - )} + {SettingsStore.getValue(UIFeature.HelpShowMatrixDisclosurePolicyAndLinks) && + _t( + "bug_reporting|before_submitting", + {}, + { + a: (sub) => ( + + {sub} + + ), + }, + )}

@@ -247,16 +250,24 @@ export default class BugReportDialog extends React.Component { {this.state.downloadProgress && {this.state.downloadProgress} ...}
+ {console.log( + "Eik bugrep: HelpShowMatrixDisclosurePolicyAndLinks " + + SettingsStore.getValue(UIFeature.HelpShowMatrixDisclosurePolicyAndLinks), + )} - + {SettingsStore.getValue(UIFeature.HelpShowMatrixDisclosurePolicyAndLinks) && ( + <> + + + )} { className="mx_CreateRoomDialog_topic" /> - + {/* For those who only want to create private room, set the CreateRoomShowJoinRuleDropdown to false in settings.tsx, + for public rooms and knock rooms, set the flag to true */} + {SettingsStore.getValue(UIFeature.CreateRoomShowJoinRuleDropdown) && ( + <> + + + )} {publicPrivateLabel} {visibilitySection} - {e2eeSection} + {/* To create only encrypted room, the options can be hidden by setting the flag to false in in settings.tsx, */} + {SettingsStore.getValue(UIFeature.CreateRoomE2eeSection) && e2eeSection} {aliasField} -
- - {this.state.detailsOpen ? _t("action|hide_advanced") : _t("action|show_advanced")} - - -

{federateLabel}

-
+ {/* To limit the usage to only your server, set flag to false in in settings.tsx, */} + {SettingsStore.getValue(UIFeature.CreateRoomShowAdvancedSettings) && ( + <> +
+ + {this.state.detailsOpen + ? _t("action|hide_advanced") + : _t("action|show_advanced")} + + +

{federateLabel}

+
+ + )}
= ({ roomId, threadRootId, onFinished })

{_t(categoryLabels[category as unknown as Category])}

{tools.map(([label, tool]) => { - const onClick = (): void => { - setTool([label, tool]); - }; - return ( - - ); + if ( + (tool != VerificationExplorer && tool != TimelineEventEditor) || + ((tool === VerificationExplorer || tool === TimelineEventEditor) && + SettingsStore.getValue(UIFeature.EnableRoomDevTools)) + ) { + const onClick = (): void => { + setTool([label, tool]); + }; + return ( + + ); + } })}
))} diff --git a/src/components/views/dialogs/ExportDialog.tsx b/src/components/views/dialogs/ExportDialog.tsx index 54c58fe2e4a..46801c1ffdc 100644 --- a/src/components/views/dialogs/ExportDialog.tsx +++ b/src/components/views/dialogs/ExportDialog.tsx @@ -34,6 +34,8 @@ import Spinner from "../elements/Spinner"; import InfoDialog from "./InfoDialog"; import ChatExport from "../../../customisations/ChatExport"; import { validateNumberInRange } from "../../../utils/validate"; +import SettingsStore from "../../../settings/SettingsStore"; +import { UIFeature } from "../../../settings/UIFeature"; interface IProps { room: Room; @@ -62,10 +64,20 @@ const useExportFormState = (): ExportConfig => { const config = ChatExport.getForceChatExportParameters(); const [exportFormat, setExportFormat] = useState(config.format ?? ExportFormat.Html); - const [exportType, setExportType] = useState(config.range ?? ExportType.Timeline); - const [includeAttachments, setAttachments] = useState(config.includeAttachments ?? false); + const [exportType, setExportType] = useState( + SettingsStore.getValue(UIFeature.AllExportTypes) == false + ? ExportType.Beginning + : config.range ?? ExportType.Timeline, + ); + const [includeAttachments, setAttachments] = useState( + SettingsStore.getValue(UIFeature.ExportAttatchmentsDefaultOff) == false + ? true + : config.includeAttachments ?? false, + ); const [numberOfMessages, setNumberOfMessages] = useState(config.numberOfMessages ?? 100); - const [sizeLimit, setSizeLimit] = useState(config.sizeMb ?? 8); + const [sizeLimit, setSizeLimit] = useState( + SettingsStore.getValue(UIFeature.ExportDefaultSizeLimit) == false ? 20 : config.sizeMb ?? 8, + ); return { exportFormat, @@ -111,6 +123,9 @@ const ExportDialog: React.FC = ({ room, onFinished }) => { }, ); + // setExporter(ExportType.[Beginning as ExportTypeKey]); + // setNumberOfMessages(parseInt(e.target.value)); + const startExport = async (): Promise => { const exportOptions = { numberOfMessages, @@ -138,8 +153,8 @@ const ExportDialog: React.FC = ({ room, onFinished }) => { !setSizeLimit || (await sizeLimitRef.current?.validate({ focused: false, - })); - + })) || + !SettingsStore.getValue(UIFeature.ExportDefaultSizeLimit); if (!isValidSize) { sizeLimitRef.current?.validate({ focused: true }); return; @@ -340,7 +355,7 @@ const ExportDialog: React.FC = ({ room, onFinished }) => { )} - {!!setExportType && ( + {!!setExportType && SettingsStore.getValue(UIFeature.AllExportTypes) && ( <> {_t("export_chat|messages")} @@ -358,7 +373,7 @@ const ExportDialog: React.FC = ({ room, onFinished }) => { )} - {setSizeLimit && ( + {setSizeLimit && SettingsStore.getValue(UIFeature.ExportDefaultSizeLimit) && ( <> {_t("export_chat|size_limit")} diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 35e04fb12e2..76e24629422 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -9,17 +9,22 @@ Please see LICENSE files in the repository root for full details. import React, { createRef, ReactNode, SyntheticEvent } from "react"; import classNames from "classnames"; import { RoomMember, Room, MatrixError, EventType } from "matrix-js-sdk/src/matrix"; -import { KnownMembership } from "matrix-js-sdk/src/types"; import { MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import { logger } from "matrix-js-sdk/src/logger"; import { uniqBy } from "lodash"; import { CloseIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { + CustomComponentLifecycle, + CustomComponentOpts, +} from "@matrix-org/react-sdk-module-api/lib/lifecycles/CustomComponentLifecycle"; +import { KnownMembership } from "matrix-js-sdk/src/types"; import { Icon as EmailPillAvatarIcon } from "../../../../res/img/icon-email-pill-avatar.svg"; import { _t, _td } from "../../../languageHandler"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { makeRoomPermalink, makeUserPermalink } from "../../../utils/permalinks/Permalinks"; import DMRoomMap from "../../../utils/DMRoomMap"; +// import SdkConfig from "../../../SdkConfig"; import * as Email from "../../../email"; import { getDefaultIdentityServerUrl, setToDefaultIdentityServer } from "../../../utils/IdentityServerUtils"; import { buildActivityScores, buildMemberScores, compareMembers } from "../../../utils/SortMembers"; @@ -66,6 +71,7 @@ import { UNKNOWN_PROFILE_ERRORS } from "../../../utils/MultiInviter"; import AskInviteAnywayDialog, { UnknownProfiles } from "./AskInviteAnywayDialog"; import { SdkContextClass } from "../../../contexts/SDKContext"; import { UserProfilesStore } from "../../../stores/UserProfilesStore"; +import { ModuleRunner } from "../../../modules/ModuleRunner"; // we have a number of types defined from the Matrix spec which can't reasonably be altered here. /* eslint-disable camelcase */ @@ -360,6 +366,9 @@ export default class InviteDialog extends React.PureComponent - + ); } - + const customInviteDialog = { CustomComponent: React.Fragment }; + const Props = (props: any): React.JSX.Element => <>; + ModuleRunner.instance.invoke(CustomComponentLifecycle.InviteDialog, customInviteDialog as CustomComponentOpts); return ( - -
{dialogContent}
-
+ + {/* VERJI: Workaround, pass needed props in a child element of the customComponent wrapper, so we can extract in module implementation. */} + + +
{dialogContent}
+
+
); } } diff --git a/src/components/views/dialogs/RoomSettingsDialog.tsx b/src/components/views/dialogs/RoomSettingsDialog.tsx index cb804b8e004..78ec5c2bcfb 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.tsx +++ b/src/components/views/dialogs/RoomSettingsDialog.tsx @@ -10,6 +10,10 @@ Please see LICENSE files in the repository root for full details. import React from "react"; import { RoomEvent, Room, RoomStateEvent, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix"; +import { + CustomComponentLifecycle, + CustomComponentOpts, +} from "@matrix-org/react-sdk-module-api/lib/lifecycles/CustomComponentLifecycle"; import TabbedView, { Tab } from "../../structures/TabbedView"; import { _t, _td } from "../../../languageHandler"; @@ -31,6 +35,7 @@ import { NonEmptyArray } from "../../../@types/common"; import { PollHistoryTab } from "../settings/tabs/room/PollHistoryTab"; import ErrorBoundary from "../elements/ErrorBoundary"; import { PeopleRoomSettingsTab } from "../settings/tabs/room/PeopleRoomSettingsTab"; +import { ModuleRunner } from "../../../modules/ModuleRunner"; export const enum RoomSettingsTab { General = "ROOM_GENERAL_TAB", @@ -124,7 +129,11 @@ class RoomSettingsDialog extends React.Component { private getTabs(): NonEmptyArray> { const tabs: Tab[] = []; - + const customRolesRoomSettingsTabOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke( + CustomComponentLifecycle.RolesRoomSettingsTab, + customRolesRoomSettingsTabOpts as CustomComponentOpts, + ); tabs.push( new Tab( RoomSettingsTab.General, @@ -154,21 +163,32 @@ class RoomSettingsDialog extends React.Component { ), ); } - tabs.push( - new Tab( - RoomSettingsTab.Security, - _td("room_settings|security|title"), - "mx_RoomSettingsDialog_securityIcon", - this.props.onFinished(true)} />, - "RoomSettingsSecurityPrivacy", - ), - ); + if (SettingsStore.getValue(UIFeature.RoomSettingsSecurity)) { + tabs.push( + new Tab( + RoomSettingsTab.Security, + _td("room_settings|security|title"), + "mx_RoomSettingsDialog_securityIcon", + ( + this.props.onFinished(true)} + /> + ), + "RoomSettingsSecurityPrivacy", + ), + ); + } tabs.push( new Tab( RoomSettingsTab.Roles, _td("room_settings|permissions|title"), "mx_RoomSettingsDialog_rolesIcon", - , + ( + + + + ), "RoomSettingsRolesPermissions", ), ); diff --git a/src/components/views/dialogs/SpaceSettingsDialog.tsx b/src/components/views/dialogs/SpaceSettingsDialog.tsx index fd415c78973..2137b0f58e4 100644 --- a/src/components/views/dialogs/SpaceSettingsDialog.tsx +++ b/src/components/views/dialogs/SpaceSettingsDialog.tsx @@ -8,6 +8,10 @@ Please see LICENSE files in the repository root for full details. import React, { useMemo } from "react"; import { Room, MatrixClient } from "matrix-js-sdk/src/matrix"; +import { + CustomComponentOpts, + CustomComponentLifecycle, +} from "@matrix-org/react-sdk-module-api/lib/lifecycles/CustomComponentLifecycle"; import { _t, _td } from "../../../languageHandler"; import BaseDialog from "./BaseDialog"; @@ -22,6 +26,7 @@ import AdvancedRoomSettingsTab from "../settings/tabs/room/AdvancedRoomSettingsT import RolesRoomSettingsTab from "../settings/tabs/room/RolesRoomSettingsTab"; import { Action } from "../../../dispatcher/actions"; import { NonEmptyArray } from "../../../@types/common"; +import { ModuleRunner } from "../../../modules/ModuleRunner"; export enum SpaceSettingsTab { General = "SPACE_GENERAL_TAB", @@ -44,6 +49,11 @@ const SpaceSettingsDialog: React.FC = ({ matrixClient: cli, space, onFin }); const tabs = useMemo(() => { + const customRolesRoomSettingsTabOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke( + CustomComponentLifecycle.RolesRoomSettingsTab, + customRolesRoomSettingsTabOpts as CustomComponentOpts, + ); return [ new Tab( SpaceSettingsTab.General, @@ -61,7 +71,11 @@ const SpaceSettingsDialog: React.FC = ({ matrixClient: cli, space, onFin SpaceSettingsTab.Roles, _td("room_settings|permissions|title"), "mx_RoomSettingsDialog_rolesIcon", - , + ( + + + + ), ), SettingsStore.getValue(UIFeature.AdvancedSettings) ? new Tab( diff --git a/src/components/views/dialogs/UserSettingsDialog.tsx b/src/components/views/dialogs/UserSettingsDialog.tsx index 8ae7a302ac4..0fd73437d64 100644 --- a/src/components/views/dialogs/UserSettingsDialog.tsx +++ b/src/components/views/dialogs/UserSettingsDialog.tsx @@ -21,6 +21,10 @@ import LockIcon from "@vector-im/compound-design-tokens/assets/web/icons/lock"; import LabsIcon from "@vector-im/compound-design-tokens/assets/web/icons/labs"; import BlockIcon from "@vector-im/compound-design-tokens/assets/web/icons/block"; import HelpIcon from "@vector-im/compound-design-tokens/assets/web/icons/help"; +import { + CustomComponentOpts, + CustomComponentLifecycle, +} from "@matrix-org/react-sdk-module-api/lib/lifecycles/CustomComponentLifecycle"; import TabbedView, { Tab, useActiveTabWithDefault } from "../../structures/TabbedView"; import { _t, _td } from "../../../languageHandler"; @@ -44,7 +48,7 @@ import { NonEmptyArray } from "../../../@types/common"; import { SDKContext, SdkContextClass } from "../../../contexts/SDKContext"; import { useSettingValue } from "../../../hooks/useSettings"; import { ToastContext, useActiveToast } from "../../../contexts/ToastContext"; - +import { ModuleRunner } from "../../../modules/ModuleRunner"; interface IProps { initialTabId?: UserTab; showMsc4108QrCode?: boolean; @@ -92,7 +96,11 @@ export default function UserSettingsDialog(props: IProps): JSX.Element { const getTabs = (): NonEmptyArray> => { const tabs: Tab[] = []; - + const customSessionManagerTabOpts = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke( + CustomComponentLifecycle.SessionManagerTab, + customSessionManagerTabOpts as CustomComponentOpts, + ); tabs.push( new Tab( UserTab.Account, @@ -107,7 +115,12 @@ export default function UserSettingsDialog(props: IProps): JSX.Element { UserTab.SessionManager, _td("settings|sessions|title"), , - , + ( + + + + ) + , undefined, ), ); diff --git a/src/components/views/dialogs/devtools/AccountData.tsx b/src/components/views/dialogs/devtools/AccountData.tsx index f1fc081b009..813e09fb6e6 100644 --- a/src/components/views/dialogs/devtools/AccountData.tsx +++ b/src/components/views/dialogs/devtools/AccountData.tsx @@ -15,6 +15,8 @@ import MatrixClientContext from "../../../../contexts/MatrixClientContext"; import { EventEditor, EventViewer, eventTypeField, IEditorProps, stringify } from "./Event"; import FilteredList from "./FilteredList"; import { _td, TranslationKey } from "../../../../languageHandler"; +import SettingsStore from "../../../../settings/SettingsStore"; +import { UIFeature } from "../../../../settings/UIFeature"; export const AccountDataEventEditor: React.FC = ({ mxEvent, onBack }) => { const cli = useContext(MatrixClientContext); @@ -90,9 +92,12 @@ export const AccountDataExplorer: React.FC = ({ onBack, setTool ); }; @@ -104,9 +109,12 @@ export const RoomAccountDataExplorer: React.FC = ({ onBack, setT ); }; diff --git a/src/components/views/dialogs/devtools/BaseTool.tsx b/src/components/views/dialogs/devtools/BaseTool.tsx index 88dd2ea1785..419f6ad4e01 100644 --- a/src/components/views/dialogs/devtools/BaseTool.tsx +++ b/src/components/views/dialogs/devtools/BaseTool.tsx @@ -14,6 +14,8 @@ import classNames from "classnames"; import { _t, TranslationKey } from "../../../../languageHandler"; import { XOR } from "../../../../@types/common"; import { Tool } from "../DevtoolsDialog"; +import SettingsStore from "../../../../settings/SettingsStore"; +import { UIFeature } from "../../../../settings/UIFeature"; export interface IDevtoolsProps { onBack(): void; @@ -70,7 +72,7 @@ const BaseTool: React.FC> = ({
{extraButton} - {actionButton} + {SettingsStore.getValue(UIFeature.BaseToolActionButton) && actionButton}
); diff --git a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx index a87b7341e7d..3dc50540d58 100644 --- a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx +++ b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx @@ -79,6 +79,7 @@ import { useFeatureEnabled } from "../../../../hooks/useSettings"; import { filterBoolean } from "../../../../utils/arrays"; import { transformSearchTerm } from "../../../../utils/SearchInput"; import { Filter } from "./Filter"; +import { UIFeature } from "../../../../settings/UIFeature"; const MAX_RECENT_SEARCHES = 10; const SECTION_LIMIT = 50; // only show 50 results per section for performance reasons @@ -377,7 +378,9 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n userResults.push(result); } } - addUserResults(findVisibleRoomMembers(visibleRooms, cli), false); + if (SettingsStore.getValue(UIFeature.ShowRoomMembersInSuggestions)) { + addUserResults(findVisibleRoomMembers(visibleRooms, cli), false); + } addUserResults(userDirectorySearchResults, true); if (profile) { addUserResults([new DirectoryMember(profile)], true); @@ -419,7 +422,6 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n if (trimmedQuery) { const lcQuery = trimmedQuery.toLowerCase(); const normalizedQuery = normalize(trimmedQuery); - possibleResults.forEach((entry) => { if (isRoomResult(entry)) { // If the room is a DM with a user that is part of the user directory search results, @@ -559,7 +561,10 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n }; let otherSearchesSection: JSX.Element | undefined; - if (trimmedQuery || (filter !== Filter.PublicRooms && filter !== Filter.PublicSpaces)) { + if ( + SettingsStore.getValue(UIFeature.SpotlightDialogShowOtherSearches) && + (trimmedQuery || (filter !== Filter.PublicRooms && filter !== Filter.PublicSpaces)) + ) { otherSearchesSection = (
= ({ protocols, config, setConfig })); const addNewServer = useCallback( - ({ closeMenu }: AdditionalOptionsProps) => ( + ({ closeMenu }: AdditionalOptionsProps) => + SettingsStore.getValue(UIFeature.NetworkOptions) && ( <> = ({ protocols, config, setConfig "mx_NetworkDropdown_dialog", ); - const [ok, newServer] = await finished; - if (!ok) return; - - if (!allServers.includes(newServer)) { - setUserDefinedServers([...userDefinedServers, newServer]); - setConfig({ - roomServer: newServer, - }); - } - }} - > -
- - {_t("spotlight|public_rooms|network_dropdown_add_server_option")} - -
-
- - ), + const [ok, newServer] = await finished; + if (!ok) return; + + if (!allServers.includes(newServer)) { + setUserDefinedServers([...userDefinedServers, newServer]); + setConfig({ + roomServer: newServer, + }); + } + }} + > +
+ + {_t("spotlight|public_rooms|network_dropdown_add_server_option")} + +
+ + + ), [allServers, setConfig, setUserDefinedServers, userDefinedServers], ); @@ -228,7 +230,7 @@ export const NetworkDropdown: React.FC = ({ protocols, config, setConfig toKey={(config: IPublicRoomDirectoryConfig | null) => config ? `${config.roomServer}-${config.instanceId}` : "null" } - options={options} + options={SettingsStore.getValue(UIFeature.NetworkOptions) ? options : []} onChange={(option) => setConfig(option)} selectedLabel={(option) => option?.key diff --git a/src/components/views/elements/ErrorBoundary.tsx b/src/components/views/elements/ErrorBoundary.tsx index 98e38350fdf..a7008aa00f3 100644 --- a/src/components/views/elements/ErrorBoundary.tsx +++ b/src/components/views/elements/ErrorBoundary.tsx @@ -8,6 +8,10 @@ Please see LICENSE files in the repository root for full details. import React, { ErrorInfo, ReactNode } from "react"; import { logger } from "matrix-js-sdk/src/logger"; +import { + CustomComponentLifecycle, + CustomComponentOpts, +} from "@matrix-org/react-sdk-module-api/lib/lifecycles/CustomComponentLifecycle"; import { _t } from "../../../languageHandler"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; @@ -16,6 +20,7 @@ import Modal from "../../../Modal"; import SdkConfig from "../../../SdkConfig"; import BugReportDialog from "../dialogs/BugReportDialog"; import AccessibleButton from "./AccessibleButton"; +import { ModuleRunner } from "../../../modules/ModuleRunner"; interface Props { children: ReactNode; @@ -68,6 +73,12 @@ export default class ErrorBoundary extends React.PureComponent { }; public render(): ReactNode { + const CustomErrorBoundary = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke( + CustomComponentLifecycle.ErrorBoundary, + CustomErrorBoundary as CustomComponentOpts, + ); + if (this.state.error) { const newIssueUrl = SdkConfig.get().feedback.new_issue_url; @@ -113,16 +124,18 @@ export default class ErrorBoundary extends React.PureComponent { } return ( -
-
-

{_t("error|something_went_wrong")}

- {bugReportSection} - {clearCacheButton} + +
+
+

{_t("error|something_went_wrong")}

+ {bugReportSection} + {clearCacheButton} +
-
+ ); } - return this.props.children; + return {this.props.children}; } } diff --git a/src/components/views/elements/PowerSelector.tsx b/src/components/views/elements/PowerSelector.tsx index 385d932b870..74c656639db 100644 --- a/src/components/views/elements/PowerSelector.tsx +++ b/src/components/views/elements/PowerSelector.tsx @@ -14,6 +14,8 @@ import Field from "./Field"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { objectHasDiff } from "../../../utils/objects"; +import SettingsStore from "../../../settings/SettingsStore"; +import { UIFeature } from "../../../settings/UIFeature"; const CUSTOM_VALUE = "SELECT_VALUE_CUSTOM"; @@ -183,7 +185,8 @@ export default class PowerSelector extends React.C text: Roles.textualPowerLevel(level, this.props.usersDefault), }; }); - options.push({ value: CUSTOM_VALUE, text: _t("power_level|custom_level") }); + SettingsStore.getValue(UIFeature.PowerSelectorCustomValue) && + options.push({ value: CUSTOM_VALUE, text: _t("power_level|custom_level") }); const optionsElements = options.map((op) => { return (
- + {SettingsStore.getValue(UIFeature.RoomSummaryFilesOption) && ( + + )} { const user = cli.getUser(userId); - if (user) { + if (user && SettingsStore.getValue(UIFeature.UserInfoVerifyDevice)) { verifyDevice(cli, user, device); } }; @@ -550,14 +551,16 @@ export const UserOptionsSection: React.FC<{ isMe || !shouldShowComponent(UIComponent.CreateRooms) ? null : ; return ( - - {children} - {directMessageButton} - {inviteUserButton} - {readReceiptButton} - {shareUserButton} - {insertPillButton} - +
+

{_t("common|options")}

+
+ {SettingsStore.getValue(UIFeature.ShowSendMessageToUserLink) && directMessageButton} + {readReceiptButton} + {SettingsStore.getValue(UIFeature.ShowSendMessageToUserLink) && directMessageButton} + {insertPillButton} + {inviteUserButton} +
+
); }; @@ -1138,6 +1141,8 @@ export const RoomAdminToolsContainer: React.FC = ({ {redactButton} {kickButton} {banButton} + {/* If you dont want users to be able to delete messages, set the flag to false in settings.tsx */} + {SettingsStore.getValue(UIFeature.UserInfoRedactButton) && redactButton} {children} ); diff --git a/src/components/views/rooms/AppsDrawer.tsx b/src/components/views/rooms/AppsDrawer.tsx index c02bfe8cf28..c032608224b 100644 --- a/src/components/views/rooms/AppsDrawer.tsx +++ b/src/components/views/rooms/AppsDrawer.tsx @@ -11,6 +11,10 @@ import classNames from "classnames"; import { Resizable, Size } from "re-resizable"; import { Room } from "matrix-js-sdk/src/matrix"; import { IWidget } from "matrix-widget-api"; +import { + CustomComponentLifecycle, + CustomComponentOpts, +} from "@matrix-org/react-sdk-module-api/lib/lifecycles/CustomComponentLifecycle"; import AppTile from "../elements/AppTile"; import dis from "../../../dispatcher/dispatcher"; @@ -27,6 +31,7 @@ import UIStore from "../../../stores/UIStore"; import { ActionPayload } from "../../../dispatcher/payloads"; import Spinner from "../elements/Spinner"; import SdkConfig from "../../../SdkConfig"; +import { ModuleRunner } from "../../../modules/ModuleRunner"; interface IProps { userId: string; @@ -78,6 +83,12 @@ export default class AppsDrawer extends React.Component { ScalarMessaging.startListening(); WidgetLayoutStore.instance.on(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps); this.dispatcherRef = dis.register(this.onAction); + //verji custom start + dis.dispatch({ + action: "appsDrawer", + show: true, + }); + //verji custom end } public componentWillUnmount(): void { @@ -288,11 +299,16 @@ export default class AppsDrawer extends React.Component { ); } + const CustomAppDrawer = { CustomComponent: React.Fragment }; + ModuleRunner.instance.invoke(CustomComponentLifecycle.AppsDrawer, CustomAppDrawer as CustomComponentOpts); + return ( -
- {drawer} - {spinner} -
+ +
+ {drawer} + {spinner} +
+
); } } @@ -329,7 +345,7 @@ const PersistentVResizer: React.FC = ({ defaultHeight = clamp(defaultHeight, 0, 100); defaultHeight = percentageWithin(defaultHeight / 100, minHeight, maxHeight); } else { - defaultHeight = SdkConfig.get().default_widget_container_height ?? 280; + defaultHeight = SdkConfig.get().default_widget_container_height ?? 380; // Verji changed fallback } return ( diff --git a/src/components/views/rooms/EntityTile.tsx b/src/components/views/rooms/EntityTile.tsx index 946a5cd46b4..a62383492a5 100644 --- a/src/components/views/rooms/EntityTile.tsx +++ b/src/components/views/rooms/EntityTile.tsx @@ -10,21 +10,34 @@ Please see LICENSE files in the repository root for full details. import React from "react"; import classNames from "classnames"; +import { + CustomComponentLifecycle, + CustomComponentOpts, +} from "@matrix-org/react-sdk-module-api/lib/lifecycles/CustomComponentLifecycle"; import AccessibleButton from "../elements/AccessibleButton"; import { _t, _td, TranslationKey } from "../../../languageHandler"; import E2EIcon, { E2EState } from "./E2EIcon"; import BaseAvatar from "../avatars/BaseAvatar"; import PresenceLabel from "./PresenceLabel"; +import { ModuleRunner } from "../../../modules/ModuleRunner"; export enum PowerStatus { Admin = "admin", Moderator = "moderator", + CustomerAdmin = "customer", + RosbergAdmin = "rosberg", + SystemAdmin = "system", + Standard = "standard", } const PowerLabel: Record = { [PowerStatus.Admin]: _td("power_level|admin"), [PowerStatus.Moderator]: _td("power_level|mod"), + [PowerStatus.CustomerAdmin]: _td("verji|power_levels|customer_admin"), + [PowerStatus.RosbergAdmin]: _td("verji|power_levels|rosberg_admin"), + [PowerStatus.SystemAdmin]: _td("verji|power_levels|system_admin"), + [PowerStatus.Standard]: _td("verji|power_levels|standard"), }; export type PresenceState = "offline" | "online" | "unavailable" | "io.element.unreachable"; @@ -149,22 +162,27 @@ export default class EntityTile extends React.PureComponent { const av = this.props.avatarJsx ||
diff --git a/test/unit-tests/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap b/test/unit-tests/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap index 4e65f7b61f4..6c3ca36382b 100644 --- a/test/unit-tests/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap +++ b/test/unit-tests/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap @@ -17,68 +17,6 @@ exports[` renders settings marked as beta as beta cards 1 > What's next for BrandedClient? Labs are the best way to get things early, test out new features and help shape them before they actually launch. -
-
-
-

- - Video rooms - - - Beta - -

-
-

- A new way to chat over voice and video in BrandedClient. -

-

- Video rooms are always-on VoIP channels embedded within a room in BrandedClient. -

-
-
-
- Join the beta -
-
-
- Joining the beta will reload BrandedClient. -
-
-
-
- -
-
-
diff --git a/test/unit-tests/components/views/spaces/__snapshots__/SpaceTreeLevel-test.tsx.snap b/test/unit-tests/components/views/spaces/__snapshots__/SpaceTreeLevel-test.tsx.snap index ac89c05d0a3..12612a0014d 100644 --- a/test/unit-tests/components/views/spaces/__snapshots__/SpaceTreeLevel-test.tsx.snap +++ b/test/unit-tests/components/views/spaces/__snapshots__/SpaceTreeLevel-test.tsx.snap @@ -26,7 +26,7 @@ exports[`SpaceButton metaspace should render notificationState if one is provide class="mx_SpacePanel_badgeContainer" >
diff --git a/yarn.lock b/yarn.lock index bf2b3480a58..ac97be812a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1543,6 +1543,9 @@ strip-json-comments "^3.1.1" "@eslint/eslintrc@^3.0.2": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" + integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== version "3.1.0" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== @@ -1583,6 +1586,7 @@ integrity sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A== dependencies: "@floating-ui/dom" "^1.0.0" + "@floating-ui/dom" "^1.0.0" "@floating-ui/react@^0.27.0": version "0.27.0" @@ -1683,6 +1687,7 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": "@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" @@ -1889,6 +1894,7 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.24" +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" @@ -2035,6 +2041,7 @@ integrity sha512-l/SmiO47gPIRd6YJJGj+B6qbxyypJF6SEsfYr7j9rSW6E85ZYCqf+TpMM2LmfwZRADyKfCVkaJbbBZYpoD02VA== dependencies: "@babel/runtime" "^7.17.9" + matrix-events-sdk "^2.0.0" "@matrix-org/spec@^1.7.0": version "1.12.0" @@ -2538,6 +2545,9 @@ integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== "@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== version "3.0.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== @@ -2740,6 +2750,9 @@ version "1.0.11" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== "@tsconfig/node12@^1.0.7": version "1.0.11" @@ -2773,6 +2786,9 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== version "7.6.8" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== @@ -2780,6 +2796,9 @@ "@babel/types" "^7.0.0" "@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== version "7.4.4" resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== @@ -2788,6 +2807,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== version "7.20.6" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== @@ -2953,6 +2975,9 @@ "@types/node" "*" "@types/hoist-non-react-statics@^3.3.0": + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== version "3.3.5" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== @@ -2981,8 +3006,14 @@ version "2.0.6" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== version "3.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== @@ -2990,6 +3021,9 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== version "3.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== @@ -3211,11 +3245,6 @@ resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-3.0.8.tgz#61cc8ed88f93a3c31289c295e6df8ca40be42bdf" integrity sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ== -"@types/semver@^7.5.8": - version "7.5.8" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" - integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== - "@types/send@*": version "0.17.4" resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" @@ -3246,6 +3275,7 @@ integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== dependencies: "@types/node" "*" + "@types/send" "*" "@types/stack-utils@^2.0.0": version "2.0.3" @@ -3756,6 +3786,9 @@ argparse@^2.0.1: integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== aria-hidden@^1.1.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.4.tgz#b78e383fdbc04d05762c78b4a25a501e736c4522" + integrity sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A== version "1.2.4" resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.4.tgz#b78e383fdbc04d05762c78b4a25a501e736c4522" integrity sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A== @@ -3792,6 +3825,11 @@ array-includes@^3.1.6, array-includes@^3.1.8: resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" call-bind "^1.0.7" define-properties "^1.2.1" es-abstract "^1.23.2" @@ -3827,6 +3865,12 @@ array.prototype.findlastindex@^1.2.5: es-errors "^1.3.0" es-object-atoms "^1.0.0" es-shim-unscopables "^1.0.2" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: version "1.3.2" @@ -4141,6 +4185,10 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -5143,6 +5191,9 @@ dijkstrajs@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz#4c8dbdea1f0f6478bff94d9c49c784d623e4fc23" integrity sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA== + version "1.0.3" + resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz#4c8dbdea1f0f6478bff94d9c49c784d623e4fc23" + integrity sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA== dir-glob@^3.0.1: version "3.0.1" @@ -5234,6 +5285,7 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" +domhandler@^5.0.2, domhandler@^5.0.3: domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" @@ -5251,6 +5303,9 @@ domutils@^2.5.2, domutils@^2.8.0: domhandler "^4.2.0" domutils@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== version "3.1.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== @@ -5258,6 +5313,7 @@ domutils@^3.0.1: dom-serializer "^2.0.0" domelementtype "^2.3.0" domhandler "^5.0.3" + domhandler "^5.0.3" dot-case@^3.0.4: version "3.0.4" @@ -5462,10 +5518,12 @@ es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23 string.prototype.trim "^1.2.9" string.prototype.trimend "^1.0.8" string.prototype.trimstart "^1.0.8" + string.prototype.trimstart "^1.0.8" typed-array-buffer "^1.0.2" typed-array-byte-length "^1.0.1" typed-array-byte-offset "^1.0.2" typed-array-length "^1.0.6" + typed-array-length "^1.0.6" unbox-primitive "^1.0.2" which-typed-array "^1.1.15" @@ -5487,6 +5545,7 @@ es-iterator-helpers@^1.1.0: call-bind "^1.0.7" define-properties "^1.2.1" es-abstract "^1.23.3" + es-abstract "^1.23.3" es-errors "^1.3.0" es-set-tostringtag "^2.0.3" function-bind "^1.1.2" @@ -5512,6 +5571,7 @@ es-object-atoms@^1.0.0: dependencies: es-errors "^1.3.0" +es-set-tostringtag@^2.0.3: es-set-tostringtag@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" @@ -6135,6 +6195,10 @@ filesize@10.1.6: resolved "https://registry.yarnpkg.com/filesize/-/filesize-10.1.6.tgz#31194da825ac58689c0bce3948f33ce83aabd361" integrity sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -6188,10 +6252,15 @@ find-up@^6.3.0: path-exists "^5.0.0" flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== version "3.2.0" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== dependencies: + flatted "^3.2.9" + keyv "^4.5.3" flatted "^3.2.9" keyv "^4.5.3" rimraf "^3.0.2" @@ -6299,6 +6368,7 @@ fsevents@^2.3.2, fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +function-bind@^1.1.2: function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" @@ -6487,6 +6557,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== @@ -6510,6 +6583,8 @@ globalthis@^1.0.4: dependencies: define-properties "^1.2.1" gopd "^1.0.1" + define-properties "^1.2.1" + gopd "^1.0.1" globby@^11.1.0: version "11.1.0" @@ -7114,6 +7189,7 @@ is-map@^2.0.3: resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== +is-negative-zero@^2.0.3: is-negative-zero@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" @@ -7279,6 +7355,9 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-instrument@^5.0.4: version "5.2.1" @@ -7296,6 +7375,9 @@ istanbul-lib-instrument@^6.0.0: resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== dependencies: + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" "@babel/core" "^7.23.9" "@babel/parser" "^7.23.9" "@istanbuljs/schema" "^0.1.3" @@ -7321,6 +7403,9 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.1.3: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== version "3.1.7" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== @@ -7930,6 +8015,7 @@ kdbush@^4.0.2: resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-4.0.2.tgz#2f7b7246328b4657dd122b6c7f025fbc2c868e39" integrity sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA== +keyv@^4.5.3, keyv@^4.5.4: keyv@^4.5.3, keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -7983,6 +8069,9 @@ language-subtag-registry@^0.3.20: version "0.3.23" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== + version "0.3.23" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" + integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== language-tags@^1.0.9: version "1.0.9" @@ -8211,6 +8300,7 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lz-string@^1.5.0: lz-string@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" @@ -8421,6 +8511,7 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.8, micromatch@~4.0.8: resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: + braces "^3.0.3" braces "^3.0.3" picomatch "^2.3.1" @@ -8820,6 +8911,9 @@ opener@^1.5.2: integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== @@ -8830,6 +8924,7 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" word-wrap "^1.2.5" + word-wrap "^1.2.5" opus-recorder@^8.0.3: version "8.0.5" @@ -9862,6 +9957,7 @@ psl@^1.1.33: resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== +punycode@^2.1.0, punycode@^2.1.1: punycode@^2.1.0, punycode@^2.1.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -9871,6 +9967,9 @@ pure-rand@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== pvtsutils@^1.3.2, pvtsutils@^1.3.5: version "1.3.5" @@ -10010,6 +10109,11 @@ react-focus-lock@^2.5.1: use-callback-ref "^1.3.2" use-sidecar "^1.1.2" +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -10056,6 +10160,14 @@ react-remove-scroll@2.6.0: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" +react-shallow-renderer@^16.13.1: + version "16.15.0" + resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" + integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== + dependencies: + object-assign "^4.1.1" + react-is "^16.12.0 || ^17.0.0 || ^18.0.0" + react-style-singleton@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4" @@ -10065,6 +10177,16 @@ react-style-singleton@^2.2.1: invariant "^2.2.4" tslib "^2.0.0" +react-test-renderer@17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c" + integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ== + dependencies: + object-assign "^4.1.1" + react-is "^17.0.2" + react-shallow-renderer "^16.13.1" + scheduler "^0.20.2" + react-transition-group@^4.4.1: version "4.4.5" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" @@ -10460,6 +10582,11 @@ sanitize-html@2.13.1: parse-srcset "^1.0.2" postcss "^8.3.11" +sax@>=0.6.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + saxes@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" @@ -10497,6 +10624,9 @@ sdp-transform@^2.14.1: version "2.14.2" resolved "https://registry.yarnpkg.com/sdp-transform/-/sdp-transform-2.14.2.tgz#d2cee6a1f7abe44e6332ac6cbb94e8600f32d813" integrity sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA== + version "2.14.2" + resolved "https://registry.yarnpkg.com/sdp-transform/-/sdp-transform-2.14.2.tgz#d2cee6a1f7abe44e6332ac6cbb94e8600f32d813" + integrity sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA== seedrandom@^3.0.5: version "3.0.5" @@ -10597,6 +10727,7 @@ set-function-length@^1.2.1, set-function-length@^1.2.2: gopd "^1.0.1" has-property-descriptors "^1.0.2" +set-function-name@^2.0.1, set-function-name@^2.0.2: set-function-name@^2.0.1, set-function-name@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" @@ -10783,6 +10914,9 @@ spdx-exceptions@^2.1.0: version "2.5.0" resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== spdx-expression-parse@^3.0.0: version "3.0.1" @@ -10824,6 +10958,9 @@ sprintf-js@^1.0.3: version "1.1.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== sprintf-js@~1.0.2: version "1.0.3" @@ -10922,6 +11059,10 @@ string.prototype.matchall@^4.0.11: regexp.prototype.flags "^1.5.2" set-function-name "^2.0.2" side-channel "^1.0.6" + internal-slot "^1.0.7" + regexp.prototype.flags "^1.5.2" + set-function-name "^2.0.2" + side-channel "^1.0.6" string.prototype.repeat@^1.0.0: version "1.0.0" @@ -10931,6 +11072,7 @@ string.prototype.repeat@^1.0.0: define-properties "^1.1.3" es-abstract "^1.17.5" +string.prototype.trim@^1.2.9: string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" @@ -10941,6 +11083,7 @@ string.prototype.trim@^1.2.9: es-abstract "^1.23.0" es-object-atoms "^1.0.0" +string.prototype.trimend@^1.0.8: string.prototype.trimend@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" @@ -10950,6 +11093,10 @@ string.prototype.trimend@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== string.prototype.trimstart@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" @@ -11335,6 +11482,9 @@ totalist@^3.0.0: integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== tough-cookie@^4.1.2: + version "4.1.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== version "4.1.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== @@ -11494,6 +11644,7 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-buffer@^1.0.2: typed-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" @@ -11503,6 +11654,7 @@ typed-array-buffer@^1.0.2: es-errors "^1.3.0" is-typed-array "^1.1.13" +typed-array-byte-length@^1.0.1: typed-array-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" @@ -11660,6 +11812,7 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +use-callback-ref@^1.3.0, use-callback-ref@^1.3.2: use-callback-ref@^1.3.0, use-callback-ref@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.2.tgz#6134c7f6ff76e2be0b56c809b17a650c942b1693" @@ -11684,6 +11837,9 @@ utf8-byte-length@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz#f9f63910d15536ee2b2d5dd4665389715eac5c1e" integrity sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz#f9f63910d15536ee2b2d5dd4665389715eac5c1e" + integrity sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA== util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" @@ -11739,6 +11895,7 @@ v8-to-istanbul@^9.0.1: "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" + convert-source-map "^2.0.0" validate-npm-package-license@^3.0.1: version "3.0.4" @@ -12201,6 +12358,11 @@ xml@1.0.1: resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"