mirror of https://github.com/vector-im/riot-web
Merge remote-tracking branch 'upstream/develop' into compact-reply-rendering
commit
cdd2773aa6
|
@ -0,0 +1,40 @@
|
||||||
|
name: Develop jobs
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [develop]
|
||||||
|
pull_request:
|
||||||
|
branches: [develop]
|
||||||
|
jobs:
|
||||||
|
end-to-end:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: vectorim/element-web-ci-e2etests-env:latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: End-to-End tests
|
||||||
|
run: ./scripts/ci/end-to-end-tests.sh
|
||||||
|
- name: Archive logs
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
test/end-to-end-tests/logs/**/*
|
||||||
|
test/end-to-end-tests/synapse/installations/consent/homeserver.log
|
||||||
|
retention-days: 14
|
||||||
|
- name: Download previous benchmark data
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ./cache
|
||||||
|
key: ${{ runner.os }}-benchmark
|
||||||
|
- name: Store benchmark result
|
||||||
|
uses: matrix-org/github-action-benchmark@jsperfentry-1
|
||||||
|
with:
|
||||||
|
tool: 'jsperformanceentry'
|
||||||
|
output-file-path: test/end-to-end-tests/performance-entries.json
|
||||||
|
fail-on-alert: false
|
||||||
|
# Secrets are not passed to fork, the action won't be able to comment
|
||||||
|
# for community PRs
|
||||||
|
comment-on-alert: ${{ github.repository_owner == 'matrix-org' }}
|
||||||
|
# Only temporary to monitor where failures occur
|
||||||
|
alert-comment-cc-users: '@gsouquet'
|
||||||
|
github-token: ${{ secrets.DEPLOY_GH_PAGES }}
|
||||||
|
auto-push: ${{ github.ref == 'refs/heads/develop' }}
|
106
CHANGELOG.md
106
CHANGELOG.md
|
@ -1,3 +1,109 @@
|
||||||
|
Changes in [3.23.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.23.0) (2021-06-07)
|
||||||
|
=====================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.23.0-rc.1...v3.23.0)
|
||||||
|
|
||||||
|
* Upgrade to JS SDK 11.2.0
|
||||||
|
* [Release] Fix notif panel timestamp padding
|
||||||
|
[\#6158](https://github.com/matrix-org/matrix-react-sdk/pull/6158)
|
||||||
|
|
||||||
|
Changes in [3.23.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.23.0-rc.1) (2021-06-01)
|
||||||
|
===============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.22.0...v3.23.0-rc.1)
|
||||||
|
|
||||||
|
* Upgrade to JS SDK 11.2.0-rc.1
|
||||||
|
* Translations update from Weblate
|
||||||
|
[\#6128](https://github.com/matrix-org/matrix-react-sdk/pull/6128)
|
||||||
|
* Fix all DMs wrongly appearing in room list when `m.direct` is changed
|
||||||
|
[\#6122](https://github.com/matrix-org/matrix-react-sdk/pull/6122)
|
||||||
|
* Update way of checking for registration disabled
|
||||||
|
[\#6123](https://github.com/matrix-org/matrix-react-sdk/pull/6123)
|
||||||
|
* Fix the ability to remove avatar from a space via settings
|
||||||
|
[\#6126](https://github.com/matrix-org/matrix-react-sdk/pull/6126)
|
||||||
|
* Switch to stable endpoint/fields for MSC2858
|
||||||
|
[\#6125](https://github.com/matrix-org/matrix-react-sdk/pull/6125)
|
||||||
|
* Clear stored editor state when canceling editing using a shortcut
|
||||||
|
[\#6117](https://github.com/matrix-org/matrix-react-sdk/pull/6117)
|
||||||
|
* Respect newlines in space topics
|
||||||
|
[\#6124](https://github.com/matrix-org/matrix-react-sdk/pull/6124)
|
||||||
|
* Add url param `defaultUsername` to prefill the login username field
|
||||||
|
[\#5674](https://github.com/matrix-org/matrix-react-sdk/pull/5674)
|
||||||
|
* Bump ws from 7.4.2 to 7.4.6
|
||||||
|
[\#6115](https://github.com/matrix-org/matrix-react-sdk/pull/6115)
|
||||||
|
* Sticky headers repositioning without layout trashing
|
||||||
|
[\#6110](https://github.com/matrix-org/matrix-react-sdk/pull/6110)
|
||||||
|
* Handle user_busy in voip calls
|
||||||
|
[\#6112](https://github.com/matrix-org/matrix-react-sdk/pull/6112)
|
||||||
|
* Avoid showing warning modals from the invite dialog after it unmounts
|
||||||
|
[\#6105](https://github.com/matrix-org/matrix-react-sdk/pull/6105)
|
||||||
|
* Fix misleading child counts in spaces
|
||||||
|
[\#6109](https://github.com/matrix-org/matrix-react-sdk/pull/6109)
|
||||||
|
* Close creation menu when expanding space panel via expand hierarchy
|
||||||
|
[\#6090](https://github.com/matrix-org/matrix-react-sdk/pull/6090)
|
||||||
|
* Prevent having duplicates in pending room state
|
||||||
|
[\#6108](https://github.com/matrix-org/matrix-react-sdk/pull/6108)
|
||||||
|
* Update reactions row on event decryption
|
||||||
|
[\#6106](https://github.com/matrix-org/matrix-react-sdk/pull/6106)
|
||||||
|
* Destroy playback instance on voice message unmount
|
||||||
|
[\#6101](https://github.com/matrix-org/matrix-react-sdk/pull/6101)
|
||||||
|
* Fix message preview not up to date
|
||||||
|
[\#6102](https://github.com/matrix-org/matrix-react-sdk/pull/6102)
|
||||||
|
* Convert some Flow typed files to TS (round 2)
|
||||||
|
[\#6076](https://github.com/matrix-org/matrix-react-sdk/pull/6076)
|
||||||
|
* Remove unused middlePanelResized event listener
|
||||||
|
[\#6086](https://github.com/matrix-org/matrix-react-sdk/pull/6086)
|
||||||
|
* Fix accessing currentState on an invalid joinedRoom
|
||||||
|
[\#6100](https://github.com/matrix-org/matrix-react-sdk/pull/6100)
|
||||||
|
* Remove Promise allSettled polyfill as js-sdk uses it directly
|
||||||
|
[\#6097](https://github.com/matrix-org/matrix-react-sdk/pull/6097)
|
||||||
|
* Prevent DecoratedRoomAvatar to update its state for the same value
|
||||||
|
[\#6099](https://github.com/matrix-org/matrix-react-sdk/pull/6099)
|
||||||
|
* Skip generatePreview if event is not part of the live timeline
|
||||||
|
[\#6098](https://github.com/matrix-org/matrix-react-sdk/pull/6098)
|
||||||
|
* fix sticky headers when results num get displayed
|
||||||
|
[\#6095](https://github.com/matrix-org/matrix-react-sdk/pull/6095)
|
||||||
|
* Improve addEventsToTimeline performance scoping WhoIsTypingTile::setState
|
||||||
|
[\#6094](https://github.com/matrix-org/matrix-react-sdk/pull/6094)
|
||||||
|
* Safeguards to prevent layout trashing for window dimensions
|
||||||
|
[\#6092](https://github.com/matrix-org/matrix-react-sdk/pull/6092)
|
||||||
|
* Use local room state to render space hierarchy if the room is known
|
||||||
|
[\#6089](https://github.com/matrix-org/matrix-react-sdk/pull/6089)
|
||||||
|
* Add spinner in UserMenu to list pending long running actions
|
||||||
|
[\#6085](https://github.com/matrix-org/matrix-react-sdk/pull/6085)
|
||||||
|
* Stop overscroll in Firefox Nightly for macOS
|
||||||
|
[\#6093](https://github.com/matrix-org/matrix-react-sdk/pull/6093)
|
||||||
|
* Move SettingsStore watchers/monitors over to ES6 maps for performance
|
||||||
|
[\#6063](https://github.com/matrix-org/matrix-react-sdk/pull/6063)
|
||||||
|
* Bump libolm version.
|
||||||
|
[\#6080](https://github.com/matrix-org/matrix-react-sdk/pull/6080)
|
||||||
|
* Improve styling of the message action bar
|
||||||
|
[\#6066](https://github.com/matrix-org/matrix-react-sdk/pull/6066)
|
||||||
|
* Improve explore rooms when no results are found
|
||||||
|
[\#6070](https://github.com/matrix-org/matrix-react-sdk/pull/6070)
|
||||||
|
* Remove logo spinner
|
||||||
|
[\#6078](https://github.com/matrix-org/matrix-react-sdk/pull/6078)
|
||||||
|
* Fix add reaction prompt showing even when user is not joined to room
|
||||||
|
[\#6073](https://github.com/matrix-org/matrix-react-sdk/pull/6073)
|
||||||
|
* Vectorize spinners
|
||||||
|
[\#5680](https://github.com/matrix-org/matrix-react-sdk/pull/5680)
|
||||||
|
* Fix handling of via servers for suggested rooms
|
||||||
|
[\#6077](https://github.com/matrix-org/matrix-react-sdk/pull/6077)
|
||||||
|
* Upgrade showChatEffects to room-level setting exposure
|
||||||
|
[\#6075](https://github.com/matrix-org/matrix-react-sdk/pull/6075)
|
||||||
|
* Delete RoomView dead code
|
||||||
|
[\#6071](https://github.com/matrix-org/matrix-react-sdk/pull/6071)
|
||||||
|
* Reduce noise in tests
|
||||||
|
[\#6074](https://github.com/matrix-org/matrix-react-sdk/pull/6074)
|
||||||
|
* Fix room name issues in right panel summary card
|
||||||
|
[\#6069](https://github.com/matrix-org/matrix-react-sdk/pull/6069)
|
||||||
|
* Cache normalized room name
|
||||||
|
[\#6072](https://github.com/matrix-org/matrix-react-sdk/pull/6072)
|
||||||
|
* Update MemberList to reflect changes for invite permission change
|
||||||
|
[\#6061](https://github.com/matrix-org/matrix-react-sdk/pull/6061)
|
||||||
|
* Delete RoomView dead code
|
||||||
|
[\#6065](https://github.com/matrix-org/matrix-react-sdk/pull/6065)
|
||||||
|
* Show subspace rooms count even if it is 0 for consistency
|
||||||
|
[\#6067](https://github.com/matrix-org/matrix-react-sdk/pull/6067)
|
||||||
|
|
||||||
Changes in [3.22.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.22.0) (2021-05-24)
|
Changes in [3.22.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.22.0) (2021-05-24)
|
||||||
=====================================================================================================
|
=====================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.22.0-rc.1...v3.22.0)
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.22.0-rc.1...v3.22.0)
|
||||||
|
|
15
package.json
15
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "matrix-react-sdk",
|
"name": "matrix-react-sdk",
|
||||||
"version": "3.22.0",
|
"version": "3.23.0",
|
||||||
"description": "SDK for matrix.org using React",
|
"description": "SDK for matrix.org using React",
|
||||||
"author": "matrix.org",
|
"author": "matrix.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -55,7 +55,6 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
"await-lock": "^2.1.0",
|
"await-lock": "^2.1.0",
|
||||||
"blueimp-canvas-to-blob": "^3.28.0",
|
|
||||||
"browser-encrypt-attachment": "^0.3.0",
|
"browser-encrypt-attachment": "^0.3.0",
|
||||||
"browser-request": "^0.3.3",
|
"browser-request": "^0.3.3",
|
||||||
"cheerio": "^1.0.0-rc.9",
|
"cheerio": "^1.0.0-rc.9",
|
||||||
|
@ -88,18 +87,16 @@
|
||||||
"png-chunks-extract": "^1.0.0",
|
"png-chunks-extract": "^1.0.0",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"qrcode": "^1.4.4",
|
"qrcode": "^1.4.4",
|
||||||
"qs": "^6.9.6",
|
|
||||||
"re-resizable": "^6.9.0",
|
"re-resizable": "^6.9.0",
|
||||||
"react": "^16.14.0",
|
"react": "^17.0.2",
|
||||||
"react-beautiful-dnd": "^4.0.1",
|
"react-beautiful-dnd": "^4.0.1",
|
||||||
"react-dom": "^16.14.0",
|
"react-dom": "^17.0.2",
|
||||||
"react-focus-lock": "^2.5.0",
|
"react-focus-lock": "^2.5.0",
|
||||||
"react-transition-group": "^4.4.1",
|
"react-transition-group": "^4.4.1",
|
||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"rfc4648": "^1.4.0",
|
"rfc4648": "^1.4.0",
|
||||||
"sanitize-html": "^2.3.2",
|
"sanitize-html": "^2.3.2",
|
||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"text-encoding-utf-8": "^1.0.2",
|
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
"what-input": "^5.2.10",
|
"what-input": "^5.2.10",
|
||||||
"zxcvbn": "^4.4.2"
|
"zxcvbn": "^4.4.2"
|
||||||
|
@ -147,7 +144,7 @@
|
||||||
"chokidar": "^3.5.1",
|
"chokidar": "^3.5.1",
|
||||||
"concurrently": "^5.3.0",
|
"concurrently": "^5.3.0",
|
||||||
"enzyme": "^3.11.0",
|
"enzyme": "^3.11.0",
|
||||||
"enzyme-adapter-react-16": "^1.15.6",
|
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.1",
|
||||||
"eslint": "7.18.0",
|
"eslint": "7.18.0",
|
||||||
"eslint-config-matrix-org": "^0.2.0",
|
"eslint-config-matrix-org": "^0.2.0",
|
||||||
"eslint-plugin-babel": "^5.3.1",
|
"eslint-plugin-babel": "^5.3.1",
|
||||||
|
@ -160,9 +157,9 @@
|
||||||
"jest-environment-jsdom-sixteen": "^1.0.3",
|
"jest-environment-jsdom-sixteen": "^1.0.3",
|
||||||
"jest-fetch-mock": "^3.0.3",
|
"jest-fetch-mock": "^3.0.3",
|
||||||
"matrix-mock-request": "^1.2.3",
|
"matrix-mock-request": "^1.2.3",
|
||||||
"matrix-react-test-utils": "^0.2.2",
|
"matrix-react-test-utils": "^0.2.3",
|
||||||
"matrix-web-i18n": "github:matrix-org/matrix-web-i18n",
|
"matrix-web-i18n": "github:matrix-org/matrix-web-i18n",
|
||||||
"react-test-renderer": "^16.14.0",
|
"react-test-renderer": "^17.0.2",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"stylelint": "^13.9.0",
|
"stylelint": "^13.9.0",
|
||||||
"stylelint-config-standard": "^20.0.0",
|
"stylelint-config-standard": "^20.0.0",
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
@import "./views/dialogs/_DevtoolsDialog.scss";
|
@import "./views/dialogs/_DevtoolsDialog.scss";
|
||||||
@import "./views/dialogs/_EditCommunityPrototypeDialog.scss";
|
@import "./views/dialogs/_EditCommunityPrototypeDialog.scss";
|
||||||
@import "./views/dialogs/_FeedbackDialog.scss";
|
@import "./views/dialogs/_FeedbackDialog.scss";
|
||||||
|
@import "./views/dialogs/_ForwardDialog.scss";
|
||||||
@import "./views/dialogs/_GroupAddressPicker.scss";
|
@import "./views/dialogs/_GroupAddressPicker.scss";
|
||||||
@import "./views/dialogs/_HostSignupDialog.scss";
|
@import "./views/dialogs/_HostSignupDialog.scss";
|
||||||
@import "./views/dialogs/_IncomingSasDialog.scss";
|
@import "./views/dialogs/_IncomingSasDialog.scss";
|
||||||
|
|
|
@ -328,7 +328,8 @@ $SpaceRoomViewInnerWidth: 428px;
|
||||||
font-size: $font-15px;
|
font-size: $font-15px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
white-space: pre;
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
> hr {
|
> hr {
|
||||||
|
@ -365,6 +366,45 @@ $SpaceRoomViewInnerWidth: 428px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_SpaceRoomView_betaWarning {
|
||||||
|
padding: 12px 12px 12px 54px;
|
||||||
|
position: relative;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
width: 432px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: $info-plinth-bg-color;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
> h3 {
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> p {
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
|
||||||
|
mask-position: center;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: contain;
|
||||||
|
content: '';
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
top: 14px;
|
||||||
|
left: 14px;
|
||||||
|
background-color: $secondary-fg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mx_SpaceRoomView_inviteTeammates {
|
.mx_SpaceRoomView_inviteTeammates {
|
||||||
// XXX remove this when spaces leaves Beta
|
// XXX remove this when spaces leaves Beta
|
||||||
.mx_SpaceRoomView_inviteTeammates_betaDisclaimer {
|
.mx_SpaceRoomView_inviteTeammates_betaDisclaimer {
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Robin Townsend <robin@robin.town>
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_ForwardDialog {
|
||||||
|
width: 520px;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
min-height: 0;
|
||||||
|
height: 80vh;
|
||||||
|
|
||||||
|
> h3 {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
font-size: $font-12px;
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
line-height: $font-15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .mx_ForwardDialog_preview {
|
||||||
|
max-height: 30%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
overflow: scroll;
|
||||||
|
|
||||||
|
div {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_msgOption {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When forwarding messages from encrypted rooms, EventTile will complain
|
||||||
|
// that our preview is unencrypted, which doesn't actually matter
|
||||||
|
.mx_EventTile_e2eIcon_unencrypted {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We also hide download links to not encourage users to try interacting
|
||||||
|
.mx_MFileBody_download {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> hr {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid $input-border-color;
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .mx_ForwardList {
|
||||||
|
display: contents;
|
||||||
|
|
||||||
|
.mx_SearchBox {
|
||||||
|
// To match the space around the title
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ForwardList_content {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ForwardList_noResults {
|
||||||
|
display: block;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ForwardList_results {
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ForwardList_entry {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 32px;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $groupFilterPanel-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ForwardList_roomButton {
|
||||||
|
display: flex;
|
||||||
|
margin-right: 12px;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.mx_DecoratedRoomAvatar {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ForwardList_entry_name {
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: 30px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ForwardList_sendButton {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:not(.mx_ForwardList_canSend) .mx_ForwardList_sendLabel {
|
||||||
|
// Hide the "Send" label while preserving button size
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ForwardList_sendIcon, .mx_NotificationBadge {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_NotificationBadge {
|
||||||
|
// Match the failed to send indicator's color with the disabled button
|
||||||
|
background-color: $button-danger-disabled-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_ForwardList_sending .mx_ForwardList_sendIcon {
|
||||||
|
background-color: $button-primary-bg-color;
|
||||||
|
mask-image: url('$(res)/img/element-icons/circle-sending.svg');
|
||||||
|
mask-position: center;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: 14px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_ForwardList_sent .mx_ForwardList_sendIcon {
|
||||||
|
background-color: $button-primary-bg-color;
|
||||||
|
mask-image: url('$(res)/img/element-icons/circle-sent.svg');
|
||||||
|
mask-position: center;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: 14px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,9 @@ limitations under the License.
|
||||||
.mx_InviteDialog_addressBar {
|
.mx_InviteDialog_addressBar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
// Right margin for the design. We could apply this to the whole dialog, but then the scrollbar
|
||||||
|
// for the user section gets weird.
|
||||||
|
margin: 8px 45px 0 0;
|
||||||
|
|
||||||
.mx_InviteDialog_editor {
|
.mx_InviteDialog_editor {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -73,7 +76,7 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_InviteDialog_section {
|
.mx_InviteDialog_section {
|
||||||
padding-bottom: 10px;
|
padding-bottom: 4px;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
|
@ -82,6 +85,14 @@ limitations under the License.
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> span {
|
||||||
|
color: $primary-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_InviteDialog_subname {
|
.mx_InviteDialog_subname {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
margin-top: -10px; // HACK: Positioning with margins is bad
|
margin-top: -10px; // HACK: Positioning with margins is bad
|
||||||
|
@ -90,6 +101,63 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_InviteDialog_section_hidden_suggestions_disclaimer {
|
||||||
|
padding: 8px 0 16px 0;
|
||||||
|
font-size: $font-14px;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
color: $primary-fg-color;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
> p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_InviteDialog_footer {
|
||||||
|
border-top: 1px solid $input-border-color;
|
||||||
|
|
||||||
|
> h3 {
|
||||||
|
margin: 12px 0;
|
||||||
|
font-size: $font-12px;
|
||||||
|
color: $muted-fg-color;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_InviteDialog_footer_link {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: solid 1px $light-fg-color;
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
> a {
|
||||||
|
text-decoration: none;
|
||||||
|
flex-shrink: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_InviteDialog_footer_link_copy {
|
||||||
|
flex-shrink: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 20px;
|
||||||
|
display: inherit;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
mask-image: url($copy-button-url);
|
||||||
|
background-color: $message-action-bar-fg-color;
|
||||||
|
margin-left: 5px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mx_InviteDialog_roomTile {
|
.mx_InviteDialog_roomTile {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
@ -142,6 +210,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_InviteDialog_roomTile_nameStack {
|
.mx_InviteDialog_roomTile_nameStack {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_InviteDialog_roomTile_name {
|
.mx_InviteDialog_roomTile_name {
|
||||||
|
@ -157,6 +226,13 @@ limitations under the License.
|
||||||
margin-left: 7px;
|
margin-left: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_InviteDialog_roomTile_name,
|
||||||
|
.mx_InviteDialog_roomTile_userId {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_InviteDialog_roomTile_time {
|
.mx_InviteDialog_roomTile_time {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
|
@ -212,22 +288,29 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_InviteDialog {
|
.mx_InviteDialog {
|
||||||
// Prevent the dialog from jumping around randomly when elements change.
|
// Prevent the dialog from jumping around randomly when elements change.
|
||||||
height: 590px;
|
height: 600px;
|
||||||
padding-left: 20px; // the design wants some padding on the left
|
padding-left: 20px; // the design wants some padding on the left
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.mx_InviteDialog_content {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_InviteDialog_userSections {
|
.mx_InviteDialog_userSections {
|
||||||
margin-top: 10px;
|
margin-top: 4px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding-right: 45px;
|
padding: 0 45px 4px 0;
|
||||||
height: 455px; // mx_InviteDialog's height minus some for the upper elements
|
height: calc(100% - 115px); // mx_InviteDialog's height minus some for the upper and lower elements
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right margin for the design. We could apply this to the whole dialog, but then the scrollbar
|
.mx_InviteDialog_hasFooter .mx_InviteDialog_userSections {
|
||||||
// for the user section gets weird.
|
height: calc(100% - 175px);
|
||||||
.mx_InviteDialog_helpText,
|
}
|
||||||
.mx_InviteDialog_addressBar {
|
|
||||||
margin-right: 45px;
|
.mx_InviteDialog_helpText {
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_InviteDialog_helpText .mx_AccessibleButton_kind_link {
|
.mx_InviteDialog_helpText .mx_AccessibleButton_kind_link {
|
||||||
|
|
|
@ -50,7 +50,8 @@ limitations under the License.
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
display: inherit;
|
display: inherit;
|
||||||
}
|
}
|
||||||
.mx_ShareDialog_matrixto_copy > div {
|
.mx_ShareDialog_matrixto_copy::after {
|
||||||
|
content: "";
|
||||||
mask-image: url($copy-button-url);
|
mask-image: url($copy-button-url);
|
||||||
background-color: $message-action-bar-fg-color;
|
background-color: $message-action-bar-fg-color;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
|
|
@ -14,7 +14,13 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_SenderProfile_name {
|
.mx_SenderProfile_displayName {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_SenderProfile_mxid {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-left: 5px;
|
||||||
|
opacity: 0.5; // Match mx_TextualEvent
|
||||||
|
}
|
||||||
|
|
|
@ -32,4 +32,59 @@ limitations under the License.
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_PinnedMessagesCard_empty {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
height: max-content;
|
||||||
|
text-align: center;
|
||||||
|
margin: auto 40px;
|
||||||
|
|
||||||
|
.mx_PinnedMessagesCard_MessageActionBar {
|
||||||
|
pointer-events: none;
|
||||||
|
display: flex;
|
||||||
|
height: 32px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: $primary-bg-color;
|
||||||
|
border: 1px solid $input-border-color;
|
||||||
|
padding: 1px;
|
||||||
|
width: max-content;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.mx_MessageActionBar_maskButton {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MessageActionBar_optionsButton {
|
||||||
|
background: $roomlist-button-bg-color;
|
||||||
|
border-radius: 6px;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background-color: $primary-fg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> h2 {
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> span {
|
||||||
|
font-size: $font-12px;
|
||||||
|
line-height: $font-15px;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$left-gutter: 64px;
|
$left-gutter: 64px;
|
||||||
|
$hover-select-border: 4px;
|
||||||
|
|
||||||
.mx_EventTile {
|
.mx_EventTile {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
@ -142,8 +143,7 @@ $left-gutter: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_selected > div > a > .mx_MessageTimestamp {
|
.mx_EventTile_selected > div > a > .mx_MessageTimestamp {
|
||||||
left: 3px;
|
left: calc(-$hover-select-border);
|
||||||
width: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile:hover .mx_MessageActionBar,
|
.mx_EventTile:hover .mx_MessageActionBar,
|
||||||
|
@ -158,7 +158,7 @@ $left-gutter: 64px;
|
||||||
*/
|
*/
|
||||||
.mx_EventTile_selected > .mx_EventTile_line {
|
.mx_EventTile_selected > .mx_EventTile_line {
|
||||||
border-left: $accent-color 4px solid;
|
border-left: $accent-color 4px solid;
|
||||||
padding-left: 60px;
|
padding-left: calc($left-gutter - $hover-select-border);
|
||||||
background-color: $event-selected-color;
|
background-color: $event-selected-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,8 +171,12 @@ $left-gutter: 64px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_info .mx_EventTile_line {
|
||||||
|
padding-left: calc($left-gutter + 18px);
|
||||||
|
}
|
||||||
|
|
||||||
.mx_EventTile_selected.mx_EventTile_info .mx_EventTile_line {
|
.mx_EventTile_selected.mx_EventTile_info .mx_EventTile_line {
|
||||||
padding-left: 78px;
|
padding-left: calc($left-gutter + 18px - $hover-select-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile:hover .mx_EventTile_line,
|
.mx_EventTile:hover .mx_EventTile_line,
|
||||||
|
@ -408,7 +412,7 @@ $left-gutter: 64px;
|
||||||
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line,
|
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line,
|
||||||
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line,
|
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line,
|
||||||
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line {
|
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line {
|
||||||
padding-left: 60px;
|
padding-left: calc($left-gutter - $hover-select-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line {
|
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line {
|
||||||
|
@ -426,7 +430,7 @@ $left-gutter: 64px;
|
||||||
.mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line,
|
.mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line,
|
||||||
.mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line,
|
.mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line,
|
||||||
.mx_EventTile:hover.mx_EventTile_unknown.mx_EventTile_info .mx_EventTile_line {
|
.mx_EventTile:hover.mx_EventTile_unknown.mx_EventTile_info .mx_EventTile_line {
|
||||||
padding-left: 78px;
|
padding-left: calc($left-gutter + 18px - $hover-select-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End to end encryption stuff */
|
/* End to end encryption stuff */
|
||||||
|
@ -438,7 +442,7 @@ $left-gutter: 64px;
|
||||||
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp,
|
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp,
|
||||||
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp,
|
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp,
|
||||||
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > a > .mx_MessageTimestamp {
|
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > a > .mx_MessageTimestamp {
|
||||||
width: $MessageTimestamp_width_hover;
|
left: calc(-$hover-select-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
|
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
|
||||||
|
|
|
@ -24,10 +24,6 @@ $left-gutter: 64px;
|
||||||
margin-left: $left-gutter;
|
margin-left: $left-gutter;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .mx_EventTile_line {
|
|
||||||
padding-left: $left-gutter;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .mx_EventTile_avatar {
|
> .mx_EventTile_avatar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
@ -43,10 +39,6 @@ $left-gutter: 64px;
|
||||||
line-height: $font-22px;
|
line-height: $font-22px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_info .mx_EventTile_line {
|
|
||||||
padding-left: calc($left-gutter + 18px);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compact layout overrides */
|
/* Compact layout overrides */
|
||||||
|
|
|
@ -178,7 +178,7 @@ $irc-line-height: $font-18px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
> .mx_SenderProfile_name {
|
> .mx_SenderProfile_displayName {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
min-width: var(--name-width);
|
min-width: var(--name-width);
|
||||||
|
@ -207,7 +207,7 @@ $irc-line-height: $font-18px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
> .mx_SenderProfile_name {
|
> .mx_SenderProfile_displayName {
|
||||||
min-width: inherit;
|
min-width: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -542,6 +542,7 @@ export default class CallHandler extends EventEmitter {
|
||||||
if (newMappedRoomId !== mappedRoomId) {
|
if (newMappedRoomId !== mappedRoomId) {
|
||||||
this.removeCallForRoom(mappedRoomId);
|
this.removeCallForRoom(mappedRoomId);
|
||||||
mappedRoomId = newMappedRoomId;
|
mappedRoomId = newMappedRoomId;
|
||||||
|
console.log("Moving call to room " + mappedRoomId);
|
||||||
this.calls.set(mappedRoomId, call);
|
this.calls.set(mappedRoomId, call);
|
||||||
this.emit(CallHandlerEvent.CallChangeRoom, call);
|
this.emit(CallHandlerEvent.CallChangeRoom, call);
|
||||||
}
|
}
|
||||||
|
@ -607,6 +608,7 @@ export default class CallHandler extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeCallForRoom(roomId: string) {
|
private removeCallForRoom(roomId: string) {
|
||||||
|
console.log("Removing call for room ", roomId);
|
||||||
this.calls.delete(roomId);
|
this.calls.delete(roomId);
|
||||||
this.emit(CallHandlerEvent.CallsChanged, this.calls);
|
this.emit(CallHandlerEvent.CallsChanged, this.calls);
|
||||||
}
|
}
|
||||||
|
@ -680,6 +682,7 @@ export default class CallHandler extends EventEmitter {
|
||||||
console.log("Current turn creds expire in " + timeUntilTurnCresExpire + " ms");
|
console.log("Current turn creds expire in " + timeUntilTurnCresExpire + " ms");
|
||||||
const call = MatrixClientPeg.get().createCall(mappedRoomId);
|
const call = MatrixClientPeg.get().createCall(mappedRoomId);
|
||||||
|
|
||||||
|
console.log("Adding call for room ", roomId);
|
||||||
this.calls.set(roomId, call);
|
this.calls.set(roomId, call);
|
||||||
this.emit(CallHandlerEvent.CallsChanged, this.calls);
|
this.emit(CallHandlerEvent.CallsChanged, this.calls);
|
||||||
if (transferee) {
|
if (transferee) {
|
||||||
|
@ -812,6 +815,7 @@ export default class CallHandler extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
Analytics.trackEvent('voip', 'receiveCall', 'type', call.type);
|
Analytics.trackEvent('voip', 'receiveCall', 'type', call.type);
|
||||||
|
console.log("Adding call for room ", mappedRoomId);
|
||||||
this.calls.set(mappedRoomId, call)
|
this.calls.set(mappedRoomId, call)
|
||||||
this.emit(CallHandlerEvent.CallsChanged, this.calls);
|
this.emit(CallHandlerEvent.CallsChanged, this.calls);
|
||||||
this.setCallListeners(call);
|
this.setCallListeners(call);
|
||||||
|
|
|
@ -28,8 +28,6 @@ import encrypt from "browser-encrypt-attachment";
|
||||||
import extractPngChunks from "png-chunks-extract";
|
import extractPngChunks from "png-chunks-extract";
|
||||||
import Spinner from "./components/views/elements/Spinner";
|
import Spinner from "./components/views/elements/Spinner";
|
||||||
|
|
||||||
// Polyfill for Canvas.toBlob API using Canvas.toDataURL
|
|
||||||
import "blueimp-canvas-to-blob";
|
|
||||||
import { Action } from "./dispatcher/actions";
|
import { Action } from "./dispatcher/actions";
|
||||||
import CountlyAnalytics from "./CountlyAnalytics";
|
import CountlyAnalytics from "./CountlyAnalytics";
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -24,13 +24,6 @@ import {sleep} from "./utils/promise";
|
||||||
import RoomViewStore from "./stores/RoomViewStore";
|
import RoomViewStore from "./stores/RoomViewStore";
|
||||||
import { Action } from "./dispatcher/actions";
|
import { Action } from "./dispatcher/actions";
|
||||||
|
|
||||||
// polyfill textencoder if necessary
|
|
||||||
import * as TextEncodingUtf8 from 'text-encoding-utf-8';
|
|
||||||
let TextEncoder = window.TextEncoder;
|
|
||||||
if (!TextEncoder) {
|
|
||||||
TextEncoder = TextEncodingUtf8.TextEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
const INACTIVITY_TIME = 20; // seconds
|
const INACTIVITY_TIME = 20; // seconds
|
||||||
const HEARTBEAT_INTERVAL = 5_000; // ms
|
const HEARTBEAT_INTERVAL = 5_000; // ms
|
||||||
const SESSION_UPDATE_INTERVAL = 60; // seconds
|
const SESSION_UPDATE_INTERVAL = 60; // seconds
|
||||||
|
|
|
@ -21,153 +21,161 @@ import SettingsStore from "./settings/SettingsStore";
|
||||||
import {ALL_RULE_TYPES, ROOM_RULE_TYPES, SERVER_RULE_TYPES, USER_RULE_TYPES} from "./mjolnir/BanList";
|
import {ALL_RULE_TYPES, ROOM_RULE_TYPES, SERVER_RULE_TYPES, USER_RULE_TYPES} from "./mjolnir/BanList";
|
||||||
import {WIDGET_LAYOUT_EVENT_TYPE} from "./stores/widgets/WidgetLayoutStore";
|
import {WIDGET_LAYOUT_EVENT_TYPE} from "./stores/widgets/WidgetLayoutStore";
|
||||||
|
|
||||||
function textForMemberEvent(ev) {
|
// These functions are frequently used just to check whether an event has
|
||||||
|
// any text to display at all. For this reason they return deferred values
|
||||||
|
// to avoid the expense of looking up translations when they're not needed.
|
||||||
|
|
||||||
|
function textForMemberEvent(ev): () => string | null {
|
||||||
// XXX: SYJS-16 "sender is sometimes null for join messages"
|
// XXX: SYJS-16 "sender is sometimes null for join messages"
|
||||||
const senderName = ev.sender ? ev.sender.name : ev.getSender();
|
const senderName = ev.sender ? ev.sender.name : ev.getSender();
|
||||||
const targetName = ev.target ? ev.target.name : ev.getStateKey();
|
const targetName = ev.target ? ev.target.name : ev.getStateKey();
|
||||||
const prevContent = ev.getPrevContent();
|
const prevContent = ev.getPrevContent();
|
||||||
const content = ev.getContent();
|
const content = ev.getContent();
|
||||||
|
|
||||||
const reason = content.reason ? (_t('Reason') + ': ' + content.reason) : '';
|
const getReason = () => content.reason ? (_t('Reason') + ': ' + content.reason) : '';
|
||||||
switch (content.membership) {
|
switch (content.membership) {
|
||||||
case 'invite': {
|
case 'invite': {
|
||||||
const threePidContent = content.third_party_invite;
|
const threePidContent = content.third_party_invite;
|
||||||
if (threePidContent) {
|
if (threePidContent) {
|
||||||
if (threePidContent.display_name) {
|
if (threePidContent.display_name) {
|
||||||
return _t('%(targetName)s accepted the invitation for %(displayName)s.', {
|
return () => _t('%(targetName)s accepted the invitation for %(displayName)s.', {
|
||||||
targetName,
|
targetName,
|
||||||
displayName: threePidContent.display_name,
|
displayName: threePidContent.display_name,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return _t('%(targetName)s accepted an invitation.', {targetName});
|
return () => _t('%(targetName)s accepted an invitation.', {targetName});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return _t('%(senderName)s invited %(targetName)s.', {senderName, targetName});
|
return () => _t('%(senderName)s invited %(targetName)s.', {senderName, targetName});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'ban':
|
case 'ban':
|
||||||
return _t('%(senderName)s banned %(targetName)s.', {senderName, targetName}) + ' ' + reason;
|
return () => _t('%(senderName)s banned %(targetName)s.', {senderName, targetName}) + ' ' + getReason();
|
||||||
case 'join':
|
case 'join':
|
||||||
if (prevContent && prevContent.membership === 'join') {
|
if (prevContent && prevContent.membership === 'join') {
|
||||||
if (prevContent.displayname && content.displayname && prevContent.displayname !== content.displayname) {
|
if (prevContent.displayname && content.displayname && prevContent.displayname !== content.displayname) {
|
||||||
return _t('%(oldDisplayName)s changed their display name to %(displayName)s.', {
|
return () => _t('%(oldDisplayName)s changed their display name to %(displayName)s.', {
|
||||||
oldDisplayName: prevContent.displayname,
|
oldDisplayName: prevContent.displayname,
|
||||||
displayName: content.displayname,
|
displayName: content.displayname,
|
||||||
});
|
});
|
||||||
} else if (!prevContent.displayname && content.displayname) {
|
} else if (!prevContent.displayname && content.displayname) {
|
||||||
return _t('%(senderName)s set their display name to %(displayName)s.', {
|
return () => _t('%(senderName)s set their display name to %(displayName)s.', {
|
||||||
senderName: ev.getSender(),
|
senderName: ev.getSender(),
|
||||||
displayName: content.displayname,
|
displayName: content.displayname,
|
||||||
});
|
});
|
||||||
} else if (prevContent.displayname && !content.displayname) {
|
} else if (prevContent.displayname && !content.displayname) {
|
||||||
return _t('%(senderName)s removed their display name (%(oldDisplayName)s).', {
|
return () => _t('%(senderName)s removed their display name (%(oldDisplayName)s).', {
|
||||||
senderName,
|
senderName,
|
||||||
oldDisplayName: prevContent.displayname,
|
oldDisplayName: prevContent.displayname,
|
||||||
});
|
});
|
||||||
} else if (prevContent.avatar_url && !content.avatar_url) {
|
} else if (prevContent.avatar_url && !content.avatar_url) {
|
||||||
return _t('%(senderName)s removed their profile picture.', {senderName});
|
return () => _t('%(senderName)s removed their profile picture.', {senderName});
|
||||||
} else if (prevContent.avatar_url && content.avatar_url &&
|
} else if (prevContent.avatar_url && content.avatar_url &&
|
||||||
prevContent.avatar_url !== content.avatar_url) {
|
prevContent.avatar_url !== content.avatar_url) {
|
||||||
return _t('%(senderName)s changed their profile picture.', {senderName});
|
return () => _t('%(senderName)s changed their profile picture.', {senderName});
|
||||||
} else if (!prevContent.avatar_url && content.avatar_url) {
|
} else if (!prevContent.avatar_url && content.avatar_url) {
|
||||||
return _t('%(senderName)s set a profile picture.', {senderName});
|
return () => _t('%(senderName)s set a profile picture.', {senderName});
|
||||||
} else if (SettingsStore.getValue("showHiddenEventsInTimeline")) {
|
} else if (SettingsStore.getValue("showHiddenEventsInTimeline")) {
|
||||||
// This is a null rejoin, it will only be visible if the Labs option is enabled
|
// This is a null rejoin, it will only be visible if the Labs option is enabled
|
||||||
return _t("%(senderName)s made no change.", {senderName});
|
return () => _t("%(senderName)s made no change.", {senderName});
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!ev.target) console.warn("Join message has no target! -- " + ev.getContent().state_key);
|
if (!ev.target) console.warn("Join message has no target! -- " + ev.getContent().state_key);
|
||||||
return _t('%(targetName)s joined the room.', {targetName});
|
return () => _t('%(targetName)s joined the room.', {targetName});
|
||||||
}
|
}
|
||||||
case 'leave':
|
case 'leave':
|
||||||
if (ev.getSender() === ev.getStateKey()) {
|
if (ev.getSender() === ev.getStateKey()) {
|
||||||
if (prevContent.membership === "invite") {
|
if (prevContent.membership === "invite") {
|
||||||
return _t('%(targetName)s rejected the invitation.', {targetName});
|
return () => _t('%(targetName)s rejected the invitation.', {targetName});
|
||||||
} else {
|
} else {
|
||||||
return _t('%(targetName)s left the room.', {targetName});
|
return () => _t('%(targetName)s left the room.', {targetName});
|
||||||
}
|
}
|
||||||
} else if (prevContent.membership === "ban") {
|
} else if (prevContent.membership === "ban") {
|
||||||
return _t('%(senderName)s unbanned %(targetName)s.', {senderName, targetName});
|
return () => _t('%(senderName)s unbanned %(targetName)s.', {senderName, targetName});
|
||||||
} else if (prevContent.membership === "invite") {
|
} else if (prevContent.membership === "invite") {
|
||||||
return _t('%(senderName)s withdrew %(targetName)s\'s invitation.', {
|
return () => _t('%(senderName)s withdrew %(targetName)s\'s invitation.', {
|
||||||
senderName,
|
senderName,
|
||||||
targetName,
|
targetName,
|
||||||
}) + ' ' + reason;
|
}) + ' ' + getReason();
|
||||||
} else if (prevContent.membership === "join") {
|
} else if (prevContent.membership === "join") {
|
||||||
return _t('%(senderName)s kicked %(targetName)s.', {senderName, targetName}) + ' ' + reason;
|
return () => _t('%(senderName)s kicked %(targetName)s.', {senderName, targetName}) + ' ' + getReason();
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForTopicEvent(ev) {
|
function textForTopicEvent(ev): () => string | null {
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
return _t('%(senderDisplayName)s changed the topic to "%(topic)s".', {
|
return () => _t('%(senderDisplayName)s changed the topic to "%(topic)s".', {
|
||||||
senderDisplayName,
|
senderDisplayName,
|
||||||
topic: ev.getContent().topic,
|
topic: ev.getContent().topic,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForRoomNameEvent(ev) {
|
function textForRoomNameEvent(ev): () => string | null {
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
|
|
||||||
if (!ev.getContent().name || ev.getContent().name.trim().length === 0) {
|
if (!ev.getContent().name || ev.getContent().name.trim().length === 0) {
|
||||||
return _t('%(senderDisplayName)s removed the room name.', {senderDisplayName});
|
return () => _t('%(senderDisplayName)s removed the room name.', {senderDisplayName});
|
||||||
}
|
}
|
||||||
if (ev.getPrevContent().name) {
|
if (ev.getPrevContent().name) {
|
||||||
return _t('%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.', {
|
return () => _t('%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.', {
|
||||||
senderDisplayName,
|
senderDisplayName,
|
||||||
oldRoomName: ev.getPrevContent().name,
|
oldRoomName: ev.getPrevContent().name,
|
||||||
newRoomName: ev.getContent().name,
|
newRoomName: ev.getContent().name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return _t('%(senderDisplayName)s changed the room name to %(roomName)s.', {
|
return () => _t('%(senderDisplayName)s changed the room name to %(roomName)s.', {
|
||||||
senderDisplayName,
|
senderDisplayName,
|
||||||
roomName: ev.getContent().name,
|
roomName: ev.getContent().name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForTombstoneEvent(ev) {
|
function textForTombstoneEvent(ev): () => string | null {
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
return _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName});
|
return () => _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName});
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForJoinRulesEvent(ev) {
|
function textForJoinRulesEvent(ev): () => string | null {
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
switch (ev.getContent().join_rule) {
|
switch (ev.getContent().join_rule) {
|
||||||
case "public":
|
case "public":
|
||||||
return _t('%(senderDisplayName)s made the room public to whoever knows the link.', {senderDisplayName});
|
return () => _t('%(senderDisplayName)s made the room public to whoever knows the link.', {
|
||||||
|
senderDisplayName,
|
||||||
|
});
|
||||||
case "invite":
|
case "invite":
|
||||||
return _t('%(senderDisplayName)s made the room invite only.', {senderDisplayName});
|
return () => _t('%(senderDisplayName)s made the room invite only.', {
|
||||||
|
senderDisplayName,
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
// The spec supports "knock" and "private", however nothing implements these.
|
// The spec supports "knock" and "private", however nothing implements these.
|
||||||
return _t('%(senderDisplayName)s changed the join rule to %(rule)s', {
|
return () => _t('%(senderDisplayName)s changed the join rule to %(rule)s', {
|
||||||
senderDisplayName,
|
senderDisplayName,
|
||||||
rule: ev.getContent().join_rule,
|
rule: ev.getContent().join_rule,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForGuestAccessEvent(ev) {
|
function textForGuestAccessEvent(ev): () => string | null {
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
switch (ev.getContent().guest_access) {
|
switch (ev.getContent().guest_access) {
|
||||||
case "can_join":
|
case "can_join":
|
||||||
return _t('%(senderDisplayName)s has allowed guests to join the room.', {senderDisplayName});
|
return () => _t('%(senderDisplayName)s has allowed guests to join the room.', {senderDisplayName});
|
||||||
case "forbidden":
|
case "forbidden":
|
||||||
return _t('%(senderDisplayName)s has prevented guests from joining the room.', {senderDisplayName});
|
return () => _t('%(senderDisplayName)s has prevented guests from joining the room.', {senderDisplayName});
|
||||||
default:
|
default:
|
||||||
// There's no other options we can expect, however just for safety's sake we'll do this.
|
// There's no other options we can expect, however just for safety's sake we'll do this.
|
||||||
return _t('%(senderDisplayName)s changed guest access to %(rule)s', {
|
return () => _t('%(senderDisplayName)s changed guest access to %(rule)s', {
|
||||||
senderDisplayName,
|
senderDisplayName,
|
||||||
rule: ev.getContent().guest_access,
|
rule: ev.getContent().guest_access,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForRelatedGroupsEvent(ev) {
|
function textForRelatedGroupsEvent(ev): () => string | null {
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
const groups = ev.getContent().groups || [];
|
const groups = ev.getContent().groups || [];
|
||||||
const prevGroups = ev.getPrevContent().groups || [];
|
const prevGroups = ev.getPrevContent().groups || [];
|
||||||
|
@ -175,17 +183,17 @@ function textForRelatedGroupsEvent(ev) {
|
||||||
const removed = prevGroups.filter((g) => !groups.includes(g));
|
const removed = prevGroups.filter((g) => !groups.includes(g));
|
||||||
|
|
||||||
if (added.length && !removed.length) {
|
if (added.length && !removed.length) {
|
||||||
return _t('%(senderDisplayName)s enabled flair for %(groups)s in this room.', {
|
return () => _t('%(senderDisplayName)s enabled flair for %(groups)s in this room.', {
|
||||||
senderDisplayName,
|
senderDisplayName,
|
||||||
groups: added.join(', '),
|
groups: added.join(', '),
|
||||||
});
|
});
|
||||||
} else if (!added.length && removed.length) {
|
} else if (!added.length && removed.length) {
|
||||||
return _t('%(senderDisplayName)s disabled flair for %(groups)s in this room.', {
|
return () => _t('%(senderDisplayName)s disabled flair for %(groups)s in this room.', {
|
||||||
senderDisplayName,
|
senderDisplayName,
|
||||||
groups: removed.join(', '),
|
groups: removed.join(', '),
|
||||||
});
|
});
|
||||||
} else if (added.length && removed.length) {
|
} else if (added.length && removed.length) {
|
||||||
return _t('%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for ' +
|
return () => _t('%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for ' +
|
||||||
'%(oldGroups)s in this room.', {
|
'%(oldGroups)s in this room.', {
|
||||||
senderDisplayName,
|
senderDisplayName,
|
||||||
newGroups: added.join(', '),
|
newGroups: added.join(', '),
|
||||||
|
@ -193,11 +201,11 @@ function textForRelatedGroupsEvent(ev) {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Don't bother rendering this change (because there were no changes)
|
// Don't bother rendering this change (because there were no changes)
|
||||||
return '';
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForServerACLEvent(ev) {
|
function textForServerACLEvent(ev): () => string | null {
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
const prevContent = ev.getPrevContent();
|
const prevContent = ev.getPrevContent();
|
||||||
const current = ev.getContent();
|
const current = ev.getContent();
|
||||||
|
@ -207,11 +215,11 @@ function textForServerACLEvent(ev) {
|
||||||
allow_ip_literals: !(prevContent.allow_ip_literals === false),
|
allow_ip_literals: !(prevContent.allow_ip_literals === false),
|
||||||
};
|
};
|
||||||
|
|
||||||
let text = "";
|
let getText = null;
|
||||||
if (prev.deny.length === 0 && prev.allow.length === 0) {
|
if (prev.deny.length === 0 && prev.allow.length === 0) {
|
||||||
text = _t("%(senderDisplayName)s set the server ACLs for this room.", {senderDisplayName});
|
getText = () => _t("%(senderDisplayName)s set the server ACLs for this room.", {senderDisplayName});
|
||||||
} else {
|
} else {
|
||||||
text = _t("%(senderDisplayName)s changed the server ACLs for this room.", {senderDisplayName});
|
getText = () => _t("%(senderDisplayName)s changed the server ACLs for this room.", {senderDisplayName});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(current.allow)) {
|
if (!Array.isArray(current.allow)) {
|
||||||
|
@ -220,24 +228,27 @@ function textForServerACLEvent(ev) {
|
||||||
|
|
||||||
// If we know for sure everyone is banned, mark the room as obliterated
|
// If we know for sure everyone is banned, mark the room as obliterated
|
||||||
if (current.allow.length === 0) {
|
if (current.allow.length === 0) {
|
||||||
return text + " " + _t("🎉 All servers are banned from participating! This room can no longer be used.");
|
return () => getText() + " " +
|
||||||
|
_t("🎉 All servers are banned from participating! This room can no longer be used.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return getText;
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForMessageEvent(ev) {
|
function textForMessageEvent(ev): () => string | null {
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
return () => {
|
||||||
let message = senderDisplayName + ': ' + ev.getContent().body;
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
if (ev.getContent().msgtype === "m.emote") {
|
let message = senderDisplayName + ': ' + ev.getContent().body;
|
||||||
message = "* " + senderDisplayName + " " + message;
|
if (ev.getContent().msgtype === "m.emote") {
|
||||||
} else if (ev.getContent().msgtype === "m.image") {
|
message = "* " + senderDisplayName + " " + message;
|
||||||
message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName});
|
} else if (ev.getContent().msgtype === "m.image") {
|
||||||
}
|
message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName});
|
||||||
return message;
|
}
|
||||||
|
return message;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForCanonicalAliasEvent(ev) {
|
function textForCanonicalAliasEvent(ev): () => string | null {
|
||||||
const senderName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
const oldAlias = ev.getPrevContent().alias;
|
const oldAlias = ev.getPrevContent().alias;
|
||||||
const oldAltAliases = ev.getPrevContent().alt_aliases || [];
|
const oldAltAliases = ev.getPrevContent().alt_aliases || [];
|
||||||
|
@ -248,96 +259,100 @@ function textForCanonicalAliasEvent(ev) {
|
||||||
|
|
||||||
if (!removedAltAliases.length && !addedAltAliases.length) {
|
if (!removedAltAliases.length && !addedAltAliases.length) {
|
||||||
if (newAlias) {
|
if (newAlias) {
|
||||||
return _t('%(senderName)s set the main address for this room to %(address)s.', {
|
return () => _t('%(senderName)s set the main address for this room to %(address)s.', {
|
||||||
senderName: senderName,
|
senderName: senderName,
|
||||||
address: ev.getContent().alias,
|
address: ev.getContent().alias,
|
||||||
});
|
});
|
||||||
} else if (oldAlias) {
|
} else if (oldAlias) {
|
||||||
return _t('%(senderName)s removed the main address for this room.', {
|
return () => _t('%(senderName)s removed the main address for this room.', {
|
||||||
senderName: senderName,
|
senderName: senderName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (newAlias === oldAlias) {
|
} else if (newAlias === oldAlias) {
|
||||||
if (addedAltAliases.length && !removedAltAliases.length) {
|
if (addedAltAliases.length && !removedAltAliases.length) {
|
||||||
return _t('%(senderName)s added the alternative addresses %(addresses)s for this room.', {
|
return () => _t('%(senderName)s added the alternative addresses %(addresses)s for this room.', {
|
||||||
senderName: senderName,
|
senderName: senderName,
|
||||||
addresses: addedAltAliases.join(", "),
|
addresses: addedAltAliases.join(", "),
|
||||||
count: addedAltAliases.length,
|
count: addedAltAliases.length,
|
||||||
});
|
});
|
||||||
} if (removedAltAliases.length && !addedAltAliases.length) {
|
} if (removedAltAliases.length && !addedAltAliases.length) {
|
||||||
return _t('%(senderName)s removed the alternative addresses %(addresses)s for this room.', {
|
return () => _t('%(senderName)s removed the alternative addresses %(addresses)s for this room.', {
|
||||||
senderName: senderName,
|
senderName: senderName,
|
||||||
addresses: removedAltAliases.join(", "),
|
addresses: removedAltAliases.join(", "),
|
||||||
count: removedAltAliases.length,
|
count: removedAltAliases.length,
|
||||||
});
|
});
|
||||||
} if (removedAltAliases.length && addedAltAliases.length) {
|
} if (removedAltAliases.length && addedAltAliases.length) {
|
||||||
return _t('%(senderName)s changed the alternative addresses for this room.', {
|
return () => _t('%(senderName)s changed the alternative addresses for this room.', {
|
||||||
senderName: senderName,
|
senderName: senderName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// both alias and alt_aliases where modified
|
// both alias and alt_aliases where modified
|
||||||
return _t('%(senderName)s changed the main and alternative addresses for this room.', {
|
return () => _t('%(senderName)s changed the main and alternative addresses for this room.', {
|
||||||
senderName: senderName,
|
senderName: senderName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// in case there is no difference between the two events,
|
// in case there is no difference between the two events,
|
||||||
// say something as we can't simply hide the tile from here
|
// say something as we can't simply hide the tile from here
|
||||||
return _t('%(senderName)s changed the addresses for this room.', {
|
return () => _t('%(senderName)s changed the addresses for this room.', {
|
||||||
senderName: senderName,
|
senderName: senderName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForCallAnswerEvent(event) {
|
function textForCallAnswerEvent(event): () => string | null {
|
||||||
const senderName = event.sender ? event.sender.name : _t('Someone');
|
return () => {
|
||||||
const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)');
|
const senderName = event.sender ? event.sender.name : _t('Someone');
|
||||||
return _t('%(senderName)s answered the call.', {senderName}) + ' ' + supported;
|
const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)');
|
||||||
|
return _t('%(senderName)s answered the call.', {senderName}) + ' ' + supported;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForCallHangupEvent(event) {
|
function textForCallHangupEvent(event): () => string | null {
|
||||||
const senderName = event.sender ? event.sender.name : _t('Someone');
|
const getSenderName = () => event.sender ? event.sender.name : _t('Someone');
|
||||||
const eventContent = event.getContent();
|
const eventContent = event.getContent();
|
||||||
let reason = "";
|
let getReason = () => "";
|
||||||
if (!MatrixClientPeg.get().supportsVoip()) {
|
if (!MatrixClientPeg.get().supportsVoip()) {
|
||||||
reason = _t('(not supported by this browser)');
|
getReason = () => _t('(not supported by this browser)');
|
||||||
} else if (eventContent.reason) {
|
} else if (eventContent.reason) {
|
||||||
if (eventContent.reason === "ice_failed") {
|
if (eventContent.reason === "ice_failed") {
|
||||||
// We couldn't establish a connection at all
|
// We couldn't establish a connection at all
|
||||||
reason = _t('(could not connect media)');
|
getReason = () => _t('(could not connect media)');
|
||||||
} else if (eventContent.reason === "ice_timeout") {
|
} else if (eventContent.reason === "ice_timeout") {
|
||||||
// We established a connection but it died
|
// We established a connection but it died
|
||||||
reason = _t('(connection failed)');
|
getReason = () => _t('(connection failed)');
|
||||||
} else if (eventContent.reason === "user_media_failed") {
|
} else if (eventContent.reason === "user_media_failed") {
|
||||||
// The other side couldn't open capture devices
|
// The other side couldn't open capture devices
|
||||||
reason = _t("(their device couldn't start the camera / microphone)");
|
getReason = () => _t("(their device couldn't start the camera / microphone)");
|
||||||
} else if (eventContent.reason === "unknown_error") {
|
} else if (eventContent.reason === "unknown_error") {
|
||||||
// An error code the other side doesn't have a way to express
|
// An error code the other side doesn't have a way to express
|
||||||
// (as opposed to an error code they gave but we don't know about,
|
// (as opposed to an error code they gave but we don't know about,
|
||||||
// in which case we show the error code)
|
// in which case we show the error code)
|
||||||
reason = _t("(an error occurred)");
|
getReason = () => _t("(an error occurred)");
|
||||||
} else if (eventContent.reason === "invite_timeout") {
|
} else if (eventContent.reason === "invite_timeout") {
|
||||||
reason = _t('(no answer)');
|
getReason = () => _t('(no answer)');
|
||||||
} else if (eventContent.reason === "user hangup" || eventContent.reason === "user_hangup") {
|
} else if (eventContent.reason === "user hangup" || eventContent.reason === "user_hangup") {
|
||||||
// workaround for https://github.com/vector-im/element-web/issues/5178
|
// workaround for https://github.com/vector-im/element-web/issues/5178
|
||||||
// it seems Android randomly sets a reason of "user hangup" which is
|
// it seems Android randomly sets a reason of "user hangup" which is
|
||||||
// interpreted as an error code :(
|
// interpreted as an error code :(
|
||||||
// https://github.com/vector-im/riot-android/issues/2623
|
// https://github.com/vector-im/riot-android/issues/2623
|
||||||
// Also the correct hangup code as of VoIP v1 (with underscore)
|
// Also the correct hangup code as of VoIP v1 (with underscore)
|
||||||
reason = '';
|
getReason = () => '';
|
||||||
} else {
|
} else {
|
||||||
reason = _t('(unknown failure: %(reason)s)', {reason: eventContent.reason});
|
getReason = () => _t('(unknown failure: %(reason)s)', {reason: eventContent.reason});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _t('%(senderName)s ended the call.', {senderName}) + ' ' + reason;
|
return () => _t('%(senderName)s ended the call.', {senderName: getSenderName()}) + ' ' + getReason();
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForCallRejectEvent(event) {
|
function textForCallRejectEvent(event): () => string | null {
|
||||||
const senderName = event.sender ? event.sender.name : _t('Someone');
|
return () => {
|
||||||
return _t('%(senderName)s declined the call.', {senderName});
|
const senderName = event.sender ? event.sender.name : _t('Someone');
|
||||||
|
return _t('%(senderName)s declined the call.', {senderName});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForCallInviteEvent(event) {
|
function textForCallInviteEvent(event): () => string | null {
|
||||||
const senderName = event.sender ? event.sender.name : _t('Someone');
|
const getSenderName = () => event.sender ? event.sender.name : _t('Someone');
|
||||||
// FIXME: Find a better way to determine this from the event?
|
// FIXME: Find a better way to determine this from the event?
|
||||||
let isVoice = true;
|
let isVoice = true;
|
||||||
if (event.getContent().offer && event.getContent().offer.sdp &&
|
if (event.getContent().offer && event.getContent().offer.sdp &&
|
||||||
|
@ -350,48 +365,55 @@ function textForCallInviteEvent(event) {
|
||||||
// can have a hard time translating those strings. In an effort to make translations easier
|
// can have a hard time translating those strings. In an effort to make translations easier
|
||||||
// and more accurate, we break out the string-based variables to a couple booleans.
|
// and more accurate, we break out the string-based variables to a couple booleans.
|
||||||
if (isVoice && isSupported) {
|
if (isVoice && isSupported) {
|
||||||
return _t("%(senderName)s placed a voice call.", {senderName});
|
return () => _t("%(senderName)s placed a voice call.", {
|
||||||
|
senderName: getSenderName(),
|
||||||
|
});
|
||||||
} else if (isVoice && !isSupported) {
|
} else if (isVoice && !isSupported) {
|
||||||
return _t("%(senderName)s placed a voice call. (not supported by this browser)", {senderName});
|
return () => _t("%(senderName)s placed a voice call. (not supported by this browser)", {
|
||||||
|
senderName: getSenderName(),
|
||||||
|
});
|
||||||
} else if (!isVoice && isSupported) {
|
} else if (!isVoice && isSupported) {
|
||||||
return _t("%(senderName)s placed a video call.", {senderName});
|
return () => _t("%(senderName)s placed a video call.", {
|
||||||
|
senderName: getSenderName(),
|
||||||
|
});
|
||||||
} else if (!isVoice && !isSupported) {
|
} else if (!isVoice && !isSupported) {
|
||||||
return _t("%(senderName)s placed a video call. (not supported by this browser)", {senderName});
|
return () => _t("%(senderName)s placed a video call. (not supported by this browser)", {
|
||||||
|
senderName: getSenderName(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForThreePidInviteEvent(event) {
|
function textForThreePidInviteEvent(event): () => string | null {
|
||||||
const senderName = event.sender ? event.sender.name : event.getSender();
|
const senderName = event.sender ? event.sender.name : event.getSender();
|
||||||
|
|
||||||
if (!isValid3pidInvite(event)) {
|
if (!isValid3pidInvite(event)) {
|
||||||
const targetDisplayName = event.getPrevContent().display_name || _t("Someone");
|
return () => _t('%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.', {
|
||||||
return _t('%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.', {
|
|
||||||
senderName,
|
senderName,
|
||||||
targetDisplayName,
|
targetDisplayName: event.getPrevContent().display_name || _t("Someone"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.', {
|
return () => _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.', {
|
||||||
senderName,
|
senderName,
|
||||||
targetDisplayName: event.getContent().display_name,
|
targetDisplayName: event.getContent().display_name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForHistoryVisibilityEvent(event) {
|
function textForHistoryVisibilityEvent(event): () => string | null {
|
||||||
const senderName = event.sender ? event.sender.name : event.getSender();
|
const senderName = event.sender ? event.sender.name : event.getSender();
|
||||||
switch (event.getContent().history_visibility) {
|
switch (event.getContent().history_visibility) {
|
||||||
case 'invited':
|
case 'invited':
|
||||||
return _t('%(senderName)s made future room history visible to all room members, '
|
return () => _t('%(senderName)s made future room history visible to all room members, '
|
||||||
+ 'from the point they are invited.', {senderName});
|
+ 'from the point they are invited.', {senderName});
|
||||||
case 'joined':
|
case 'joined':
|
||||||
return _t('%(senderName)s made future room history visible to all room members, '
|
return () => _t('%(senderName)s made future room history visible to all room members, '
|
||||||
+ 'from the point they joined.', {senderName});
|
+ 'from the point they joined.', {senderName});
|
||||||
case 'shared':
|
case 'shared':
|
||||||
return _t('%(senderName)s made future room history visible to all room members.', {senderName});
|
return () => _t('%(senderName)s made future room history visible to all room members.', {senderName});
|
||||||
case 'world_readable':
|
case 'world_readable':
|
||||||
return _t('%(senderName)s made future room history visible to anyone.', {senderName});
|
return () => _t('%(senderName)s made future room history visible to anyone.', {senderName});
|
||||||
default:
|
default:
|
||||||
return _t('%(senderName)s made future room history visible to unknown (%(visibility)s).', {
|
return () => _t('%(senderName)s made future room history visible to unknown (%(visibility)s).', {
|
||||||
senderName,
|
senderName,
|
||||||
visibility: event.getContent().history_visibility,
|
visibility: event.getContent().history_visibility,
|
||||||
});
|
});
|
||||||
|
@ -399,11 +421,11 @@ function textForHistoryVisibilityEvent(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently will only display a change if a user's power level is changed
|
// Currently will only display a change if a user's power level is changed
|
||||||
function textForPowerEvent(event) {
|
function textForPowerEvent(event): () => string | null {
|
||||||
const senderName = event.sender ? event.sender.name : event.getSender();
|
const senderName = event.sender ? event.sender.name : event.getSender();
|
||||||
if (!event.getPrevContent() || !event.getPrevContent().users ||
|
if (!event.getPrevContent() || !event.getPrevContent().users ||
|
||||||
!event.getContent() || !event.getContent().users) {
|
!event.getContent() || !event.getContent().users) {
|
||||||
return '';
|
return null;
|
||||||
}
|
}
|
||||||
const userDefault = event.getContent().users_default || 0;
|
const userDefault = event.getContent().users_default || 0;
|
||||||
// Construct set of userIds
|
// Construct set of userIds
|
||||||
|
@ -418,38 +440,38 @@ function textForPowerEvent(event) {
|
||||||
if (users.indexOf(userId) === -1) users.push(userId);
|
if (users.indexOf(userId) === -1) users.push(userId);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const diff = [];
|
const diffs = [];
|
||||||
// XXX: This is also surely broken for i18n
|
|
||||||
users.forEach((userId) => {
|
users.forEach((userId) => {
|
||||||
// Previous power level
|
// Previous power level
|
||||||
const from = event.getPrevContent().users[userId];
|
const from = event.getPrevContent().users[userId];
|
||||||
// Current power level
|
// Current power level
|
||||||
const to = event.getContent().users[userId];
|
const to = event.getContent().users[userId];
|
||||||
if (to !== from) {
|
if (to !== from) {
|
||||||
diff.push(
|
diffs.push({ userId, from, to });
|
||||||
_t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', {
|
|
||||||
userId,
|
|
||||||
fromPowerLevel: Roles.textualPowerLevel(from, userDefault),
|
|
||||||
toPowerLevel: Roles.textualPowerLevel(to, userDefault),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!diff.length) {
|
if (!diffs.length) {
|
||||||
return '';
|
return null;
|
||||||
}
|
}
|
||||||
return _t('%(senderName)s changed the power level of %(powerLevelDiffText)s.', {
|
// XXX: This is also surely broken for i18n
|
||||||
|
return () => _t('%(senderName)s changed the power level of %(powerLevelDiffText)s.', {
|
||||||
senderName,
|
senderName,
|
||||||
powerLevelDiffText: diff.join(", "),
|
powerLevelDiffText: diffs.map(diff =>
|
||||||
|
_t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', {
|
||||||
|
userId: diff.userId,
|
||||||
|
fromPowerLevel: Roles.textualPowerLevel(diff.from, userDefault),
|
||||||
|
toPowerLevel: Roles.textualPowerLevel(diff.to, userDefault),
|
||||||
|
}),
|
||||||
|
).join(", "),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForPinnedEvent(event) {
|
function textForPinnedEvent(event): () => string | null {
|
||||||
const senderName = event.sender ? event.sender.name : event.getSender();
|
const senderName = event.sender ? event.sender.name : event.getSender();
|
||||||
return _t("%(senderName)s changed the pinned messages for the room.", {senderName});
|
return () => _t("%(senderName)s changed the pinned messages for the room.", {senderName});
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForWidgetEvent(event) {
|
function textForWidgetEvent(event): () => string | null {
|
||||||
const senderName = event.getSender();
|
const senderName = event.getSender();
|
||||||
const {name: prevName, type: prevType, url: prevUrl} = event.getPrevContent();
|
const {name: prevName, type: prevType, url: prevUrl} = event.getPrevContent();
|
||||||
const {name, type, url} = event.getContent() || {};
|
const {name, type, url} = event.getContent() || {};
|
||||||
|
@ -464,27 +486,27 @@ function textForWidgetEvent(event) {
|
||||||
// equivalent to that condition.
|
// equivalent to that condition.
|
||||||
if (url) {
|
if (url) {
|
||||||
if (prevUrl) {
|
if (prevUrl) {
|
||||||
return _t('%(widgetName)s widget modified by %(senderName)s', {
|
return () => _t('%(widgetName)s widget modified by %(senderName)s', {
|
||||||
widgetName, senderName,
|
widgetName, senderName,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return _t('%(widgetName)s widget added by %(senderName)s', {
|
return () => _t('%(widgetName)s widget added by %(senderName)s', {
|
||||||
widgetName, senderName,
|
widgetName, senderName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return _t('%(widgetName)s widget removed by %(senderName)s', {
|
return () => _t('%(widgetName)s widget removed by %(senderName)s', {
|
||||||
widgetName, senderName,
|
widgetName, senderName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForWidgetLayoutEvent(event) {
|
function textForWidgetLayoutEvent(event): () => string | null {
|
||||||
const senderName = event.sender?.name || event.getSender();
|
const senderName = event.sender?.name || event.getSender();
|
||||||
return _t("%(senderName)s has updated the widget layout", {senderName});
|
return () => _t("%(senderName)s has updated the widget layout", {senderName});
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForMjolnirEvent(event) {
|
function textForMjolnirEvent(event): () => string | null {
|
||||||
const senderName = event.getSender();
|
const senderName = event.getSender();
|
||||||
const {entity: prevEntity} = event.getPrevContent();
|
const {entity: prevEntity} = event.getPrevContent();
|
||||||
const {entity, recommendation, reason} = event.getContent();
|
const {entity, recommendation, reason} = event.getContent();
|
||||||
|
@ -492,74 +514,74 @@ function textForMjolnirEvent(event) {
|
||||||
// Rule removed
|
// Rule removed
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
if (USER_RULE_TYPES.includes(event.getType())) {
|
if (USER_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t("%(senderName)s removed the rule banning users matching %(glob)s",
|
return () => _t("%(senderName)s removed the rule banning users matching %(glob)s",
|
||||||
{senderName, glob: prevEntity});
|
{senderName, glob: prevEntity});
|
||||||
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
|
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t("%(senderName)s removed the rule banning rooms matching %(glob)s",
|
return () => _t("%(senderName)s removed the rule banning rooms matching %(glob)s",
|
||||||
{senderName, glob: prevEntity});
|
{senderName, glob: prevEntity});
|
||||||
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
|
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t("%(senderName)s removed the rule banning servers matching %(glob)s",
|
return () => _t("%(senderName)s removed the rule banning servers matching %(glob)s",
|
||||||
{senderName, glob: prevEntity});
|
{senderName, glob: prevEntity});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown type. We'll say something, but we shouldn't end up here.
|
// Unknown type. We'll say something, but we shouldn't end up here.
|
||||||
return _t("%(senderName)s removed a ban rule matching %(glob)s", {senderName, glob: prevEntity});
|
return () => _t("%(senderName)s removed a ban rule matching %(glob)s", {senderName, glob: prevEntity});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid rule
|
// Invalid rule
|
||||||
if (!recommendation || !reason) return _t(`%(senderName)s updated an invalid ban rule`, {senderName});
|
if (!recommendation || !reason) return () => _t(`%(senderName)s updated an invalid ban rule`, {senderName});
|
||||||
|
|
||||||
// Rule updated
|
// Rule updated
|
||||||
if (entity === prevEntity) {
|
if (entity === prevEntity) {
|
||||||
if (USER_RULE_TYPES.includes(event.getType())) {
|
if (USER_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t("%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s",
|
return () => _t("%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s",
|
||||||
{senderName, glob: entity, reason});
|
{senderName, glob: entity, reason});
|
||||||
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
|
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t("%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s",
|
return () => _t("%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s",
|
||||||
{senderName, glob: entity, reason});
|
{senderName, glob: entity, reason});
|
||||||
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
|
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t("%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s",
|
return () => _t("%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s",
|
||||||
{senderName, glob: entity, reason});
|
{senderName, glob: entity, reason});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown type. We'll say something but we shouldn't end up here.
|
// Unknown type. We'll say something but we shouldn't end up here.
|
||||||
return _t("%(senderName)s updated a ban rule matching %(glob)s for %(reason)s",
|
return () => _t("%(senderName)s updated a ban rule matching %(glob)s for %(reason)s",
|
||||||
{senderName, glob: entity, reason});
|
{senderName, glob: entity, reason});
|
||||||
}
|
}
|
||||||
|
|
||||||
// New rule
|
// New rule
|
||||||
if (!prevEntity) {
|
if (!prevEntity) {
|
||||||
if (USER_RULE_TYPES.includes(event.getType())) {
|
if (USER_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t("%(senderName)s created a rule banning users matching %(glob)s for %(reason)s",
|
return () => _t("%(senderName)s created a rule banning users matching %(glob)s for %(reason)s",
|
||||||
{senderName, glob: entity, reason});
|
{senderName, glob: entity, reason});
|
||||||
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
|
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t("%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s",
|
return () => _t("%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s",
|
||||||
{senderName, glob: entity, reason});
|
{senderName, glob: entity, reason});
|
||||||
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
|
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t("%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s",
|
return () => _t("%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s",
|
||||||
{senderName, glob: entity, reason});
|
{senderName, glob: entity, reason});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown type. We'll say something but we shouldn't end up here.
|
// Unknown type. We'll say something but we shouldn't end up here.
|
||||||
return _t("%(senderName)s created a ban rule matching %(glob)s for %(reason)s",
|
return () => _t("%(senderName)s created a ban rule matching %(glob)s for %(reason)s",
|
||||||
{senderName, glob: entity, reason});
|
{senderName, glob: entity, reason});
|
||||||
}
|
}
|
||||||
|
|
||||||
// else the entity !== prevEntity - count as a removal & add
|
// else the entity !== prevEntity - count as a removal & add
|
||||||
if (USER_RULE_TYPES.includes(event.getType())) {
|
if (USER_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t(
|
return () => _t(
|
||||||
"%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " +
|
"%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " +
|
||||||
"%(newGlob)s for %(reason)s",
|
"%(newGlob)s for %(reason)s",
|
||||||
{senderName, oldGlob: prevEntity, newGlob: entity, reason},
|
{senderName, oldGlob: prevEntity, newGlob: entity, reason},
|
||||||
);
|
);
|
||||||
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
|
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t(
|
return () => _t(
|
||||||
"%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " +
|
"%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " +
|
||||||
"%(newGlob)s for %(reason)s",
|
"%(newGlob)s for %(reason)s",
|
||||||
{senderName, oldGlob: prevEntity, newGlob: entity, reason},
|
{senderName, oldGlob: prevEntity, newGlob: entity, reason},
|
||||||
);
|
);
|
||||||
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
|
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
|
||||||
return _t(
|
return () => _t(
|
||||||
"%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " +
|
"%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " +
|
||||||
"%(newGlob)s for %(reason)s",
|
"%(newGlob)s for %(reason)s",
|
||||||
{senderName, oldGlob: prevEntity, newGlob: entity, reason},
|
{senderName, oldGlob: prevEntity, newGlob: entity, reason},
|
||||||
|
@ -567,11 +589,15 @@ function textForMjolnirEvent(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown type. We'll say something but we shouldn't end up here.
|
// Unknown type. We'll say something but we shouldn't end up here.
|
||||||
return _t("%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s " +
|
return () => _t("%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s " +
|
||||||
"for %(reason)s", {senderName, oldGlob: prevEntity, newGlob: entity, reason});
|
"for %(reason)s", {senderName, oldGlob: prevEntity, newGlob: entity, reason});
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlers = {
|
interface IHandlers {
|
||||||
|
[type: string]: (ev: any) => (() => string | null);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlers: IHandlers = {
|
||||||
'm.room.message': textForMessageEvent,
|
'm.room.message': textForMessageEvent,
|
||||||
'm.call.invite': textForCallInviteEvent,
|
'm.call.invite': textForCallInviteEvent,
|
||||||
'm.call.answer': textForCallAnswerEvent,
|
'm.call.answer': textForCallAnswerEvent,
|
||||||
|
@ -579,7 +605,7 @@ const handlers = {
|
||||||
'm.call.reject': textForCallRejectEvent,
|
'm.call.reject': textForCallRejectEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
const stateHandlers = {
|
const stateHandlers: IHandlers = {
|
||||||
'm.room.canonical_alias': textForCanonicalAliasEvent,
|
'm.room.canonical_alias': textForCanonicalAliasEvent,
|
||||||
'm.room.name': textForRoomNameEvent,
|
'm.room.name': textForRoomNameEvent,
|
||||||
'm.room.topic': textForTopicEvent,
|
'm.room.topic': textForTopicEvent,
|
||||||
|
@ -604,8 +630,12 @@ for (const evType of ALL_RULE_TYPES) {
|
||||||
stateHandlers[evType] = textForMjolnirEvent;
|
stateHandlers[evType] = textForMjolnirEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function textForEvent(ev) {
|
export function hasText(ev): boolean {
|
||||||
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
|
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
|
||||||
if (handler) return handler(ev);
|
return Boolean(handler?.(ev));
|
||||||
return '';
|
}
|
||||||
|
|
||||||
|
export function textForEvent(ev): string {
|
||||||
|
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
|
||||||
|
return handler?.(ev)?.() || '';
|
||||||
}
|
}
|
|
@ -24,13 +24,16 @@ import { HostSignupStore } from "../../stores/HostSignupStore";
|
||||||
import SdkConfig from "../../SdkConfig";
|
import SdkConfig from "../../SdkConfig";
|
||||||
import {replaceableComponent} from "../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../utils/replaceableComponent";
|
||||||
|
|
||||||
interface IProps {}
|
interface IProps {
|
||||||
|
onClick?(): void;
|
||||||
|
}
|
||||||
|
|
||||||
interface IState {}
|
interface IState {}
|
||||||
|
|
||||||
@replaceableComponent("structures.HostSignupAction")
|
@replaceableComponent("structures.HostSignupAction")
|
||||||
export default class HostSignupAction extends React.PureComponent<IProps, IState> {
|
export default class HostSignupAction extends React.PureComponent<IProps, IState> {
|
||||||
private openDialog = async () => {
|
private openDialog = async () => {
|
||||||
|
this.props.onClick?.();
|
||||||
await HostSignupStore.instance.setHostSignupActive(true);
|
await HostSignupStore.instance.setHostSignupActive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -439,6 +439,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
onBlur={this.onBlur}
|
onBlur={this.onBlur}
|
||||||
isMinimized={this.props.isMinimized}
|
isMinimized={this.props.isMinimized}
|
||||||
activeSpace={this.state.activeSpace}
|
activeSpace={this.state.activeSpace}
|
||||||
|
onResize={this.refreshStickyHeaders}
|
||||||
onListCollapse={this.refreshStickyHeaders}
|
onListCollapse={this.refreshStickyHeaders}
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
|
|
|
@ -1953,6 +1953,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
// Create and start the client
|
// Create and start the client
|
||||||
await Lifecycle.setLoggedIn(credentials);
|
await Lifecycle.setLoggedIn(credentials);
|
||||||
await this.postLoginSetup();
|
await this.postLoginSetup();
|
||||||
|
|
||||||
PerformanceMonitor.instance.stop(PerformanceEntryNames.LOGIN);
|
PerformanceMonitor.instance.stop(PerformanceEntryNames.LOGIN);
|
||||||
PerformanceMonitor.instance.stop(PerformanceEntryNames.REGISTER);
|
PerformanceMonitor.instance.stop(PerformanceEntryNames.REGISTER);
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,10 +25,11 @@ import * as sdk from '../../index';
|
||||||
|
|
||||||
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
||||||
import SettingsStore from '../../settings/SettingsStore';
|
import SettingsStore from '../../settings/SettingsStore';
|
||||||
|
import RoomContext from "../../contexts/RoomContext";
|
||||||
import {Layout, LayoutPropType} from "../../settings/Layout";
|
import {Layout, LayoutPropType} from "../../settings/Layout";
|
||||||
import {_t} from "../../languageHandler";
|
import {_t} from "../../languageHandler";
|
||||||
import {haveTileForEvent} from "../views/rooms/EventTile";
|
import {haveTileForEvent} from "../views/rooms/EventTile";
|
||||||
import {textForEvent} from "../../TextForEvent";
|
import {hasText} from "../../TextForEvent";
|
||||||
import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer";
|
import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer";
|
||||||
import DMRoomMap from "../../utils/DMRoomMap";
|
import DMRoomMap from "../../utils/DMRoomMap";
|
||||||
import NewRoomIntro from "../views/rooms/NewRoomIntro";
|
import NewRoomIntro from "../views/rooms/NewRoomIntro";
|
||||||
|
@ -151,6 +152,8 @@ export default class MessagePanel extends React.Component {
|
||||||
enableFlair: PropTypes.bool,
|
enableFlair: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static contextType = RoomContext;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
@ -380,7 +383,7 @@ export default class MessagePanel extends React.Component {
|
||||||
// Always show highlighted event
|
// Always show highlighted event
|
||||||
if (this.props.highlightedEventId === mxEv.getId()) return true;
|
if (this.props.highlightedEventId === mxEv.getId()) return true;
|
||||||
|
|
||||||
return !shouldHideEvent(mxEv);
|
return !shouldHideEvent(mxEv, this.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
_readMarkerForEvent(eventId, isLastEvent) {
|
_readMarkerForEvent(eventId, isLastEvent) {
|
||||||
|
@ -1164,11 +1167,8 @@ class MemberGrouper {
|
||||||
|
|
||||||
add(ev) {
|
add(ev) {
|
||||||
if (ev.getType() === 'm.room.member') {
|
if (ev.getType() === 'm.room.member') {
|
||||||
// We'll just double check that it's worth our time to do so, through an
|
// We can ignore any events that don't actually have a message to display
|
||||||
// ugly hack. If textForEvent returns something, we should group it for
|
if (!hasText(ev)) return;
|
||||||
// rendering but if it doesn't then we'll exclude it.
|
|
||||||
const renderText = textForEvent(ev);
|
|
||||||
if (!renderText || renderText.trim().length === 0) return; // quietly ignore
|
|
||||||
}
|
}
|
||||||
this.readMarker = this.readMarker || this.panel._readMarkerForEvent(
|
this.readMarker = this.readMarker || this.panel._readMarkerForEvent(
|
||||||
ev.getId(),
|
ev.getId(),
|
||||||
|
|
|
@ -59,7 +59,6 @@ import ScrollPanel from "./ScrollPanel";
|
||||||
import TimelinePanel from "./TimelinePanel";
|
import TimelinePanel from "./TimelinePanel";
|
||||||
import ErrorBoundary from "../views/elements/ErrorBoundary";
|
import ErrorBoundary from "../views/elements/ErrorBoundary";
|
||||||
import RoomPreviewBar from "../views/rooms/RoomPreviewBar";
|
import RoomPreviewBar from "../views/rooms/RoomPreviewBar";
|
||||||
import ForwardMessage from "../views/rooms/ForwardMessage";
|
|
||||||
import SearchBar from "../views/rooms/SearchBar";
|
import SearchBar from "../views/rooms/SearchBar";
|
||||||
import RoomUpgradeWarningBar from "../views/rooms/RoomUpgradeWarningBar";
|
import RoomUpgradeWarningBar from "../views/rooms/RoomUpgradeWarningBar";
|
||||||
import AuxPanel from "../views/rooms/AuxPanel";
|
import AuxPanel from "../views/rooms/AuxPanel";
|
||||||
|
@ -136,7 +135,6 @@ export interface IState {
|
||||||
// Whether to highlight the event scrolled to
|
// Whether to highlight the event scrolled to
|
||||||
isInitialEventHighlighted?: boolean;
|
isInitialEventHighlighted?: boolean;
|
||||||
replyToEvent?: MatrixEvent;
|
replyToEvent?: MatrixEvent;
|
||||||
forwardingEvent?: MatrixEvent;
|
|
||||||
numUnreadMessages: number;
|
numUnreadMessages: number;
|
||||||
draggingFile: boolean;
|
draggingFile: boolean;
|
||||||
searching: boolean;
|
searching: boolean;
|
||||||
|
@ -155,7 +153,6 @@ export interface IState {
|
||||||
canPeek: boolean;
|
canPeek: boolean;
|
||||||
showApps: boolean;
|
showApps: boolean;
|
||||||
isPeeking: boolean;
|
isPeeking: boolean;
|
||||||
showReadReceipts: boolean;
|
|
||||||
showRightPanel: boolean;
|
showRightPanel: boolean;
|
||||||
// error object, as from the matrix client/server API
|
// error object, as from the matrix client/server API
|
||||||
// If we failed to load information about the room,
|
// If we failed to load information about the room,
|
||||||
|
@ -183,6 +180,12 @@ export interface IState {
|
||||||
canReact: boolean;
|
canReact: boolean;
|
||||||
canReply: boolean;
|
canReply: boolean;
|
||||||
layout: Layout;
|
layout: Layout;
|
||||||
|
lowBandwidth: boolean;
|
||||||
|
showReadReceipts: boolean;
|
||||||
|
showRedactions: boolean;
|
||||||
|
showJoinLeaves: boolean;
|
||||||
|
showAvatarChanges: boolean;
|
||||||
|
showDisplaynameChanges: boolean;
|
||||||
matrixClientIsReady: boolean;
|
matrixClientIsReady: boolean;
|
||||||
showUrlPreview?: boolean;
|
showUrlPreview?: boolean;
|
||||||
e2eStatus?: E2EStatus;
|
e2eStatus?: E2EStatus;
|
||||||
|
@ -200,8 +203,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
private readonly dispatcherRef: string;
|
private readonly dispatcherRef: string;
|
||||||
private readonly roomStoreToken: EventSubscription;
|
private readonly roomStoreToken: EventSubscription;
|
||||||
private readonly rightPanelStoreToken: EventSubscription;
|
private readonly rightPanelStoreToken: EventSubscription;
|
||||||
private readonly showReadReceiptsWatchRef: string;
|
private settingWatchers: string[];
|
||||||
private readonly layoutWatcherRef: string;
|
|
||||||
|
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
private permalinkCreators: Record<string, RoomPermalinkCreator> = {};
|
private permalinkCreators: Record<string, RoomPermalinkCreator> = {};
|
||||||
|
@ -232,7 +234,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
canPeek: false,
|
canPeek: false,
|
||||||
showApps: false,
|
showApps: false,
|
||||||
isPeeking: false,
|
isPeeking: false,
|
||||||
showReadReceipts: true,
|
|
||||||
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
|
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
|
||||||
joining: false,
|
joining: false,
|
||||||
atEndOfLiveTimeline: true,
|
atEndOfLiveTimeline: true,
|
||||||
|
@ -242,6 +243,12 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
canReact: false,
|
canReact: false,
|
||||||
canReply: false,
|
canReply: false,
|
||||||
layout: SettingsStore.getValue("layout"),
|
layout: SettingsStore.getValue("layout"),
|
||||||
|
lowBandwidth: SettingsStore.getValue("lowBandwidth"),
|
||||||
|
showReadReceipts: true,
|
||||||
|
showRedactions: true,
|
||||||
|
showJoinLeaves: true,
|
||||||
|
showAvatarChanges: true,
|
||||||
|
showDisplaynameChanges: true,
|
||||||
matrixClientIsReady: this.context && this.context.isInitialSyncComplete(),
|
matrixClientIsReady: this.context && this.context.isInitialSyncComplete(),
|
||||||
dragCounter: 0,
|
dragCounter: 0,
|
||||||
};
|
};
|
||||||
|
@ -268,9 +275,14 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
WidgetEchoStore.on(UPDATE_EVENT, this.onWidgetEchoStoreUpdate);
|
WidgetEchoStore.on(UPDATE_EVENT, this.onWidgetEchoStoreUpdate);
|
||||||
WidgetStore.instance.on(UPDATE_EVENT, this.onWidgetStoreUpdate);
|
WidgetStore.instance.on(UPDATE_EVENT, this.onWidgetStoreUpdate);
|
||||||
|
|
||||||
this.showReadReceiptsWatchRef = SettingsStore.watchSetting("showReadReceipts", null,
|
this.settingWatchers = [
|
||||||
this.onReadReceiptsChange);
|
SettingsStore.watchSetting("layout", null, () =>
|
||||||
this.layoutWatcherRef = SettingsStore.watchSetting("layout", null, this.onLayoutChange);
|
this.setState({ layout: SettingsStore.getValue("layout") }),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("lowBandwidth", null, () =>
|
||||||
|
this.setState({ lowBandwidth: SettingsStore.getValue("lowBandwidth") }),
|
||||||
|
),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private onWidgetStoreUpdate = () => {
|
private onWidgetStoreUpdate = () => {
|
||||||
|
@ -323,13 +335,45 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
initialEventId: RoomViewStore.getInitialEventId(),
|
initialEventId: RoomViewStore.getInitialEventId(),
|
||||||
isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
|
isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
|
||||||
replyToEvent: RoomViewStore.getQuotingEvent(),
|
replyToEvent: RoomViewStore.getQuotingEvent(),
|
||||||
forwardingEvent: RoomViewStore.getForwardingEvent(),
|
|
||||||
// we should only peek once we have a ready client
|
// we should only peek once we have a ready client
|
||||||
shouldPeek: this.state.matrixClientIsReady && RoomViewStore.shouldPeek(),
|
shouldPeek: this.state.matrixClientIsReady && RoomViewStore.shouldPeek(),
|
||||||
showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId),
|
showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId),
|
||||||
|
showRedactions: SettingsStore.getValue("showRedactions", roomId),
|
||||||
|
showJoinLeaves: SettingsStore.getValue("showJoinLeaves", roomId),
|
||||||
|
showAvatarChanges: SettingsStore.getValue("showAvatarChanges", roomId),
|
||||||
|
showDisplaynameChanges: SettingsStore.getValue("showDisplaynameChanges", roomId),
|
||||||
wasContextSwitch: RoomViewStore.getWasContextSwitch(),
|
wasContextSwitch: RoomViewStore.getWasContextSwitch(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add watchers for each of the settings we just looked up
|
||||||
|
this.settingWatchers = this.settingWatchers.concat([
|
||||||
|
SettingsStore.watchSetting("showReadReceipts", null, () =>
|
||||||
|
this.setState({
|
||||||
|
showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("showRedactions", null, () =>
|
||||||
|
this.setState({
|
||||||
|
showRedactions: SettingsStore.getValue("showRedactions", roomId),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("showJoinLeaves", null, () =>
|
||||||
|
this.setState({
|
||||||
|
showJoinLeaves: SettingsStore.getValue("showJoinLeaves", roomId),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("showAvatarChanges", null, () =>
|
||||||
|
this.setState({
|
||||||
|
showAvatarChanges: SettingsStore.getValue("showAvatarChanges", roomId),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
SettingsStore.watchSetting("showDisplaynameChanges", null, () =>
|
||||||
|
this.setState({
|
||||||
|
showDisplaynameChanges: SettingsStore.getValue("showDisplaynameChanges", roomId),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
if (!initial && this.state.shouldPeek && !newState.shouldPeek) {
|
if (!initial && this.state.shouldPeek && !newState.shouldPeek) {
|
||||||
// Stop peeking because we have joined this room now
|
// Stop peeking because we have joined this room now
|
||||||
this.context.stopPeeking();
|
this.context.stopPeeking();
|
||||||
|
@ -638,10 +682,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.showReadReceiptsWatchRef) {
|
|
||||||
SettingsStore.unwatchSetting(this.showReadReceiptsWatchRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
// cancel any pending calls to the rate_limited_funcs
|
// cancel any pending calls to the rate_limited_funcs
|
||||||
this.updateRoomMembers.cancelPendingCall();
|
this.updateRoomMembers.cancelPendingCall();
|
||||||
|
|
||||||
|
@ -649,7 +689,9 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
// console.log("Tinter.tint from RoomView.unmount");
|
// console.log("Tinter.tint from RoomView.unmount");
|
||||||
// Tinter.tint(); // reset colourscheme
|
// Tinter.tint(); // reset colourscheme
|
||||||
|
|
||||||
SettingsStore.unwatchSetting(this.layoutWatcherRef);
|
for (const watcher of this.settingWatchers) {
|
||||||
|
SettingsStore.unwatchSetting(watcher);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onUserScroll = () => {
|
private onUserScroll = () => {
|
||||||
|
@ -819,7 +861,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
// update unread count when scrolled up
|
// update unread count when scrolled up
|
||||||
if (!this.state.searchResults && this.state.atEndOfLiveTimeline) {
|
if (!this.state.searchResults && this.state.atEndOfLiveTimeline) {
|
||||||
// no change
|
// no change
|
||||||
} else if (!shouldHideEvent(ev)) {
|
} else if (!shouldHideEvent(ev, this.state)) {
|
||||||
this.setState((state, props) => {
|
this.setState((state, props) => {
|
||||||
return {numUnreadMessages: state.numUnreadMessages + 1};
|
return {numUnreadMessages: state.numUnreadMessages + 1};
|
||||||
});
|
});
|
||||||
|
@ -1410,18 +1452,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
dis.dispatch({ action: "open_room_settings" });
|
dis.dispatch({ action: "open_room_settings" });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onCancelClick = () => {
|
|
||||||
console.log("updateTint from onCancelClick");
|
|
||||||
this.updateTint();
|
|
||||||
if (this.state.forwardingEvent) {
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'forward_event',
|
|
||||||
event: null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
dis.fire(Action.FocusComposer);
|
|
||||||
};
|
|
||||||
|
|
||||||
private onAppsClick = () => {
|
private onAppsClick = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "appsDrawer",
|
action: "appsDrawer",
|
||||||
|
@ -1639,6 +1669,24 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called by the parent component when PageUp/Down/etc is pressed.
|
||||||
|
*
|
||||||
|
* We pass it down to the scroll panel.
|
||||||
|
*/
|
||||||
|
private handleScrollKey = ev => {
|
||||||
|
let panel;
|
||||||
|
if (this.searchResultsPanel.current) {
|
||||||
|
panel = this.searchResultsPanel.current;
|
||||||
|
} else if (this.messagePanel) {
|
||||||
|
panel = this.messagePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panel) {
|
||||||
|
panel.handleScrollKey(ev);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get any current call for this room
|
* get any current call for this room
|
||||||
*/
|
*/
|
||||||
|
@ -1837,11 +1885,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
let aux = null;
|
let aux = null;
|
||||||
let previewBar;
|
let previewBar;
|
||||||
let hideCancel = false;
|
if (this.state.searching) {
|
||||||
if (this.state.forwardingEvent) {
|
|
||||||
aux = <ForwardMessage onCancelClick={this.onCancelClick} />;
|
|
||||||
} else if (this.state.searching) {
|
|
||||||
hideCancel = true; // has own cancel
|
|
||||||
aux = <SearchBar
|
aux = <SearchBar
|
||||||
searchInProgress={this.state.searchInProgress}
|
searchInProgress={this.state.searchInProgress}
|
||||||
onCancelClick={this.onCancelSearchClick}
|
onCancelClick={this.onCancelSearchClick}
|
||||||
|
@ -1850,7 +1894,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
/>;
|
/>;
|
||||||
} else if (showRoomUpgradeBar) {
|
} else if (showRoomUpgradeBar) {
|
||||||
aux = <RoomUpgradeWarningBar room={this.state.room} recommendation={roomVersionRecommendation} />;
|
aux = <RoomUpgradeWarningBar room={this.state.room} recommendation={roomVersionRecommendation} />;
|
||||||
hideCancel = true;
|
|
||||||
} else if (myMembership !== "join") {
|
} else if (myMembership !== "join") {
|
||||||
// We do have a room object for this room, but we're not currently in it.
|
// We do have a room object for this room, but we're not currently in it.
|
||||||
// We may have a 3rd party invite to it.
|
// We may have a 3rd party invite to it.
|
||||||
|
@ -1859,7 +1902,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
inviterName = this.props.oobData.inviterName;
|
inviterName = this.props.oobData.inviterName;
|
||||||
}
|
}
|
||||||
const invitedEmail = this.props.threepidInvite?.toEmail;
|
const invitedEmail = this.props.threepidInvite?.toEmail;
|
||||||
hideCancel = true;
|
|
||||||
previewBar = (
|
previewBar = (
|
||||||
<RoomPreviewBar
|
<RoomPreviewBar
|
||||||
onJoinClick={this.onJoinButtonClicked}
|
onJoinClick={this.onJoinButtonClicked}
|
||||||
|
@ -1977,11 +2019,8 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
hideMessagePanel = true;
|
hideMessagePanel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldHighlight = this.state.isInitialEventHighlighted;
|
|
||||||
let highlightedEventId = null;
|
let highlightedEventId = null;
|
||||||
if (this.state.forwardingEvent) {
|
if (this.state.isInitialEventHighlighted) {
|
||||||
highlightedEventId = this.state.forwardingEvent.getId();
|
|
||||||
} else if (shouldHighlight) {
|
|
||||||
highlightedEventId = this.state.initialEventId;
|
highlightedEventId = this.state.initialEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2070,7 +2109,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
inRoom={myMembership === 'join'}
|
inRoom={myMembership === 'join'}
|
||||||
onSearchClick={this.onSearchClick}
|
onSearchClick={this.onSearchClick}
|
||||||
onSettingsClick={this.onSettingsClick}
|
onSettingsClick={this.onSettingsClick}
|
||||||
onCancelClick={(aux && !hideCancel) ? this.onCancelClick : null}
|
|
||||||
onForgetClick={(myMembership === "leave") ? this.onForgetClick : null}
|
onForgetClick={(myMembership === "leave") ? this.onForgetClick : null}
|
||||||
onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null}
|
onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null}
|
||||||
e2eStatus={this.state.e2eStatus}
|
e2eStatus={this.state.e2eStatus}
|
||||||
|
|
|
@ -520,6 +520,7 @@ export const SpaceHierarchy: React.FC<IHierarchyProps> = ({
|
||||||
setError("Failed to update some suggestions. Try again later");
|
setError("Failed to update some suggestions. Try again later");
|
||||||
}
|
}
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
|
setSelected(new Map());
|
||||||
}}
|
}}
|
||||||
kind="primary_outline"
|
kind="primary_outline"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import RoomTopic from "../views/elements/RoomTopic";
|
||||||
import InlineSpinner from "../views/elements/InlineSpinner";
|
import InlineSpinner from "../views/elements/InlineSpinner";
|
||||||
import {inviteMultipleToRoom, showRoomInviteDialog} from "../../RoomInvite";
|
import {inviteMultipleToRoom, showRoomInviteDialog} from "../../RoomInvite";
|
||||||
import {useRoomMembers} from "../../hooks/useRoomMembers";
|
import {useRoomMembers} from "../../hooks/useRoomMembers";
|
||||||
import createRoom, {IOpts, Preset} from "../../createRoom";
|
import createRoom, {IOpts} from "../../createRoom";
|
||||||
import Field from "../views/elements/Field";
|
import Field from "../views/elements/Field";
|
||||||
import {useEventEmitter} from "../../hooks/useEventEmitter";
|
import {useEventEmitter} from "../../hooks/useEventEmitter";
|
||||||
import withValidation from "../views/elements/Validation";
|
import withValidation from "../views/elements/Validation";
|
||||||
|
@ -65,6 +65,7 @@ import dis from "../../dispatcher/dispatcher";
|
||||||
import Modal from "../../Modal";
|
import Modal from "../../Modal";
|
||||||
import BetaFeedbackDialog from "../views/dialogs/BetaFeedbackDialog";
|
import BetaFeedbackDialog from "../views/dialogs/BetaFeedbackDialog";
|
||||||
import SdkConfig from "../../SdkConfig";
|
import SdkConfig from "../../SdkConfig";
|
||||||
|
import { Preset } from "matrix-js-sdk/src/@types/partials";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
space: Room;
|
space: Room;
|
||||||
|
@ -587,6 +588,10 @@ const SpaceSetupPrivateScope = ({ space, justCreatedOpts, onFinished }) => {
|
||||||
<h3>{ _t("Me and my teammates") }</h3>
|
<h3>{ _t("Me and my teammates") }</h3>
|
||||||
<div>{ _t("A private space for you and your teammates") }</div>
|
<div>{ _t("A private space for you and your teammates") }</div>
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
<div className="mx_SpaceRoomView_betaWarning">
|
||||||
|
<h3>{ _t("Teammates might not be able to view or join any private rooms you make.") }</h3>
|
||||||
|
<p>{ _t("We're working on this as part of the beta, but just want to let you know.") }</p>
|
||||||
|
</div>
|
||||||
<SpaceFeedbackPrompt />
|
<SpaceFeedbackPrompt />
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,6 +26,7 @@ import {EventTimeline} from "matrix-js-sdk/src/models/event-timeline";
|
||||||
import {TimelineWindow} from "matrix-js-sdk/src/timeline-window";
|
import {TimelineWindow} from "matrix-js-sdk/src/timeline-window";
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
import {MatrixClientPeg} from "../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../MatrixClientPeg";
|
||||||
|
import RoomContext from "../../contexts/RoomContext";
|
||||||
import UserActivity from "../../UserActivity";
|
import UserActivity from "../../UserActivity";
|
||||||
import Modal from "../../Modal";
|
import Modal from "../../Modal";
|
||||||
import dis from "../../dispatcher/dispatcher";
|
import dis from "../../dispatcher/dispatcher";
|
||||||
|
@ -125,6 +126,8 @@ class TimelinePanel extends React.Component {
|
||||||
alwaysShowTimestamps: PropTypes.bool,
|
alwaysShowTimestamps: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static contextType = RoomContext;
|
||||||
|
|
||||||
// a map from room id to read marker event timestamp
|
// a map from room id to read marker event timestamp
|
||||||
static roomReadMarkerTsMap = {};
|
static roomReadMarkerTsMap = {};
|
||||||
|
|
||||||
|
@ -1288,7 +1291,7 @@ class TimelinePanel extends React.Component {
|
||||||
|
|
||||||
const shouldIgnore = !!ev.status || // local echo
|
const shouldIgnore = !!ev.status || // local echo
|
||||||
(ignoreOwn && ev.sender && ev.sender.userId == myUserId); // own message
|
(ignoreOwn && ev.sender && ev.sender.userId == myUserId); // own message
|
||||||
const isWithoutTile = !haveTileForEvent(ev) || shouldHideEvent(ev);
|
const isWithoutTile = !haveTileForEvent(ev) || shouldHideEvent(ev, this.context);
|
||||||
|
|
||||||
if (isWithoutTile || !node) {
|
if (isWithoutTile || !node) {
|
||||||
// don't start counting if the event should be ignored,
|
// don't start counting if the event should be ignored,
|
||||||
|
|
|
@ -366,9 +366,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
const mxDomain = MatrixClientPeg.get().getDomain();
|
const mxDomain = MatrixClientPeg.get().getDomain();
|
||||||
const validDomains = hostSignupDomains.filter(d => (d === mxDomain || mxDomain.endsWith(`.${d}`)));
|
const validDomains = hostSignupDomains.filter(d => (d === mxDomain || mxDomain.endsWith(`.${d}`)));
|
||||||
if (!hostSignupConfig.domains || validDomains.length > 0) {
|
if (!hostSignupConfig.domains || validDomains.length > 0) {
|
||||||
topSection = <div onClick={this.onCloseMenu}>
|
topSection = <HostSignupAction onClick={this.onCloseMenu} />;
|
||||||
<HostSignupAction />
|
|
||||||
</div>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import classNames from 'classnames';
|
||||||
import * as AvatarLogic from '../../../Avatar';
|
import * as AvatarLogic from '../../../Avatar';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
import RoomContext from "../../../contexts/RoomContext";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
||||||
import {toPx} from "../../../utils/units";
|
import {toPx} from "../../../utils/units";
|
||||||
|
@ -44,12 +45,12 @@ interface IProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const calculateUrls = (url, urls) => {
|
const calculateUrls = (url, urls, lowBandwidth) => {
|
||||||
// work out the full set of urls to try to load. This is formed like so:
|
// work out the full set of urls to try to load. This is formed like so:
|
||||||
// imageUrls: [ props.url, ...props.urls ]
|
// imageUrls: [ props.url, ...props.urls ]
|
||||||
|
|
||||||
let _urls = [];
|
let _urls = [];
|
||||||
if (!SettingsStore.getValue("lowBandwidth")) {
|
if (!lowBandwidth) {
|
||||||
_urls = urls || [];
|
_urls = urls || [];
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
|
@ -63,7 +64,13 @@ const calculateUrls = (url, urls) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const useImageUrl = ({url, urls}): [string, () => void] => {
|
const useImageUrl = ({url, urls}): [string, () => void] => {
|
||||||
const [imageUrls, setUrls] = useState<string[]>(calculateUrls(url, urls));
|
// Since this is a hot code path and the settings store can be slow, we
|
||||||
|
// use the cached lowBandwidth value from the room context if it exists
|
||||||
|
const roomContext = useContext(RoomContext);
|
||||||
|
const lowBandwidth = roomContext ?
|
||||||
|
roomContext.lowBandwidth : SettingsStore.getValue("lowBandwidth");
|
||||||
|
|
||||||
|
const [imageUrls, setUrls] = useState<string[]>(calculateUrls(url, urls, lowBandwidth));
|
||||||
const [urlsIndex, setIndex] = useState<number>(0);
|
const [urlsIndex, setIndex] = useState<number>(0);
|
||||||
|
|
||||||
const onError = useCallback(() => {
|
const onError = useCallback(() => {
|
||||||
|
@ -71,7 +78,7 @@ const useImageUrl = ({url, urls}): [string, () => void] => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUrls(calculateUrls(url, urls));
|
setUrls(calculateUrls(url, urls, lowBandwidth));
|
||||||
setIndex(0);
|
setIndex(0);
|
||||||
}, [url, JSON.stringify(urls)]); // eslint-disable-line react-hooks/exhaustive-deps
|
}, [url, JSON.stringify(urls)]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import { MenuItem } from "../../structures/ContextMenu";
|
||||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { ReadPinsEventId } from "../right_panel/PinnedMessagesCard";
|
import { ReadPinsEventId } from "../right_panel/PinnedMessagesCard";
|
||||||
|
import ForwardDialog from "../dialogs/ForwardDialog";
|
||||||
|
|
||||||
export function canCancel(eventStatus) {
|
export function canCancel(eventStatus) {
|
||||||
return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
|
return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
|
||||||
|
@ -157,10 +158,10 @@ export default class MessageContextMenu extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
onForwardClick = () => {
|
onForwardClick = () => {
|
||||||
if (this.props.onCloseDialog) this.props.onCloseDialog();
|
Modal.createTrackedDialog('Forward Message', '', ForwardDialog, {
|
||||||
dis.dispatch({
|
matrixClient: MatrixClientPeg.get(),
|
||||||
action: 'forward_event',
|
|
||||||
event: this.props.mxEvent,
|
event: this.props.mxEvent,
|
||||||
|
permalinkCreator: this.props.permalinkCreator,
|
||||||
});
|
});
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,6 +40,8 @@ interface IProps extends React.ComponentProps<typeof IconizedContextMenu> {
|
||||||
showUnpin?: boolean;
|
showUnpin?: boolean;
|
||||||
// override delete handler
|
// override delete handler
|
||||||
onDeleteClick?(): void;
|
onDeleteClick?(): void;
|
||||||
|
// override edit handler
|
||||||
|
onEditClick?(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const WidgetContextMenu: React.FC<IProps> = ({
|
const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
|
@ -47,6 +49,7 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
app,
|
app,
|
||||||
userWidget,
|
userWidget,
|
||||||
onDeleteClick,
|
onDeleteClick,
|
||||||
|
onEditClick,
|
||||||
showUnpin,
|
showUnpin,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -89,12 +92,16 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
|
|
||||||
let editButton;
|
let editButton;
|
||||||
if (canModify && WidgetUtils.isManagedByManager(app)) {
|
if (canModify && WidgetUtils.isManagedByManager(app)) {
|
||||||
const onEditClick = () => {
|
const _onEditClick = () => {
|
||||||
WidgetUtils.editWidget(room, app);
|
if (onEditClick) {
|
||||||
|
onEditClick();
|
||||||
|
} else {
|
||||||
|
WidgetUtils.editWidget(room, app);
|
||||||
|
}
|
||||||
onFinished();
|
onFinished();
|
||||||
};
|
};
|
||||||
|
|
||||||
editButton = <IconizedContextMenuOption onClick={onEditClick} label={_t("Edit")} />;
|
editButton = <IconizedContextMenuOption onClick={_onEditClick} label={_t("Edit")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let snapshotButton;
|
let snapshotButton;
|
||||||
|
@ -116,24 +123,29 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
|
|
||||||
let deleteButton;
|
let deleteButton;
|
||||||
if (onDeleteClick || canModify) {
|
if (onDeleteClick || canModify) {
|
||||||
const onDeleteClickDefault = () => {
|
const _onDeleteClick = () => {
|
||||||
// Show delete confirmation dialog
|
if (onDeleteClick) {
|
||||||
Modal.createTrackedDialog('Delete Widget', '', QuestionDialog, {
|
onDeleteClick();
|
||||||
title: _t("Delete Widget"),
|
} else {
|
||||||
description: _t(
|
// Show delete confirmation dialog
|
||||||
"Deleting a widget removes it for all users in this room." +
|
Modal.createTrackedDialog('Delete Widget', '', QuestionDialog, {
|
||||||
" Are you sure you want to delete this widget?"),
|
title: _t("Delete Widget"),
|
||||||
button: _t("Delete widget"),
|
description: _t(
|
||||||
onFinished: (confirmed) => {
|
"Deleting a widget removes it for all users in this room." +
|
||||||
if (!confirmed) return;
|
" Are you sure you want to delete this widget?"),
|
||||||
WidgetUtils.setRoomWidget(roomId, app.id);
|
button: _t("Delete widget"),
|
||||||
},
|
onFinished: (confirmed) => {
|
||||||
});
|
if (!confirmed) return;
|
||||||
|
WidgetUtils.setRoomWidget(roomId, app.id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onFinished();
|
onFinished();
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteButton = <IconizedContextMenuOption
|
deleteButton = <IconizedContextMenuOption
|
||||||
onClick={onDeleteClick || onDeleteClickDefault}
|
onClick={_onDeleteClick}
|
||||||
label={userWidget ? _t("Remove") : _t("Remove for everyone")}
|
label={userWidget ? _t("Remove") : _t("Remove for everyone")}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,22 +15,23 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {ChangeEvent, createRef, KeyboardEvent, SyntheticEvent} from "react";
|
import React, { ChangeEvent, createRef, KeyboardEvent, SyntheticEvent } from "react";
|
||||||
import {Room} from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
|
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import withValidation, {IFieldState} from '../elements/Validation';
|
import withValidation, { IFieldState } from '../elements/Validation';
|
||||||
import {_t} from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||||
import {Key} from "../../../Keyboard";
|
import { Key } from "../../../Keyboard";
|
||||||
import {IOpts, Preset, privateShouldBeEncrypted, Visibility} from "../../../createRoom";
|
import { IOpts, privateShouldBeEncrypted } from "../../../createRoom";
|
||||||
import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore";
|
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import Field from "../elements/Field";
|
import Field from "../elements/Field";
|
||||||
import RoomAliasField from "../elements/RoomAliasField";
|
import RoomAliasField from "../elements/RoomAliasField";
|
||||||
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
||||||
import DialogButtons from "../elements/DialogButtons";
|
import DialogButtons from "../elements/DialogButtons";
|
||||||
import BaseDialog from "../dialogs/BaseDialog";
|
import BaseDialog from "../dialogs/BaseDialog";
|
||||||
|
import { Preset, Visibility } from "matrix-js-sdk/src/@types/partials";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
defaultPublic?: boolean;
|
defaultPublic?: boolean;
|
||||||
|
@ -72,7 +73,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
canChangeEncryption: true,
|
canChangeEncryption: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
MatrixClientPeg.get().doesServerForceEncryptionForPreset("private")
|
MatrixClientPeg.get().doesServerForceEncryptionForPreset(Preset.PrivateChat)
|
||||||
.then(isForced => this.setState({ canChangeEncryption: !isForced }));
|
.then(isForced => this.setState({ canChangeEncryption: !isForced }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,247 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Robin Townsend <robin@robin.town>
|
||||||
|
|
||||||
|
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, {useMemo, useState, useEffect} from "react";
|
||||||
|
import classnames from "classnames";
|
||||||
|
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
||||||
|
import {Room} from "matrix-js-sdk/src/models/room";
|
||||||
|
import {MatrixClient} from "matrix-js-sdk/src/client";
|
||||||
|
|
||||||
|
import {_t} from "../../../languageHandler";
|
||||||
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
|
import {useSettingValue, useFeatureEnabled} from "../../../hooks/useSettings";
|
||||||
|
import {UIFeature} from "../../../settings/UIFeature";
|
||||||
|
import {Layout} from "../../../settings/Layout";
|
||||||
|
import {IDialogProps} from "./IDialogProps";
|
||||||
|
import BaseDialog from "./BaseDialog";
|
||||||
|
import {avatarUrlForUser} from "../../../Avatar";
|
||||||
|
import EventTile from "../rooms/EventTile";
|
||||||
|
import SearchBox from "../../structures/SearchBox";
|
||||||
|
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
||||||
|
import {Alignment} from '../elements/Tooltip';
|
||||||
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
|
import {StaticNotificationState} from "../../../stores/notifications/StaticNotificationState";
|
||||||
|
import NotificationBadge from "../rooms/NotificationBadge";
|
||||||
|
import {RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";
|
||||||
|
import {sortRooms} from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
|
||||||
|
import QueryMatcher from "../../../autocomplete/QueryMatcher";
|
||||||
|
|
||||||
|
const AVATAR_SIZE = 30;
|
||||||
|
|
||||||
|
interface IProps extends IDialogProps {
|
||||||
|
matrixClient: MatrixClient;
|
||||||
|
// The event to forward
|
||||||
|
event: MatrixEvent;
|
||||||
|
// We need a permalink creator for the source room to pass through to EventTile
|
||||||
|
// in case the event is a reply (even though the user can't get at the link)
|
||||||
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IEntryProps {
|
||||||
|
room: Room;
|
||||||
|
event: MatrixEvent;
|
||||||
|
matrixClient: MatrixClient;
|
||||||
|
onFinished(success: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SendState {
|
||||||
|
CanSend,
|
||||||
|
Sending,
|
||||||
|
Sent,
|
||||||
|
Failed,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Entry: React.FC<IEntryProps> = ({ room, event, matrixClient: cli, onFinished }) => {
|
||||||
|
const [sendState, setSendState] = useState<SendState>(SendState.CanSend);
|
||||||
|
|
||||||
|
const jumpToRoom = () => {
|
||||||
|
dis.dispatch({
|
||||||
|
action: "view_room",
|
||||||
|
room_id: room.roomId,
|
||||||
|
});
|
||||||
|
onFinished(true);
|
||||||
|
};
|
||||||
|
const send = async () => {
|
||||||
|
setSendState(SendState.Sending);
|
||||||
|
try {
|
||||||
|
await cli.sendEvent(room.roomId, event.getType(), event.getContent());
|
||||||
|
setSendState(SendState.Sent);
|
||||||
|
} catch (e) {
|
||||||
|
setSendState(SendState.Failed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let className;
|
||||||
|
let disabled = false;
|
||||||
|
let title;
|
||||||
|
let icon;
|
||||||
|
if (sendState === SendState.CanSend) {
|
||||||
|
className = "mx_ForwardList_canSend";
|
||||||
|
if (room.maySendMessage()) {
|
||||||
|
title = _t("Send");
|
||||||
|
} else {
|
||||||
|
disabled = true;
|
||||||
|
title = _t("You don't have permission to do this");
|
||||||
|
}
|
||||||
|
} else if (sendState === SendState.Sending) {
|
||||||
|
className = "mx_ForwardList_sending";
|
||||||
|
disabled = true;
|
||||||
|
title = _t("Sending");
|
||||||
|
icon = <div className="mx_ForwardList_sendIcon" aria-label={title}></div>;
|
||||||
|
} else if (sendState === SendState.Sent) {
|
||||||
|
className = "mx_ForwardList_sent";
|
||||||
|
disabled = true;
|
||||||
|
title = _t("Sent");
|
||||||
|
icon = <div className="mx_ForwardList_sendIcon" aria-label={title}></div>;
|
||||||
|
} else {
|
||||||
|
className = "mx_ForwardList_sendFailed";
|
||||||
|
disabled = true;
|
||||||
|
title = _t("Failed to send");
|
||||||
|
icon = <NotificationBadge
|
||||||
|
notification={StaticNotificationState.RED_EXCLAMATION}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="mx_ForwardList_entry">
|
||||||
|
<AccessibleTooltipButton
|
||||||
|
className="mx_ForwardList_roomButton"
|
||||||
|
onClick={jumpToRoom}
|
||||||
|
title={_t("Open link")}
|
||||||
|
yOffset={-20}
|
||||||
|
alignment={Alignment.Top}
|
||||||
|
>
|
||||||
|
<DecoratedRoomAvatar room={room} avatarSize={32} />
|
||||||
|
<span className="mx_ForwardList_entry_name">{ room.name }</span>
|
||||||
|
</AccessibleTooltipButton>
|
||||||
|
<AccessibleTooltipButton
|
||||||
|
kind={sendState === SendState.Failed ? "danger_outline" : "primary_outline"}
|
||||||
|
className={`mx_ForwardList_sendButton ${className}`}
|
||||||
|
onClick={send}
|
||||||
|
disabled={disabled}
|
||||||
|
title={title}
|
||||||
|
yOffset={-20}
|
||||||
|
alignment={Alignment.Top}
|
||||||
|
>
|
||||||
|
<div className="mx_ForwardList_sendLabel">{ _t("Send") }</div>
|
||||||
|
{ icon }
|
||||||
|
</AccessibleTooltipButton>
|
||||||
|
</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCreator, onFinished }) => {
|
||||||
|
const userId = cli.getUserId();
|
||||||
|
const [profileInfo, setProfileInfo] = useState<any>({});
|
||||||
|
useEffect(() => {
|
||||||
|
cli.getProfileInfo(userId).then(info => setProfileInfo(info));
|
||||||
|
}, [cli, userId]);
|
||||||
|
|
||||||
|
// For the message preview we fake the sender as ourselves
|
||||||
|
const mockEvent = new MatrixEvent({
|
||||||
|
type: "m.room.message",
|
||||||
|
sender: userId,
|
||||||
|
content: event.getContent(),
|
||||||
|
unsigned: {
|
||||||
|
age: 97,
|
||||||
|
},
|
||||||
|
event_id: "$9999999999999999999999999999999999999999999",
|
||||||
|
room_id: event.getRoomId(),
|
||||||
|
});
|
||||||
|
mockEvent.sender = {
|
||||||
|
name: profileInfo.displayname || userId,
|
||||||
|
userId,
|
||||||
|
getAvatarUrl: (..._) => {
|
||||||
|
return avatarUrlForUser(
|
||||||
|
{ avatarUrl: profileInfo.avatar_url },
|
||||||
|
AVATAR_SIZE, AVATAR_SIZE, "crop",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getMxcAvatarUrl: () => profileInfo.avatar_url,
|
||||||
|
};
|
||||||
|
|
||||||
|
const [query, setQuery] = useState("");
|
||||||
|
const lcQuery = query.toLowerCase();
|
||||||
|
|
||||||
|
const spacesEnabled = useFeatureEnabled("feature_spaces");
|
||||||
|
const flairEnabled = useFeatureEnabled(UIFeature.Flair);
|
||||||
|
const previewLayout = useSettingValue<Layout>("layout");
|
||||||
|
|
||||||
|
let rooms = useMemo(() => sortRooms(
|
||||||
|
cli.getVisibleRooms().filter(
|
||||||
|
room => room.getMyMembership() === "join" &&
|
||||||
|
!(spacesEnabled && room.isSpaceRoom()),
|
||||||
|
),
|
||||||
|
), [cli, spacesEnabled]);
|
||||||
|
|
||||||
|
if (lcQuery) {
|
||||||
|
rooms = new QueryMatcher<Room>(rooms, {
|
||||||
|
keys: ["name"],
|
||||||
|
funcs: [r => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean)],
|
||||||
|
shouldMatchWordsOnly: false,
|
||||||
|
}).match(lcQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <BaseDialog
|
||||||
|
title={_t("Forward message")}
|
||||||
|
className="mx_ForwardDialog"
|
||||||
|
contentId="mx_ForwardList"
|
||||||
|
onFinished={onFinished}
|
||||||
|
fixedWidth={false}
|
||||||
|
>
|
||||||
|
<h3>{ _t("Message preview") }</h3>
|
||||||
|
<div className={classnames("mx_ForwardDialog_preview", {
|
||||||
|
"mx_IRCLayout": previewLayout == Layout.IRC,
|
||||||
|
"mx_GroupLayout": previewLayout == Layout.Group,
|
||||||
|
})}>
|
||||||
|
<EventTile
|
||||||
|
mxEvent={mockEvent}
|
||||||
|
layout={previewLayout}
|
||||||
|
enableFlair={flairEnabled}
|
||||||
|
permalinkCreator={permalinkCreator}
|
||||||
|
as="div"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div className="mx_ForwardList" id="mx_ForwardList">
|
||||||
|
<SearchBox
|
||||||
|
className="mx_textinput_icon mx_textinput_search"
|
||||||
|
placeholder={_t("Search for rooms or people")}
|
||||||
|
onSearch={setQuery}
|
||||||
|
autoComplete={true}
|
||||||
|
autoFocus={true}
|
||||||
|
/>
|
||||||
|
<AutoHideScrollbar className="mx_ForwardList_content">
|
||||||
|
{ rooms.length > 0 ? (
|
||||||
|
<div className="mx_ForwardList_results">
|
||||||
|
{ rooms.map(room =>
|
||||||
|
<Entry
|
||||||
|
key={room.roomId}
|
||||||
|
room={room}
|
||||||
|
event={event}
|
||||||
|
matrixClient={cli}
|
||||||
|
onFinished={onFinished}
|
||||||
|
/>,
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
|
) : <span className="mx_ForwardList_noResults">
|
||||||
|
{ _t("No results") }
|
||||||
|
</span> }
|
||||||
|
</AutoHideScrollbar>
|
||||||
|
</div>
|
||||||
|
</BaseDialog>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ForwardDialog;
|
|
@ -14,7 +14,9 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, { createRef } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import {_t, _td} from "../../../languageHandler";
|
import {_t, _td} from "../../../languageHandler";
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
|
@ -31,7 +33,6 @@ import Modal from "../../../Modal";
|
||||||
import {humanizeTime} from "../../../utils/humanize";
|
import {humanizeTime} from "../../../utils/humanize";
|
||||||
import createRoom, {
|
import createRoom, {
|
||||||
canEncryptToAllUsers, ensureDMExists, findDMForUser, privateShouldBeEncrypted,
|
canEncryptToAllUsers, ensureDMExists, findDMForUser, privateShouldBeEncrypted,
|
||||||
IInvite3PID,
|
|
||||||
} from "../../../createRoom";
|
} from "../../../createRoom";
|
||||||
import {inviteMultipleToRoom, showCommunityInviteDialog} from "../../../RoomInvite";
|
import {inviteMultipleToRoom, showCommunityInviteDialog} from "../../../RoomInvite";
|
||||||
import {Key} from "../../../Keyboard";
|
import {Key} from "../../../Keyboard";
|
||||||
|
@ -50,6 +51,12 @@ import {getAddressType} from "../../../UserAddress";
|
||||||
import BaseAvatar from '../avatars/BaseAvatar';
|
import BaseAvatar from '../avatars/BaseAvatar';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import { compare } from '../../../utils/strings';
|
import { compare } from '../../../utils/strings';
|
||||||
|
import { IInvite3PID } from "matrix-js-sdk/src/@types/requests";
|
||||||
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
|
import { copyPlaintext, selectText } from "../../../utils/strings";
|
||||||
|
import * as ContextMenu from "../../structures/ContextMenu";
|
||||||
|
import { toRightOf } from "../../structures/ContextMenu";
|
||||||
|
import GenericTextContextMenu from "../context_menus/GenericTextContextMenu";
|
||||||
|
|
||||||
// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
|
// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
|
@ -351,6 +358,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
initialText: "",
|
initialText: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private closeCopiedTooltip: () => void;
|
||||||
private debounceTimer: NodeJS.Timeout = null; // actually number because we're in the browser
|
private debounceTimer: NodeJS.Timeout = null; // actually number because we're in the browser
|
||||||
private editorRef = createRef<HTMLInputElement>();
|
private editorRef = createRef<HTMLInputElement>();
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
|
@ -403,6 +411,9 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
|
// if the Copied tooltip is open then get rid of it, there are ways to close the modal which wouldn't close
|
||||||
|
// the tooltip otherwise, such as pressing Escape or clicking X really quickly
|
||||||
|
if (this.closeCopiedTooltip) this.closeCopiedTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onConsultFirstChange = (ev) => {
|
private onConsultFirstChange = (ev) => {
|
||||||
|
@ -1238,6 +1249,25 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async onLinkClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
selectText(e.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onCopyClick = async e => {
|
||||||
|
e.preventDefault();
|
||||||
|
const target = e.target; // copy target before we go async and React throws it away
|
||||||
|
|
||||||
|
const successful = await copyPlaintext(makeUserPermalink(MatrixClientPeg.get().getUserId()));
|
||||||
|
const buttonRect = target.getBoundingClientRect();
|
||||||
|
const { close } = ContextMenu.createMenu(GenericTextContextMenu, {
|
||||||
|
...toRightOf(buttonRect, 2),
|
||||||
|
message: successful ? _t("Copied!") : _t("Failed to copy"),
|
||||||
|
});
|
||||||
|
// Drop a reference to this close handler for componentWillUnmount
|
||||||
|
this.closeCopiedTooltip = target.onmouseleave = close;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||||
|
@ -1248,12 +1278,12 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
spinner = <Spinner w={20} h={20} />;
|
spinner = <Spinner w={20} h={20} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let title;
|
let title;
|
||||||
let helpText;
|
let helpText;
|
||||||
let buttonText;
|
let buttonText;
|
||||||
let goButtonFn;
|
let goButtonFn;
|
||||||
let consultSection;
|
let extraSection;
|
||||||
|
let footer;
|
||||||
let keySharingWarning = <span />;
|
let keySharingWarning = <span />;
|
||||||
|
|
||||||
const identityServersEnabled = SettingsStore.getValue(UIFeature.IdentityServer);
|
const identityServersEnabled = SettingsStore.getValue(UIFeature.IdentityServer);
|
||||||
|
@ -1316,6 +1346,26 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
}
|
}
|
||||||
buttonText = _t("Go");
|
buttonText = _t("Go");
|
||||||
goButtonFn = this.startDm;
|
goButtonFn = this.startDm;
|
||||||
|
extraSection = <div className="mx_InviteDialog_section_hidden_suggestions_disclaimer">
|
||||||
|
<span>{ _t("Some suggestions may be hidden for privacy.") }</span>
|
||||||
|
<p>{ _t("If you can't see who you’re looking for, send them your invite link below.") }</p>
|
||||||
|
</div>;
|
||||||
|
const link = makeUserPermalink(MatrixClientPeg.get().getUserId());
|
||||||
|
footer = <div className="mx_InviteDialog_footer">
|
||||||
|
<h3>{ _t("Or send invite link") }</h3>
|
||||||
|
<div className="mx_InviteDialog_footer_link">
|
||||||
|
<a href={link} onClick={this.onLinkClick}>
|
||||||
|
{ link }
|
||||||
|
</a>
|
||||||
|
<AccessibleTooltipButton
|
||||||
|
title={_t("Copy")}
|
||||||
|
onClick={this.onCopyClick}
|
||||||
|
className="mx_InviteDialog_footer_link_copy"
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
</AccessibleTooltipButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
} else if (this.props.kind === KIND_INVITE) {
|
} else if (this.props.kind === KIND_INVITE) {
|
||||||
const room = MatrixClientPeg.get()?.getRoom(this.props.roomId);
|
const room = MatrixClientPeg.get()?.getRoom(this.props.roomId);
|
||||||
const isSpace = SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom();
|
const isSpace = SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom();
|
||||||
|
@ -1377,7 +1427,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
title = _t("Transfer");
|
title = _t("Transfer");
|
||||||
buttonText = _t("Transfer");
|
buttonText = _t("Transfer");
|
||||||
goButtonFn = this.transferCall;
|
goButtonFn = this.transferCall;
|
||||||
consultSection = <div>
|
footer = <div>
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" checked={this.state.consultFirst} onChange={this.onConsultFirstChange} />
|
<input type="checkbox" checked={this.state.consultFirst} onChange={this.onConsultFirstChange} />
|
||||||
{_t("Consult first")}
|
{_t("Consult first")}
|
||||||
|
@ -1391,7 +1441,9 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
|| (this.state.filterText && this.state.filterText.includes('@'));
|
|| (this.state.filterText && this.state.filterText.includes('@'));
|
||||||
return (
|
return (
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
className='mx_InviteDialog'
|
className={classNames("mx_InviteDialog", {
|
||||||
|
mx_InviteDialog_hasFooter: !!footer,
|
||||||
|
})}
|
||||||
hasCancel={true}
|
hasCancel={true}
|
||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
title={title}
|
title={title}
|
||||||
|
@ -1418,8 +1470,9 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
<div className='mx_InviteDialog_userSections'>
|
<div className='mx_InviteDialog_userSections'>
|
||||||
{this.renderSection('recents')}
|
{this.renderSection('recents')}
|
||||||
{this.renderSection('suggestions')}
|
{this.renderSection('suggestions')}
|
||||||
|
{extraSection}
|
||||||
</div>
|
</div>
|
||||||
{consultSection}
|
{footer}
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import TabbedView, {Tab} from "../../structures/TabbedView";
|
import TabbedView, {Tab} from "../../structures/TabbedView";
|
||||||
import {_t, _td} from "../../../languageHandler";
|
import {_t, _td} from "../../../languageHandler";
|
||||||
import AdvancedRoomSettingsTab from "../settings/tabs/room/AdvancedRoomSettingsTab";
|
import AdvancedRoomSettingsTab from "../settings/tabs/room/AdvancedRoomSettingsTab";
|
||||||
|
@ -39,31 +38,36 @@ export const ROOM_NOTIFICATIONS_TAB = "ROOM_NOTIFICATIONS_TAB";
|
||||||
export const ROOM_BRIDGES_TAB = "ROOM_BRIDGES_TAB";
|
export const ROOM_BRIDGES_TAB = "ROOM_BRIDGES_TAB";
|
||||||
export const ROOM_ADVANCED_TAB = "ROOM_ADVANCED_TAB";
|
export const ROOM_ADVANCED_TAB = "ROOM_ADVANCED_TAB";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
roomId: string;
|
||||||
|
onFinished: (success: boolean) => void;
|
||||||
|
initialTabId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.dialogs.RoomSettingsDialog")
|
@replaceableComponent("views.dialogs.RoomSettingsDialog")
|
||||||
export default class RoomSettingsDialog extends React.Component {
|
export default class RoomSettingsDialog extends React.Component<IProps> {
|
||||||
static propTypes = {
|
private dispatcherRef: string;
|
||||||
roomId: PropTypes.string.isRequired,
|
|
||||||
onFinished: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
public componentDidMount() {
|
||||||
this._dispatcherRef = dis.register(this._onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
if (this._dispatcherRef) dis.unregister(this._dispatcherRef);
|
if (this.dispatcherRef) {
|
||||||
|
dis.unregister(this.dispatcherRef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onAction = (payload) => {
|
private onAction = (payload): void => {
|
||||||
// When view changes below us, close the room settings
|
// When view changes below us, close the room settings
|
||||||
// whilst the modal is open this can only be triggered when someone hits Leave Room
|
// whilst the modal is open this can only be triggered when someone hits Leave Room
|
||||||
if (payload.action === 'view_home_page') {
|
if (payload.action === 'view_home_page') {
|
||||||
this.props.onFinished();
|
this.props.onFinished(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_getTabs() {
|
private getTabs(): Tab[] {
|
||||||
const tabs = [];
|
const tabs: Tab[] = [];
|
||||||
|
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
ROOM_GENERAL_TAB,
|
ROOM_GENERAL_TAB,
|
||||||
|
@ -123,7 +127,10 @@ export default class RoomSettingsDialog extends React.Component {
|
||||||
title={_t("Room Settings - %(roomName)s", {roomName})}
|
title={_t("Room Settings - %(roomName)s", {roomName})}
|
||||||
>
|
>
|
||||||
<div className='mx_SettingsDialog_content'>
|
<div className='mx_SettingsDialog_content'>
|
||||||
<TabbedView tabs={this._getTabs()} />
|
<TabbedView
|
||||||
|
tabs={this.getTabs()}
|
||||||
|
initialTabId={this.props.initialTabId}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
|
@ -19,7 +19,7 @@ import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import AccessibleButton from "./AccessibleButton";
|
import AccessibleButton from "./AccessibleButton";
|
||||||
import Tooltip from './Tooltip';
|
import Tooltip, {Alignment} from './Tooltip';
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
interface ITooltipProps extends React.ComponentProps<typeof AccessibleButton> {
|
interface ITooltipProps extends React.ComponentProps<typeof AccessibleButton> {
|
||||||
|
@ -28,6 +28,7 @@ interface ITooltipProps extends React.ComponentProps<typeof AccessibleButton> {
|
||||||
tooltipClassName?: string;
|
tooltipClassName?: string;
|
||||||
forceHide?: boolean;
|
forceHide?: boolean;
|
||||||
yOffset?: number;
|
yOffset?: number;
|
||||||
|
alignment?: Alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -66,13 +67,14 @@ export default class AccessibleTooltipButton extends React.PureComponent<IToolti
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const {title, tooltip, children, tooltipClassName, forceHide, yOffset, ...props} = this.props;
|
const {title, tooltip, children, tooltipClassName, forceHide, yOffset, alignment, ...props} = this.props;
|
||||||
|
|
||||||
const tip = this.state.hover ? <Tooltip
|
const tip = this.state.hover ? <Tooltip
|
||||||
className="mx_AccessibleTooltipButton_container"
|
className="mx_AccessibleTooltipButton_container"
|
||||||
tooltipClassName={classNames("mx_AccessibleTooltipButton_tooltip", tooltipClassName)}
|
tooltipClassName={classNames("mx_AccessibleTooltipButton_tooltip", tooltipClassName)}
|
||||||
label={tooltip || title}
|
label={tooltip || title}
|
||||||
yOffset={yOffset}
|
yOffset={yOffset}
|
||||||
|
alignment={alignment}
|
||||||
/> : null;
|
/> : null;
|
||||||
return (
|
return (
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
|
|
|
@ -47,9 +47,14 @@ export default class AppTile extends React.Component {
|
||||||
|
|
||||||
// The key used for PersistedElement
|
// The key used for PersistedElement
|
||||||
this._persistKey = getPersistKey(this.props.app.id);
|
this._persistKey = getPersistKey(this.props.app.id);
|
||||||
this._sgWidget = new StopGapWidget(this.props);
|
try {
|
||||||
this._sgWidget.on("preparing", this._onWidgetPrepared);
|
this._sgWidget = new StopGapWidget(this.props);
|
||||||
this._sgWidget.on("ready", this._onWidgetReady);
|
this._sgWidget.on("preparing", this._onWidgetPrepared);
|
||||||
|
this._sgWidget.on("ready", this._onWidgetReady);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Failed to construct widget", e);
|
||||||
|
this._sgWidget = null;
|
||||||
|
}
|
||||||
this.iframe = null; // ref to the iframe (callback style)
|
this.iframe = null; // ref to the iframe (callback style)
|
||||||
|
|
||||||
this.state = this._getNewState(props);
|
this.state = this._getNewState(props);
|
||||||
|
@ -97,7 +102,7 @@ export default class AppTile extends React.Component {
|
||||||
// Force the widget to be non-persistent (able to be deleted/forgotten)
|
// Force the widget to be non-persistent (able to be deleted/forgotten)
|
||||||
ActiveWidgetStore.destroyPersistentWidget(this.props.app.id);
|
ActiveWidgetStore.destroyPersistentWidget(this.props.app.id);
|
||||||
PersistedElement.destroyElement(this._persistKey);
|
PersistedElement.destroyElement(this._persistKey);
|
||||||
this._sgWidget.stop();
|
if (this._sgWidget) this._sgWidget.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ hasPermissionToLoad });
|
this.setState({ hasPermissionToLoad });
|
||||||
|
@ -117,7 +122,7 @@ export default class AppTile extends React.Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
// Only fetch IM token on mount if we're showing and have permission to load
|
// Only fetch IM token on mount if we're showing and have permission to load
|
||||||
if (this.state.hasPermissionToLoad) {
|
if (this._sgWidget && this.state.hasPermissionToLoad) {
|
||||||
this._startWidget();
|
this._startWidget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,10 +151,15 @@ export default class AppTile extends React.Component {
|
||||||
if (this._sgWidget) {
|
if (this._sgWidget) {
|
||||||
this._sgWidget.stop();
|
this._sgWidget.stop();
|
||||||
}
|
}
|
||||||
this._sgWidget = new StopGapWidget(newProps);
|
try {
|
||||||
this._sgWidget.on("preparing", this._onWidgetPrepared);
|
this._sgWidget = new StopGapWidget(newProps);
|
||||||
this._sgWidget.on("ready", this._onWidgetReady);
|
this._sgWidget.on("preparing", this._onWidgetPrepared);
|
||||||
this._startWidget();
|
this._sgWidget.on("ready", this._onWidgetReady);
|
||||||
|
this._startWidget();
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Failed to construct widget", e);
|
||||||
|
this._sgWidget = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_startWidget() {
|
_startWidget() {
|
||||||
|
@ -161,7 +171,7 @@ export default class AppTile extends React.Component {
|
||||||
_iframeRefChange = (ref) => {
|
_iframeRefChange = (ref) => {
|
||||||
this.iframe = ref;
|
this.iframe = ref;
|
||||||
if (ref) {
|
if (ref) {
|
||||||
this._sgWidget.start(ref);
|
if (this._sgWidget) this._sgWidget.start(ref);
|
||||||
} else {
|
} else {
|
||||||
this._resetWidget(this.props);
|
this._resetWidget(this.props);
|
||||||
}
|
}
|
||||||
|
@ -209,7 +219,7 @@ export default class AppTile extends React.Component {
|
||||||
// Delete the widget from the persisted store for good measure.
|
// Delete the widget from the persisted store for good measure.
|
||||||
PersistedElement.destroyElement(this._persistKey);
|
PersistedElement.destroyElement(this._persistKey);
|
||||||
|
|
||||||
this._sgWidget.stop({forceDestroy: true});
|
if (this._sgWidget) this._sgWidget.stop({forceDestroy: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onWidgetPrepared = () => {
|
_onWidgetPrepared = () => {
|
||||||
|
@ -340,7 +350,13 @@ export default class AppTile extends React.Component {
|
||||||
<Spinner message={_t("Loading...")} />
|
<Spinner message={_t("Loading...")} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
if (!this.state.hasPermissionToLoad) {
|
if (this._sgWidget === null) {
|
||||||
|
appTileBody = (
|
||||||
|
<div className={appTileBodyClass} style={appTileBodyStyles}>
|
||||||
|
<AppWarning errorMsg={_t("Error loading Widget")} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (!this.state.hasPermissionToLoad) {
|
||||||
// only possible for room widgets, can assert this.props.room here
|
// only possible for room widgets, can assert this.props.room here
|
||||||
const isEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
|
const isEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
|
||||||
appTileBody = (
|
appTileBody = (
|
||||||
|
@ -364,7 +380,7 @@ export default class AppTile extends React.Component {
|
||||||
if (this.isMixedContent()) {
|
if (this.isMixedContent()) {
|
||||||
appTileBody = (
|
appTileBody = (
|
||||||
<div className={appTileBodyClass} style={appTileBodyStyles}>
|
<div className={appTileBodyClass} style={appTileBodyStyles}>
|
||||||
<AppWarning errorMsg="Error - Mixed content" />
|
<AppWarning errorMsg={_t("Error - Mixed content")} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -417,6 +433,8 @@ export default class AppTile extends React.Component {
|
||||||
onFinished={this._closeContextMenu}
|
onFinished={this._closeContextMenu}
|
||||||
showUnpin={!this.props.userWidget}
|
showUnpin={!this.props.userWidget}
|
||||||
userWidget={this.props.userWidget}
|
userWidget={this.props.userWidget}
|
||||||
|
onEditClick={this.props.onEditClick}
|
||||||
|
onDeleteClick={this.props.onDeleteClick}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,7 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
|
||||||
mxEvent={event}
|
mxEvent={event}
|
||||||
layout={this.props.layout}
|
layout={this.props.layout}
|
||||||
enableFlair={SettingsStore.getValue(UIFeature.Flair)}
|
enableFlair={SettingsStore.getValue(UIFeature.Flair)}
|
||||||
|
as="div"
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,14 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {forwardRef, ReactNode} from "react";
|
import React, {forwardRef, ReactNode, ReactChildren} from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
className: string;
|
className: string;
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: ReactNode;
|
subtitle?: ReactNode;
|
||||||
|
children?: ReactChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EventTileBubble = forwardRef<HTMLDivElement, IProps>(({ className, title, subtitle, children }, ref) => {
|
const EventTileBubble = forwardRef<HTMLDivElement, IProps>(({ className, title, subtitle, children }, ref) => {
|
||||||
|
|
|
@ -15,24 +15,31 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Flair from '../elements/Flair.js';
|
import Flair from '../elements/Flair.js';
|
||||||
import FlairStore from '../../../stores/FlairStore';
|
import FlairStore from '../../../stores/FlairStore';
|
||||||
import {getUserNameColorClass} from '../../../utils/FormattingUtils';
|
import {getUserNameColorClass} from '../../../utils/FormattingUtils';
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||||
|
import MatrixEvent from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
mxEvent: MatrixEvent;
|
||||||
|
onClick(): void;
|
||||||
|
enableFlair: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
userGroups;
|
||||||
|
relatedGroups;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.messages.SenderProfile")
|
@replaceableComponent("views.messages.SenderProfile")
|
||||||
export default class SenderProfile extends React.Component {
|
export default class SenderProfile extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
|
||||||
mxEvent: PropTypes.object.isRequired, // event whose sender we're showing
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
static contextType = MatrixClientContext;
|
static contextType = MatrixClientContext;
|
||||||
|
private unmounted: boolean;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props)
|
||||||
const senderId = this.props.mxEvent.getSender();
|
const senderId = this.props.mxEvent.getSender();
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -40,6 +47,7 @@ export default class SenderProfile extends React.Component {
|
||||||
relatedGroups: [],
|
relatedGroups: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.unmounted = false;
|
this.unmounted = false;
|
||||||
this._updateRelatedGroups();
|
this._updateRelatedGroups();
|
||||||
|
@ -100,14 +108,26 @@ export default class SenderProfile extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const {mxEvent} = this.props;
|
const {mxEvent} = this.props;
|
||||||
const colorClass = getUserNameColorClass(mxEvent.getSender());
|
const colorClass = getUserNameColorClass(mxEvent.getSender());
|
||||||
const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
|
|
||||||
const {msgtype} = mxEvent.getContent();
|
const {msgtype} = mxEvent.getContent();
|
||||||
|
|
||||||
|
const disambiguate = mxEvent.sender?.disambiguate;
|
||||||
|
const displayName = mxEvent.sender?.rawDisplayName || mxEvent.getSender() || "";
|
||||||
|
const mxid = mxEvent.sender?.userId || mxEvent.getSender() || "";
|
||||||
|
|
||||||
if (msgtype === 'm.emote') {
|
if (msgtype === 'm.emote') {
|
||||||
return null; // emote message must include the name so don't duplicate it
|
return null; // emote message must include the name so don't duplicate it
|
||||||
}
|
}
|
||||||
|
|
||||||
let flair = null;
|
let mxidElement;
|
||||||
|
if (disambiguate) {
|
||||||
|
mxidElement = (
|
||||||
|
<span className="mx_SenderProfile_mxid">
|
||||||
|
{ mxid }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let flair;
|
||||||
if (this.props.enableFlair) {
|
if (this.props.enableFlair) {
|
||||||
const displayedGroups = this._getDisplayedGroups(
|
const displayedGroups = this._getDisplayedGroups(
|
||||||
this.state.userGroups, this.state.relatedGroups,
|
this.state.userGroups, this.state.relatedGroups,
|
||||||
|
@ -119,13 +139,12 @@ export default class SenderProfile extends React.Component {
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nameElem = name || '';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_SenderProfile mx_SenderProfile_hover" dir="auto" onClick={this.props.onClick}>
|
<div className="mx_SenderProfile mx_SenderProfile_hover" dir="auto" onClick={this.props.onClick}>
|
||||||
<span className={`mx_SenderProfile_name ${colorClass}`}>
|
<span className={`mx_SenderProfile_displayName ${colorClass}`}>
|
||||||
{ nameElem }
|
{ displayName }
|
||||||
</span>
|
</span>
|
||||||
|
{ mxidElement }
|
||||||
{ flair }
|
{ flair }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
|
@ -155,12 +155,24 @@ const PinnedMessagesCard = ({ room, onClose }: IProps) => {
|
||||||
|
|
||||||
// show them in reverse, with latest pinned at the top
|
// show them in reverse, with latest pinned at the top
|
||||||
content = pinnedEvents.filter(Boolean).reverse().map(ev => (
|
content = pinnedEvents.filter(Boolean).reverse().map(ev => (
|
||||||
<PinnedEventTile key={ev.getId()} room={room} event={ev} onUnpinClicked={onUnpinClicked} />
|
<PinnedEventTile key={ev.getId()} room={room} event={ev} onUnpinClicked={() => onUnpinClicked(ev)} />
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
content = <div className="mx_RightPanel_empty mx_PinnedMessagesCard_empty">
|
content = <div className="mx_PinnedMessagesCard_empty">
|
||||||
<h2>{_t("You’re all caught up")}</h2>
|
<div>
|
||||||
<p>{_t("You have no visible notifications.")}</p>
|
{ /* XXX: We reuse the classes for simplicity, but deliberately not the components for non-interactivity. */ }
|
||||||
|
<div className="mx_PinnedMessagesCard_MessageActionBar">
|
||||||
|
<div className="mx_MessageActionBar_maskButton mx_MessageActionBar_reactButton" />
|
||||||
|
<div className="mx_MessageActionBar_maskButton mx_MessageActionBar_replyButton" />
|
||||||
|
<div className="mx_MessageActionBar_maskButton mx_MessageActionBar_optionsButton" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>{ _t("Nothing pinned, yet") }</h2>
|
||||||
|
{ _t("If you have permissions, open the menu on any message and select " +
|
||||||
|
"<b>Pin</b> to stick them here.", {}, {
|
||||||
|
b: sub => <b>{ sub }</b>,
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
|
|
||||||
import ReplyThread from "../elements/ReplyThread";
|
import ReplyThread from "../elements/ReplyThread";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import * as TextForEvent from "../../../TextForEvent";
|
import { hasText } from "../../../TextForEvent";
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
@ -644,7 +644,18 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
// return early if there are no read receipts
|
// return early if there are no read receipts
|
||||||
if (!this.props.readReceipts || this.props.readReceipts.length === 0) {
|
if (!this.props.readReceipts || this.props.readReceipts.length === 0) {
|
||||||
return null;
|
// We currently must include `mx_EventTile_readAvatars` in the DOM
|
||||||
|
// of all events, as it is the positioned parent of the animated
|
||||||
|
// read receipts. We can't let it unmount when a receipt moves
|
||||||
|
// events, so for now we mount it for all events. Without it, the
|
||||||
|
// animation will start from the top of the timeline (because it
|
||||||
|
// lost its container).
|
||||||
|
// See also https://github.com/vector-im/element-web/issues/17561
|
||||||
|
return (
|
||||||
|
<div className="mx_EventTile_msgOption">
|
||||||
|
<span className="mx_EventTile_readAvatars" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker');
|
const ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker');
|
||||||
|
@ -652,11 +663,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
const receiptOffset = 15;
|
const receiptOffset = 15;
|
||||||
let left = 0;
|
let left = 0;
|
||||||
|
|
||||||
const receipts = this.props.readReceipts || [];
|
const receipts = this.props.readReceipts;
|
||||||
|
|
||||||
if (receipts.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < receipts.length; ++i) {
|
for (let i = 0; i < receipts.length; ++i) {
|
||||||
const receipt = receipts[i];
|
const receipt = receipts[i];
|
||||||
|
@ -1052,58 +1059,65 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
switch (this.props.tileShape) {
|
switch (this.props.tileShape) {
|
||||||
case 'notif': {
|
case 'notif': {
|
||||||
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
|
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
|
||||||
return (
|
return React.createElement(this.props.as || "li", {
|
||||||
<li className={classes} aria-live={ariaLive} aria-atomic="true" data-scroll-tokens={scrollToken}>
|
"className": classes,
|
||||||
<div className="mx_EventTile_roomName">
|
"aria-live": ariaLive,
|
||||||
<RoomAvatar room={room} width={28} height={28} />
|
"aria-atomic": true,
|
||||||
<a href={permalink} onClick={this.onPermalinkClicked}>
|
"data-scroll-tokens": scrollToken,
|
||||||
{ room ? room.name : '' }
|
}, [
|
||||||
</a>
|
<div className="mx_EventTile_roomName" key="mx_EventTile_roomName">
|
||||||
</div>
|
<RoomAvatar room={room} width={28} height={28} />
|
||||||
<div className="mx_EventTile_senderDetails">
|
<a href={permalink} onClick={this.onPermalinkClicked}>
|
||||||
{ avatar }
|
{ room ? room.name : '' }
|
||||||
<a href={permalink} onClick={this.onPermalinkClicked}>
|
</a>
|
||||||
{ sender }
|
</div>,
|
||||||
{ timestamp }
|
<div className="mx_EventTile_senderDetails" key="mx_EventTile_senderDetails">
|
||||||
</a>
|
{ avatar }
|
||||||
</div>
|
<a href={permalink} onClick={this.onPermalinkClicked}>
|
||||||
<div className="mx_EventTile_line">
|
{ sender }
|
||||||
<EventTileType ref={this.tile}
|
{ timestamp }
|
||||||
mxEvent={this.props.mxEvent}
|
</a>
|
||||||
highlights={this.props.highlights}
|
</div>,
|
||||||
highlightLink={this.props.highlightLink}
|
<div className="mx_EventTile_line" key="mx_EventTile_line">
|
||||||
showUrlPreview={this.props.showUrlPreview}
|
<EventTileType ref={this.tile}
|
||||||
onHeightChanged={this.props.onHeightChanged}
|
mxEvent={this.props.mxEvent}
|
||||||
/>
|
highlights={this.props.highlights}
|
||||||
</div>
|
highlightLink={this.props.highlightLink}
|
||||||
</li>
|
showUrlPreview={this.props.showUrlPreview}
|
||||||
);
|
onHeightChanged={this.props.onHeightChanged}
|
||||||
|
/>
|
||||||
|
</div>,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
case 'file_grid': {
|
case 'file_grid': {
|
||||||
return (
|
return React.createElement(this.props.as || "li", {
|
||||||
<li className={classes} aria-live={ariaLive} aria-atomic="true" data-scroll-tokens={scrollToken}>
|
"className": classes,
|
||||||
<div className="mx_EventTile_line">
|
"aria-live": ariaLive,
|
||||||
<EventTileType ref={this.tile}
|
"aria-atomic": true,
|
||||||
mxEvent={this.props.mxEvent}
|
"data-scroll-tokens": scrollToken,
|
||||||
highlights={this.props.highlights}
|
}, [
|
||||||
highlightLink={this.props.highlightLink}
|
<div className="mx_EventTile_line" key="mx_EventTile_line">
|
||||||
showUrlPreview={this.props.showUrlPreview}
|
<EventTileType ref={this.tile}
|
||||||
tileShape={this.props.tileShape}
|
mxEvent={this.props.mxEvent}
|
||||||
onHeightChanged={this.props.onHeightChanged}
|
highlights={this.props.highlights}
|
||||||
/>
|
highlightLink={this.props.highlightLink}
|
||||||
|
showUrlPreview={this.props.showUrlPreview}
|
||||||
|
tileShape={this.props.tileShape}
|
||||||
|
onHeightChanged={this.props.onHeightChanged}
|
||||||
|
/>
|
||||||
|
</div>,
|
||||||
|
<a
|
||||||
|
className="mx_EventTile_senderDetailsLink"
|
||||||
|
key="mx_EventTile_senderDetailsLink"
|
||||||
|
href={permalink}
|
||||||
|
onClick={this.onPermalinkClicked}
|
||||||
|
>
|
||||||
|
<div className="mx_EventTile_senderDetails">
|
||||||
|
{ sender }
|
||||||
|
{ timestamp }
|
||||||
</div>
|
</div>
|
||||||
<a
|
</a>,
|
||||||
className="mx_EventTile_senderDetailsLink"
|
]);
|
||||||
href={permalink}
|
|
||||||
onClick={this.onPermalinkClicked}
|
|
||||||
>
|
|
||||||
<div className="mx_EventTile_senderDetails">
|
|
||||||
{ sender }
|
|
||||||
{ timestamp }
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
@ -1175,7 +1189,7 @@ export function haveTileForEvent(e) {
|
||||||
const handler = getHandlerTile(e);
|
const handler = getHandlerTile(e);
|
||||||
if (handler === undefined) return false;
|
if (handler === undefined) return false;
|
||||||
if (handler === 'messages.TextualEvent') {
|
if (handler === 'messages.TextualEvent') {
|
||||||
return TextForEvent.textForEvent(e) !== '';
|
return hasText(e);
|
||||||
} else if (handler === 'messages.RoomCreate') {
|
} else if (handler === 'messages.RoomCreate') {
|
||||||
return Boolean(e.getContent()['predecessor']);
|
return Boolean(e.getContent()['predecessor']);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 Vector Creations Ltd
|
|
||||||
Copyright 2017 Michael Telatynski
|
|
||||||
|
|
||||||
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 from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { _t } from '../../../languageHandler';
|
|
||||||
import {Key} from '../../../Keyboard';
|
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.ForwardMessage")
|
|
||||||
export default class ForwardMessage extends React.Component {
|
|
||||||
static propTypes = {
|
|
||||||
onCancelClick: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
document.addEventListener('keydown', this._onKeyDown);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
document.removeEventListener('keydown', this._onKeyDown);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onKeyDown = ev => {
|
|
||||||
switch (ev.key) {
|
|
||||||
case Key.ESCAPE:
|
|
||||||
this.props.onCancelClick();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="mx_ForwardMessage">
|
|
||||||
<h1>{ _t('Please select the destination room for this message') }</h1>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -31,6 +31,17 @@ import dis from "../../../dispatcher/dispatcher";
|
||||||
import SpaceStore from "../../../stores/SpaceStore";
|
import SpaceStore from "../../../stores/SpaceStore";
|
||||||
import {showSpaceInvite} from "../../../utils/space";
|
import {showSpaceInvite} from "../../../utils/space";
|
||||||
|
|
||||||
|
import { privateShouldBeEncrypted } from "../../../createRoom";
|
||||||
|
|
||||||
|
import EventTileBubble from "../messages/EventTileBubble";
|
||||||
|
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
|
||||||
|
|
||||||
|
function hasExpectedEncryptionSettings(room): boolean {
|
||||||
|
const isEncrypted: boolean = room._client?.isRoomEncrypted(room.roomId);
|
||||||
|
const isPublic: boolean = room.getJoinRule() === "public";
|
||||||
|
return isPublic || !privateShouldBeEncrypted() || isEncrypted;
|
||||||
|
}
|
||||||
|
|
||||||
const NewRoomIntro = () => {
|
const NewRoomIntro = () => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const {room, roomId} = useContext(RoomContext);
|
const {room, roomId} = useContext(RoomContext);
|
||||||
|
@ -166,7 +177,31 @@ const NewRoomIntro = () => {
|
||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openRoomSettings(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
dis.dispatch({
|
||||||
|
action: "open_room_settings",
|
||||||
|
initial_tab_id: ROOM_SECURITY_TAB,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const sub2 = _t(
|
||||||
|
"Your private messages are normally encrypted, but this room isn't. "+
|
||||||
|
"Usually this is due to an unsupported device or method being used, " +
|
||||||
|
"like email invites. <a>Enable encryption in settings.</a>", {},
|
||||||
|
{ a: sub => <a onClick={openRoomSettings} href="#">{sub}</a> },
|
||||||
|
);
|
||||||
|
|
||||||
return <div className="mx_NewRoomIntro">
|
return <div className="mx_NewRoomIntro">
|
||||||
|
|
||||||
|
{ !hasExpectedEncryptionSettings(room) && (
|
||||||
|
<EventTileBubble
|
||||||
|
className="mx_cryptoEvent mx_cryptoEvent_icon_warning"
|
||||||
|
title={_t("End-to-end encryption isn't enabled")}
|
||||||
|
subtitle={sub2}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{ body }
|
{ body }
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -89,7 +89,7 @@ export default class ReplyPreview extends React.Component {
|
||||||
<div className="mx_ReplyPreview_tile">
|
<div className="mx_ReplyPreview_tile">
|
||||||
<ReplyTile
|
<ReplyTile
|
||||||
isRedacted={this.state.event.isRedacted()}
|
isRedacted={this.state.event.isRedacted()}
|
||||||
mxEvent={this.state.event}
|
mxEvent={this.state.event}
|
||||||
permalinkCreator={this.props.permalinkCreator}
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,7 +22,6 @@ import { _t } from '../../../languageHandler';
|
||||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||||
import RateLimitedFunc from '../../../ratelimitedfunc';
|
import RateLimitedFunc from '../../../ratelimitedfunc';
|
||||||
|
|
||||||
import { CancelButton } from './SimpleRoomHeader';
|
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import RoomHeaderButtons from '../right_panel/RoomHeaderButtons';
|
import RoomHeaderButtons from '../right_panel/RoomHeaderButtons';
|
||||||
import E2EIcon from './E2EIcon';
|
import E2EIcon from './E2EIcon';
|
||||||
|
@ -42,7 +41,6 @@ export default class RoomHeader extends React.Component {
|
||||||
onSettingsClick: PropTypes.func,
|
onSettingsClick: PropTypes.func,
|
||||||
onSearchClick: PropTypes.func,
|
onSearchClick: PropTypes.func,
|
||||||
onLeaveClick: PropTypes.func,
|
onLeaveClick: PropTypes.func,
|
||||||
onCancelClick: PropTypes.func,
|
|
||||||
e2eStatus: PropTypes.string,
|
e2eStatus: PropTypes.string,
|
||||||
onAppsClick: PropTypes.func,
|
onAppsClick: PropTypes.func,
|
||||||
appsShown: PropTypes.bool,
|
appsShown: PropTypes.bool,
|
||||||
|
@ -52,7 +50,6 @@ export default class RoomHeader extends React.Component {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
editing: false,
|
editing: false,
|
||||||
inRoom: false,
|
inRoom: false,
|
||||||
onCancelClick: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -83,11 +80,6 @@ export default class RoomHeader extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let searchStatus = null;
|
let searchStatus = null;
|
||||||
let cancelButton = null;
|
|
||||||
|
|
||||||
if (this.props.onCancelClick) {
|
|
||||||
cancelButton = <CancelButton onClick={this.props.onCancelClick} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't display the search count until the search completes and
|
// don't display the search count until the search completes and
|
||||||
// gives us a valid (possibly zero) searchCount.
|
// gives us a valid (possibly zero) searchCount.
|
||||||
|
@ -207,7 +199,6 @@ export default class RoomHeader extends React.Component {
|
||||||
<div className="mx_RoomHeader_e2eIcon">{ e2eIcon }</div>
|
<div className="mx_RoomHeader_e2eIcon">{ e2eIcon }</div>
|
||||||
{ name }
|
{ name }
|
||||||
{ topicElement }
|
{ topicElement }
|
||||||
{ cancelButton }
|
|
||||||
{ rightRow }
|
{ rightRow }
|
||||||
<RoomHeaderButtons room={this.props.room} />
|
<RoomHeaderButtons room={this.props.room} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -55,6 +55,7 @@ interface IProps {
|
||||||
onKeyDown: (ev: React.KeyboardEvent) => void;
|
onKeyDown: (ev: React.KeyboardEvent) => void;
|
||||||
onFocus: (ev: React.FocusEvent) => void;
|
onFocus: (ev: React.FocusEvent) => void;
|
||||||
onBlur: (ev: React.FocusEvent) => void;
|
onBlur: (ev: React.FocusEvent) => void;
|
||||||
|
onResize: () => void;
|
||||||
onListCollapse?: (isExpanded: boolean) => void;
|
onListCollapse?: (isExpanded: boolean) => void;
|
||||||
resizeNotifier: ResizeNotifier;
|
resizeNotifier: ResizeNotifier;
|
||||||
isMinimized: boolean;
|
isMinimized: boolean;
|
||||||
|
@ -404,7 +405,9 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
const newSublists = objectWithOnly(newLists, newListIds);
|
const newSublists = objectWithOnly(newLists, newListIds);
|
||||||
const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v));
|
const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v));
|
||||||
|
|
||||||
this.setState({sublists, isNameFiltering});
|
this.setState({sublists, isNameFiltering}, () => {
|
||||||
|
this.props.onResize();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -454,8 +454,9 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
const sublist = possibleSticky.parentElement.parentElement;
|
const sublist = possibleSticky.parentElement.parentElement;
|
||||||
const list = sublist.parentElement.parentElement;
|
const list = sublist.parentElement.parentElement;
|
||||||
// the scrollTop is capped at the height of the header in LeftPanel, the top header is always sticky
|
// the scrollTop is capped at the height of the header in LeftPanel, the top header is always sticky
|
||||||
const isAtTop = list.scrollTop <= HEADER_HEIGHT;
|
const listScrollTop = Math.round(list.scrollTop);
|
||||||
const isAtBottom = list.scrollTop >= list.scrollHeight - list.offsetHeight;
|
const isAtTop = listScrollTop <= Math.round(HEADER_HEIGHT);
|
||||||
|
const isAtBottom = listScrollTop >= Math.round(list.scrollHeight - list.offsetHeight);
|
||||||
const isStickyTop = possibleSticky.classList.contains('mx_RoomSublist_headerContainer_stickyTop');
|
const isStickyTop = possibleSticky.classList.contains('mx_RoomSublist_headerContainer_stickyTop');
|
||||||
const isStickyBottom = possibleSticky.classList.contains('mx_RoomSublist_headerContainer_stickyBottom');
|
const isStickyBottom = possibleSticky.classList.contains('mx_RoomSublist_headerContainer_stickyBottom');
|
||||||
|
|
||||||
|
|
|
@ -16,23 +16,9 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
// cancel button which is shared between room header and simple room header
|
|
||||||
export function CancelButton(props) {
|
|
||||||
const {onClick} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AccessibleButton className='mx_RoomHeader_cancelButton' onClick={onClick}>
|
|
||||||
<img src={require("../../../../res/img/cancel.svg")} className='mx_filterFlipColor'
|
|
||||||
width="18" height="18" alt={_t("Cancel")} />
|
|
||||||
</AccessibleButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A stripped-down room header used for things like the user settings
|
* A stripped-down room header used for things like the user settings
|
||||||
* and room directory.
|
* and room directory.
|
||||||
|
@ -41,18 +27,13 @@ export function CancelButton(props) {
|
||||||
export default class SimpleRoomHeader extends React.Component {
|
export default class SimpleRoomHeader extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
onCancelClick: PropTypes.func,
|
|
||||||
|
|
||||||
// `src` to a TintableSvg. Optional.
|
// `src` to a TintableSvg. Optional.
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let cancelButton;
|
|
||||||
let icon;
|
let icon;
|
||||||
if (this.props.onCancelClick) {
|
|
||||||
cancelButton = <CancelButton onClick={this.props.onCancelClick} />;
|
|
||||||
}
|
|
||||||
if (this.props.icon) {
|
if (this.props.icon) {
|
||||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||||
icon = <TintableSvg
|
icon = <TintableSvg
|
||||||
|
@ -66,7 +47,6 @@ export default class SimpleRoomHeader extends React.Component {
|
||||||
<div className="mx_RoomHeader_simpleHeader">
|
<div className="mx_RoomHeader_simpleHeader">
|
||||||
{ icon }
|
{ icon }
|
||||||
{ this.props.title }
|
{ this.props.title }
|
||||||
{ cancelButton }
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -367,7 +367,7 @@ export default class Stickerpicker extends React.PureComponent {
|
||||||
/**
|
/**
|
||||||
* Launch the integration manager on the stickers integration page
|
* Launch the integration manager on the stickers integration page
|
||||||
*/
|
*/
|
||||||
_launchManageIntegrations() {
|
_launchManageIntegrations = () => {
|
||||||
// TODO: Open the right integration manager for the widget
|
// TODO: Open the right integration manager for the widget
|
||||||
if (SettingsStore.getValue("feature_many_integration_managers")) {
|
if (SettingsStore.getValue("feature_many_integration_managers")) {
|
||||||
IntegrationManagers.sharedInstance().openAll(
|
IntegrationManagers.sharedInstance().openAll(
|
||||||
|
@ -382,7 +382,7 @@ export default class Stickerpicker extends React.PureComponent {
|
||||||
this.state.widgetId,
|
this.state.widgetId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let stickerPicker;
|
let stickerPicker;
|
||||||
|
@ -401,7 +401,7 @@ export default class Stickerpicker extends React.PureComponent {
|
||||||
key="controls_hide_stickers"
|
key="controls_hide_stickers"
|
||||||
className={className}
|
className={className}
|
||||||
onClick={this._onHideStickersClick}
|
onClick={this._onHideStickersClick}
|
||||||
active={this.state.showStickers}
|
active={this.state.showStickers.toString()}
|
||||||
title={_t("Hide Stickers")}
|
title={_t("Hide Stickers")}
|
||||||
>
|
>
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
|
|
|
@ -22,7 +22,7 @@ import FocusLock from "react-focus-lock";
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import {ChevronFace, ContextMenu} from "../../structures/ContextMenu";
|
import {ChevronFace, ContextMenu} from "../../structures/ContextMenu";
|
||||||
import createRoom, {IStateEvent, Preset} from "../../../createRoom";
|
import createRoom from "../../../createRoom";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import {SpaceAvatar} from "./SpaceBasicSettings";
|
import {SpaceAvatar} from "./SpaceBasicSettings";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
@ -33,6 +33,8 @@ import {USER_LABS_TAB} from "../dialogs/UserSettingsDialog";
|
||||||
import Field from "../elements/Field";
|
import Field from "../elements/Field";
|
||||||
import withValidation from "../elements/Validation";
|
import withValidation from "../elements/Validation";
|
||||||
import {SpaceFeedbackPrompt} from "../../structures/SpaceRoomView";
|
import {SpaceFeedbackPrompt} from "../../structures/SpaceRoomView";
|
||||||
|
import { Preset } from "matrix-js-sdk/src/@types/partials";
|
||||||
|
import { ICreateRoomStateEvent } from "matrix-js-sdk/src/@types/requests";
|
||||||
|
|
||||||
const SpaceCreateMenuType = ({ title, description, className, onClick }) => {
|
const SpaceCreateMenuType = ({ title, description, className, onClick }) => {
|
||||||
return (
|
return (
|
||||||
|
@ -81,7 +83,7 @@ const SpaceCreateMenu = ({ onFinished }) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: IStateEvent[] = [
|
const initialState: ICreateRoomStateEvent[] = [
|
||||||
{
|
{
|
||||||
type: EventType.RoomHistoryVisibility,
|
type: EventType.RoomHistoryVisibility,
|
||||||
content: {
|
content: {
|
||||||
|
|
|
@ -31,7 +31,6 @@ const RoomContext = createContext<IState>({
|
||||||
canPeek: false,
|
canPeek: false,
|
||||||
showApps: false,
|
showApps: false,
|
||||||
isPeeking: false,
|
isPeeking: false,
|
||||||
showReadReceipts: true,
|
|
||||||
showRightPanel: true,
|
showRightPanel: true,
|
||||||
joining: false,
|
joining: false,
|
||||||
atEndOfLiveTimeline: true,
|
atEndOfLiveTimeline: true,
|
||||||
|
@ -41,6 +40,12 @@ const RoomContext = createContext<IState>({
|
||||||
canReact: false,
|
canReact: false,
|
||||||
canReply: false,
|
canReply: false,
|
||||||
layout: Layout.Group,
|
layout: Layout.Group,
|
||||||
|
lowBandwidth: false,
|
||||||
|
showReadReceipts: true,
|
||||||
|
showRedactions: true,
|
||||||
|
showJoinLeaves: true,
|
||||||
|
showAvatarChanges: true,
|
||||||
|
showDisplaynameChanges: true,
|
||||||
matrixClientIsReady: false,
|
matrixClientIsReady: false,
|
||||||
dragCounter: 0,
|
dragCounter: 0,
|
||||||
});
|
});
|
||||||
|
|
|
@ -35,53 +35,15 @@ import { VIRTUAL_ROOM_EVENT_TYPE } from "./CallHandler";
|
||||||
import SpaceStore from "./stores/SpaceStore";
|
import SpaceStore from "./stores/SpaceStore";
|
||||||
import { makeSpaceParentEvent } from "./utils/space";
|
import { makeSpaceParentEvent } from "./utils/space";
|
||||||
import { Action } from "./dispatcher/actions"
|
import { Action } from "./dispatcher/actions"
|
||||||
|
import { ICreateRoomOpts } from "matrix-js-sdk/src/@types/requests";
|
||||||
|
import { Preset, Visibility } from "matrix-js-sdk/src/@types/partials";
|
||||||
|
|
||||||
// we define a number of interfaces which take their names from the js-sdk
|
// we define a number of interfaces which take their names from the js-sdk
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
|
|
||||||
// TODO move these interfaces over to js-sdk once it has been typescripted enough to accept them
|
|
||||||
export enum Visibility {
|
|
||||||
Public = "public",
|
|
||||||
Private = "private",
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum Preset {
|
|
||||||
PrivateChat = "private_chat",
|
|
||||||
TrustedPrivateChat = "trusted_private_chat",
|
|
||||||
PublicChat = "public_chat",
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Invite3PID {
|
|
||||||
id_server: string;
|
|
||||||
id_access_token?: string; // this gets injected by the js-sdk
|
|
||||||
medium: string;
|
|
||||||
address: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IStateEvent {
|
|
||||||
type: string;
|
|
||||||
state_key?: string; // defaults to an empty string
|
|
||||||
content: object;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ICreateOpts {
|
|
||||||
visibility?: Visibility;
|
|
||||||
room_alias_name?: string;
|
|
||||||
name?: string;
|
|
||||||
topic?: string;
|
|
||||||
invite?: string[];
|
|
||||||
invite_3pid?: Invite3PID[];
|
|
||||||
room_version?: string;
|
|
||||||
creation_content?: object;
|
|
||||||
initial_state?: IStateEvent[];
|
|
||||||
preset?: Preset;
|
|
||||||
is_direct?: boolean;
|
|
||||||
power_level_content_override?: object;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IOpts {
|
export interface IOpts {
|
||||||
dmUserId?: string;
|
dmUserId?: string;
|
||||||
createOpts?: ICreateOpts;
|
createOpts?: ICreateRoomOpts;
|
||||||
spinner?: boolean;
|
spinner?: boolean;
|
||||||
guestAccess?: boolean;
|
guestAccess?: boolean;
|
||||||
encryption?: boolean;
|
encryption?: boolean;
|
||||||
|
@ -91,12 +53,6 @@ export interface IOpts {
|
||||||
parentSpace?: Room;
|
parentSpace?: Room;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IInvite3PID {
|
|
||||||
id_server: string,
|
|
||||||
medium: 'email',
|
|
||||||
address: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new room, and switch to it.
|
* Create a new room, and switch to it.
|
||||||
*
|
*
|
||||||
|
@ -136,7 +92,7 @@ export default function createRoom(opts: IOpts): Promise<string | null> {
|
||||||
const defaultPreset = opts.dmUserId ? Preset.TrustedPrivateChat : Preset.PrivateChat;
|
const defaultPreset = opts.dmUserId ? Preset.TrustedPrivateChat : Preset.PrivateChat;
|
||||||
|
|
||||||
// set some defaults for the creation
|
// set some defaults for the creation
|
||||||
const createOpts = opts.createOpts || {};
|
const createOpts: ICreateRoomOpts = opts.createOpts || {};
|
||||||
createOpts.preset = createOpts.preset || defaultPreset;
|
createOpts.preset = createOpts.preset || defaultPreset;
|
||||||
createOpts.visibility = createOpts.visibility || Visibility.Private;
|
createOpts.visibility = createOpts.visibility || Visibility.Private;
|
||||||
if (opts.dmUserId && createOpts.invite === undefined) {
|
if (opts.dmUserId && createOpts.invite === undefined) {
|
||||||
|
|
|
@ -35,8 +35,8 @@ export const useSettingValue = <T>(settingName: string, roomId: string = null, e
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hook to fetch whether a feature is enabled and dynamically update when that changes
|
// Hook to fetch whether a feature is enabled and dynamically update when that changes
|
||||||
export const useFeatureEnabled = (featureName: string, roomId: string = null) => {
|
export const useFeatureEnabled = (featureName: string, roomId: string = null): boolean => {
|
||||||
const [enabled, setEnabled] = useState(SettingsStore.getValue(featureName, roomId));
|
const [enabled, setEnabled] = useState(SettingsStore.getValue<boolean>(featureName, roomId));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ref = SettingsStore.watchSetting(featureName, roomId, () => {
|
const ref = SettingsStore.watchSetting(featureName, roomId, () => {
|
||||||
|
|
|
@ -556,8 +556,8 @@
|
||||||
"%(senderName)s made future room history visible to all room members.": "%(senderName)s made future room history visible to all room members.",
|
"%(senderName)s made future room history visible to all room members.": "%(senderName)s made future room history visible to all room members.",
|
||||||
"%(senderName)s made future room history visible to anyone.": "%(senderName)s made future room history visible to anyone.",
|
"%(senderName)s made future room history visible to anyone.": "%(senderName)s made future room history visible to anyone.",
|
||||||
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s made future room history visible to unknown (%(visibility)s).",
|
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s made future room history visible to unknown (%(visibility)s).",
|
||||||
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s",
|
|
||||||
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s changed the power level of %(powerLevelDiffText)s.",
|
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s changed the power level of %(powerLevelDiffText)s.",
|
||||||
|
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s",
|
||||||
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s changed the pinned messages for the room.",
|
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s changed the pinned messages for the room.",
|
||||||
"%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s",
|
"%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s",
|
||||||
"%(widgetName)s widget added by %(senderName)s": "%(widgetName)s widget added by %(senderName)s",
|
"%(widgetName)s widget added by %(senderName)s": "%(widgetName)s widget added by %(senderName)s",
|
||||||
|
@ -1473,7 +1473,6 @@
|
||||||
"Encrypting your message...": "Encrypting your message...",
|
"Encrypting your message...": "Encrypting your message...",
|
||||||
"Your message was sent": "Your message was sent",
|
"Your message was sent": "Your message was sent",
|
||||||
"Failed to send": "Failed to send",
|
"Failed to send": "Failed to send",
|
||||||
"Please select the destination room for this message": "Please select the destination room for this message",
|
|
||||||
"Scroll to most recent messages": "Scroll to most recent messages",
|
"Scroll to most recent messages": "Scroll to most recent messages",
|
||||||
"Close preview": "Close preview",
|
"Close preview": "Close preview",
|
||||||
"and %(count)s others...|other": "and %(count)s others...",
|
"and %(count)s others...|other": "and %(count)s others...",
|
||||||
|
@ -1510,6 +1509,8 @@
|
||||||
"Invite to just this room": "Invite to just this room",
|
"Invite to just this room": "Invite to just this room",
|
||||||
"Add a photo, so people can easily spot your room.": "Add a photo, so people can easily spot your room.",
|
"Add a photo, so people can easily spot your room.": "Add a photo, so people can easily spot your room.",
|
||||||
"This is the start of <roomName/>.": "This is the start of <roomName/>.",
|
"This is the start of <roomName/>.": "This is the start of <roomName/>.",
|
||||||
|
"Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites. <a>Enable encryption in settings.</a>": "Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites. <a>Enable encryption in settings.</a>",
|
||||||
|
"End-to-end encryption isn't enabled": "End-to-end encryption isn't enabled",
|
||||||
"Unpin": "Unpin",
|
"Unpin": "Unpin",
|
||||||
"View message": "View message",
|
"View message": "View message",
|
||||||
"%(duration)ss": "%(duration)ss",
|
"%(duration)ss": "%(duration)ss",
|
||||||
|
@ -1720,8 +1721,8 @@
|
||||||
"The homeserver the user you’re verifying is connected to": "The homeserver the user you’re verifying is connected to",
|
"The homeserver the user you’re verifying is connected to": "The homeserver the user you’re verifying is connected to",
|
||||||
"Yours, or the other users’ internet connection": "Yours, or the other users’ internet connection",
|
"Yours, or the other users’ internet connection": "Yours, or the other users’ internet connection",
|
||||||
"Yours, or the other users’ session": "Yours, or the other users’ session",
|
"Yours, or the other users’ session": "Yours, or the other users’ session",
|
||||||
"You’re all caught up": "You’re all caught up",
|
"Nothing pinned, yet": "Nothing pinned, yet",
|
||||||
"You have no visible notifications.": "You have no visible notifications.",
|
"If you have permissions, open the menu on any message and select <b>Pin</b> to stick them here.": "If you have permissions, open the menu on any message and select <b>Pin</b> to stick them here.",
|
||||||
"Pinned messages": "Pinned messages",
|
"Pinned messages": "Pinned messages",
|
||||||
"Room Info": "Room Info",
|
"Room Info": "Room Info",
|
||||||
"You can only pin up to %(count)s widgets|other": "You can only pin up to %(count)s widgets",
|
"You can only pin up to %(count)s widgets|other": "You can only pin up to %(count)s widgets",
|
||||||
|
@ -1929,6 +1930,8 @@
|
||||||
"Widgets do not use message encryption.": "Widgets do not use message encryption.",
|
"Widgets do not use message encryption.": "Widgets do not use message encryption.",
|
||||||
"Widget added by": "Widget added by",
|
"Widget added by": "Widget added by",
|
||||||
"This widget may use cookies.": "This widget may use cookies.",
|
"This widget may use cookies.": "This widget may use cookies.",
|
||||||
|
"Error loading Widget": "Error loading Widget",
|
||||||
|
"Error - Mixed content": "Error - Mixed content",
|
||||||
"Popout widget": "Popout widget",
|
"Popout widget": "Popout widget",
|
||||||
"Use the <a>Desktop app</a> to see all encrypted files": "Use the <a>Desktop app</a> to see all encrypted files",
|
"Use the <a>Desktop app</a> to see all encrypted files": "Use the <a>Desktop app</a> to see all encrypted files",
|
||||||
"Use the <a>Desktop app</a> to search encrypted messages": "Use the <a>Desktop app</a> to search encrypted messages",
|
"Use the <a>Desktop app</a> to search encrypted messages": "Use the <a>Desktop app</a> to search encrypted messages",
|
||||||
|
@ -2207,6 +2210,13 @@
|
||||||
"PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.": "PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.",
|
"PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.": "PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.",
|
||||||
"Report a bug": "Report a bug",
|
"Report a bug": "Report a bug",
|
||||||
"Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.": "Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.",
|
"Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.": "Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.",
|
||||||
|
"You don't have permission to do this": "You don't have permission to do this",
|
||||||
|
"Sending": "Sending",
|
||||||
|
"Sent": "Sent",
|
||||||
|
"Open link": "Open link",
|
||||||
|
"Forward message": "Forward message",
|
||||||
|
"Message preview": "Message preview",
|
||||||
|
"Search for rooms or people": "Search for rooms or people",
|
||||||
"Confirm abort of host creation": "Confirm abort of host creation",
|
"Confirm abort of host creation": "Confirm abort of host creation",
|
||||||
"Are you sure you wish to abort creation of the host? The process cannot be continued.": "Are you sure you wish to abort creation of the host? The process cannot be continued.",
|
"Are you sure you wish to abort creation of the host? The process cannot be continued.": "Are you sure you wish to abort creation of the host? The process cannot be continued.",
|
||||||
"Abort": "Abort",
|
"Abort": "Abort",
|
||||||
|
@ -2250,6 +2260,9 @@
|
||||||
"Start a conversation with someone using their name or username (like <userId/>).": "Start a conversation with someone using their name or username (like <userId/>).",
|
"Start a conversation with someone using their name or username (like <userId/>).": "Start a conversation with someone using their name or username (like <userId/>).",
|
||||||
"This won't invite them to %(communityName)s. To invite someone to %(communityName)s, click <a>here</a>": "This won't invite them to %(communityName)s. To invite someone to %(communityName)s, click <a>here</a>",
|
"This won't invite them to %(communityName)s. To invite someone to %(communityName)s, click <a>here</a>": "This won't invite them to %(communityName)s. To invite someone to %(communityName)s, click <a>here</a>",
|
||||||
"Go": "Go",
|
"Go": "Go",
|
||||||
|
"Some suggestions may be hidden for privacy.": "Some suggestions may be hidden for privacy.",
|
||||||
|
"If you can't see who you’re looking for, send them your invite link below.": "If you can't see who you’re looking for, send them your invite link below.",
|
||||||
|
"Or send invite link": "Or send invite link",
|
||||||
"Unnamed Space": "Unnamed Space",
|
"Unnamed Space": "Unnamed Space",
|
||||||
"Invite to %(roomName)s": "Invite to %(roomName)s",
|
"Invite to %(roomName)s": "Invite to %(roomName)s",
|
||||||
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.": "Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.",
|
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.": "Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.",
|
||||||
|
@ -2631,6 +2644,8 @@
|
||||||
"Create a new community": "Create a new community",
|
"Create a new community": "Create a new community",
|
||||||
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.",
|
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.",
|
||||||
"Communities are changing to Spaces": "Communities are changing to Spaces",
|
"Communities are changing to Spaces": "Communities are changing to Spaces",
|
||||||
|
"You’re all caught up": "You’re all caught up",
|
||||||
|
"You have no visible notifications.": "You have no visible notifications.",
|
||||||
"%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.",
|
"%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.",
|
||||||
"%(brand)s failed to get the public room list.": "%(brand)s failed to get the public room list.",
|
"%(brand)s failed to get the public room list.": "%(brand)s failed to get the public room list.",
|
||||||
"The homeserver may be unavailable or overloaded.": "The homeserver may be unavailable or overloaded.",
|
"The homeserver may be unavailable or overloaded.": "The homeserver may be unavailable or overloaded.",
|
||||||
|
@ -2665,7 +2680,6 @@
|
||||||
"Some of your messages have not been sent": "Some of your messages have not been sent",
|
"Some of your messages have not been sent": "Some of your messages have not been sent",
|
||||||
"Delete all": "Delete all",
|
"Delete all": "Delete all",
|
||||||
"Retry all": "Retry all",
|
"Retry all": "Retry all",
|
||||||
"Sending": "Sending",
|
|
||||||
"You can select all or individual messages to retry or delete": "You can select all or individual messages to retry or delete",
|
"You can select all or individual messages to retry or delete": "You can select all or individual messages to retry or delete",
|
||||||
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
||||||
"Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
|
"Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
|
||||||
|
@ -2726,6 +2740,8 @@
|
||||||
"A private space to organise your rooms": "A private space to organise your rooms",
|
"A private space to organise your rooms": "A private space to organise your rooms",
|
||||||
"Me and my teammates": "Me and my teammates",
|
"Me and my teammates": "Me and my teammates",
|
||||||
"A private space for you and your teammates": "A private space for you and your teammates",
|
"A private space for you and your teammates": "A private space for you and your teammates",
|
||||||
|
"Teammates might not be able to view or join any private rooms you make.": "Teammates might not be able to view or join any private rooms you make.",
|
||||||
|
"We're working on this as part of the beta, but just want to let you know.": "We're working on this as part of the beta, but just want to let you know.",
|
||||||
"Failed to invite the following users to your space: %(csvUsers)s": "Failed to invite the following users to your space: %(csvUsers)s",
|
"Failed to invite the following users to your space: %(csvUsers)s": "Failed to invite the following users to your space: %(csvUsers)s",
|
||||||
"Inviting...": "Inviting...",
|
"Inviting...": "Inviting...",
|
||||||
"Invite your teammates": "Invite your teammates",
|
"Invite your teammates": "Invite your teammates",
|
||||||
|
|
|
@ -20,6 +20,7 @@ import SettingsStore from "../settings/SettingsStore";
|
||||||
import {_t} from "../languageHandler";
|
import {_t} from "../languageHandler";
|
||||||
import dis from "../dispatcher/dispatcher";
|
import dis from "../dispatcher/dispatcher";
|
||||||
import {SettingLevel} from "../settings/SettingLevel";
|
import {SettingLevel} from "../settings/SettingLevel";
|
||||||
|
import { Preset } from "matrix-js-sdk/src/@types/partials";
|
||||||
|
|
||||||
// TODO: Move this and related files to the js-sdk or something once finalized.
|
// TODO: Move this and related files to the js-sdk or something once finalized.
|
||||||
|
|
||||||
|
@ -86,7 +87,7 @@ export class Mjolnir {
|
||||||
const resp = await MatrixClientPeg.get().createRoom({
|
const resp = await MatrixClientPeg.get().createRoom({
|
||||||
name: _t("My Ban List"),
|
name: _t("My Ban List"),
|
||||||
topic: _t("This is your list of users/servers you have blocked - don't leave the room!"),
|
topic: _t("This is your list of users/servers you have blocked - don't leave the room!"),
|
||||||
preset: "private_chat",
|
preset: Preset.PrivateChat,
|
||||||
});
|
});
|
||||||
personalRoomId = resp['room_id'];
|
personalRoomId = resp['room_id'];
|
||||||
await SettingsStore.setValue(
|
await SettingsStore.setValue(
|
||||||
|
|
|
@ -25,14 +25,8 @@ import Tar from "tar-js";
|
||||||
|
|
||||||
import * as rageshake from './rageshake';
|
import * as rageshake from './rageshake';
|
||||||
|
|
||||||
// polyfill textencoder if necessary
|
|
||||||
import * as TextEncodingUtf8 from 'text-encoding-utf-8';
|
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import SdkConfig from "../SdkConfig";
|
import SdkConfig from "../SdkConfig";
|
||||||
let TextEncoder = window.TextEncoder;
|
|
||||||
if (!TextEncoder) {
|
|
||||||
TextEncoder = TextEncodingUtf8.TextEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IOpts {
|
interface IOpts {
|
||||||
label?: string;
|
label?: string;
|
||||||
|
|
|
@ -63,8 +63,7 @@ export class WatchManager {
|
||||||
|
|
||||||
if (!inRoomId) {
|
if (!inRoomId) {
|
||||||
// Fire updates to all the individual room watchers too, as they probably care about the change higher up.
|
// Fire updates to all the individual room watchers too, as they probably care about the change higher up.
|
||||||
const callbacks = Array.from(roomWatchers.values()).flat(1);
|
callbacks.push(...Array.from(roomWatchers.values()).flat(1));
|
||||||
callbacks.push(...callbacks);
|
|
||||||
} else if (roomWatchers.has(IRRELEVANT_ROOM)) {
|
} else if (roomWatchers.has(IRRELEVANT_ROOM)) {
|
||||||
callbacks.push(...roomWatchers.get(IRRELEVANT_ROOM));
|
callbacks.push(...roomWatchers.get(IRRELEVANT_ROOM));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
|
import {IState} from "./components/structures/RoomView";
|
||||||
|
|
||||||
interface IDiff {
|
interface IDiff {
|
||||||
isMemberEvent: boolean;
|
isMemberEvent: boolean;
|
||||||
|
@ -47,11 +48,18 @@ function memberEventDiff(ev: MatrixEvent): IDiff {
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function shouldHideEvent(ev: MatrixEvent): boolean {
|
/**
|
||||||
// Wrap getValue() for readability. Calling the SettingsStore can be
|
* Determines whether the given event should be hidden from timelines.
|
||||||
// fairly resource heavy, so the checks below should avoid hitting it
|
* @param ev The event
|
||||||
// where possible.
|
* @param ctx An optional RoomContext to pull cached settings values from to avoid
|
||||||
const isEnabled = (name) => SettingsStore.getValue(name, ev.getRoomId());
|
* hitting the settings store
|
||||||
|
*/
|
||||||
|
export default function shouldHideEvent(ev: MatrixEvent, ctx?: IState): boolean {
|
||||||
|
// Accessing the settings store directly can be expensive if done frequently,
|
||||||
|
// so we should prefer using cached values if a RoomContext is available
|
||||||
|
const isEnabled = ctx ?
|
||||||
|
name => ctx[name] :
|
||||||
|
name => SettingsStore.getValue(name, ev.getRoomId());
|
||||||
|
|
||||||
// Hide redacted events
|
// Hide redacted events
|
||||||
if (ev.isRedacted() && !isEnabled('showRedactions')) return true;
|
if (ev.isRedacted() && !isEnabled('showRedactions')) return true;
|
||||||
|
|
|
@ -54,8 +54,6 @@ const INITIAL_STATE = {
|
||||||
// Any error that has occurred during loading
|
// Any error that has occurred during loading
|
||||||
roomLoadError: null,
|
roomLoadError: null,
|
||||||
|
|
||||||
forwardingEvent: null,
|
|
||||||
|
|
||||||
quotingEvent: null,
|
quotingEvent: null,
|
||||||
|
|
||||||
replyingToEvent: null,
|
replyingToEvent: null,
|
||||||
|
@ -150,11 +148,6 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||||
case 'on_logged_out':
|
case 'on_logged_out':
|
||||||
this.reset();
|
this.reset();
|
||||||
break;
|
break;
|
||||||
case 'forward_event':
|
|
||||||
this.setState({
|
|
||||||
forwardingEvent: payload.event,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'reply_to_event':
|
case 'reply_to_event':
|
||||||
// If currently viewed room does not match the room in which we wish to reply then change rooms
|
// If currently viewed room does not match the room in which we wish to reply then change rooms
|
||||||
// this can happen when performing a search across all rooms
|
// this can happen when performing a search across all rooms
|
||||||
|
@ -174,6 +167,7 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||||
const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog");
|
const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog");
|
||||||
Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, {
|
Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, {
|
||||||
roomId: payload.room_id || this.state.roomId,
|
roomId: payload.room_id || this.state.roomId,
|
||||||
|
initialTabId: payload.initial_tab_id,
|
||||||
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -187,7 +181,6 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||||
roomAlias: payload.room_alias,
|
roomAlias: payload.room_alias,
|
||||||
initialEventId: payload.event_id,
|
initialEventId: payload.event_id,
|
||||||
isInitialEventHighlighted: payload.highlighted,
|
isInitialEventHighlighted: payload.highlighted,
|
||||||
forwardingEvent: null,
|
|
||||||
roomLoading: false,
|
roomLoading: false,
|
||||||
roomLoadError: null,
|
roomLoadError: null,
|
||||||
// should peek by default
|
// should peek by default
|
||||||
|
@ -207,14 +200,6 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||||
newState.replyingToEvent = payload.replyingToEvent;
|
newState.replyingToEvent = payload.replyingToEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.forwardingEvent) {
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'send_event',
|
|
||||||
room_id: newState.roomId,
|
|
||||||
event: this.state.forwardingEvent,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
|
|
||||||
if (payload.auto_join) {
|
if (payload.auto_join) {
|
||||||
|
@ -428,11 +413,6 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||||
return this.state.joinError;
|
return this.state.joinError;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The mxEvent if one is about to be forwarded
|
|
||||||
public getForwardingEvent() {
|
|
||||||
return this.state.forwardingEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The mxEvent if one is currently being replied to/quoted
|
// The mxEvent if one is currently being replied to/quoted
|
||||||
public getQuotingEvent() {
|
public getQuotingEvent() {
|
||||||
return this.state.replyingToEvent;
|
return this.state.replyingToEvent;
|
||||||
|
|
|
@ -332,7 +332,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public getContainerWidgets(room: Room, container: Container): IApp[] {
|
public getContainerWidgets(room: Room, container: Container): IApp[] {
|
||||||
return this.byRoom[room.roomId]?.[container]?.ordered || [];
|
return this.byRoom[room?.roomId]?.[container]?.ordered || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public isInContainer(room: Room, widget: IApp, container: Container): boolean {
|
public isInContainer(room: Room, widget: IApp, container: Container): boolean {
|
||||||
|
|
|
@ -15,17 +15,6 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// polyfill textencoder if necessary
|
|
||||||
import * as TextEncodingUtf8 from 'text-encoding-utf-8';
|
|
||||||
let TextEncoder = window.TextEncoder;
|
|
||||||
if (!TextEncoder) {
|
|
||||||
TextEncoder = TextEncodingUtf8.TextEncoder;
|
|
||||||
}
|
|
||||||
let TextDecoder = window.TextDecoder;
|
|
||||||
if (!TextDecoder) {
|
|
||||||
TextDecoder = TextEncodingUtf8.TextDecoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
import { _t } from '../languageHandler';
|
import { _t } from '../languageHandler';
|
||||||
import SdkConfig from '../SdkConfig';
|
import SdkConfig from '../SdkConfig';
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,11 @@ limitations under the License.
|
||||||
* TODO: Convert this to a real TypeScript interface
|
* TODO: Convert this to a real TypeScript interface
|
||||||
*/
|
*/
|
||||||
export default class PermalinkConstructor {
|
export default class PermalinkConstructor {
|
||||||
forEvent(roomId: string, eventId: string, serverCandidates: string[]): string {
|
forEvent(roomId: string, eventId: string, serverCandidates: string[] = []): string {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
|
forRoom(roomIdOrAlias: string, serverCandidates: string[] = []): string {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,12 +73,12 @@ export class PermalinkParts {
|
||||||
return new PermalinkParts(null, null, null, groupId, null);
|
return new PermalinkParts(null, null, null, groupId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
static forRoom(roomIdOrAlias: string, viaServers: string[]): PermalinkParts {
|
static forRoom(roomIdOrAlias: string, viaServers: string[] = []): PermalinkParts {
|
||||||
return new PermalinkParts(roomIdOrAlias, null, null, null, viaServers || []);
|
return new PermalinkParts(roomIdOrAlias, null, null, null, viaServers);
|
||||||
}
|
}
|
||||||
|
|
||||||
static forEvent(roomId: string, eventId: string, viaServers: string[]): PermalinkParts {
|
static forEvent(roomId: string, eventId: string, viaServers: string[] = []): PermalinkParts {
|
||||||
return new PermalinkParts(roomId, eventId, null, null, viaServers || []);
|
return new PermalinkParts(roomId, eventId, null, null, viaServers);
|
||||||
}
|
}
|
||||||
|
|
||||||
get primaryEntityId(): string {
|
get primaryEntityId(): string {
|
||||||
|
|
|
@ -149,7 +149,7 @@ export class RoomPermalinkCreator {
|
||||||
// Prefer to use canonical alias for permalink if possible
|
// Prefer to use canonical alias for permalink if possible
|
||||||
const alias = this.room.getCanonicalAlias();
|
const alias = this.room.getCanonicalAlias();
|
||||||
if (alias) {
|
if (alias) {
|
||||||
return getPermalinkConstructor().forRoom(alias, this._serverCandidates);
|
return getPermalinkConstructor().forRoom(alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getPermalinkConstructor().forRoom(this.roomId, this._serverCandidates);
|
return getPermalinkConstructor().forRoom(this.roomId, this._serverCandidates);
|
||||||
|
@ -302,7 +302,7 @@ export function makeRoomPermalink(roomId: string): string {
|
||||||
}
|
}
|
||||||
const permalinkCreator = new RoomPermalinkCreator(room);
|
const permalinkCreator = new RoomPermalinkCreator(room);
|
||||||
permalinkCreator.load();
|
permalinkCreator.load();
|
||||||
return permalinkCreator.forRoom();
|
return permalinkCreator.forShareableRoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeGroupPermalink(groupId: string): string {
|
export function makeGroupPermalink(groupId: string): string {
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import '../skinned-sdk'; // Must be first for skinning to work
|
import '../skinned-sdk'; // Must be first for skinning to work
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Adapter from "enzyme-adapter-react-16";
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
import { configure, mount } from "enzyme";
|
import { configure, mount } from "enzyme";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -32,7 +32,7 @@ import Matrix from 'matrix-js-sdk';
|
||||||
const test_utils = require('../../test-utils');
|
const test_utils = require('../../test-utils');
|
||||||
const mockclock = require('../../mock-clock');
|
const mockclock = require('../../mock-clock');
|
||||||
|
|
||||||
import Adapter from "enzyme-adapter-react-16";
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
import { configure, mount } from "enzyme";
|
import { configure, mount } from "enzyme";
|
||||||
|
|
||||||
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
|
||||||
|
@ -51,8 +51,20 @@ class WrappedMessagePanel extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const roomContext = {
|
||||||
|
room,
|
||||||
|
roomId: room.roomId,
|
||||||
|
canReact: true,
|
||||||
|
canReply: true,
|
||||||
|
showReadReceipts: true,
|
||||||
|
showRedactions: false,
|
||||||
|
showJoinLeaves: false,
|
||||||
|
showAvatarChanges: false,
|
||||||
|
showDisplaynameChanges: true,
|
||||||
|
};
|
||||||
|
|
||||||
return <MatrixClientContext.Provider value={client}>
|
return <MatrixClientContext.Provider value={client}>
|
||||||
<RoomContext.Provider value={{ canReact: true, canReply: true, room, roomId: room.roomId }}>
|
<RoomContext.Provider value={roomContext}>
|
||||||
<MessagePanel room={room} {...this.props} resizeNotifier={this.state.resizeNotifier} />
|
<MessagePanel room={room} {...this.props} resizeNotifier={this.state.resizeNotifier} />
|
||||||
</RoomContext.Provider>
|
</RoomContext.Provider>
|
||||||
</MatrixClientContext.Provider>;
|
</MatrixClientContext.Provider>;
|
||||||
|
@ -77,7 +89,7 @@ describe('MessagePanel', function() {
|
||||||
DMRoomMap.makeShared();
|
DMRoomMap.makeShared();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function() {
|
||||||
clock.uninstall();
|
clock.uninstall();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -270,7 +282,7 @@ describe('MessagePanel', function() {
|
||||||
|
|
||||||
it('should show the events', function() {
|
it('should show the events', function() {
|
||||||
const res = TestUtils.renderIntoDocument(
|
const res = TestUtils.renderIntoDocument(
|
||||||
<WrappedMessagePanel className="cls" events={events} />,
|
<WrappedMessagePanel className="cls" events={events} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
// just check we have the right number of tiles for now
|
// just check we have the right number of tiles for now
|
||||||
|
@ -298,8 +310,8 @@ describe('MessagePanel', function() {
|
||||||
|
|
||||||
it('should insert the read-marker in the right place', function() {
|
it('should insert the read-marker in the right place', function() {
|
||||||
const res = TestUtils.renderIntoDocument(
|
const res = TestUtils.renderIntoDocument(
|
||||||
<WrappedMessagePanel className="cls" events={events} readMarkerEventId={events[4].getId()}
|
<WrappedMessagePanel className="cls" events={events} readMarkerEventId={events[4].getId()}
|
||||||
readMarkerVisible={true} />,
|
readMarkerVisible={true} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
const tiles = TestUtils.scryRenderedComponentsWithType(
|
const tiles = TestUtils.scryRenderedComponentsWithType(
|
||||||
|
@ -316,8 +328,8 @@ describe('MessagePanel', function() {
|
||||||
it('should show the read-marker that fall in summarised events after the summary', function() {
|
it('should show the read-marker that fall in summarised events after the summary', function() {
|
||||||
const melsEvents = mkMelsEvents();
|
const melsEvents = mkMelsEvents();
|
||||||
const res = TestUtils.renderIntoDocument(
|
const res = TestUtils.renderIntoDocument(
|
||||||
<WrappedMessagePanel className="cls" events={melsEvents} readMarkerEventId={melsEvents[4].getId()}
|
<WrappedMessagePanel className="cls" events={melsEvents} readMarkerEventId={melsEvents[4].getId()}
|
||||||
readMarkerVisible={true} />,
|
readMarkerVisible={true} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
const summary = TestUtils.findRenderedDOMComponentWithClass(res, 'mx_EventListSummary');
|
const summary = TestUtils.findRenderedDOMComponentWithClass(res, 'mx_EventListSummary');
|
||||||
|
@ -334,8 +346,8 @@ describe('MessagePanel', function() {
|
||||||
it('should hide the read-marker at the end of summarised events', function() {
|
it('should hide the read-marker at the end of summarised events', function() {
|
||||||
const melsEvents = mkMelsEventsOnly();
|
const melsEvents = mkMelsEventsOnly();
|
||||||
const res = TestUtils.renderIntoDocument(
|
const res = TestUtils.renderIntoDocument(
|
||||||
<WrappedMessagePanel className="cls" events={melsEvents} readMarkerEventId={melsEvents[9].getId()}
|
<WrappedMessagePanel className="cls" events={melsEvents} readMarkerEventId={melsEvents[9].getId()}
|
||||||
readMarkerVisible={true} />,
|
readMarkerVisible={true} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
const summary = TestUtils.findRenderedDOMComponentWithClass(res, 'mx_EventListSummary');
|
const summary = TestUtils.findRenderedDOMComponentWithClass(res, 'mx_EventListSummary');
|
||||||
|
@ -358,9 +370,9 @@ describe('MessagePanel', function() {
|
||||||
|
|
||||||
// first render with the RM in one place
|
// first render with the RM in one place
|
||||||
let mp = ReactDOM.render(
|
let mp = ReactDOM.render(
|
||||||
<WrappedMessagePanel className="cls" events={events} readMarkerEventId={events[4].getId()}
|
<WrappedMessagePanel className="cls" events={events} readMarkerEventId={events[4].getId()}
|
||||||
readMarkerVisible={true}
|
readMarkerVisible={true}
|
||||||
/>, parentDiv);
|
/>, parentDiv);
|
||||||
|
|
||||||
const tiles = TestUtils.scryRenderedComponentsWithType(
|
const tiles = TestUtils.scryRenderedComponentsWithType(
|
||||||
mp, sdk.getComponent('rooms.EventTile'));
|
mp, sdk.getComponent('rooms.EventTile'));
|
||||||
|
@ -374,9 +386,9 @@ describe('MessagePanel', function() {
|
||||||
|
|
||||||
// now move the RM
|
// now move the RM
|
||||||
mp = ReactDOM.render(
|
mp = ReactDOM.render(
|
||||||
<WrappedMessagePanel className="cls" events={events} readMarkerEventId={events[6].getId()}
|
<WrappedMessagePanel className="cls" events={events} readMarkerEventId={events[6].getId()}
|
||||||
readMarkerVisible={true}
|
readMarkerVisible={true}
|
||||||
/>, parentDiv);
|
/>, parentDiv);
|
||||||
|
|
||||||
// now there should be two RM containers
|
// now there should be two RM containers
|
||||||
const found = TestUtils.scryRenderedDOMComponentsWithClass(mp, 'mx_RoomView_myReadMarker_container');
|
const found = TestUtils.scryRenderedDOMComponentsWithClass(mp, 'mx_RoomView_myReadMarker_container');
|
||||||
|
@ -451,7 +463,7 @@ describe('MessagePanel', function() {
|
||||||
expect(isReadMarkerVisible(rm)).toBeFalsy();
|
expect(isReadMarkerVisible(rm)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render Date separators for the events', function () {
|
it('should render Date separators for the events', function() {
|
||||||
const events = mkOneDayEvents();
|
const events = mkOneDayEvents();
|
||||||
const res = mount(
|
const res = mount(
|
||||||
<WrappedMessagePanel
|
<WrappedMessagePanel
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Robin Townsend <robin@robin.town>
|
||||||
|
|
||||||
|
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 "../../../skinned-sdk";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {configure, mount} from "enzyme";
|
||||||
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
|
import {act} from "react-dom/test-utils";
|
||||||
|
|
||||||
|
import * as TestUtils from "../../../test-utils";
|
||||||
|
import {MatrixClientPeg} from "../../../../src/MatrixClientPeg";
|
||||||
|
import DMRoomMap from "../../../../src/utils/DMRoomMap";
|
||||||
|
import {RoomPermalinkCreator} from "../../../../src/utils/permalinks/Permalinks";
|
||||||
|
import ForwardDialog from "../../../../src/components/views/dialogs/ForwardDialog";
|
||||||
|
|
||||||
|
configure({ adapter: new Adapter() });
|
||||||
|
|
||||||
|
describe("ForwardDialog", () => {
|
||||||
|
const sourceRoom = "!111111111111111111:example.org";
|
||||||
|
const defaultMessage = TestUtils.mkMessage({
|
||||||
|
room: sourceRoom,
|
||||||
|
user: "@alice:example.org",
|
||||||
|
msg: "Hello world!",
|
||||||
|
event: true,
|
||||||
|
});
|
||||||
|
const defaultRooms = ["a", "A", "b"].map(name => TestUtils.mkStubRoom(name, name));
|
||||||
|
|
||||||
|
const mountForwardDialog = async (message = defaultMessage, rooms = defaultRooms) => {
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
client.getVisibleRooms = jest.fn().mockReturnValue(rooms);
|
||||||
|
|
||||||
|
let wrapper;
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mount(
|
||||||
|
<ForwardDialog
|
||||||
|
matrixClient={client}
|
||||||
|
event={message}
|
||||||
|
permalinkCreator={new RoomPermalinkCreator(undefined, sourceRoom)}
|
||||||
|
onFinished={jest.fn()}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
// Wait one tick for our profile data to load so the state update happens within act
|
||||||
|
await new Promise(resolve => setImmediate(resolve));
|
||||||
|
});
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestUtils.stubClient();
|
||||||
|
DMRoomMap.makeShared();
|
||||||
|
MatrixClientPeg.get().getUserId = jest.fn().mockReturnValue("@bob:example.org");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows a preview with us as the sender", async () => {
|
||||||
|
const wrapper = await mountForwardDialog();
|
||||||
|
|
||||||
|
const previewBody = wrapper.find(".mx_EventTile_body");
|
||||||
|
expect(previewBody.text()).toBe("Hello world!");
|
||||||
|
|
||||||
|
// We would just test SenderProfile for the user ID, but it's stubbed
|
||||||
|
const previewAvatar = wrapper.find(".mx_EventTile_avatar .mx_BaseAvatar_image");
|
||||||
|
expect(previewAvatar.prop("title")).toBe("@bob:example.org");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("filters the rooms", async () => {
|
||||||
|
const wrapper = await mountForwardDialog();
|
||||||
|
|
||||||
|
expect(wrapper.find("Entry")).toHaveLength(3);
|
||||||
|
|
||||||
|
const searchInput = wrapper.find("SearchBox input");
|
||||||
|
searchInput.instance().value = "a";
|
||||||
|
searchInput.simulate("change");
|
||||||
|
|
||||||
|
expect(wrapper.find("Entry")).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("tracks message sending progress across multiple rooms", async () => {
|
||||||
|
const wrapper = await mountForwardDialog();
|
||||||
|
|
||||||
|
// Make sendEvent require manual resolution so we can see the sending state
|
||||||
|
let finishSend;
|
||||||
|
let cancelSend;
|
||||||
|
MatrixClientPeg.get().sendEvent = jest.fn(() => new Promise((resolve, reject) => {
|
||||||
|
finishSend = resolve;
|
||||||
|
cancelSend = reject;
|
||||||
|
}));
|
||||||
|
|
||||||
|
const firstButton = wrapper.find("AccessibleButton.mx_ForwardList_sendButton").first();
|
||||||
|
expect(firstButton.render().is(".mx_ForwardList_canSend")).toBe(true);
|
||||||
|
|
||||||
|
act(() => { firstButton.simulate("click"); });
|
||||||
|
expect(firstButton.render().is(".mx_ForwardList_sending")).toBe(true);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
cancelSend();
|
||||||
|
// Wait one tick for the button to realize the send failed
|
||||||
|
await new Promise(resolve => setImmediate(resolve));
|
||||||
|
});
|
||||||
|
expect(firstButton.render().is(".mx_ForwardList_sendFailed")).toBe(true);
|
||||||
|
|
||||||
|
const secondButton = wrapper.find("AccessibleButton.mx_ForwardList_sendButton").at(1);
|
||||||
|
expect(secondButton.render().is(".mx_ForwardList_canSend")).toBe(true);
|
||||||
|
|
||||||
|
act(() => { secondButton.simulate("click"); });
|
||||||
|
expect(secondButton.render().is(".mx_ForwardList_sending")).toBe(true);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
finishSend();
|
||||||
|
// Wait one tick for the button to realize the send succeeded
|
||||||
|
await new Promise(resolve => setImmediate(resolve));
|
||||||
|
});
|
||||||
|
expect(secondButton.render().is(".mx_ForwardList_sent")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can render replies", async () => {
|
||||||
|
const replyMessage = TestUtils.mkEvent({
|
||||||
|
type: "m.room.message",
|
||||||
|
room: "!111111111111111111:example.org",
|
||||||
|
user: "@alice:example.org",
|
||||||
|
content: {
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"body": "> <@bob:example.org> Hi Alice!\n\nHi Bob!",
|
||||||
|
"m.relates_to": {
|
||||||
|
"m.in_reply_to": {
|
||||||
|
event_id: "$2222222222222222222222222222222222222222222",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
event: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = await mountForwardDialog(replyMessage);
|
||||||
|
expect(wrapper.find("ReplyThread")).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("disables buttons for rooms without send permissions", async () => {
|
||||||
|
const readOnlyRoom = TestUtils.mkStubRoom("a", "a");
|
||||||
|
readOnlyRoom.maySendMessage = jest.fn().mockReturnValue(false);
|
||||||
|
const rooms = [readOnlyRoom, TestUtils.mkStubRoom("b", "b")];
|
||||||
|
|
||||||
|
const wrapper = await mountForwardDialog(undefined, rooms);
|
||||||
|
|
||||||
|
const firstButton = wrapper.find("AccessibleButton.mx_ForwardList_sendButton").first();
|
||||||
|
expect(firstButton.prop("disabled")).toBe(true);
|
||||||
|
const secondButton = wrapper.find("AccessibleButton.mx_ForwardList_sendButton").last();
|
||||||
|
expect(secondButton.prop("disabled")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Adapter from "enzyme-adapter-react-16";
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
import { configure, mount } from "enzyme";
|
import { configure, mount } from "enzyme";
|
||||||
|
|
||||||
import sdk from "../../../skinned-sdk";
|
import sdk from "../../../skinned-sdk";
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../../../skinned-sdk'; // Must be first for skinning to work
|
import '../../../skinned-sdk'; // Must be first for skinning to work
|
||||||
import Adapter from "enzyme-adapter-react-16";
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
import { configure, mount } from "enzyme";
|
import { configure, mount } from "enzyme";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {act} from "react-dom/test-utils";
|
import {act} from "react-dom/test-utils";
|
||||||
|
|
|
@ -122,7 +122,7 @@ function getAllEventTiles(session) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMessageFromEventTile(eventTile) {
|
async function getMessageFromEventTile(eventTile) {
|
||||||
const senderElement = await eventTile.$(".mx_SenderProfile_name");
|
const senderElement = await eventTile.$(".mx_SenderProfile_displayName");
|
||||||
const className = await (await eventTile.getProperty("className")).jsonValue();
|
const className = await (await eventTile.getProperty("className")).jsonValue();
|
||||||
const classNames = className.split(" ");
|
const classNames = className.split(" ");
|
||||||
const bodyElement = await eventTile.$(".mx_EventTile_body");
|
const bodyElement = await eventTile.$(".mx_EventTile_body");
|
||||||
|
|
|
@ -79,7 +79,7 @@ async function runTests() {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5 * 60 * 1000));
|
await new Promise((resolve) => setTimeout(resolve, 5 * 60 * 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
const performanceEntries = {};
|
let performanceEntries;
|
||||||
|
|
||||||
await Promise.all(sessions.map(async (session) => {
|
await Promise.all(sessions.map(async (session) => {
|
||||||
// Collecting all performance monitoring data before closing the session
|
// Collecting all performance monitoring data before closing the session
|
||||||
|
@ -95,7 +95,11 @@ async function runTests() {
|
||||||
}, true);
|
}, true);
|
||||||
return measurements;
|
return measurements;
|
||||||
});
|
});
|
||||||
performanceEntries[session.username] = JSON.parse(measurements);
|
|
||||||
|
/**
|
||||||
|
* TODO: temporary only use one user session data
|
||||||
|
*/
|
||||||
|
performanceEntries = JSON.parse(measurements);
|
||||||
return session.close();
|
return session.close();
|
||||||
}));
|
}));
|
||||||
fs.writeFileSync(`performance-entries.json`, JSON.stringify(performanceEntries));
|
fs.writeFileSync(`performance-entries.json`, JSON.stringify(performanceEntries));
|
||||||
|
|
|
@ -760,9 +760,9 @@ wrappy@1:
|
||||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||||
|
|
||||||
ws@^6.1.0:
|
ws@^6.1.0:
|
||||||
version "6.2.1"
|
version "6.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"
|
||||||
integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
|
integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==
|
||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "~1.0.0"
|
async-limiter "~1.0.0"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import * as languageHandler from "../src/languageHandler";
|
import * as languageHandler from "../src/languageHandler";
|
||||||
|
import { TextEncoder, TextDecoder } from 'util';
|
||||||
|
|
||||||
languageHandler.setLanguage('en');
|
languageHandler.setLanguage('en');
|
||||||
languageHandler.setMissingEntryGenerator(key => key.split("|", 2)[1]);
|
languageHandler.setMissingEntryGenerator(key => key.split("|", 2)[1]);
|
||||||
|
|
||||||
require('jest-fetch-mock').enableMocks();
|
require('jest-fetch-mock').enableMocks();
|
||||||
|
|
||||||
|
// polyfilling TextEncoder as it is not available on JSDOM
|
||||||
|
// view https://github.com/facebook/jest/issues/9983
|
||||||
|
global.TextEncoder = TextEncoder;
|
||||||
|
global.TextDecoder = TextDecoder;
|
||||||
|
|
|
@ -219,7 +219,7 @@ export function mkMessage(opts) {
|
||||||
return mkEvent(opts);
|
return mkEvent(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mkStubRoom(roomId = null) {
|
export function mkStubRoom(roomId = null, name) {
|
||||||
const stubTimeline = { getEvents: () => [] };
|
const stubTimeline = { getEvents: () => [] };
|
||||||
return {
|
return {
|
||||||
roomId,
|
roomId,
|
||||||
|
@ -238,6 +238,7 @@ export function mkStubRoom(roomId = null) {
|
||||||
getPendingEvents: () => [],
|
getPendingEvents: () => [],
|
||||||
getLiveTimeline: () => stubTimeline,
|
getLiveTimeline: () => stubTimeline,
|
||||||
getUnfilteredTimelineSet: () => null,
|
getUnfilteredTimelineSet: () => null,
|
||||||
|
findEventById: () => null,
|
||||||
getAccountData: () => null,
|
getAccountData: () => null,
|
||||||
hasMembershipState: () => null,
|
hasMembershipState: () => null,
|
||||||
getVersion: () => '1',
|
getVersion: () => '1',
|
||||||
|
@ -255,13 +256,17 @@ export function mkStubRoom(roomId = null) {
|
||||||
tags: {},
|
tags: {},
|
||||||
setBlacklistUnverifiedDevices: jest.fn(),
|
setBlacklistUnverifiedDevices: jest.fn(),
|
||||||
on: jest.fn(),
|
on: jest.fn(),
|
||||||
|
off: jest.fn(),
|
||||||
removeListener: jest.fn(),
|
removeListener: jest.fn(),
|
||||||
getDMInviter: jest.fn(),
|
getDMInviter: jest.fn(),
|
||||||
|
name,
|
||||||
getAvatarUrl: () => 'mxc://avatar.url/room.png',
|
getAvatarUrl: () => 'mxc://avatar.url/room.png',
|
||||||
getMxcAvatarUrl: () => 'mxc://avatar.url/room.png',
|
getMxcAvatarUrl: () => 'mxc://avatar.url/room.png',
|
||||||
isSpaceRoom: jest.fn(() => false),
|
isSpaceRoom: jest.fn(() => false),
|
||||||
getUnreadNotificationCount: jest.fn(() => 0),
|
getUnreadNotificationCount: jest.fn(() => 0),
|
||||||
getEventReadUpTo: jest.fn(() => null),
|
getEventReadUpTo: jest.fn(() => null),
|
||||||
|
getCanonicalAlias: jest.fn(),
|
||||||
|
getAltAliases: jest.fn().mockReturnValue([]),
|
||||||
timeline: [],
|
timeline: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ function mockRoom(roomId, members, serverACL) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
roomId,
|
roomId,
|
||||||
getCanonicalAlias: () => roomId,
|
getCanonicalAlias: () => null,
|
||||||
getJoinedMembers: () => members,
|
getJoinedMembers: () => members,
|
||||||
getMember: (userId) => members.find(m => m.userId === userId),
|
getMember: (userId) => members.find(m => m.userId === userId),
|
||||||
currentState: {
|
currentState: {
|
||||||
|
|
322
yarn.lock
322
yarn.lock
|
@ -1752,6 +1752,31 @@
|
||||||
"@typescript-eslint/types" "4.14.0"
|
"@typescript-eslint/types" "4.14.0"
|
||||||
eslint-visitor-keys "^2.0.0"
|
eslint-visitor-keys "^2.0.0"
|
||||||
|
|
||||||
|
"@wojtekmaj/enzyme-adapter-react-17@^0.6.1":
|
||||||
|
version "0.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@wojtekmaj/enzyme-adapter-react-17/-/enzyme-adapter-react-17-0.6.1.tgz#28caa37118c183e5f13c4dfb68cc32cde828ecbc"
|
||||||
|
integrity sha512-xgPfzLVpN0epIHeZofahwr5qwpukEDNAbrufgeDWN6vZPtfblGCC+OZG5TlfK+A6ePVy8sBkD8S2X4tO17JKjg==
|
||||||
|
dependencies:
|
||||||
|
"@wojtekmaj/enzyme-adapter-utils" "^0.1.0"
|
||||||
|
enzyme-shallow-equal "^1.0.0"
|
||||||
|
has "^1.0.0"
|
||||||
|
object.assign "^4.1.0"
|
||||||
|
object.values "^1.1.0"
|
||||||
|
prop-types "^15.7.0"
|
||||||
|
react-is "^17.0.0"
|
||||||
|
react-test-renderer "^17.0.0"
|
||||||
|
|
||||||
|
"@wojtekmaj/enzyme-adapter-utils@^0.1.0":
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@wojtekmaj/enzyme-adapter-utils/-/enzyme-adapter-utils-0.1.0.tgz#3a2a3db756111d53357e2f119a1612a969ab8c38"
|
||||||
|
integrity sha512-EYK/Vy0Y1ap0jH2UNQjOKtR/7HWkbEq8N+cwC5+yDf+Mwp5uu7j4Qg70RmWuzsA35DGGwgkop6m4pQsGwNOF2A==
|
||||||
|
dependencies:
|
||||||
|
function.prototype.name "^1.1.0"
|
||||||
|
has "^1.0.0"
|
||||||
|
object.assign "^4.1.0"
|
||||||
|
object.fromentries "^2.0.0"
|
||||||
|
prop-types "^15.7.0"
|
||||||
|
|
||||||
abab@^2.0.3:
|
abab@^2.0.3:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
|
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
|
||||||
|
@ -1780,21 +1805,6 @@ acorn@^7.1.1, acorn@^7.4.0:
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||||
|
|
||||||
airbnb-prop-types@^2.16.0:
|
|
||||||
version "2.16.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz#b96274cefa1abb14f623f804173ee97c13971dc2"
|
|
||||||
integrity sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg==
|
|
||||||
dependencies:
|
|
||||||
array.prototype.find "^2.1.1"
|
|
||||||
function.prototype.name "^1.1.2"
|
|
||||||
is-regex "^1.1.0"
|
|
||||||
object-is "^1.1.2"
|
|
||||||
object.assign "^4.1.0"
|
|
||||||
object.entries "^1.1.2"
|
|
||||||
prop-types "^15.7.2"
|
|
||||||
prop-types-exact "^1.2.0"
|
|
||||||
react-is "^16.13.1"
|
|
||||||
|
|
||||||
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4:
|
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4:
|
||||||
version "6.12.6"
|
version "6.12.6"
|
||||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||||
|
@ -1920,14 +1930,6 @@ array-unique@^0.3.2:
|
||||||
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
||||||
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
|
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
|
||||||
|
|
||||||
array.prototype.find@^2.1.1:
|
|
||||||
version "2.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.1.tgz#3baca26108ca7affb08db06bf0be6cb3115a969c"
|
|
||||||
integrity sha512-mi+MYNJYLTx2eNYy+Yh6raoQacCsNeeMUaspFPh9Y141lFSsWxxB8V9mM2ye+eqiRs917J6/pJ4M9ZPzenWckA==
|
|
||||||
dependencies:
|
|
||||||
define-properties "^1.1.3"
|
|
||||||
es-abstract "^1.17.4"
|
|
||||||
|
|
||||||
array.prototype.flat@^1.2.3:
|
array.prototype.flat@^1.2.3:
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123"
|
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123"
|
||||||
|
@ -2179,11 +2181,6 @@ bluebird@^3.5.0:
|
||||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||||
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
||||||
|
|
||||||
blueimp-canvas-to-blob@^3.28.0:
|
|
||||||
version "3.28.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.28.0.tgz#c8ab4dc6bb08774a7f273798cdf94b0776adf6c8"
|
|
||||||
integrity sha512-5q+YHzgGsuHQ01iouGgJaPJXod2AzTxJXmVv90PpGrRxU7G7IqgPqWXz+PBmt3520jKKi6irWbNV87DicEa7wg==
|
|
||||||
|
|
||||||
boolbase@^1.0.0:
|
boolbase@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||||
|
@ -2721,9 +2718,9 @@ css-select@^4.1.2:
|
||||||
nth-check "^2.0.0"
|
nth-check "^2.0.0"
|
||||||
|
|
||||||
css-what@^5.0.0:
|
css-what@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.0.tgz#f0bf4f8bac07582722346ab243f6a35b512cfc47"
|
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad"
|
||||||
integrity sha512-qxyKHQvgKwzwDWC/rGbT821eJalfupxYW2qbSJSAtdSTimsr/MlaGONoNLllaUPZWf8QnbcKM/kPVYUQuEKAFA==
|
integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==
|
||||||
|
|
||||||
cssesc@^3.0.0:
|
cssesc@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
|
@ -3075,35 +3072,7 @@ entities@~2.0:
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
|
||||||
integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
|
integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
|
||||||
|
|
||||||
enzyme-adapter-react-16@^1.15.6:
|
enzyme-shallow-equal@^1.0.0, enzyme-shallow-equal@^1.0.1:
|
||||||
version "1.15.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz#fd677a658d62661ac5afd7f7f541f141f8085901"
|
|
||||||
integrity sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g==
|
|
||||||
dependencies:
|
|
||||||
enzyme-adapter-utils "^1.14.0"
|
|
||||||
enzyme-shallow-equal "^1.0.4"
|
|
||||||
has "^1.0.3"
|
|
||||||
object.assign "^4.1.2"
|
|
||||||
object.values "^1.1.2"
|
|
||||||
prop-types "^15.7.2"
|
|
||||||
react-is "^16.13.1"
|
|
||||||
react-test-renderer "^16.0.0-0"
|
|
||||||
semver "^5.7.0"
|
|
||||||
|
|
||||||
enzyme-adapter-utils@^1.14.0:
|
|
||||||
version "1.14.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.0.tgz#afbb0485e8033aa50c744efb5f5711e64fbf1ad0"
|
|
||||||
integrity sha512-F/z/7SeLt+reKFcb7597IThpDp0bmzcH1E9Oabqv+o01cID2/YInlqHbFl7HzWBl4h3OdZYedtwNDOmSKkk0bg==
|
|
||||||
dependencies:
|
|
||||||
airbnb-prop-types "^2.16.0"
|
|
||||||
function.prototype.name "^1.1.3"
|
|
||||||
has "^1.0.3"
|
|
||||||
object.assign "^4.1.2"
|
|
||||||
object.fromentries "^2.0.3"
|
|
||||||
prop-types "^15.7.2"
|
|
||||||
semver "^5.7.1"
|
|
||||||
|
|
||||||
enzyme-shallow-equal@^1.0.1, enzyme-shallow-equal@^1.0.4:
|
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz#b9256cb25a5f430f9bfe073a84808c1d74fced2e"
|
resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz#b9256cb25a5f430f9bfe073a84808c1d74fced2e"
|
||||||
integrity sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q==
|
integrity sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q==
|
||||||
|
@ -3146,7 +3115,7 @@ error-ex@^1.2.0, error-ex@^1.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-arrayish "^0.2.1"
|
is-arrayish "^0.2.1"
|
||||||
|
|
||||||
es-abstract@^1.17.0-next.1, es-abstract@^1.17.4:
|
es-abstract@^1.17.0-next.1:
|
||||||
version "1.17.7"
|
version "1.17.7"
|
||||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c"
|
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c"
|
||||||
integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==
|
integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==
|
||||||
|
@ -3183,6 +3152,28 @@ es-abstract@^1.18.0-next.1:
|
||||||
string.prototype.trimend "^1.0.3"
|
string.prototype.trimend "^1.0.3"
|
||||||
string.prototype.trimstart "^1.0.3"
|
string.prototype.trimstart "^1.0.3"
|
||||||
|
|
||||||
|
es-abstract@^1.18.0-next.2, es-abstract@^1.18.2:
|
||||||
|
version "1.18.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0"
|
||||||
|
integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.2"
|
||||||
|
es-to-primitive "^1.2.1"
|
||||||
|
function-bind "^1.1.1"
|
||||||
|
get-intrinsic "^1.1.1"
|
||||||
|
has "^1.0.3"
|
||||||
|
has-symbols "^1.0.2"
|
||||||
|
is-callable "^1.2.3"
|
||||||
|
is-negative-zero "^2.0.1"
|
||||||
|
is-regex "^1.1.3"
|
||||||
|
is-string "^1.0.6"
|
||||||
|
object-inspect "^1.10.3"
|
||||||
|
object-keys "^1.1.1"
|
||||||
|
object.assign "^4.1.2"
|
||||||
|
string.prototype.trimend "^1.0.4"
|
||||||
|
string.prototype.trimstart "^1.0.4"
|
||||||
|
unbox-primitive "^1.0.1"
|
||||||
|
|
||||||
es-get-iterator@^1.0.1:
|
es-get-iterator@^1.0.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.1.tgz#b93ddd867af16d5118e00881396533c1c6647ad9"
|
resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.1.tgz#b93ddd867af16d5118e00881396533c1c6647ad9"
|
||||||
|
@ -3965,7 +3956,17 @@ function-bind@^1.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||||
|
|
||||||
function.prototype.name@^1.1.2, function.prototype.name@^1.1.3:
|
function.prototype.name@^1.1.0:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.4.tgz#e4ea839b9d3672ae99d0efd9f38d9191c5eaac83"
|
||||||
|
integrity sha512-iqy1pIotY/RmhdFZygSSlW0wko2yxkSCKqsuv4pr8QESohpYyG/Z7B/XXvPRKTJS//960rgguE5mSRUsDdaJrQ==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.2"
|
||||||
|
define-properties "^1.1.3"
|
||||||
|
es-abstract "^1.18.0-next.2"
|
||||||
|
functions-have-names "^1.2.2"
|
||||||
|
|
||||||
|
function.prototype.name@^1.1.2:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.3.tgz#0bb034bb308e7682826f215eb6b2ae64918847fe"
|
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.3.tgz#0bb034bb308e7682826f215eb6b2ae64918847fe"
|
||||||
integrity sha512-H51qkbNSp8mtkJt+nyW1gyStBiKZxfRqySNUR99ylq6BPXHKI4SEvIlTKp4odLfjRKJV04DFWMU3G/YRlQOsag==
|
integrity sha512-H51qkbNSp8mtkJt+nyW1gyStBiKZxfRqySNUR99ylq6BPXHKI4SEvIlTKp4odLfjRKJV04DFWMU3G/YRlQOsag==
|
||||||
|
@ -3980,7 +3981,7 @@ functional-red-black-tree@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||||
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
||||||
|
|
||||||
functions-have-names@^1.2.0, functions-have-names@^1.2.1:
|
functions-have-names@^1.2.0, functions-have-names@^1.2.1, functions-have-names@^1.2.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.2.tgz#98d93991c39da9361f8e50b337c4f6e41f120e21"
|
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.2.tgz#98d93991c39da9361f8e50b337c4f6e41f120e21"
|
||||||
integrity sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA==
|
integrity sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA==
|
||||||
|
@ -4004,6 +4005,15 @@ get-intrinsic@^1.0.1, get-intrinsic@^1.0.2:
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
has-symbols "^1.0.1"
|
has-symbols "^1.0.1"
|
||||||
|
|
||||||
|
get-intrinsic@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
|
||||||
|
integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
|
||||||
|
dependencies:
|
||||||
|
function-bind "^1.1.1"
|
||||||
|
has "^1.0.3"
|
||||||
|
has-symbols "^1.0.1"
|
||||||
|
|
||||||
get-package-type@^0.1.0:
|
get-package-type@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
|
resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
|
||||||
|
@ -4157,6 +4167,11 @@ hard-rejection@^2.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
|
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
|
||||||
integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==
|
integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==
|
||||||
|
|
||||||
|
has-bigints@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
|
||||||
|
integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
|
||||||
|
|
||||||
has-flag@^3.0.0:
|
has-flag@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||||
|
@ -4172,6 +4187,11 @@ has-symbols@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
|
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
|
||||||
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
|
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
|
||||||
|
|
||||||
|
has-symbols@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
|
||||||
|
integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
|
||||||
|
|
||||||
has-value@^0.3.1:
|
has-value@^0.3.1:
|
||||||
version "0.3.1"
|
version "0.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
|
resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
|
||||||
|
@ -4203,7 +4223,7 @@ has-values@^1.0.0:
|
||||||
is-number "^3.0.0"
|
is-number "^3.0.0"
|
||||||
kind-of "^4.0.0"
|
kind-of "^4.0.0"
|
||||||
|
|
||||||
has@^1.0.1, has@^1.0.3:
|
has@^1.0.0, has@^1.0.1, has@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||||
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
|
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
|
||||||
|
@ -4534,6 +4554,11 @@ is-callable@^1.0.4, is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.2:
|
||||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9"
|
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9"
|
||||||
integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==
|
integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==
|
||||||
|
|
||||||
|
is-callable@^1.2.3:
|
||||||
|
version "1.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e"
|
||||||
|
integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==
|
||||||
|
|
||||||
is-ci@^2.0.0:
|
is-ci@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
|
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
|
||||||
|
@ -4736,13 +4761,21 @@ is-potential-custom-element-name@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397"
|
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397"
|
||||||
integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c=
|
integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c=
|
||||||
|
|
||||||
is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5, is-regex@^1.1.0, is-regex@^1.1.1:
|
is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5, is-regex@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
|
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
|
||||||
integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==
|
integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols "^1.0.1"
|
has-symbols "^1.0.1"
|
||||||
|
|
||||||
|
is-regex@^1.1.3:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f"
|
||||||
|
integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.2"
|
||||||
|
has-symbols "^1.0.2"
|
||||||
|
|
||||||
is-regexp@^2.0.0:
|
is-regexp@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d"
|
resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d"
|
||||||
|
@ -4768,6 +4801,11 @@ is-string@^1.0.4, is-string@^1.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6"
|
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6"
|
||||||
integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==
|
integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==
|
||||||
|
|
||||||
|
is-string@^1.0.6:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f"
|
||||||
|
integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==
|
||||||
|
|
||||||
is-subset@^0.1.1:
|
is-subset@^0.1.1:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6"
|
resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6"
|
||||||
|
@ -5674,8 +5712,8 @@ mathml-tag-names@^2.1.3:
|
||||||
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
||||||
|
|
||||||
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
|
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
|
||||||
version "11.1.0"
|
version "11.2.0"
|
||||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/acb9bc8cc5234326a7583514a8e120a4ac42eedc"
|
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/35ecbed29d16982deff27a8c37b05167738225a2"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.12.5"
|
"@babel/runtime" "^7.12.5"
|
||||||
another-json "^0.2.0"
|
another-json "^0.2.0"
|
||||||
|
@ -5695,10 +5733,10 @@ matrix-mock-request@^1.2.3:
|
||||||
bluebird "^3.5.0"
|
bluebird "^3.5.0"
|
||||||
expect "^1.20.2"
|
expect "^1.20.2"
|
||||||
|
|
||||||
matrix-react-test-utils@^0.2.2:
|
matrix-react-test-utils@^0.2.3:
|
||||||
version "0.2.2"
|
version "0.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.2.2.tgz#c87144d3b910c7edc544a6699d13c7c2bf02f853"
|
resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.2.3.tgz#27653f9d6bbfddd1856e51860fad1503b039d617"
|
||||||
integrity sha512-49+7gfV6smvBIVbeloql+37IeWMTD+fiywalwCqk8Dnz53zAFjKSltB3rmWHso1uecLtQEcPtCijfhzcLXAxTQ==
|
integrity sha512-NKZDlMEQzDZDQhBYyKBUtqidRvpkww3n9/GmGICkxtU2D6NetyBIfvm1Lf9o7167KSkPHJUVvDS9dzaS55jUnA==
|
||||||
|
|
||||||
"matrix-web-i18n@github:matrix-org/matrix-web-i18n":
|
"matrix-web-i18n@github:matrix-org/matrix-web-i18n":
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
|
@ -6081,6 +6119,11 @@ object-inspect@^1.1.0, object-inspect@^1.7.0, object-inspect@^1.8.0, object-insp
|
||||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a"
|
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a"
|
||||||
integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==
|
integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==
|
||||||
|
|
||||||
|
object-inspect@^1.10.3:
|
||||||
|
version "1.10.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369"
|
||||||
|
integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==
|
||||||
|
|
||||||
object-is@^1.0.2, object-is@^1.1.2:
|
object-is@^1.0.2, object-is@^1.1.2:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068"
|
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068"
|
||||||
|
@ -6121,7 +6164,17 @@ object.entries@^1.1.0, object.entries@^1.1.1, object.entries@^1.1.2:
|
||||||
es-abstract "^1.18.0-next.1"
|
es-abstract "^1.18.0-next.1"
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
|
|
||||||
object.fromentries@^2.0.2, object.fromentries@^2.0.3:
|
object.fromentries@^2.0.0:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.4.tgz#26e1ba5c4571c5c6f0890cef4473066456a120b8"
|
||||||
|
integrity sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.2"
|
||||||
|
define-properties "^1.1.3"
|
||||||
|
es-abstract "^1.18.0-next.2"
|
||||||
|
has "^1.0.3"
|
||||||
|
|
||||||
|
object.fromentries@^2.0.2:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.3.tgz#13cefcffa702dc67750314a3305e8cb3fad1d072"
|
resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.3.tgz#13cefcffa702dc67750314a3305e8cb3fad1d072"
|
||||||
integrity sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw==
|
integrity sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw==
|
||||||
|
@ -6138,7 +6191,16 @@ object.pick@^1.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
isobject "^3.0.1"
|
isobject "^3.0.1"
|
||||||
|
|
||||||
object.values@^1.1.1, object.values@^1.1.2:
|
object.values@^1.1.0:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30"
|
||||||
|
integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.2"
|
||||||
|
define-properties "^1.1.3"
|
||||||
|
es-abstract "^1.18.2"
|
||||||
|
|
||||||
|
object.values@^1.1.1:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.2.tgz#7a2015e06fcb0f546bd652486ce8583a4731c731"
|
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.2.tgz#7a2015e06fcb0f546bd652486ce8583a4731c731"
|
||||||
integrity sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==
|
integrity sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==
|
||||||
|
@ -6588,16 +6650,7 @@ prompts@^2.0.1:
|
||||||
kleur "^3.0.3"
|
kleur "^3.0.3"
|
||||||
sisteransi "^1.0.5"
|
sisteransi "^1.0.5"
|
||||||
|
|
||||||
prop-types-exact@^1.2.0:
|
prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.0, prop-types@^15.7.2:
|
||||||
version "1.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869"
|
|
||||||
integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==
|
|
||||||
dependencies:
|
|
||||||
has "^1.0.3"
|
|
||||||
object.assign "^4.1.0"
|
|
||||||
reflect.ownkeys "^0.2.0"
|
|
||||||
|
|
||||||
prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
|
|
||||||
version "15.7.2"
|
version "15.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||||
|
@ -6729,15 +6782,14 @@ react-clientside-effect@^1.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.0.0"
|
"@babel/runtime" "^7.0.0"
|
||||||
|
|
||||||
react-dom@^16.14.0:
|
react-dom@^17.0.2:
|
||||||
version "16.14.0"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||||
integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
|
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
prop-types "^15.6.2"
|
scheduler "^0.20.2"
|
||||||
scheduler "^0.19.1"
|
|
||||||
|
|
||||||
react-focus-lock@^2.5.0:
|
react-focus-lock@^2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
|
@ -6751,7 +6803,12 @@ react-focus-lock@^2.5.0:
|
||||||
use-callback-ref "^1.2.1"
|
use-callback-ref "^1.2.1"
|
||||||
use-sidecar "^1.0.1"
|
use-sidecar "^1.0.1"
|
||||||
|
|
||||||
react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6:
|
"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.0, react-is@^17.0.2:
|
||||||
|
version "17.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||||
|
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||||
|
|
||||||
|
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
|
@ -6788,15 +6845,23 @@ react-redux@^5.0.6:
|
||||||
react-is "^16.6.0"
|
react-is "^16.6.0"
|
||||||
react-lifecycles-compat "^3.0.0"
|
react-lifecycles-compat "^3.0.0"
|
||||||
|
|
||||||
react-test-renderer@^16.0.0-0, react-test-renderer@^16.14.0:
|
react-shallow-renderer@^16.13.1:
|
||||||
version "16.14.0"
|
version "16.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae"
|
resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz#bf0d02df8a519a558fd9b8215442efa5c840e124"
|
||||||
integrity sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==
|
integrity sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg==
|
||||||
dependencies:
|
dependencies:
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
prop-types "^15.6.2"
|
react-is "^16.12.0 || ^17.0.0"
|
||||||
react-is "^16.8.6"
|
|
||||||
scheduler "^0.19.1"
|
react-test-renderer@^17.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:
|
react-transition-group@^4.4.1:
|
||||||
version "4.4.1"
|
version "4.4.1"
|
||||||
|
@ -6808,14 +6873,13 @@ react-transition-group@^4.4.1:
|
||||||
loose-envify "^1.4.0"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
react@^16.14.0:
|
react@^17.0.2:
|
||||||
version "16.14.0"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
|
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
prop-types "^15.6.2"
|
|
||||||
|
|
||||||
read-pkg-up@^2.0.0:
|
read-pkg-up@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
|
@ -6923,11 +6987,6 @@ redux@^3.7.2:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
symbol-observable "^1.0.3"
|
symbol-observable "^1.0.3"
|
||||||
|
|
||||||
reflect.ownkeys@^0.2.0:
|
|
||||||
version "0.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"
|
|
||||||
integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=
|
|
||||||
|
|
||||||
regenerate-unicode-properties@^8.2.0:
|
regenerate-unicode-properties@^8.2.0:
|
||||||
version "8.2.0"
|
version "8.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
|
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
|
||||||
|
@ -7266,15 +7325,15 @@ saxes@^5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
xmlchars "^2.2.0"
|
xmlchars "^2.2.0"
|
||||||
|
|
||||||
scheduler@^0.19.1:
|
scheduler@^0.20.2:
|
||||||
version "0.19.1"
|
version "0.20.2"
|
||||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
|
||||||
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
|
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
|
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
|
||||||
version "5.7.1"
|
version "5.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||||
|
@ -7622,6 +7681,14 @@ string.prototype.trimend@^1.0.1, string.prototype.trimend@^1.0.3:
|
||||||
call-bind "^1.0.0"
|
call-bind "^1.0.0"
|
||||||
define-properties "^1.1.3"
|
define-properties "^1.1.3"
|
||||||
|
|
||||||
|
string.prototype.trimend@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
|
||||||
|
integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.2"
|
||||||
|
define-properties "^1.1.3"
|
||||||
|
|
||||||
string.prototype.trimstart@^1.0.1, string.prototype.trimstart@^1.0.3:
|
string.prototype.trimstart@^1.0.1, string.prototype.trimstart@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa"
|
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa"
|
||||||
|
@ -7630,6 +7697,14 @@ string.prototype.trimstart@^1.0.1, string.prototype.trimstart@^1.0.3:
|
||||||
call-bind "^1.0.0"
|
call-bind "^1.0.0"
|
||||||
define-properties "^1.1.3"
|
define-properties "^1.1.3"
|
||||||
|
|
||||||
|
string.prototype.trimstart@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed"
|
||||||
|
integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.2"
|
||||||
|
define-properties "^1.1.3"
|
||||||
|
|
||||||
string_decoder@^1.1.1:
|
string_decoder@^1.1.1:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||||
|
@ -7865,11 +7940,6 @@ test-exclude@^6.0.0:
|
||||||
glob "^7.1.4"
|
glob "^7.1.4"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
|
|
||||||
text-encoding-utf-8@^1.0.2:
|
|
||||||
version "1.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
|
|
||||||
integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==
|
|
||||||
|
|
||||||
text-table@^0.2.0:
|
text-table@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||||
|
@ -7969,9 +8039,9 @@ tree-kill@^1.2.2:
|
||||||
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
||||||
|
|
||||||
trim-newlines@^3.0.0:
|
trim-newlines@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30"
|
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
|
||||||
integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==
|
integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
|
||||||
|
|
||||||
trough@^1.0.0:
|
trough@^1.0.0:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
|
@ -8078,6 +8148,16 @@ ua-parser-js@^0.7.18:
|
||||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31"
|
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31"
|
||||||
integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==
|
integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==
|
||||||
|
|
||||||
|
unbox-primitive@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
|
||||||
|
integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==
|
||||||
|
dependencies:
|
||||||
|
function-bind "^1.1.1"
|
||||||
|
has-bigints "^1.0.1"
|
||||||
|
has-symbols "^1.0.2"
|
||||||
|
which-boxed-primitive "^1.0.2"
|
||||||
|
|
||||||
unhomoglyph@^1.0.6:
|
unhomoglyph@^1.0.6:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/unhomoglyph/-/unhomoglyph-1.0.6.tgz#ea41f926d0fcf598e3b8bb2980c2ddac66b081d3"
|
resolved "https://registry.yarnpkg.com/unhomoglyph/-/unhomoglyph-1.0.6.tgz#ea41f926d0fcf598e3b8bb2980c2ddac66b081d3"
|
||||||
|
@ -8352,7 +8432,7 @@ whatwg-url@^8.0.0:
|
||||||
tr46 "^2.0.2"
|
tr46 "^2.0.2"
|
||||||
webidl-conversions "^6.1.0"
|
webidl-conversions "^6.1.0"
|
||||||
|
|
||||||
which-boxed-primitive@^1.0.1:
|
which-boxed-primitive@^1.0.1, which-boxed-primitive@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
||||||
integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
|
integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
|
||||||
|
|
Loading…
Reference in New Issue