Merge branch 'develop' into string-pl
This commit is contained in:
commit
434b313850
59 changed files with 1196 additions and 866 deletions
|
@ -172,7 +172,7 @@
|
||||||
"jest": {
|
"jest": {
|
||||||
"testEnvironment": "./__test-utils__/environment.js",
|
"testEnvironment": "./__test-utils__/environment.js",
|
||||||
"testMatch": [
|
"testMatch": [
|
||||||
"<rootDir>/test/**/*-test.[jt]s"
|
"<rootDir>/test/**/*-test.[jt]s?(x)"
|
||||||
],
|
],
|
||||||
"setupFiles": [
|
"setupFiles": [
|
||||||
"jest-canvas-mock"
|
"jest-canvas-mock"
|
||||||
|
|
|
@ -111,6 +111,29 @@ $roomListCollapsedWidth: 68px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_LeftPanel_dialPadButton {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: $roomlist-button-bg-color;
|
||||||
|
position: relative;
|
||||||
|
margin-left: 8px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 8px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
mask-image: url('$(res)/img/element-icons/call/dialpad.svg');
|
||||||
|
mask-position: center;
|
||||||
|
mask-size: contain;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
background: $secondary-fg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mx_LeftPanel_exploreButton {
|
.mx_LeftPanel_exploreButton {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
@ -185,6 +208,12 @@ $roomListCollapsedWidth: 68px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
.mx_LeftPanel_dialPadButton {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-top: 8px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_LeftPanel_exploreButton {
|
.mx_LeftPanel_exploreButton {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
|
|
|
@ -112,7 +112,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
.mx_AccessibleButton {
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
padding-left: 28px; // 16px for the icon, 2px margin to text, 10px regular padding
|
padding-left: 30px; // 18px for the icon, 2px margin to text, 10px regular padding
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
@ -128,13 +128,14 @@ limitations under the License.
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
top: 50%; // text sizes are dynamic
|
||||||
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_RoomStatusBar_unsentCancelAllBtn::before {
|
&.mx_RoomStatusBar_unsentCancelAllBtn::before {
|
||||||
mask-image: url('$(res)/img/element-icons/trashcan.svg');
|
mask-image: url('$(res)/img/element-icons/trashcan.svg');
|
||||||
width: 12px;
|
|
||||||
height: 16px;
|
|
||||||
top: calc(50% - 8px); // text sizes are dynamic
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_RoomStatusBar_unsentResendAllBtn {
|
&.mx_RoomStatusBar_unsentResendAllBtn {
|
||||||
|
@ -142,9 +143,6 @@ limitations under the License.
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
mask-image: url('$(res)/img/element-icons/retry.svg');
|
mask-image: url('$(res)/img/element-icons/retry.svg');
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
top: calc(50% - 9px); // text sizes are dynamic
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ limitations under the License.
|
||||||
> .mx_ForwardDialog_preview {
|
> .mx_ForwardDialog_preview {
|
||||||
max-height: 30%;
|
max-height: 30%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
overflow: scroll;
|
overflow-y: auto;
|
||||||
|
|
||||||
div {
|
div {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
|
@ -295,6 +295,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_InviteDialog_content {
|
.mx_InviteDialog_content {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,3 +317,42 @@ limitations under the License.
|
||||||
.mx_InviteDialog_helpText .mx_AccessibleButton_kind_link {
|
.mx_InviteDialog_helpText .mx_AccessibleButton_kind_link {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_InviteDialog_multiInviterError {
|
||||||
|
> h4 {
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
.mx_InviteDialog_multiInviterError_entry {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.mx_InviteDialog_multiInviterError_entry_userProfile {
|
||||||
|
.mx_InviteDialog_multiInviterError_entry_name {
|
||||||
|
margin-left: 6px;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_InviteDialog_multiInviterError_entry_userId {
|
||||||
|
margin-left: 6px;
|
||||||
|
font-size: $font-12px;
|
||||||
|
line-height: $font-15px;
|
||||||
|
color: $tertiary-fg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_InviteDialog_multiInviterError_entry_error {
|
||||||
|
margin-left: 32px;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
color: $notice-primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,4 +17,9 @@ limitations under the License.
|
||||||
.mx_TextualEvent {
|
.mx_TextualEvent {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $accent-color;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ $irc-line-height: $font-18px;
|
||||||
// timestamps are links which shouldn't be underlined
|
// timestamps are links which shouldn't be underlined
|
||||||
> a {
|
> a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
min-width: 45px;
|
||||||
}
|
}
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -49,18 +50,6 @@ $irc-line-height: $font-18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .mx_SenderProfile {
|
|
||||||
order: 2;
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: var(--name-width);
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-align: left;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
overflow: visible;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_EventTile_line, .mx_EventTile_reply {
|
.mx_EventTile_line, .mx_EventTile_reply {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -173,27 +162,37 @@ $irc-line-height: $font-18px;
|
||||||
border-left: 0;
|
border-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SenderProfile_hover {
|
.mx_SenderProfile {
|
||||||
background-color: $primary-bg-color;
|
width: var(--name-width);
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
order: 2;
|
||||||
|
flex-shrink: 0;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
> .mx_SenderProfile_displayName {
|
> .mx_SenderProfile_displayName {
|
||||||
|
width: 100%;
|
||||||
|
text-align: end;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
min-width: var(--name-width);
|
}
|
||||||
text-align: end;
|
|
||||||
|
> .mx_SenderProfile_mxid {
|
||||||
|
visibility: collapse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SenderProfile:hover {
|
.mx_SenderProfile:hover {
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_SenderProfile_hover:hover {
|
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
width: max(auto, 100%);
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
||||||
|
> .mx_SenderProfile_displayName {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .mx_SenderProfile_mxid {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ReplyThread {
|
.mx_ReplyThread {
|
||||||
|
@ -201,16 +200,7 @@ $irc-line-height: $font-18px;
|
||||||
.mx_SenderProfile {
|
.mx_SenderProfile {
|
||||||
width: unset;
|
width: unset;
|
||||||
max-width: var(--name-width);
|
max-width: var(--name-width);
|
||||||
}
|
|
||||||
|
|
||||||
.mx_SenderProfile_hover {
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
> span {
|
|
||||||
> .mx_SenderProfile_displayName {
|
|
||||||
min-width: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_emote {
|
.mx_EventTile_emote {
|
||||||
|
|
|
@ -36,10 +36,10 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_VoiceRecordComposerTile_delete {
|
.mx_VoiceRecordComposerTile_delete {
|
||||||
width: 14px; // w&h are size of icon
|
width: 24px;
|
||||||
height: 18px;
|
height: 24px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: 11px; // distance from left edge of waveform container (container has some margin too)
|
margin-right: 8px; // distance from left edge of waveform container (container has some margin too)
|
||||||
background-color: $voice-record-icon-color;
|
background-color: $voice-record-icon-color;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
|
|
|
@ -73,7 +73,7 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
.mx_AccessibleButton_hasKind {
|
||||||
padding: 8px 22px;
|
padding: 8px 22px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
3
res/img/element-icons/call/dialpad.svg
Normal file
3
res/img/element-icons/call/dialpad.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="12" height="18" viewBox="0 0 12 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6 14.25C5.175 14.25 4.5 14.925 4.5 15.75C4.5 16.575 5.175 17.25 6 17.25C6.825 17.25 7.5 16.575 7.5 15.75C7.5 14.925 6.825 14.25 6 14.25ZM1.5 0.75C0.675 0.75 0 1.425 0 2.25C0 3.075 0.675 3.75 1.5 3.75C2.325 3.75 3 3.075 3 2.25C3 1.425 2.325 0.75 1.5 0.75ZM1.5 5.25C0.675 5.25 0 5.925 0 6.75C0 7.575 0.675 8.25 1.5 8.25C2.325 8.25 3 7.575 3 6.75C3 5.925 2.325 5.25 1.5 5.25ZM1.5 9.75C0.675 9.75 0 10.425 0 11.25C0 12.075 0.675 12.75 1.5 12.75C2.325 12.75 3 12.075 3 11.25C3 10.425 2.325 9.75 1.5 9.75ZM10.5 3.75C11.325 3.75 12 3.075 12 2.25C12 1.425 11.325 0.75 10.5 0.75C9.675 0.75 9 1.425 9 2.25C9 3.075 9.675 3.75 10.5 3.75ZM6 9.75C5.175 9.75 4.5 10.425 4.5 11.25C4.5 12.075 5.175 12.75 6 12.75C6.825 12.75 7.5 12.075 7.5 11.25C7.5 10.425 6.825 9.75 6 9.75ZM10.5 9.75C9.675 9.75 9 10.425 9 11.25C9 12.075 9.675 12.75 10.5 12.75C11.325 12.75 12 12.075 12 11.25C12 10.425 11.325 9.75 10.5 9.75ZM10.5 5.25C9.675 5.25 9 5.925 9 6.75C9 7.575 9.675 8.25 10.5 8.25C11.325 8.25 12 7.575 12 6.75C12 5.925 11.325 5.25 10.5 5.25ZM6 5.25C5.175 5.25 4.5 5.925 4.5 6.75C4.5 7.575 5.175 8.25 6 8.25C6.825 8.25 7.5 7.575 7.5 6.75C7.5 5.925 6.825 5.25 6 5.25ZM6 0.75C5.175 0.75 4.5 1.425 4.5 2.25C4.5 3.075 5.175 3.75 6 3.75C6.825 3.75 7.5 3.075 7.5 2.25C7.5 1.425 6.825 0.75 6 0.75Z" fill="#737D8C"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -1,3 +1,3 @@
|
||||||
<svg width="12" height="17" viewBox="0 0 12 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M0.857143 14.5C0.857143 15.4491 1.62857 16.5 2.57143 16.5H9.42857C10.3714 16.5 11.1429 15.2542 11.1429 14.3051V5.67692C11.1429 4.72781 10.3714 3.95128 9.42857 3.95128H2.57143C1.62857 3.95128 0.857143 4.72781 0.857143 5.67692V14.5ZM11.1429 1.36282H9L8.39143 0.750218C8.23714 0.59491 8.01429 0.5 7.79143 0.5H4.20857C3.98571 0.5 3.76286 0.59491 3.60857 0.750218L3 1.36282H0.857143C0.385714 1.36282 0 1.75109 0 2.22564C0 2.70019 0.385714 3.08846 0.857143 3.08846H11.1429C11.6143 3.08846 12 2.70019 12 2.22564C12 1.75109 11.6143 1.36282 11.1429 1.36282Z" fill="#737D8C"/>
|
<path d="M6 19C6 20.1 6.9 21 8 21H16C17.1 21 18 20.1 18 19V9C18 7.9 17.1 7 16 7H8C6.9 7 6 7.9 6 9V19ZM18 4H15.5L14.79 3.29C14.61 3.11 14.35 3 14.09 3H9.91C9.65 3 9.39 3.11 9.21 3.29L8.5 4H6C5.45 4 5 4.45 5 5C5 5.55 5.45 6 6 6H18C18.55 6 19 5.55 19 5C19 4.45 18.55 4 18 4Z" fill="#8D99A5"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 679 B After Width: | Height: | Size: 397 B |
|
@ -5,4 +5,4 @@ FROM node:14-buster
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
RUN apt-get -y install jq build-essential python3-dev libffi-dev python-pip python-setuptools sqlite3 libssl-dev python-virtualenv libjpeg-dev libxslt1-dev uuid-runtime
|
RUN apt-get -y install jq build-essential python3-dev libffi-dev python-pip python-setuptools sqlite3 libssl-dev python-virtualenv libjpeg-dev libxslt1-dev uuid-runtime
|
||||||
# dependencies for chrome (installed by puppeteer)
|
# dependencies for chrome (installed by puppeteer)
|
||||||
RUN apt-get -y install gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
|
RUN apt-get -y install gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm-dev libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
|
||||||
|
|
|
@ -15,20 +15,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
declare module "diff-dom" {
|
declare module "diff-dom" {
|
||||||
enum Action {
|
|
||||||
AddElement = "addElement",
|
|
||||||
AddTextElement = "addTextElement",
|
|
||||||
RemoveTextElement = "removeTextElement",
|
|
||||||
RemoveElement = "removeElement",
|
|
||||||
ReplaceElement = "replaceElement",
|
|
||||||
ModifyTextElement = "modifyTextElement",
|
|
||||||
AddAttribute = "addAttribute",
|
|
||||||
RemoveAttribute = "removeAttribute",
|
|
||||||
ModifyAttribute = "modifyAttribute",
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IDiff {
|
export interface IDiff {
|
||||||
action: Action;
|
action: string;
|
||||||
name: string;
|
name: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
route: number[];
|
route: number[];
|
||||||
|
|
|
@ -307,7 +307,7 @@ function readFileAsArrayBuffer(file: File | Blob): Promise<ArrayBuffer> {
|
||||||
* If the file is unencrypted then the object will have a "url" key.
|
* If the file is unencrypted then the object will have a "url" key.
|
||||||
* If the file is encrypted then the object will have a "file" key.
|
* If the file is encrypted then the object will have a "file" key.
|
||||||
*/
|
*/
|
||||||
function uploadFile(
|
export function uploadFile(
|
||||||
matrixClient: MatrixClient,
|
matrixClient: MatrixClient,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
file: File | Blob,
|
file: File | Blob,
|
||||||
|
|
|
@ -68,7 +68,7 @@ export const Notifier = {
|
||||||
// or not
|
// or not
|
||||||
pendingEncryptedEventIds: [],
|
pendingEncryptedEventIds: [],
|
||||||
|
|
||||||
notificationMessageForEvent: function(ev: MatrixEvent) {
|
notificationMessageForEvent: function(ev: MatrixEvent): string {
|
||||||
if (typehandlers.hasOwnProperty(ev.getContent().msgtype)) {
|
if (typehandlers.hasOwnProperty(ev.getContent().msgtype)) {
|
||||||
return typehandlers[ev.getContent().msgtype](ev);
|
return typehandlers[ev.getContent().msgtype](ev);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -16,15 +14,26 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import {MatrixClientPeg} from './MatrixClientPeg';
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import MultiInviter from './utils/MultiInviter';
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
import { User } from "matrix-js-sdk/src/models/user";
|
||||||
|
|
||||||
|
import { MatrixClientPeg } from './MatrixClientPeg';
|
||||||
|
import MultiInviter, { CompletionStates } from './utils/MultiInviter';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import * as sdk from './';
|
import * as sdk from './';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import InviteDialog, {KIND_DM, KIND_INVITE} from "./components/views/dialogs/InviteDialog";
|
import InviteDialog, { KIND_DM, KIND_INVITE, Member } from "./components/views/dialogs/InviteDialog";
|
||||||
import CommunityPrototypeInviteDialog from "./components/views/dialogs/CommunityPrototypeInviteDialog";
|
import CommunityPrototypeInviteDialog from "./components/views/dialogs/CommunityPrototypeInviteDialog";
|
||||||
import {CommunityPrototypeStore} from "./stores/CommunityPrototypeStore";
|
import { CommunityPrototypeStore } from "./stores/CommunityPrototypeStore";
|
||||||
|
import BaseAvatar from "./components/views/avatars/BaseAvatar";
|
||||||
|
import { mediaFromMxc } from "./customisations/Media";
|
||||||
|
|
||||||
|
export interface IInviteResult {
|
||||||
|
states: CompletionStates;
|
||||||
|
inviter: MultiInviter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invites multiple addresses to a room
|
* Invites multiple addresses to a room
|
||||||
|
@ -32,15 +41,15 @@ import {CommunityPrototypeStore} from "./stores/CommunityPrototypeStore";
|
||||||
* no option to cancel.
|
* no option to cancel.
|
||||||
*
|
*
|
||||||
* @param {string} roomId The ID of the room to invite to
|
* @param {string} roomId The ID of the room to invite to
|
||||||
* @param {string[]} addrs Array of strings of addresses to invite. May be matrix IDs or 3pids.
|
* @param {string[]} addresses Array of strings of addresses to invite. May be matrix IDs or 3pids.
|
||||||
* @returns {Promise} Promise
|
* @returns {Promise} Promise
|
||||||
*/
|
*/
|
||||||
export function inviteMultipleToRoom(roomId, addrs) {
|
export function inviteMultipleToRoom(roomId: string, addresses: string[]): Promise<IInviteResult> {
|
||||||
const inviter = new MultiInviter(roomId);
|
const inviter = new MultiInviter(roomId);
|
||||||
return inviter.invite(addrs).then(states => Promise.resolve({states, inviter}));
|
return inviter.invite(addresses).then(states => Promise.resolve({ states, inviter }));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showStartChatInviteDialog(initialText) {
|
export function showStartChatInviteDialog(initialText = ""): void {
|
||||||
// This dialog handles the room creation internally - we don't need to worry about it.
|
// This dialog handles the room creation internally - we don't need to worry about it.
|
||||||
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
|
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
|
||||||
Modal.createTrackedDialog(
|
Modal.createTrackedDialog(
|
||||||
|
@ -49,7 +58,7 @@ export function showStartChatInviteDialog(initialText) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showRoomInviteDialog(roomId, initialText = "") {
|
export function showRoomInviteDialog(roomId: string, initialText = ""): void {
|
||||||
// This dialog handles the room creation internally - we don't need to worry about it.
|
// This dialog handles the room creation internally - we don't need to worry about it.
|
||||||
Modal.createTrackedDialog(
|
Modal.createTrackedDialog(
|
||||||
"Invite Users", "", InviteDialog, {
|
"Invite Users", "", InviteDialog, {
|
||||||
|
@ -61,14 +70,14 @@ export function showRoomInviteDialog(roomId, initialText = "") {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showCommunityRoomInviteDialog(roomId, communityName) {
|
export function showCommunityRoomInviteDialog(roomId: string, communityName: string): void {
|
||||||
Modal.createTrackedDialog(
|
Modal.createTrackedDialog(
|
||||||
'Invite Users to Community', '', CommunityPrototypeInviteDialog, {communityName, roomId},
|
'Invite Users to Community', '', CommunityPrototypeInviteDialog, {communityName, roomId},
|
||||||
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
|
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showCommunityInviteDialog(communityId) {
|
export function showCommunityInviteDialog(communityId: string): void {
|
||||||
const chat = CommunityPrototypeStore.instance.getGeneralChat(communityId);
|
const chat = CommunityPrototypeStore.instance.getGeneralChat(communityId);
|
||||||
if (chat) {
|
if (chat) {
|
||||||
const name = CommunityPrototypeStore.instance.getCommunityName(communityId);
|
const name = CommunityPrototypeStore.instance.getCommunityName(communityId);
|
||||||
|
@ -83,7 +92,7 @@ export function showCommunityInviteDialog(communityId) {
|
||||||
* @param {MatrixEvent} event The event to check
|
* @param {MatrixEvent} event The event to check
|
||||||
* @returns {boolean} True if valid, false otherwise
|
* @returns {boolean} True if valid, false otherwise
|
||||||
*/
|
*/
|
||||||
export function isValid3pidInvite(event) {
|
export function isValid3pidInvite(event: MatrixEvent): boolean {
|
||||||
if (!event || event.getType() !== "m.room.third_party_invite") return false;
|
if (!event || event.getType() !== "m.room.third_party_invite") return false;
|
||||||
|
|
||||||
// any events without these keys are not valid 3pid invites, so we ignore them
|
// any events without these keys are not valid 3pid invites, so we ignore them
|
||||||
|
@ -96,7 +105,7 @@ export function isValid3pidInvite(event) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function inviteUsersToRoom(roomId, userIds) {
|
export function inviteUsersToRoom(roomId: string, userIds: string[]): Promise<void> {
|
||||||
return inviteMultipleToRoom(roomId, userIds).then((result) => {
|
return inviteMultipleToRoom(roomId, userIds).then((result) => {
|
||||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
showAnyInviteErrors(result.states, room, result.inviter);
|
showAnyInviteErrors(result.states, room, result.inviter);
|
||||||
|
@ -110,9 +119,14 @@ export function inviteUsersToRoom(roomId, userIds) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showAnyInviteErrors(addrs, room, inviter) {
|
export function showAnyInviteErrors(
|
||||||
|
states: CompletionStates,
|
||||||
|
room: Room,
|
||||||
|
inviter: MultiInviter,
|
||||||
|
userMap?: Map<string, Member>,
|
||||||
|
): boolean {
|
||||||
// Show user any errors
|
// Show user any errors
|
||||||
const failedUsers = Object.keys(addrs).filter(a => addrs[a] === 'error');
|
const failedUsers = Object.keys(states).filter(a => states[a] === 'error');
|
||||||
if (failedUsers.length === 1 && inviter.fatal) {
|
if (failedUsers.length === 1 && inviter.fatal) {
|
||||||
// Just get the first message because there was a fatal problem on the first
|
// Just get the first message because there was a fatal problem on the first
|
||||||
// user. This usually means that no other users were attempted, making it
|
// user. This usually means that no other users were attempted, making it
|
||||||
|
@ -126,19 +140,47 @@ export function showAnyInviteErrors(addrs, room, inviter) {
|
||||||
} else {
|
} else {
|
||||||
const errorList = [];
|
const errorList = [];
|
||||||
for (const addr of failedUsers) {
|
for (const addr of failedUsers) {
|
||||||
if (addrs[addr] === "error") {
|
if (states[addr] === "error") {
|
||||||
const reason = inviter.getErrorText(addr);
|
const reason = inviter.getErrorText(addr);
|
||||||
errorList.push(addr + ": " + reason);
|
errorList.push(addr + ": " + reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
if (errorList.length > 0) {
|
if (errorList.length > 0) {
|
||||||
// React 16 doesn't let us use `errorList.join(<br />)` anymore, so this is our solution
|
// React 16 doesn't let us use `errorList.join(<br />)` anymore, so this is our solution
|
||||||
const description = <div>{errorList.map(e => <div key={e}>{e}</div>)}</div>;
|
const description = <div className="mx_InviteDialog_multiInviterError">
|
||||||
|
<h4>{ _t("We sent the others, but the below people couldn't be invited to <RoomName/>", {}, {
|
||||||
|
RoomName: () => <b>{ room.name }</b>,
|
||||||
|
}) }</h4>
|
||||||
|
<div>
|
||||||
|
{ failedUsers.map(addr => {
|
||||||
|
const user = userMap?.get(addr) || cli.getUser(addr);
|
||||||
|
const name = (user as Member).name || (user as User).rawDisplayName;
|
||||||
|
const avatarUrl = (user as Member).getMxcAvatarUrl?.() || (user as User).avatarUrl;
|
||||||
|
return <div key={addr} className="mx_InviteDialog_multiInviterError_entry">
|
||||||
|
<div className="mx_InviteDialog_multiInviterError_entry_userProfile">
|
||||||
|
<BaseAvatar
|
||||||
|
url={avatarUrl ? mediaFromMxc(avatarUrl).getSquareThumbnailHttp(24) : null}
|
||||||
|
name={name}
|
||||||
|
idName={user.userId}
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
/>
|
||||||
|
<span className="mx_InviteDialog_multiInviterError_entry_name">{ name }</span>
|
||||||
|
<span className="mx_InviteDialog_multiInviterError_entry_userId">{ user.userId }</span>
|
||||||
|
</div>
|
||||||
|
<div className="mx_InviteDialog_multiInviterError_entry_error">
|
||||||
|
{ inviter.getErrorText(addr) }
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createTrackedDialog('Failed to invite the following users to the room', '', ErrorDialog, {
|
Modal.createTrackedDialog("Some invites could not be sent", "", ErrorDialog, {
|
||||||
title: _t("Failed to invite the following users to the %(roomName)s room:", {roomName: room.name}),
|
title: _t("Some invites couldn't be sent"),
|
||||||
description,
|
description,
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ICryptoCallbacks, IDeviceTrustLevel, ISecretStorageKeyInfo } from 'matrix-js-sdk/src/matrix';
|
import { ICryptoCallbacks, ISecretStorageKeyInfo } from 'matrix-js-sdk/src/matrix';
|
||||||
import { MatrixClient } from 'matrix-js-sdk/src/client';
|
import { MatrixClient } from 'matrix-js-sdk/src/client';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import * as sdk from './index';
|
import * as sdk from './index';
|
||||||
|
@ -28,6 +28,7 @@ import AccessSecretStorageDialog from './components/views/dialogs/security/Acces
|
||||||
import RestoreKeyBackupDialog from './components/views/dialogs/security/RestoreKeyBackupDialog';
|
import RestoreKeyBackupDialog from './components/views/dialogs/security/RestoreKeyBackupDialog';
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import SecurityCustomisations from "./customisations/Security";
|
import SecurityCustomisations from "./customisations/Security";
|
||||||
|
import { DeviceTrustLevel } from 'matrix-js-sdk/src/crypto/CrossSigning';
|
||||||
|
|
||||||
// This stores the secret storage private keys in memory for the JS SDK. This is
|
// This stores the secret storage private keys in memory for the JS SDK. This is
|
||||||
// only meant to act as a cache to avoid prompting the user multiple times
|
// only meant to act as a cache to avoid prompting the user multiple times
|
||||||
|
@ -244,7 +245,7 @@ async function onSecretRequested(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
requestId: string,
|
requestId: string,
|
||||||
name: string,
|
name: string,
|
||||||
deviceTrust: IDeviceTrustLevel,
|
deviceTrust: DeviceTrustLevel,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
console.log("onSecretRequested", userId, deviceId, requestId, name, deviceTrust);
|
console.log("onSecretRequested", userId, deviceId, requestId, name, deviceTrust);
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
|
|
|
@ -13,6 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
import {MatrixClientPeg} from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import * as Roles from './Roles';
|
import * as Roles from './Roles';
|
||||||
|
@ -20,6 +22,11 @@ import {isValid3pidInvite} from "./RoomInvite";
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
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";
|
||||||
|
import { RightPanelPhases } from './stores/RightPanelStorePhases';
|
||||||
|
import { Action } from './dispatcher/actions';
|
||||||
|
import defaultDispatcher from './dispatcher/dispatcher';
|
||||||
|
import { SetRightPanelPhasePayload } from './dispatcher/payloads/SetRightPanelPhasePayload';
|
||||||
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
// These functions are frequently used just to check whether an event has
|
// 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
|
// any text to display at all. For this reason they return deferred values
|
||||||
|
@ -31,76 +38,89 @@ function textForMemberEvent(ev): () => string | null {
|
||||||
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;
|
||||||
|
|
||||||
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}) + ' ' + getReason();
|
return () => reason
|
||||||
|
? _t('%(senderName)s banned %(targetName)s: %(reason)s', { senderName, targetName, reason })
|
||||||
|
: _t('%(senderName)s banned %(targetName)s', { senderName, targetName });
|
||||||
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 using 'show hidden events' (labs)
|
||||||
return () => _t("%(senderName)s made no change.", {senderName});
|
return () => _t("%(senderName)s made no change", { senderName });
|
||||||
} else {
|
} else {
|
||||||
return null;
|
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 () => reason
|
||||||
|
? _t('%(targetName)s left the room: %(reason)s', { targetName, reason })
|
||||||
|
: _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 () => reason
|
||||||
senderName,
|
? _t('%(senderName)s withdrew %(targetName)s\'s invitation: %(reason)s', {
|
||||||
targetName,
|
senderName,
|
||||||
}) + ' ' + getReason();
|
targetName,
|
||||||
|
reason,
|
||||||
|
})
|
||||||
|
: _t('%(senderName)s withdrew %(targetName)s\'s invitation', { senderName, targetName })
|
||||||
} else if (prevContent.membership === "join") {
|
} else if (prevContent.membership === "join") {
|
||||||
return () => _t('%(senderName)s kicked %(targetName)s.', {senderName, targetName}) + ' ' + getReason();
|
return () => reason
|
||||||
|
? _t('%(senderName)s kicked %(targetName)s: %(reason)s', {
|
||||||
|
senderName,
|
||||||
|
targetName,
|
||||||
|
reason,
|
||||||
|
})
|
||||||
|
: _t('%(senderName)s kicked %(targetName)s', { senderName, targetName });
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -474,9 +494,33 @@ function textForPowerEvent(event): () => string | null {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForPinnedEvent(event): () => string | null {
|
const onPinnedMessagesClick = (): void => {
|
||||||
|
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||||
|
action: Action.SetRightPanelPhase,
|
||||||
|
phase: RightPanelPhases.PinnedMessages,
|
||||||
|
allowClose: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string | JSX.Element | null {
|
||||||
|
if (!SettingsStore.getValue("feature_pinning")) return 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});
|
|
||||||
|
if (allowJSX) {
|
||||||
|
return () => (
|
||||||
|
<span>
|
||||||
|
{
|
||||||
|
_t(
|
||||||
|
"%(senderName)s changed the <a>pinned messages</a> for the room.",
|
||||||
|
{ senderName },
|
||||||
|
{ "a": (sub) => <a onClick={onPinnedMessagesClick}> { sub } </a> },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => _t("%(senderName)s changed the pinned messages for the room.", { senderName });
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForWidgetEvent(event): () => string | null {
|
function textForWidgetEvent(event): () => string | null {
|
||||||
|
@ -602,7 +646,7 @@ function textForMjolnirEvent(event): () => string | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IHandlers {
|
interface IHandlers {
|
||||||
[type: string]: (ev: any) => (() => string | null);
|
[type: string]: (ev: MatrixEvent, allowJSX?: boolean) => (() => string | JSX.Element | null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlers: IHandlers = {
|
const handlers: IHandlers = {
|
||||||
|
@ -643,7 +687,9 @@ export function hasText(ev): boolean {
|
||||||
return Boolean(handler?.(ev));
|
return Boolean(handler?.(ev));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function textForEvent(ev): string {
|
export function textForEvent(ev: MatrixEvent): string;
|
||||||
|
export function textForEvent(ev: MatrixEvent, allowJSX: true): string | JSX.Element;
|
||||||
|
export function textForEvent(ev: MatrixEvent, allowJSX = false): string | JSX.Element {
|
||||||
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
|
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
|
||||||
return handler?.(ev)?.() || '';
|
return handler?.(ev, allowJSX)?.() || '';
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { EventType, MsgType } from "matrix-js-sdk/src/@types/event";
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||||
import shouldHideEvent from './shouldHideEvent';
|
import shouldHideEvent from './shouldHideEvent';
|
||||||
|
@ -43,12 +43,6 @@ export function eventTriggersUnreadCount(ev: MatrixEvent): boolean {
|
||||||
case EventType.RoomCanonicalAlias:
|
case EventType.RoomCanonicalAlias:
|
||||||
case EventType.RoomServerAcl:
|
case EventType.RoomServerAcl:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case EventType.RoomMessage:
|
|
||||||
if (ev.getContent().msgtype === MsgType.Notice) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.isRedacted()) return false;
|
if (ev.isRedacted()) return false;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 New Vector Ltd
|
Copyright 2017 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,15 +14,19 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const emailRegex = /^\S+@\S+\.\S+$/;
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
const emailRegex = /^\S+@\S+\.\S+$/;
|
||||||
const mxUserIdRegex = /^@\S+:\S+$/;
|
const mxUserIdRegex = /^@\S+:\S+$/;
|
||||||
const mxRoomIdRegex = /^!\S+:\S+$/;
|
const mxRoomIdRegex = /^!\S+:\S+$/;
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
export const addressTypes = ['mx-user-id', 'mx-room-id', 'email'];
|
||||||
export const addressTypes = [
|
|
||||||
'mx-user-id', 'mx-room-id', 'email',
|
export enum AddressType {
|
||||||
];
|
Email = "email",
|
||||||
|
MatrixUserId = "mx-user-id",
|
||||||
|
MatrixRoomId = "mx-room-id",
|
||||||
|
}
|
||||||
|
|
||||||
// PropType definition for an object describing
|
// PropType definition for an object describing
|
||||||
// an address that can be invited to a room (which
|
// an address that can be invited to a room (which
|
||||||
|
@ -40,18 +44,13 @@ export const UserAddressType = PropTypes.shape({
|
||||||
isKnown: PropTypes.bool,
|
isKnown: PropTypes.bool,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function getAddressType(inputText) {
|
export function getAddressType(inputText: string): AddressType | null {
|
||||||
const isEmailAddress = emailRegex.test(inputText);
|
if (emailRegex.test(inputText)) {
|
||||||
const isUserId = mxUserIdRegex.test(inputText);
|
return AddressType.Email;
|
||||||
const isRoomId = mxRoomIdRegex.test(inputText);
|
} else if (mxUserIdRegex.test(inputText)) {
|
||||||
|
return AddressType.MatrixUserId;
|
||||||
// sanity check the input for user IDs
|
} else if (mxRoomIdRegex.test(inputText)) {
|
||||||
if (isEmailAddress) {
|
return AddressType.MatrixRoomId;
|
||||||
return 'email';
|
|
||||||
} else if (isUserId) {
|
|
||||||
return 'mx-user-id';
|
|
||||||
} else if (isRoomId) {
|
|
||||||
return 'mx-room-id';
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
|
@ -24,6 +24,7 @@ import CustomRoomTagPanel from "./CustomRoomTagPanel";
|
||||||
import dis from "../../dispatcher/dispatcher";
|
import dis from "../../dispatcher/dispatcher";
|
||||||
import { _t } from "../../languageHandler";
|
import { _t } from "../../languageHandler";
|
||||||
import RoomList from "../views/rooms/RoomList";
|
import RoomList from "../views/rooms/RoomList";
|
||||||
|
import CallHandler from "../../CallHandler";
|
||||||
import { HEADER_HEIGHT } from "../views/rooms/RoomSublist";
|
import { HEADER_HEIGHT } from "../views/rooms/RoomSublist";
|
||||||
import { Action } from "../../dispatcher/actions";
|
import { Action } from "../../dispatcher/actions";
|
||||||
import UserMenu from "./UserMenu";
|
import UserMenu from "./UserMenu";
|
||||||
|
@ -124,6 +125,10 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
this.setState({ activeSpace });
|
this.setState({ activeSpace });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onDialPad = () => {
|
||||||
|
dis.fire(Action.OpenDialPad);
|
||||||
|
}
|
||||||
|
|
||||||
private onExplore = () => {
|
private onExplore = () => {
|
||||||
dis.fire(Action.ViewRoomDirectory);
|
dis.fire(Action.ViewRoomDirectory);
|
||||||
};
|
};
|
||||||
|
@ -397,7 +402,20 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderSearchExplore(): React.ReactNode {
|
private renderSearchDialExplore(): React.ReactNode {
|
||||||
|
let dialPadButton = null;
|
||||||
|
|
||||||
|
// If we have dialer support, show a button to bring up the dial pad
|
||||||
|
// to start a new call
|
||||||
|
if (CallHandler.sharedInstance().getSupportsPstnProtocol()) {
|
||||||
|
dialPadButton =
|
||||||
|
<AccessibleTooltipButton
|
||||||
|
className={classNames("mx_LeftPanel_dialPadButton", {})}
|
||||||
|
onClick={this.onDialPad}
|
||||||
|
title={_t("Open dial pad")}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="mx_LeftPanel_filterContainer"
|
className="mx_LeftPanel_filterContainer"
|
||||||
|
@ -410,6 +428,9 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
onSelectRoom={this.selectRoom}
|
onSelectRoom={this.selectRoom}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{dialPadButton}
|
||||||
|
|
||||||
<AccessibleTooltipButton
|
<AccessibleTooltipButton
|
||||||
className={classNames("mx_LeftPanel_exploreButton", {
|
className={classNames("mx_LeftPanel_exploreButton", {
|
||||||
mx_LeftPanel_exploreButton_space: !!this.state.activeSpace,
|
mx_LeftPanel_exploreButton_space: !!this.state.activeSpace,
|
||||||
|
@ -458,7 +479,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
{leftLeftPanel}
|
{leftLeftPanel}
|
||||||
<aside className="mx_LeftPanel_roomListContainer">
|
<aside className="mx_LeftPanel_roomListContainer">
|
||||||
{this.renderHeader()}
|
{this.renderHeader()}
|
||||||
{this.renderSearchExplore()}
|
{this.renderSearchDialExplore()}
|
||||||
{this.renderBreadcrumbs()}
|
{this.renderBreadcrumbs()}
|
||||||
<RoomListNumResults onVisibilityChange={this.refreshStickyHeaders} />
|
<RoomListNumResults onVisibilityChange={this.refreshStickyHeaders} />
|
||||||
<div className="mx_LeftPanel_roomListWrapper">
|
<div className="mx_LeftPanel_roomListWrapper">
|
||||||
|
|
|
@ -48,7 +48,7 @@ import createRoom, {IOpts} from "../../createRoom";
|
||||||
import {_t, _td, getCurrentLanguage} from '../../languageHandler';
|
import {_t, _td, getCurrentLanguage} from '../../languageHandler';
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import ThemeController from "../../settings/controllers/ThemeController";
|
import ThemeController from "../../settings/controllers/ThemeController";
|
||||||
import { startAnyRegistrationFlow } from "../../Registration.js";
|
import { startAnyRegistrationFlow } from "../../Registration";
|
||||||
import { messageForSyncError } from '../../utils/ErrorUtils';
|
import { messageForSyncError } from '../../utils/ErrorUtils';
|
||||||
import ResizeNotifier from "../../utils/ResizeNotifier";
|
import ResizeNotifier from "../../utils/ResizeNotifier";
|
||||||
import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils";
|
import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils";
|
||||||
|
|
|
@ -82,6 +82,7 @@ import SpaceRoomView from "./SpaceRoomView";
|
||||||
import { IOpts } from "../../createRoom";
|
import { IOpts } from "../../createRoom";
|
||||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||||
import UIStore from "../../stores/UIStore";
|
import UIStore from "../../stores/UIStore";
|
||||||
|
import EditorStateTransfer from "../../utils/EditorStateTransfer";
|
||||||
|
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
let debuglog = function(msg: string) {};
|
let debuglog = function(msg: string) {};
|
||||||
|
@ -192,6 +193,7 @@ export interface IState {
|
||||||
// whether or not a spaces context switch brought us here,
|
// whether or not a spaces context switch brought us here,
|
||||||
// if it did we don't want the room to be marked as read as soon as it is loaded.
|
// if it did we don't want the room to be marked as read as soon as it is loaded.
|
||||||
wasContextSwitch?: boolean;
|
wasContextSwitch?: boolean;
|
||||||
|
editState?: EditorStateTransfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("structures.RoomView")
|
@replaceableComponent("structures.RoomView")
|
||||||
|
@ -815,6 +817,36 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
case 'focus_search':
|
case 'focus_search':
|
||||||
this.onSearchClick();
|
this.onSearchClick();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "edit_event": {
|
||||||
|
const editState = payload.event ? new EditorStateTransfer(payload.event) : null;
|
||||||
|
this.setState({ editState }, () => {
|
||||||
|
if (payload.event) {
|
||||||
|
this.messagePanel?.scrollToEventIfNeeded(payload.event.getId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Action.ComposerInsert: {
|
||||||
|
// re-dispatch to the correct composer
|
||||||
|
if (this.state.editState) {
|
||||||
|
dis.dispatch({
|
||||||
|
...payload,
|
||||||
|
action: "edit_composer_insert",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dis.dispatch({
|
||||||
|
...payload,
|
||||||
|
action: "send_composer_insert",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "scroll_to_bottom":
|
||||||
|
this.messagePanel?.jumpToLiveTimeline();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,58 +14,60 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {RefObject, useContext, useRef, useState} from "react";
|
import React, { RefObject, useContext, useRef, useState } from "react";
|
||||||
import {EventType} from "matrix-js-sdk/src/@types/event";
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
import {Room} from "matrix-js-sdk/src/models/room";
|
import { Preset } from "matrix-js-sdk/src/@types/partials";
|
||||||
import {EventSubscription} from "fbemitter";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
|
import { EventSubscription } from "fbemitter";
|
||||||
|
|
||||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||||
import RoomAvatar from "../views/avatars/RoomAvatar";
|
import RoomAvatar from "../views/avatars/RoomAvatar";
|
||||||
import {_t} from "../../languageHandler";
|
import { _t } from "../../languageHandler";
|
||||||
import AccessibleButton from "../views/elements/AccessibleButton";
|
import AccessibleButton from "../views/elements/AccessibleButton";
|
||||||
import RoomName from "../views/elements/RoomName";
|
import RoomName from "../views/elements/RoomName";
|
||||||
import RoomTopic from "../views/elements/RoomTopic";
|
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} 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";
|
||||||
import * as Email from "../../email";
|
import * as Email from "../../email";
|
||||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||||
import {Action} from "../../dispatcher/actions";
|
import dis from "../../dispatcher/dispatcher";
|
||||||
|
import { Action } from "../../dispatcher/actions";
|
||||||
import ResizeNotifier from "../../utils/ResizeNotifier"
|
import ResizeNotifier from "../../utils/ResizeNotifier"
|
||||||
import MainSplit from './MainSplit';
|
import MainSplit from './MainSplit';
|
||||||
import ErrorBoundary from "../views/elements/ErrorBoundary";
|
import ErrorBoundary from "../views/elements/ErrorBoundary";
|
||||||
import {ActionPayload} from "../../dispatcher/payloads";
|
import { ActionPayload } from "../../dispatcher/payloads";
|
||||||
import RightPanel from "./RightPanel";
|
import RightPanel from "./RightPanel";
|
||||||
import RightPanelStore from "../../stores/RightPanelStore";
|
import RightPanelStore from "../../stores/RightPanelStore";
|
||||||
import {RightPanelPhases} from "../../stores/RightPanelStorePhases";
|
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
|
||||||
import {SetRightPanelPhasePayload} from "../../dispatcher/payloads/SetRightPanelPhasePayload";
|
import { SetRightPanelPhasePayload } from "../../dispatcher/payloads/SetRightPanelPhasePayload";
|
||||||
import {useStateArray} from "../../hooks/useStateArray";
|
import { useStateArray } from "../../hooks/useStateArray";
|
||||||
import SpacePublicShare from "../views/spaces/SpacePublicShare";
|
import SpacePublicShare from "../views/spaces/SpacePublicShare";
|
||||||
import {showAddExistingRooms, showCreateNewRoom, shouldShowSpaceSettings, showSpaceSettings} from "../../utils/space";
|
import { shouldShowSpaceSettings, showAddExistingRooms, showCreateNewRoom, showSpaceSettings } from "../../utils/space";
|
||||||
import {showRoom, SpaceHierarchy} from "./SpaceRoomDirectory";
|
import { showRoom, SpaceHierarchy } from "./SpaceRoomDirectory";
|
||||||
import MemberAvatar from "../views/avatars/MemberAvatar";
|
import MemberAvatar from "../views/avatars/MemberAvatar";
|
||||||
import {useStateToggle} from "../../hooks/useStateToggle";
|
import { useStateToggle } from "../../hooks/useStateToggle";
|
||||||
import SpaceStore from "../../stores/SpaceStore";
|
import SpaceStore from "../../stores/SpaceStore";
|
||||||
import FacePile from "../views/elements/FacePile";
|
import FacePile from "../views/elements/FacePile";
|
||||||
import {AddExistingToSpace} from "../views/dialogs/AddExistingToSpaceDialog";
|
import { AddExistingToSpace } from "../views/dialogs/AddExistingToSpaceDialog";
|
||||||
import {ChevronFace, ContextMenuButton, useContextMenu} from "./ContextMenu";
|
import { ChevronFace, ContextMenuButton, useContextMenu } from "./ContextMenu";
|
||||||
import IconizedContextMenu, {
|
import IconizedContextMenu, {
|
||||||
IconizedContextMenuOption,
|
IconizedContextMenuOption,
|
||||||
IconizedContextMenuOptionList,
|
IconizedContextMenuOptionList,
|
||||||
} from "../views/context_menus/IconizedContextMenu";
|
} from "../views/context_menus/IconizedContextMenu";
|
||||||
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
||||||
import {BetaPill} from "../views/beta/BetaCard";
|
import { BetaPill } from "../views/beta/BetaCard";
|
||||||
import { UserTab } from "../views/dialogs/UserSettingsDialog";
|
import { UserTab } from "../views/dialogs/UserSettingsDialog";
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
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";
|
import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
|
||||||
|
import { JoinRule } from "../views/settings/tabs/room/SecurityRoomSettingsTab";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
space: Room;
|
space: Room;
|
||||||
|
@ -178,6 +180,9 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
||||||
|
|
||||||
const spacesEnabled = SettingsStore.getValue("feature_spaces");
|
const spacesEnabled = SettingsStore.getValue("feature_spaces");
|
||||||
|
|
||||||
|
const cannotJoin = getEffectiveMembership(myMembership) === EffectiveMembership.Leave
|
||||||
|
&& space.getJoinRule() !== JoinRule.Public;
|
||||||
|
|
||||||
let inviterSection;
|
let inviterSection;
|
||||||
let joinButtons;
|
let joinButtons;
|
||||||
if (myMembership === "join") {
|
if (myMembership === "join") {
|
||||||
|
@ -244,7 +249,7 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
onJoinButtonClicked();
|
onJoinButtonClicked();
|
||||||
}}
|
}}
|
||||||
disabled={!spacesEnabled}
|
disabled={!spacesEnabled || cannotJoin}
|
||||||
>
|
>
|
||||||
{ _t("Join") }
|
{ _t("Join") }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
@ -255,6 +260,30 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
||||||
joinButtons = <InlineSpinner />;
|
joinButtons = <InlineSpinner />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let footer;
|
||||||
|
if (!spacesEnabled) {
|
||||||
|
footer = <div className="mx_SpaceRoomView_preview_spaceBetaPrompt">
|
||||||
|
{ myMembership === "join"
|
||||||
|
? _t("To view %(spaceName)s, turn on the <a>Spaces beta</a>", {
|
||||||
|
spaceName: space.name,
|
||||||
|
}, {
|
||||||
|
a: sub => <AccessibleButton onClick={onBetaClick} kind="link">{ sub }</AccessibleButton>,
|
||||||
|
})
|
||||||
|
: _t("To join %(spaceName)s, turn on the <a>Spaces beta</a>", {
|
||||||
|
spaceName: space.name,
|
||||||
|
}, {
|
||||||
|
a: sub => <AccessibleButton onClick={onBetaClick} kind="link">{ sub }</AccessibleButton>,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>;
|
||||||
|
} else if (cannotJoin) {
|
||||||
|
footer = <div className="mx_SpaceRoomView_preview_spaceBetaPrompt">
|
||||||
|
{ _t("To view %(spaceName)s, you need an invite", {
|
||||||
|
spaceName: space.name,
|
||||||
|
}) }
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return <div className="mx_SpaceRoomView_preview">
|
return <div className="mx_SpaceRoomView_preview">
|
||||||
<BetaPill onClick={onBetaClick} />
|
<BetaPill onClick={onBetaClick} />
|
||||||
{ inviterSection }
|
{ inviterSection }
|
||||||
|
@ -274,20 +303,7 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
||||||
<div className="mx_SpaceRoomView_preview_joinButtons">
|
<div className="mx_SpaceRoomView_preview_joinButtons">
|
||||||
{ joinButtons }
|
{ joinButtons }
|
||||||
</div>
|
</div>
|
||||||
{ !spacesEnabled && <div className="mx_SpaceRoomView_preview_spaceBetaPrompt">
|
{ footer }
|
||||||
{ myMembership === "join"
|
|
||||||
? _t("To view %(spaceName)s, turn on the <a>Spaces beta</a>", {
|
|
||||||
spaceName: space.name,
|
|
||||||
}, {
|
|
||||||
a: sub => <AccessibleButton onClick={onBetaClick} kind="link">{ sub }</AccessibleButton>,
|
|
||||||
})
|
|
||||||
: _t("To join %(spaceName)s, turn on the <a>Spaces beta</a>", {
|
|
||||||
spaceName: space.name,
|
|
||||||
}, {
|
|
||||||
a: sub => <AccessibleButton onClick={onBetaClick} kind="link">{ sub }</AccessibleButton>,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div> }
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -34,12 +34,10 @@ import * as sdk from "../../index";
|
||||||
import { Key } from '../../Keyboard';
|
import { Key } from '../../Keyboard';
|
||||||
import Timer from '../../utils/Timer';
|
import Timer from '../../utils/Timer';
|
||||||
import shouldHideEvent from '../../shouldHideEvent';
|
import shouldHideEvent from '../../shouldHideEvent';
|
||||||
import EditorStateTransfer from '../../utils/EditorStateTransfer';
|
|
||||||
import { haveTileForEvent } from "../views/rooms/EventTile";
|
import { haveTileForEvent } from "../views/rooms/EventTile";
|
||||||
import { UIFeature } from "../../settings/UIFeature";
|
import { UIFeature } from "../../settings/UIFeature";
|
||||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||||
import { arrayFastClone } from "../../utils/arrays";
|
import { arrayFastClone } from "../../utils/arrays";
|
||||||
import { Action } from "../../dispatcher/actions";
|
|
||||||
|
|
||||||
const PAGINATE_SIZE = 20;
|
const PAGINATE_SIZE = 20;
|
||||||
const INITIAL_SIZE = 20;
|
const INITIAL_SIZE = 20;
|
||||||
|
@ -72,6 +70,8 @@ class TimelinePanel extends React.Component {
|
||||||
manageReadReceipts: PropTypes.bool,
|
manageReadReceipts: PropTypes.bool,
|
||||||
sendReadReceiptOnLoad: PropTypes.bool,
|
sendReadReceiptOnLoad: PropTypes.bool,
|
||||||
manageReadMarkers: PropTypes.bool,
|
manageReadMarkers: PropTypes.bool,
|
||||||
|
// with this enabled it'll listen and react to Action.ComposerInsert and `edit_event`
|
||||||
|
manageComposerDispatches: PropTypes.bool,
|
||||||
|
|
||||||
// true to give the component a 'display: none' style.
|
// true to give the component a 'display: none' style.
|
||||||
hidden: PropTypes.bool,
|
hidden: PropTypes.bool,
|
||||||
|
@ -444,38 +444,6 @@ class TimelinePanel extends React.Component {
|
||||||
case "ignore_state_changed":
|
case "ignore_state_changed":
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "edit_event": {
|
|
||||||
const editState = payload.event ? new EditorStateTransfer(payload.event) : null;
|
|
||||||
this.setState({editState}, () => {
|
|
||||||
if (payload.event && this._messagePanel.current) {
|
|
||||||
this._messagePanel.current.scrollToEventIfNeeded(
|
|
||||||
payload.event.getId(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Action.ComposerInsert: {
|
|
||||||
// re-dispatch to the correct composer
|
|
||||||
if (this.state.editState) {
|
|
||||||
dis.dispatch({
|
|
||||||
...payload,
|
|
||||||
action: "edit_composer_insert",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
dis.dispatch({
|
|
||||||
...payload,
|
|
||||||
action: "send_composer_insert",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "scroll_to_bottom":
|
|
||||||
this.jumpToLiveTimeline();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -866,6 +834,12 @@ class TimelinePanel extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
scrollToEventIfNeeded = (eventId) => {
|
||||||
|
if (this._messagePanel.current) {
|
||||||
|
this._messagePanel.current.scrollToEventIfNeeded(eventId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* scroll to show the read-up-to marker. We put it 1/3 of the way down
|
/* scroll to show the read-up-to marker. We put it 1/3 of the way down
|
||||||
* the container.
|
* the container.
|
||||||
*/
|
*/
|
||||||
|
@ -1473,7 +1447,7 @@ class TimelinePanel extends React.Component {
|
||||||
tileShape={this.props.tileShape}
|
tileShape={this.props.tileShape}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
getRelationsForEvent={this.getRelationsForEvent}
|
getRelationsForEvent={this.getRelationsForEvent}
|
||||||
editState={this.state.editState}
|
editState={this.props.editState}
|
||||||
showReactions={this.props.showReactions}
|
showReactions={this.props.showReactions}
|
||||||
layout={this.props.layout}
|
layout={this.props.layout}
|
||||||
enableFlair={SettingsStore.getValue(UIFeature.Flair)}
|
enableFlair={SettingsStore.getValue(UIFeature.Flair)}
|
||||||
|
|
|
@ -179,7 +179,7 @@ export default class MessageContextMenu extends React.Component {
|
||||||
pinnedIds.push(eventId);
|
pinnedIds.push(eventId);
|
||||||
cli.setRoomAccountData(room.roomId, ReadPinsEventId, {
|
cli.setRoomAccountData(room.roomId, ReadPinsEventId, {
|
||||||
event_ids: [
|
event_ids: [
|
||||||
...room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids,
|
...(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids || []),
|
||||||
eventId,
|
eventId,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { _t, _td } from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import { addressTypes, getAddressType } from '../../../UserAddress.js';
|
import { addressTypes, getAddressType } from '../../../UserAddress';
|
||||||
import GroupStore from '../../../stores/GroupStore';
|
import GroupStore from '../../../stores/GroupStore';
|
||||||
import * as Email from '../../../email';
|
import * as Email from '../../../email';
|
||||||
import IdentityAuthClient from '../../../IdentityAuthClient';
|
import IdentityAuthClient from '../../../IdentityAuthClient';
|
||||||
|
|
|
@ -766,7 +766,7 @@ class VerificationExplorer extends React.PureComponent<IExplorerProps> {
|
||||||
render() {
|
render() {
|
||||||
const cli = this.context;
|
const cli = this.context;
|
||||||
const room = this.props.room;
|
const room = this.props.room;
|
||||||
const inRoomChannel = cli.crypto._inRoomVerificationRequests;
|
const inRoomChannel = cli.crypto.inRoomVerificationRequests;
|
||||||
const inRoomRequests = (inRoomChannel._requestsByRoomId || new Map()).get(room.roomId) || new Map();
|
const inRoomRequests = (inRoomChannel._requestsByRoomId || new Map()).get(room.roomId) || new Map();
|
||||||
|
|
||||||
return (<div>
|
return (<div>
|
||||||
|
|
|
@ -17,37 +17,45 @@ limitations under the License.
|
||||||
import React, { createRef } from 'react';
|
import React, { createRef } from 'react';
|
||||||
import classNames from 'classnames';
|
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";
|
||||||
import {makeRoomPermalink, makeUserPermalink} from "../../../utils/permalinks/Permalinks";
|
import { makeRoomPermalink, makeUserPermalink } from "../../../utils/permalinks/Permalinks";
|
||||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||||
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import * as Email from "../../../email";
|
import * as Email from "../../../email";
|
||||||
import {getDefaultIdentityServerUrl, useDefaultIdentityServer} from "../../../utils/IdentityServerUtils";
|
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from "../../../utils/IdentityServerUtils";
|
||||||
import {abbreviateUrl} from "../../../utils/UrlUtils";
|
import { abbreviateUrl } from "../../../utils/UrlUtils";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import IdentityAuthClient from "../../../IdentityAuthClient";
|
import IdentityAuthClient from "../../../IdentityAuthClient";
|
||||||
import Modal from "../../../Modal";
|
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,
|
||||||
} from "../../../createRoom";
|
} from "../../../createRoom";
|
||||||
import {inviteMultipleToRoom, showCommunityInviteDialog} from "../../../RoomInvite";
|
import {
|
||||||
import {Key} from "../../../Keyboard";
|
IInviteResult,
|
||||||
import {Action} from "../../../dispatcher/actions";
|
inviteMultipleToRoom,
|
||||||
import {DefaultTagID} from "../../../stores/room-list/models";
|
showAnyInviteErrors,
|
||||||
|
showCommunityInviteDialog,
|
||||||
|
} from "../../../RoomInvite";
|
||||||
|
import { Key } from "../../../Keyboard";
|
||||||
|
import { Action } from "../../../dispatcher/actions";
|
||||||
|
import { DefaultTagID } from "../../../stores/room-list/models";
|
||||||
import RoomListStore from "../../../stores/room-list/RoomListStore";
|
import RoomListStore from "../../../stores/room-list/RoomListStore";
|
||||||
import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore";
|
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import {UIFeature} from "../../../settings/UIFeature";
|
import { UIFeature } from "../../../settings/UIFeature";
|
||||||
import CountlyAnalytics from "../../../CountlyAnalytics";
|
import CountlyAnalytics from "../../../CountlyAnalytics";
|
||||||
import {Room} from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import {mediaFromMxc} from "../../../customisations/Media";
|
import { mediaFromMxc } from "../../../customisations/Media";
|
||||||
import {getAddressType} from "../../../UserAddress";
|
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';
|
||||||
|
@ -74,10 +82,10 @@ export const KIND_CALL_TRANSFER = "call_transfer";
|
||||||
const INITIAL_ROOMS_SHOWN = 3; // Number of rooms to show at first
|
const INITIAL_ROOMS_SHOWN = 3; // Number of rooms to show at first
|
||||||
const INCREMENT_ROOMS_SHOWN = 5; // Number of rooms to add when 'show more' is clicked
|
const INCREMENT_ROOMS_SHOWN = 5; // Number of rooms to add when 'show more' is clicked
|
||||||
|
|
||||||
// This is the interface that is expected by various components in this file. It is a bit
|
// This is the interface that is expected by various components in the Invite Dialog and RoomInvite.
|
||||||
// awkward because it also matches the RoomMember class from the js-sdk with some extra support
|
// It is a bit awkward because it also matches the RoomMember class from the js-sdk with some extra support
|
||||||
// for 3PIDs/email addresses.
|
// for 3PIDs/email addresses.
|
||||||
abstract class Member {
|
export abstract class Member {
|
||||||
/**
|
/**
|
||||||
* The display name of this Member. For users this should be their profile's display
|
* The display name of this Member. For users this should be their profile's display
|
||||||
* name or user ID if none set. For 3PIDs this should be the 3PID address (email).
|
* name or user ID if none set. For 3PIDs this should be the 3PID address (email).
|
||||||
|
@ -102,7 +110,8 @@ class DirectoryMember extends Member {
|
||||||
private readonly displayName: string;
|
private readonly displayName: string;
|
||||||
private readonly avatarUrl: string;
|
private readonly avatarUrl: string;
|
||||||
|
|
||||||
constructor(userDirResult: {user_id: string, display_name: string, avatar_url: string}) {
|
// eslint-disable-next-line camelcase
|
||||||
|
constructor(userDirResult: { user_id: string, display_name: string, avatar_url: string }) {
|
||||||
super();
|
super();
|
||||||
this._userId = userDirResult.user_id;
|
this._userId = userDirResult.user_id;
|
||||||
this.displayName = userDirResult.display_name;
|
this.displayName = userDirResult.display_name;
|
||||||
|
@ -601,19 +610,10 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
return members.map(m => ({userId: m.member.userId, user: m.member}));
|
return members.map(m => ({userId: m.member.userId, user: m.member}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private shouldAbortAfterInviteError(result): boolean {
|
private shouldAbortAfterInviteError(result: IInviteResult, room: Room): boolean {
|
||||||
const failedUsers = Object.keys(result.states).filter(a => result.states[a] === 'error');
|
this.setState({ busy: false });
|
||||||
if (failedUsers.length > 0) {
|
const userMap = new Map<string, Member>(this.state.targets.map(member => [member.userId, member]));
|
||||||
console.log("Failed to invite users: ", result);
|
return !showAnyInviteErrors(result.states, room, result.inviter, userMap);
|
||||||
this.setState({
|
|
||||||
busy: false,
|
|
||||||
errorText: _t("Failed to invite the following users to chat: %(csvUsers)s", {
|
|
||||||
csvUsers: failedUsers.join(", "),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return true; // abort
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertFilter(): Member[] {
|
private convertFilter(): Member[] {
|
||||||
|
@ -731,7 +731,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
try {
|
try {
|
||||||
const result = await inviteMultipleToRoom(this.props.roomId, targetIds)
|
const result = await inviteMultipleToRoom(this.props.roomId, targetIds)
|
||||||
CountlyAnalytics.instance.trackSendInvite(startTime, this.props.roomId, targetIds.length);
|
CountlyAnalytics.instance.trackSendInvite(startTime, this.props.roomId, targetIds.length);
|
||||||
if (!this.shouldAbortAfterInviteError(result)) { // handles setting error message too
|
if (!this.shouldAbortAfterInviteError(result, room)) { // handles setting error message too
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { UserAddressType } from '../../../UserAddress.js';
|
import { UserAddressType } from '../../../UserAddress';
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import {mediaFromMxc} from "../../../customisations/Media";
|
import { mediaFromMxc } from "../../../customisations/Media";
|
||||||
|
|
||||||
@replaceableComponent("views.elements.AddressTile")
|
@replaceableComponent("views.elements.AddressTile")
|
||||||
export default class AddressTile extends React.Component {
|
export default class AddressTile extends React.Component {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Flair from '../elements/Flair.js';
|
import Flair from '../elements/Flair';
|
||||||
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";
|
||||||
|
@ -140,7 +140,7 @@ export default class SenderProfile extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_SenderProfile mx_SenderProfile_hover" dir="auto" onClick={this.props.onClick}>
|
<div className="mx_SenderProfile" dir="auto" onClick={this.props.onClick}>
|
||||||
<span className={`mx_SenderProfile_displayName ${colorClass}`}>
|
<span className={`mx_SenderProfile_displayName ${colorClass}`}>
|
||||||
{ displayName }
|
{ displayName }
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -28,7 +28,7 @@ export default class TextualEvent extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const text = TextForEvent.textForEvent(this.props.mxEvent);
|
const text = TextForEvent.textForEvent(this.props.mxEvent, true);
|
||||||
if (text == null || text.length === 0) return null;
|
if (text == null || text.length === 0) return null;
|
||||||
return (
|
return (
|
||||||
<div className="mx_TextualEvent">{ text }</div>
|
<div className="mx_TextualEvent">{ text }</div>
|
||||||
|
|
|
@ -503,7 +503,7 @@ const isMuted = (member: RoomMember, powerLevelContent: IPowerLevelsContent) =>
|
||||||
return member.powerLevel < levelToSend;
|
return member.powerLevel < levelToSend;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPowerLevels = room => room.currentState.getStateEvents(EventType.RoomPowerLevels, "")?.getContent() || {};
|
const getPowerLevels = room => room?.currentState?.getStateEvents(EventType.RoomPowerLevels, "")?.getContent() || {};
|
||||||
|
|
||||||
export const useRoomPowerLevels = (cli: MatrixClient, room: Room) => {
|
export const useRoomPowerLevels = (cli: MatrixClient, room: Room) => {
|
||||||
const [powerLevels, setPowerLevels] = useState<IPowerLevelsContent>(getPowerLevels(room));
|
const [powerLevels, setPowerLevels] = useState<IPowerLevelsContent>(getPowerLevels(room));
|
||||||
|
|
|
@ -17,13 +17,23 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import * as sdk from '../../../index';
|
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _td } from '../../../languageHandler';
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import E2EIcon from './E2EIcon';
|
import E2EIcon from './E2EIcon';
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import BaseAvatar from '../avatars/BaseAvatar';
|
||||||
|
import PresenceLabel from "./PresenceLabel";
|
||||||
|
|
||||||
|
export enum PowerStatus {
|
||||||
|
Admin = "admin",
|
||||||
|
Moderator = "moderator",
|
||||||
|
}
|
||||||
|
|
||||||
|
const PowerLabel: Record<PowerStatus, string> = {
|
||||||
|
[PowerStatus.Admin]: _td("Admin"),
|
||||||
|
[PowerStatus.Moderator]: _td("Mod"),
|
||||||
|
}
|
||||||
|
|
||||||
const PRESENCE_CLASS = {
|
const PRESENCE_CLASS = {
|
||||||
"offline": "mx_EntityTile_offline",
|
"offline": "mx_EntityTile_offline",
|
||||||
|
@ -31,14 +41,14 @@ const PRESENCE_CLASS = {
|
||||||
"unavailable": "mx_EntityTile_unavailable",
|
"unavailable": "mx_EntityTile_unavailable",
|
||||||
};
|
};
|
||||||
|
|
||||||
function presenceClassForMember(presenceState, lastActiveAgo, showPresence) {
|
function presenceClassForMember(presenceState: string, lastActiveAgo: number, showPresence: boolean): string {
|
||||||
if (showPresence === false) {
|
if (showPresence === false) {
|
||||||
return 'mx_EntityTile_online_beenactive';
|
return 'mx_EntityTile_online_beenactive';
|
||||||
}
|
}
|
||||||
|
|
||||||
// offline is split into two categories depending on whether we have
|
// offline is split into two categories depending on whether we have
|
||||||
// a last_active_ago for them.
|
// a last_active_ago for them.
|
||||||
if (presenceState == 'offline') {
|
if (presenceState === 'offline') {
|
||||||
if (lastActiveAgo) {
|
if (lastActiveAgo) {
|
||||||
return PRESENCE_CLASS['offline'] + '_beenactive';
|
return PRESENCE_CLASS['offline'] + '_beenactive';
|
||||||
} else {
|
} else {
|
||||||
|
@ -51,29 +61,32 @@ function presenceClassForMember(presenceState, lastActiveAgo, showPresence) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.EntityTile")
|
interface IProps {
|
||||||
class EntityTile extends React.Component {
|
name?: string;
|
||||||
static propTypes = {
|
title?: string;
|
||||||
name: PropTypes.string,
|
avatarJsx?: JSX.Element; // <BaseAvatar />
|
||||||
title: PropTypes.string,
|
className?: string;
|
||||||
avatarJsx: PropTypes.any, // <BaseAvatar />
|
presenceState?: string;
|
||||||
className: PropTypes.string,
|
presenceLastActiveAgo?: number;
|
||||||
presenceState: PropTypes.string,
|
presenceLastTs?: number;
|
||||||
presenceLastActiveAgo: PropTypes.number,
|
presenceCurrentlyActive?: boolean;
|
||||||
presenceLastTs: PropTypes.number,
|
showInviteButton?: boolean;
|
||||||
presenceCurrentlyActive: PropTypes.bool,
|
onClick?(): void;
|
||||||
showInviteButton: PropTypes.bool,
|
suppressOnHover?: boolean;
|
||||||
shouldComponentUpdate: PropTypes.func,
|
showPresence?: boolean;
|
||||||
onClick: PropTypes.func,
|
subtextLabel?: string;
|
||||||
suppressOnHover: PropTypes.bool,
|
e2eStatus?: string;
|
||||||
showPresence: PropTypes.bool,
|
powerStatus?: PowerStatus;
|
||||||
subtextLabel: PropTypes.string,
|
}
|
||||||
e2eStatus: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
hover: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@replaceableComponent("views.rooms.EntityTile")
|
||||||
|
export default class EntityTile extends React.PureComponent<IProps, IState> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
shouldComponentUpdate: function(nextProps, nextState) { return true; },
|
onClick: () => {},
|
||||||
onClick: function() {},
|
|
||||||
presenceState: "offline",
|
presenceState: "offline",
|
||||||
presenceLastActiveAgo: 0,
|
presenceLastActiveAgo: 0,
|
||||||
presenceLastTs: 0,
|
presenceLastTs: 0,
|
||||||
|
@ -82,13 +95,12 @@ class EntityTile extends React.Component {
|
||||||
showPresence: true,
|
showPresence: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
constructor(props: IProps) {
|
||||||
hover: false,
|
super(props);
|
||||||
};
|
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
this.state = {
|
||||||
if (this.state.hover !== nextState.hover) return true;
|
hover: false,
|
||||||
return this.props.shouldComponentUpdate(nextProps, nextState);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -110,7 +122,6 @@ class EntityTile extends React.Component {
|
||||||
const activeAgo = this.props.presenceLastActiveAgo ?
|
const activeAgo = this.props.presenceLastActiveAgo ?
|
||||||
(Date.now() - (this.props.presenceLastTs - this.props.presenceLastActiveAgo)) : -1;
|
(Date.now() - (this.props.presenceLastTs - this.props.presenceLastActiveAgo)) : -1;
|
||||||
|
|
||||||
const PresenceLabel = sdk.getComponent("rooms.PresenceLabel");
|
|
||||||
let presenceLabel = null;
|
let presenceLabel = null;
|
||||||
if (this.props.showPresence) {
|
if (this.props.showPresence) {
|
||||||
presenceLabel = <PresenceLabel activeAgo={activeAgo}
|
presenceLabel = <PresenceLabel activeAgo={activeAgo}
|
||||||
|
@ -155,10 +166,7 @@ class EntityTile extends React.Component {
|
||||||
let powerLabel;
|
let powerLabel;
|
||||||
const powerStatus = this.props.powerStatus;
|
const powerStatus = this.props.powerStatus;
|
||||||
if (powerStatus) {
|
if (powerStatus) {
|
||||||
const powerText = {
|
const powerText = PowerLabel[powerStatus];
|
||||||
[EntityTile.POWER_STATUS_MODERATOR]: _t("Mod"),
|
|
||||||
[EntityTile.POWER_STATUS_ADMIN]: _t("Admin"),
|
|
||||||
}[powerStatus];
|
|
||||||
powerLabel = <div className="mx_EntityTile_power">{powerText}</div>;
|
powerLabel = <div className="mx_EntityTile_power">{powerText}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,14 +176,12 @@ class EntityTile extends React.Component {
|
||||||
e2eIcon = <E2EIcon status={e2eStatus} isUser={true} bordered={true} />;
|
e2eIcon = <E2EIcon status={e2eStatus} isUser={true} bordered={true} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
|
||||||
|
|
||||||
const av = this.props.avatarJsx ||
|
const av = this.props.avatarJsx ||
|
||||||
<BaseAvatar name={this.props.name} width={36} height={36} aria-hidden="true" />;
|
<BaseAvatar name={this.props.name} width={36} height={36} aria-hidden="true" />;
|
||||||
|
|
||||||
// The wrapping div is required to make the magic mouse listener work, for some reason.
|
// The wrapping div is required to make the magic mouse listener work, for some reason.
|
||||||
return (
|
return (
|
||||||
<div ref={(c) => this.container = c} >
|
<div>
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
className={classNames(mainClassNames)}
|
className={classNames(mainClassNames)}
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
|
@ -193,8 +199,3 @@ class EntityTile extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTile.POWER_STATUS_MODERATOR = "moderator";
|
|
||||||
EntityTile.POWER_STATUS_ADMIN = "admin";
|
|
||||||
|
|
||||||
export default EntityTile;
|
|
|
@ -847,7 +847,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
let tileHandler = getHandlerTile(this.props.mxEvent);
|
let tileHandler = getHandlerTile(this.props.mxEvent);
|
||||||
|
|
||||||
// Info messages are basically information about commands processed on a room
|
// Info messages are basically information about commands processed on a room
|
||||||
const isBubbleMessage = eventType.startsWith("m.key.verification") ||
|
let isBubbleMessage = eventType.startsWith("m.key.verification") ||
|
||||||
(eventType === EventType.RoomMessage && msgtype && msgtype.startsWith("m.key.verification")) ||
|
(eventType === EventType.RoomMessage && msgtype && msgtype.startsWith("m.key.verification")) ||
|
||||||
(eventType === EventType.RoomCreate) ||
|
(eventType === EventType.RoomCreate) ||
|
||||||
(eventType === EventType.RoomEncryption) ||
|
(eventType === EventType.RoomEncryption) ||
|
||||||
|
@ -863,6 +863,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
// duplicate of the thing they are replacing).
|
// duplicate of the thing they are replacing).
|
||||||
if (SettingsStore.getValue("showHiddenEventsInTimeline") && !haveTileForEvent(this.props.mxEvent)) {
|
if (SettingsStore.getValue("showHiddenEventsInTimeline") && !haveTileForEvent(this.props.mxEvent)) {
|
||||||
tileHandler = "messages.ViewSourceEvent";
|
tileHandler = "messages.ViewSourceEvent";
|
||||||
|
isBubbleMessage = false;
|
||||||
// Reuse info message avatar and sender profile styling
|
// Reuse info message avatar and sender profile styling
|
||||||
isInfoMessage = true;
|
isInfoMessage = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
Copyright 2017, 2018 New Vector Ltd
|
||||||
|
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -20,17 +21,28 @@ import React from 'react';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import {isValid3pidInvite} from "../../../RoomInvite";
|
import { isValid3pidInvite } from "../../../RoomInvite";
|
||||||
import rate_limited_func from "../../../ratelimitedfunc";
|
import rateLimitedFunction from "../../../ratelimitedfunc";
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import * as sdk from "../../../index";
|
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
|
||||||
import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore";
|
|
||||||
import BaseCard from "../right_panel/BaseCard";
|
import BaseCard from "../right_panel/BaseCard";
|
||||||
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
|
||||||
import RoomAvatar from "../avatars/RoomAvatar";
|
import RoomAvatar from "../avatars/RoomAvatar";
|
||||||
import RoomName from "../elements/RoomName";
|
import RoomName from "../elements/RoomName";
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||||
|
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||||
|
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
|
||||||
|
import { RoomState } from 'matrix-js-sdk/src/models/room-state';
|
||||||
|
import { User } from "matrix-js-sdk/src/models/user";
|
||||||
|
import TruncatedList from '../elements/TruncatedList';
|
||||||
|
import Spinner from "../elements/Spinner";
|
||||||
|
import SearchBox from "../../structures/SearchBox";
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
import EntityTile from "./EntityTile";
|
||||||
|
import MemberTile from "./MemberTile";
|
||||||
|
import BaseAvatar from '../avatars/BaseAvatar';
|
||||||
|
|
||||||
const INITIAL_LOAD_NUM_MEMBERS = 30;
|
const INITIAL_LOAD_NUM_MEMBERS = 30;
|
||||||
const INITIAL_LOAD_NUM_INVITED = 5;
|
const INITIAL_LOAD_NUM_INVITED = 5;
|
||||||
|
@ -40,41 +52,59 @@ const SHOW_MORE_INCREMENT = 100;
|
||||||
// matches all ASCII punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
|
// matches all ASCII punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
|
||||||
const SORT_REGEX = /[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]+/g;
|
const SORT_REGEX = /[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]+/g;
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
roomId: string;
|
||||||
|
onClose(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
loading: boolean;
|
||||||
|
members: Array<RoomMember>;
|
||||||
|
filteredJoinedMembers: Array<RoomMember>;
|
||||||
|
filteredInvitedMembers: Array<RoomMember | MatrixEvent>;
|
||||||
|
canInvite: boolean;
|
||||||
|
truncateAtJoined: number;
|
||||||
|
truncateAtInvited: number;
|
||||||
|
searchQuery: string;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.MemberList")
|
@replaceableComponent("views.rooms.MemberList")
|
||||||
export default class MemberList extends React.Component {
|
export default class MemberList extends React.Component<IProps, IState> {
|
||||||
|
private showPresence = true;
|
||||||
|
private mounted = false;
|
||||||
|
private collator: Intl.Collator;
|
||||||
|
private sortNames = new Map<RoomMember, string>(); // RoomMember -> sortName
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli.hasLazyLoadMembersEnabled()) {
|
if (cli.hasLazyLoadMembersEnabled()) {
|
||||||
// show an empty list
|
// show an empty list
|
||||||
this.state = this._getMembersState([]);
|
this.state = this.getMembersState([]);
|
||||||
} else {
|
} else {
|
||||||
this.state = this._getMembersState(this.roomMembers());
|
this.state = this.getMembersState(this.roomMembers());
|
||||||
}
|
}
|
||||||
|
|
||||||
cli.on("Room", this.onRoom); // invites & joining after peek
|
cli.on("Room", this.onRoom); // invites & joining after peek
|
||||||
const enablePresenceByHsUrl = SdkConfig.get()["enable_presence_by_hs_url"];
|
const enablePresenceByHsUrl = SdkConfig.get()["enable_presence_by_hs_url"];
|
||||||
const hsUrl = MatrixClientPeg.get().baseUrl;
|
const hsUrl = MatrixClientPeg.get().baseUrl;
|
||||||
this._showPresence = true;
|
this.showPresence = enablePresenceByHsUrl?.[hsUrl] ?? true;
|
||||||
if (enablePresenceByHsUrl && enablePresenceByHsUrl[hsUrl] !== undefined) {
|
|
||||||
this._showPresence = enablePresenceByHsUrl[hsUrl];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
UNSAFE_componentWillMount() {
|
UNSAFE_componentWillMount() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
this._mounted = true;
|
this.mounted = true;
|
||||||
if (cli.hasLazyLoadMembersEnabled()) {
|
if (cli.hasLazyLoadMembersEnabled()) {
|
||||||
this._showMembersAccordingToMembershipWithLL();
|
this.showMembersAccordingToMembershipWithLL();
|
||||||
cli.on("Room.myMembership", this.onMyMembership);
|
cli.on("Room.myMembership", this.onMyMembership);
|
||||||
} else {
|
} else {
|
||||||
this._listenForMembersChanges();
|
this.listenForMembersChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_listenForMembersChanges() {
|
private listenForMembersChanges(): void {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
cli.on("RoomState.members", this.onRoomStateMember);
|
cli.on("RoomState.members", this.onRoomStateMember);
|
||||||
cli.on("RoomMember.name", this.onRoomMemberName);
|
cli.on("RoomMember.name", this.onRoomMemberName);
|
||||||
|
@ -89,7 +119,7 @@ export default class MemberList extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this._mounted = false;
|
this.mounted = false;
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli) {
|
if (cli) {
|
||||||
cli.removeListener("RoomState.members", this.onRoomStateMember);
|
cli.removeListener("RoomState.members", this.onRoomStateMember);
|
||||||
|
@ -103,7 +133,7 @@ export default class MemberList extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancel any pending calls to the rate_limited_funcs
|
// cancel any pending calls to the rate_limited_funcs
|
||||||
this._updateList.cancelPendingCall();
|
this.updateList.cancelPendingCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -111,7 +141,7 @@ export default class MemberList extends React.Component {
|
||||||
* show a spinner and load the members if the user is joined,
|
* show a spinner and load the members if the user is joined,
|
||||||
* or show the members available so far if the user is invited
|
* or show the members available so far if the user is invited
|
||||||
*/
|
*/
|
||||||
async _showMembersAccordingToMembershipWithLL() {
|
private async showMembersAccordingToMembershipWithLL(): Promise<void> {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli.hasLazyLoadMembersEnabled()) {
|
if (cli.hasLazyLoadMembersEnabled()) {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
@ -122,31 +152,31 @@ export default class MemberList extends React.Component {
|
||||||
try {
|
try {
|
||||||
await room.loadMembersIfNeeded();
|
await room.loadMembersIfNeeded();
|
||||||
} catch (ex) {/* already logged in RoomView */}
|
} catch (ex) {/* already logged in RoomView */}
|
||||||
if (this._mounted) {
|
if (this.mounted) {
|
||||||
this.setState(this._getMembersState(this.roomMembers()));
|
this.setState(this.getMembersState(this.roomMembers()));
|
||||||
this._listenForMembersChanges();
|
this.listenForMembersChanges();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// show the members we already have loaded
|
// show the members we already have loaded
|
||||||
this.setState(this._getMembersState(this.roomMembers()));
|
this.setState(this.getMembersState(this.roomMembers()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get canInvite() {
|
private get canInvite(): boolean {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const room = cli.getRoom(this.props.roomId);
|
const room = cli.getRoom(this.props.roomId);
|
||||||
return room && room.canInvite(cli.getUserId());
|
return room && room.canInvite(cli.getUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
_getMembersState(members) {
|
private getMembersState(members: Array<RoomMember>): IState {
|
||||||
// set the state after determining _showPresence to make sure it's
|
// set the state after determining showPresence to make sure it's
|
||||||
// taken into account while rerendering
|
// taken into account while rendering
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
members: members,
|
members: members,
|
||||||
filteredJoinedMembers: this._filterMembers(members, 'join'),
|
filteredJoinedMembers: this.filterMembers(members, 'join'),
|
||||||
filteredInvitedMembers: this._filterMembers(members, 'invite'),
|
filteredInvitedMembers: this.filterMembers(members, 'invite'),
|
||||||
canInvite: this.canInvite,
|
canInvite: this.canInvite,
|
||||||
|
|
||||||
// ideally we'd size this to the page height, but
|
// ideally we'd size this to the page height, but
|
||||||
|
@ -157,72 +187,72 @@ export default class MemberList extends React.Component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onUserPresenceChange = (event, user) => {
|
private onUserPresenceChange = (event: MatrixEvent, user: User): void => {
|
||||||
// Attach a SINGLE listener for global presence changes then locate the
|
// Attach a SINGLE listener for global presence changes then locate the
|
||||||
// member tile and re-render it. This is more efficient than every tile
|
// member tile and re-render it. This is more efficient than every tile
|
||||||
// ever attaching their own listener.
|
// ever attaching their own listener.
|
||||||
const tile = this.refs[user.userId];
|
const tile = this.refs[user.userId];
|
||||||
// console.log(`Got presence update for ${user.userId}. hasTile=${!!tile}`);
|
// console.log(`Got presence update for ${user.userId}. hasTile=${!!tile}`);
|
||||||
if (tile) {
|
if (tile) {
|
||||||
this._updateList(); // reorder the membership list
|
this.updateList(); // reorder the membership list
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onRoom = room => {
|
private onRoom = (room: Room): void => {
|
||||||
if (room.roomId !== this.props.roomId) {
|
if (room.roomId !== this.props.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We listen for room events because when we accept an invite
|
// We listen for room events because when we accept an invite
|
||||||
// we need to wait till the room is fully populated with state
|
// we need to wait till the room is fully populated with state
|
||||||
// before refreshing the member list else we get a stale list.
|
// before refreshing the member list else we get a stale list.
|
||||||
this._showMembersAccordingToMembershipWithLL();
|
this.showMembersAccordingToMembershipWithLL();
|
||||||
};
|
};
|
||||||
|
|
||||||
onMyMembership = (room, membership, oldMembership) => {
|
private onMyMembership = (room: Room, membership: string, oldMembership: string): void => {
|
||||||
if (room.roomId === this.props.roomId && membership === "join") {
|
if (room.roomId === this.props.roomId && membership === "join") {
|
||||||
this._showMembersAccordingToMembershipWithLL();
|
this.showMembersAccordingToMembershipWithLL();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onRoomStateMember = (ev, state, member) => {
|
private onRoomStateMember = (ev: MatrixEvent, state: RoomState, member: RoomMember): void => {
|
||||||
if (member.roomId !== this.props.roomId) {
|
if (member.roomId !== this.props.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._updateList();
|
this.updateList();
|
||||||
};
|
};
|
||||||
|
|
||||||
onRoomMemberName = (ev, member) => {
|
private onRoomMemberName = (ev: MatrixEvent, member: RoomMember): void => {
|
||||||
if (member.roomId !== this.props.roomId) {
|
if (member.roomId !== this.props.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._updateList();
|
this.updateList();
|
||||||
};
|
};
|
||||||
|
|
||||||
onRoomStateEvent = (event, state) => {
|
private onRoomStateEvent = (event: MatrixEvent, state: RoomState): void => {
|
||||||
if (event.getRoomId() === this.props.roomId &&
|
if (event.getRoomId() === this.props.roomId &&
|
||||||
event.getType() === "m.room.third_party_invite") {
|
event.getType() === "m.room.third_party_invite") {
|
||||||
this._updateList();
|
this.updateList();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.canInvite !== this.state.canInvite) this.setState({ canInvite: this.canInvite });
|
if (this.canInvite !== this.state.canInvite) this.setState({ canInvite: this.canInvite });
|
||||||
};
|
};
|
||||||
|
|
||||||
_updateList = rate_limited_func(() => {
|
private updateList = rateLimitedFunction(() => {
|
||||||
this._updateListNow();
|
this.updateListNow();
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
_updateListNow() {
|
private updateListNow(): void {
|
||||||
// console.log("Updating memberlist");
|
const members = this.roomMembers()
|
||||||
const newState = {
|
|
||||||
|
this.setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
members: this.roomMembers(),
|
members: members,
|
||||||
};
|
filteredJoinedMembers: this.filterMembers(members, 'join', this.state.searchQuery),
|
||||||
newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join', this.state.searchQuery);
|
filteredInvitedMembers: this.filterMembers(members, 'invite', this.state.searchQuery),
|
||||||
newState.filteredInvitedMembers = this._filterMembers(newState.members, 'invite', this.state.searchQuery);
|
});
|
||||||
this.setState(newState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMembersWithUser() {
|
private getMembersWithUser(): Array<RoomMember> {
|
||||||
if (!this.props.roomId) return [];
|
if (!this.props.roomId) return [];
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const room = cli.getRoom(this.props.roomId);
|
const room = cli.getRoom(this.props.roomId);
|
||||||
|
@ -230,15 +260,18 @@ export default class MemberList extends React.Component {
|
||||||
|
|
||||||
const allMembers = Object.values(room.currentState.members);
|
const allMembers = Object.values(room.currentState.members);
|
||||||
|
|
||||||
allMembers.forEach(function(member) {
|
allMembers.forEach((member) => {
|
||||||
// work around a race where you might have a room member object
|
// work around a race where you might have a room member object
|
||||||
// before the user object exists. This may or may not cause
|
// before the user object exists. This may or may not cause
|
||||||
// https://github.com/vector-im/vector-web/issues/186
|
// https://github.com/vector-im/vector-web/issues/186
|
||||||
if (member.user === null) {
|
if (!member.user) {
|
||||||
member.user = cli.getUser(member.userId);
|
member.user = cli.getUser(member.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
member.sortName = (member.name[0] === '@' ? member.name.substr(1) : member.name).replace(SORT_REGEX, "");
|
this.sortNames.set(
|
||||||
|
member,
|
||||||
|
(member.name[0] === '@' ? member.name.substr(1) : member.name).replace(SORT_REGEX, ""),
|
||||||
|
);
|
||||||
|
|
||||||
// XXX: this user may have no lastPresenceTs value!
|
// XXX: this user may have no lastPresenceTs value!
|
||||||
// the right solution here is to fix the race rather than leave it as 0
|
// the right solution here is to fix the race rather than leave it as 0
|
||||||
|
@ -247,7 +280,7 @@ export default class MemberList extends React.Component {
|
||||||
return allMembers;
|
return allMembers;
|
||||||
}
|
}
|
||||||
|
|
||||||
roomMembers() {
|
private roomMembers(): Array<RoomMember> {
|
||||||
const allMembers = this.getMembersWithUser();
|
const allMembers = this.getMembersWithUser();
|
||||||
const filteredAndSortedMembers = allMembers.filter((m) => {
|
const filteredAndSortedMembers = allMembers.filter((m) => {
|
||||||
return (
|
return (
|
||||||
|
@ -255,23 +288,21 @@ export default class MemberList extends React.Component {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const language = SettingsStore.getValue("language");
|
const language = SettingsStore.getValue("language");
|
||||||
this.collator = new Intl.Collator(language, { sensitivity: 'base', usePunctuation: true });
|
this.collator = new Intl.Collator(language, { sensitivity: 'base', ignorePunctuation: false });
|
||||||
filteredAndSortedMembers.sort(this.memberSort);
|
filteredAndSortedMembers.sort(this.memberSort);
|
||||||
return filteredAndSortedMembers;
|
return filteredAndSortedMembers;
|
||||||
}
|
}
|
||||||
|
|
||||||
_createOverflowTileJoined = (overflowCount, totalCount) => {
|
private createOverflowTileJoined = (overflowCount: number, totalCount: number): JSX.Element => {
|
||||||
return this._createOverflowTile(overflowCount, totalCount, this._showMoreJoinedMemberList);
|
return this.createOverflowTile(overflowCount, totalCount, this.showMoreJoinedMemberList);
|
||||||
};
|
};
|
||||||
|
|
||||||
_createOverflowTileInvited = (overflowCount, totalCount) => {
|
private createOverflowTileInvited = (overflowCount: number, totalCount: number): JSX.Element => {
|
||||||
return this._createOverflowTile(overflowCount, totalCount, this._showMoreInvitedMemberList);
|
return this.createOverflowTile(overflowCount, totalCount, this.showMoreInvitedMemberList);
|
||||||
};
|
};
|
||||||
|
|
||||||
_createOverflowTile = (overflowCount, totalCount, onClick) => {
|
private createOverflowTile = (overflowCount: number, totalCount: number, onClick: () => void): JSX.Element=> {
|
||||||
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
||||||
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
|
||||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
|
||||||
const text = _t("and %(count)s others...", { count: overflowCount });
|
const text = _t("and %(count)s others...", { count: overflowCount });
|
||||||
return (
|
return (
|
||||||
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
|
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
|
||||||
|
@ -281,31 +312,48 @@ export default class MemberList extends React.Component {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
_showMoreJoinedMemberList = () => {
|
private showMoreJoinedMemberList = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
truncateAtJoined: this.state.truncateAtJoined + SHOW_MORE_INCREMENT,
|
truncateAtJoined: this.state.truncateAtJoined + SHOW_MORE_INCREMENT,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_showMoreInvitedMemberList = () => {
|
private showMoreInvitedMemberList = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
truncateAtInvited: this.state.truncateAtInvited + SHOW_MORE_INCREMENT,
|
truncateAtInvited: this.state.truncateAtInvited + SHOW_MORE_INCREMENT,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
memberString(member) {
|
/**
|
||||||
|
* SHOULD ONLY BE USED BY TESTS
|
||||||
|
*/
|
||||||
|
public memberString(member: RoomMember): string {
|
||||||
if (!member) {
|
if (!member) {
|
||||||
return "(null)";
|
return "(null)";
|
||||||
} else {
|
} else {
|
||||||
const u = member.user;
|
const u = member.user;
|
||||||
return "(" + member.name + ", " + member.powerLevel + ", " + (u ? u.lastActiveAgo : "<null>") + ", " + (u ? u.getLastActiveTs() : "<null>") + ", " + (u ? u.currentlyActive : "<null>") + ", " + (u ? u.presence : "<null>") + ")";
|
return (
|
||||||
|
"(" +
|
||||||
|
member.name +
|
||||||
|
", " +
|
||||||
|
member.powerLevel +
|
||||||
|
", " +
|
||||||
|
(u ? u.lastActiveAgo : "<null>") +
|
||||||
|
", " +
|
||||||
|
(u ? u.getLastActiveTs() : "<null>") +
|
||||||
|
", " +
|
||||||
|
(u ? u.currentlyActive : "<null>") +
|
||||||
|
", " +
|
||||||
|
(u ? u.presence : "<null>") +
|
||||||
|
")"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns negative if a comes before b,
|
// returns negative if a comes before b,
|
||||||
// returns 0 if a and b are equivalent in ordering
|
// returns 0 if a and b are equivalent in ordering
|
||||||
// returns positive if a comes after b.
|
// returns positive if a comes after b.
|
||||||
memberSort = (memberA, memberB) => {
|
private memberSort = (memberA: RoomMember, memberB: RoomMember): number => {
|
||||||
// order by presence, with "active now" first.
|
// order by presence, with "active now" first.
|
||||||
// ...and then by power level
|
// ...and then by power level
|
||||||
// ...and then by last active
|
// ...and then by last active
|
||||||
|
@ -325,7 +373,7 @@ export default class MemberList extends React.Component {
|
||||||
if (!userA && userB) return 1;
|
if (!userA && userB) return 1;
|
||||||
|
|
||||||
// First by presence
|
// First by presence
|
||||||
if (this._showPresence) {
|
if (this.showPresence) {
|
||||||
const convertPresence = (p) => p === 'unavailable' ? 'online' : p;
|
const convertPresence = (p) => p === 'unavailable' ? 'online' : p;
|
||||||
const presenceIndex = p => {
|
const presenceIndex = p => {
|
||||||
const order = ['active', 'online', 'offline'];
|
const order = ['active', 'online', 'offline'];
|
||||||
|
@ -349,31 +397,31 @@ export default class MemberList extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Third by last active
|
// Third by last active
|
||||||
if (this._showPresence && userA.getLastActiveTs() !== userB.getLastActiveTs()) {
|
if (this.showPresence && userA.getLastActiveTs() !== userB.getLastActiveTs()) {
|
||||||
// console.log("Comparing on last active timestamp - returning");
|
// console.log("Comparing on last active timestamp - returning");
|
||||||
return userB.getLastActiveTs() - userA.getLastActiveTs();
|
return userB.getLastActiveTs() - userA.getLastActiveTs();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fourth by name (alphabetical)
|
// Fourth by name (alphabetical)
|
||||||
return this.collator.compare(memberA.sortName, memberB.sortName);
|
return this.collator.compare(this.sortNames.get(memberA), this.sortNames.get(memberB));
|
||||||
};
|
};
|
||||||
|
|
||||||
onSearchQueryChanged = searchQuery => {
|
private onSearchQueryChanged = (searchQuery: string): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
searchQuery,
|
searchQuery,
|
||||||
filteredJoinedMembers: this._filterMembers(this.state.members, 'join', searchQuery),
|
filteredJoinedMembers: this.filterMembers(this.state.members, 'join', searchQuery),
|
||||||
filteredInvitedMembers: this._filterMembers(this.state.members, 'invite', searchQuery),
|
filteredInvitedMembers: this.filterMembers(this.state.members, 'invite', searchQuery),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_onPending3pidInviteClick = inviteEvent => {
|
private onPending3pidInviteClick = (inviteEvent: MatrixEvent): void => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_3pid_invite',
|
action: 'view_3pid_invite',
|
||||||
event: inviteEvent,
|
event: inviteEvent,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_filterMembers(members, membership, query) {
|
private filterMembers(members: Array<RoomMember>, membership: string, query?: string): Array<RoomMember> {
|
||||||
return members.filter((m) => {
|
return members.filter((m) => {
|
||||||
if (query) {
|
if (query) {
|
||||||
query = query.toLowerCase();
|
query = query.toLowerCase();
|
||||||
|
@ -389,7 +437,7 @@ export default class MemberList extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_getPending3PidInvites() {
|
private getPending3PidInvites(): Array<MatrixEvent> {
|
||||||
// include 3pid invites (m.room.third_party_invite) state events.
|
// include 3pid invites (m.room.third_party_invite) state events.
|
||||||
// The HS may have already converted these into m.room.member invites so
|
// The HS may have already converted these into m.room.member invites so
|
||||||
// we shouldn't add them if the 3pid invite state key (token) is in the
|
// we shouldn't add them if the 3pid invite state key (token) is in the
|
||||||
|
@ -409,42 +457,40 @@ export default class MemberList extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_makeMemberTiles(members) {
|
private makeMemberTiles(members: Array<RoomMember | MatrixEvent>) {
|
||||||
const MemberTile = sdk.getComponent("rooms.MemberTile");
|
|
||||||
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
|
||||||
|
|
||||||
return members.map((m) => {
|
return members.map((m) => {
|
||||||
if (m.userId) {
|
if (m instanceof RoomMember) {
|
||||||
// Is a Matrix invite
|
// Is a Matrix invite
|
||||||
return <MemberTile key={m.userId} member={m} ref={m.userId} showPresence={this._showPresence} />;
|
return <MemberTile key={m.userId} member={m} ref={m.userId} showPresence={this.showPresence} />;
|
||||||
} else {
|
} else {
|
||||||
// Is a 3pid invite
|
// Is a 3pid invite
|
||||||
return <EntityTile key={m.getStateKey()} name={m.getContent().display_name} suppressOnHover={true}
|
return <EntityTile key={m.getStateKey()} name={m.getContent().display_name} suppressOnHover={true}
|
||||||
onClick={() => this._onPending3pidInviteClick(m)} />;
|
onClick={() => this.onPending3pidInviteClick(m)} />;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_getChildrenJoined = (start, end) => this._makeMemberTiles(this.state.filteredJoinedMembers.slice(start, end));
|
private getChildrenJoined = (start: number, end: number): Array<JSX.Element> => {
|
||||||
|
return this.makeMemberTiles(this.state.filteredJoinedMembers.slice(start, end))
|
||||||
_getChildCountJoined = () => this.state.filteredJoinedMembers.length;
|
|
||||||
|
|
||||||
_getChildrenInvited = (start, end) => {
|
|
||||||
let targets = this.state.filteredInvitedMembers;
|
|
||||||
if (end > this.state.filteredInvitedMembers.length) {
|
|
||||||
targets = targets.concat(this._getPending3PidInvites());
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._makeMemberTiles(targets.slice(start, end));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_getChildCountInvited = () => {
|
private getChildCountJoined = (): number => this.state.filteredJoinedMembers.length;
|
||||||
return this.state.filteredInvitedMembers.length + (this._getPending3PidInvites() || []).length;
|
|
||||||
|
private getChildrenInvited = (start: number, end: number): Array<JSX.Element> => {
|
||||||
|
let targets = this.state.filteredInvitedMembers;
|
||||||
|
if (end > this.state.filteredInvitedMembers.length) {
|
||||||
|
targets = targets.concat(this.getPending3PidInvites());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.makeMemberTiles(targets.slice(start, end));
|
||||||
|
};
|
||||||
|
|
||||||
|
private getChildCountInvited = (): number => {
|
||||||
|
return this.state.filteredInvitedMembers.length + (this.getPending3PidInvites() || []).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.loading) {
|
if (this.state.loading) {
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
|
||||||
return <BaseCard
|
return <BaseCard
|
||||||
className="mx_MemberList"
|
className="mx_MemberList"
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
|
@ -454,9 +500,6 @@ export default class MemberList extends React.Component {
|
||||||
</BaseCard>;
|
</BaseCard>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchBox = sdk.getComponent('structures.SearchBox');
|
|
||||||
const TruncatedList = sdk.getComponent("elements.TruncatedList");
|
|
||||||
|
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const room = cli.getRoom(this.props.roomId);
|
const room = cli.getRoom(this.props.roomId);
|
||||||
let inviteButton;
|
let inviteButton;
|
||||||
|
@ -470,22 +513,30 @@ export default class MemberList extends React.Component {
|
||||||
inviteButtonText = _t("Invite to this space");
|
inviteButtonText = _t("Invite to this space");
|
||||||
}
|
}
|
||||||
|
|
||||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
inviteButton = (
|
||||||
inviteButton =
|
<AccessibleButton
|
||||||
<AccessibleButton className="mx_MemberList_invite" onClick={this.onInviteButtonClick} disabled={!this.state.canInvite}>
|
className="mx_MemberList_invite"
|
||||||
|
onClick={this.onInviteButtonClick}
|
||||||
|
disabled={!this.state.canInvite}
|
||||||
|
>
|
||||||
<span>{ inviteButtonText }</span>
|
<span>{ inviteButtonText }</span>
|
||||||
</AccessibleButton>;
|
</AccessibleButton>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let invitedHeader;
|
let invitedHeader;
|
||||||
let invitedSection;
|
let invitedSection;
|
||||||
if (this._getChildCountInvited() > 0) {
|
if (this.getChildCountInvited() > 0) {
|
||||||
invitedHeader = <h2>{ _t("Invited") }</h2>;
|
invitedHeader = <h2>{ _t("Invited") }</h2>;
|
||||||
invitedSection = <TruncatedList className="mx_MemberList_section mx_MemberList_invited" truncateAt={this.state.truncateAtInvited}
|
invitedSection = (
|
||||||
createOverflowElement={this._createOverflowTileInvited}
|
<TruncatedList
|
||||||
getChildren={this._getChildrenInvited}
|
className="mx_MemberList_section mx_MemberList_invited"
|
||||||
getChildCount={this._getChildCountInvited}
|
truncateAt={this.state.truncateAtInvited}
|
||||||
/>;
|
createOverflowElement={this.createOverflowTileInvited}
|
||||||
|
getChildren={this.getChildrenInvited}
|
||||||
|
getChildCount={this.getChildCountInvited}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const footer = (
|
const footer = (
|
||||||
|
@ -517,17 +568,19 @@ export default class MemberList extends React.Component {
|
||||||
previousPhase={previousPhase}
|
previousPhase={previousPhase}
|
||||||
>
|
>
|
||||||
<div className="mx_MemberList_wrapper">
|
<div className="mx_MemberList_wrapper">
|
||||||
<TruncatedList className="mx_MemberList_section mx_MemberList_joined" truncateAt={this.state.truncateAtJoined}
|
<TruncatedList
|
||||||
createOverflowElement={this._createOverflowTileJoined}
|
className="mx_MemberList_section mx_MemberList_joined"
|
||||||
getChildren={this._getChildrenJoined}
|
truncateAt={this.state.truncateAtJoined}
|
||||||
getChildCount={this._getChildCountJoined} />
|
createOverflowElement={this.createOverflowTileJoined}
|
||||||
|
getChildren={this.getChildrenJoined}
|
||||||
|
getChildCount={this.getChildCountJoined} />
|
||||||
{ invitedHeader }
|
{ invitedHeader }
|
||||||
{ invitedSection }
|
{ invitedSection }
|
||||||
</div>
|
</div>
|
||||||
</BaseCard>;
|
</BaseCard>;
|
||||||
}
|
}
|
||||||
|
|
||||||
onInviteButtonClick = () => {
|
onInviteButtonClick = (): void => {
|
||||||
if (MatrixClientPeg.get().isGuest()) {
|
if (MatrixClientPeg.get().isGuest()) {
|
||||||
dis.dispatch({action: 'require_registration'});
|
dis.dispatch({action: 'require_registration'});
|
||||||
return;
|
return;
|
|
@ -17,20 +17,33 @@ limitations under the License.
|
||||||
|
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import * as sdk from "../../../index";
|
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import {Action} from "../../../dispatcher/actions";
|
import {Action} from "../../../dispatcher/actions";
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||||
|
import EntityTile, { PowerStatus } from "./EntityTile";
|
||||||
|
import MemberAvatar from "./../avatars/MemberAvatar";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
member: RoomMember;
|
||||||
|
showPresence?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
statusMessage: string;
|
||||||
|
isRoomEncrypted: boolean;
|
||||||
|
e2eStatus: string;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.MemberTile")
|
@replaceableComponent("views.rooms.MemberTile")
|
||||||
export default class MemberTile extends React.Component {
|
export default class MemberTile extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
private userLastModifiedTime: number;
|
||||||
member: PropTypes.any.isRequired, // RoomMember
|
private memberLastModifiedTime: number;
|
||||||
showPresence: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
showPresence: true,
|
showPresence: true,
|
||||||
|
@ -52,7 +65,7 @@ export default class MemberTile extends React.Component {
|
||||||
if (SettingsStore.getValue("feature_custom_status")) {
|
if (SettingsStore.getValue("feature_custom_status")) {
|
||||||
const { user } = this.props.member;
|
const { user } = this.props.member;
|
||||||
if (user) {
|
if (user) {
|
||||||
user.on("User._unstable_statusMessage", this._onStatusMessageCommitted);
|
user.on("User._unstable_statusMessage", this.onStatusMessageCommitted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +93,7 @@ export default class MemberTile extends React.Component {
|
||||||
if (user) {
|
if (user) {
|
||||||
user.removeListener(
|
user.removeListener(
|
||||||
"User._unstable_statusMessage",
|
"User._unstable_statusMessage",
|
||||||
this._onStatusMessageCommitted,
|
this.onStatusMessageCommitted,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,8 +104,8 @@ export default class MemberTile extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onRoomStateEvents = ev => {
|
private onRoomStateEvents = (ev: MatrixEvent): void => {
|
||||||
if (ev.getType() !== "m.room.encryption") return;
|
if (ev.getType() !== EventType.RoomEncryption) return;
|
||||||
const { roomId } = this.props.member;
|
const { roomId } = this.props.member;
|
||||||
if (ev.getRoomId() !== roomId) return;
|
if (ev.getRoomId() !== roomId) return;
|
||||||
|
|
||||||
|
@ -105,17 +118,17 @@ export default class MemberTile extends React.Component {
|
||||||
this.updateE2EStatus();
|
this.updateE2EStatus();
|
||||||
};
|
};
|
||||||
|
|
||||||
onUserTrustStatusChanged = (userId, trustStatus) => {
|
private onUserTrustStatusChanged = (userId: string, trustStatus: string): void => {
|
||||||
if (userId !== this.props.member.userId) return;
|
if (userId !== this.props.member.userId) return;
|
||||||
this.updateE2EStatus();
|
this.updateE2EStatus();
|
||||||
};
|
};
|
||||||
|
|
||||||
onDeviceVerificationChanged = (userId, deviceId, deviceInfo) => {
|
private onDeviceVerificationChanged = (userId: string, deviceId: string, deviceInfo: DeviceInfo): void => {
|
||||||
if (userId !== this.props.member.userId) return;
|
if (userId !== this.props.member.userId) return;
|
||||||
this.updateE2EStatus();
|
this.updateE2EStatus();
|
||||||
};
|
};
|
||||||
|
|
||||||
async updateE2EStatus() {
|
private async updateE2EStatus(): Promise<void> {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const { userId } = this.props.member;
|
const { userId } = this.props.member;
|
||||||
const isMe = userId === cli.getUserId();
|
const isMe = userId === cli.getUserId();
|
||||||
|
@ -143,32 +156,32 @@ export default class MemberTile extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatusMessage() {
|
private getStatusMessage(): string {
|
||||||
const { user } = this.props.member;
|
const { user } = this.props.member;
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return user._unstable_statusMessage;
|
return user.unstable_statusMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onStatusMessageCommitted = () => {
|
private onStatusMessageCommitted = (): void => {
|
||||||
// The `User` object has observed a status message change.
|
// The `User` object has observed a status message change.
|
||||||
this.setState({
|
this.setState({
|
||||||
statusMessage: this.getStatusMessage(),
|
statusMessage: this.getStatusMessage(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps: IProps, nextState: IState): boolean {
|
||||||
if (
|
if (
|
||||||
this.member_last_modified_time === undefined ||
|
this.memberLastModifiedTime === undefined ||
|
||||||
this.member_last_modified_time < nextProps.member.getLastModifiedTime()
|
this.memberLastModifiedTime < nextProps.member.getLastModifiedTime()
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
nextProps.member.user &&
|
nextProps.member.user &&
|
||||||
(this.user_last_modified_time === undefined ||
|
(this.userLastModifiedTime === undefined ||
|
||||||
this.user_last_modified_time < nextProps.member.user.getLastModifiedTime())
|
this.userLastModifiedTime < nextProps.member.user.getLastModifiedTime())
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -181,18 +194,18 @@ export default class MemberTile extends React.Component {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick = e => {
|
private onClick = (): void => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: Action.ViewUser,
|
action: Action.ViewUser,
|
||||||
member: this.props.member,
|
member: this.props.member,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_getDisplayName() {
|
private getDisplayName(): string {
|
||||||
return this.props.member.name;
|
return this.props.member.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPowerLabel() {
|
private getPowerLabel(): string {
|
||||||
return _t("%(userName)s (power %(powerLevelNumber)s)", {
|
return _t("%(userName)s (power %(powerLevelNumber)s)", {
|
||||||
userName: this.props.member.userId,
|
userName: this.props.member.userId,
|
||||||
powerLevelNumber: this.props.member.powerLevel,
|
powerLevelNumber: this.props.member.powerLevel,
|
||||||
|
@ -200,11 +213,8 @@ export default class MemberTile extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
|
||||||
const EntityTile = sdk.getComponent('rooms.EntityTile');
|
|
||||||
|
|
||||||
const member = this.props.member;
|
const member = this.props.member;
|
||||||
const name = this._getDisplayName();
|
const name = this.getDisplayName();
|
||||||
const presenceState = member.user ? member.user.presence : null;
|
const presenceState = member.user ? member.user.presence : null;
|
||||||
|
|
||||||
let statusMessage = null;
|
let statusMessage = null;
|
||||||
|
@ -217,13 +227,13 @@ export default class MemberTile extends React.Component {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (member.user) {
|
if (member.user) {
|
||||||
this.user_last_modified_time = member.user.getLastModifiedTime();
|
this.userLastModifiedTime = member.user.getLastModifiedTime();
|
||||||
}
|
}
|
||||||
this.member_last_modified_time = member.getLastModifiedTime();
|
this.memberLastModifiedTime = member.getLastModifiedTime();
|
||||||
|
|
||||||
const powerStatusMap = new Map([
|
const powerStatusMap = new Map([
|
||||||
[100, EntityTile.POWER_STATUS_ADMIN],
|
[100, PowerStatus.Admin],
|
||||||
[50, EntityTile.POWER_STATUS_MODERATOR],
|
[50, PowerStatus.Moderator],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Find the nearest power level with a badge
|
// Find the nearest power level with a badge
|
|
@ -15,26 +15,23 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
// number of milliseconds ago this user was last active.
|
||||||
|
// zero = unknown
|
||||||
|
activeAgo?: number;
|
||||||
|
// if true, activeAgo is an approximation and "Now" should
|
||||||
|
// be shown instead
|
||||||
|
currentlyActive?: boolean;
|
||||||
|
// offline, online, etc
|
||||||
|
presenceState?: string;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.PresenceLabel")
|
@replaceableComponent("views.rooms.PresenceLabel")
|
||||||
export default class PresenceLabel extends React.Component {
|
export default class PresenceLabel extends React.Component<IProps> {
|
||||||
static propTypes = {
|
|
||||||
// number of milliseconds ago this user was last active.
|
|
||||||
// zero = unknown
|
|
||||||
activeAgo: PropTypes.number,
|
|
||||||
|
|
||||||
// if true, activeAgo is an approximation and "Now" should
|
|
||||||
// be shown instead
|
|
||||||
currentlyActive: PropTypes.bool,
|
|
||||||
|
|
||||||
// offline, online, etc
|
|
||||||
presenceState: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
activeAgo: -1,
|
activeAgo: -1,
|
||||||
presenceState: null,
|
presenceState: null,
|
||||||
|
@ -42,29 +39,29 @@ export default class PresenceLabel extends React.Component {
|
||||||
|
|
||||||
// Return duration as a string using appropriate time units
|
// Return duration as a string using appropriate time units
|
||||||
// XXX: This would be better handled using a culture-aware library, but we don't use one yet.
|
// XXX: This would be better handled using a culture-aware library, but we don't use one yet.
|
||||||
getDuration(time) {
|
private getDuration(time: number): string {
|
||||||
if (!time) return;
|
if (!time) return;
|
||||||
const t = parseInt(time / 1000);
|
const t = Math.round(time / 1000);
|
||||||
const s = t % 60;
|
const s = t % 60;
|
||||||
const m = parseInt(t / 60) % 60;
|
const m = Math.round(t / 60) % 60;
|
||||||
const h = parseInt(t / (60 * 60)) % 24;
|
const h = Math.round(t / (60 * 60)) % 24;
|
||||||
const d = parseInt(t / (60 * 60 * 24));
|
const d = Math.round(t / (60 * 60 * 24));
|
||||||
if (t < 60) {
|
if (t < 60) {
|
||||||
if (t < 0) {
|
if (t < 0) {
|
||||||
return _t("%(duration)ss", {duration: 0});
|
return _t("%(duration)ss", { duration: 0 });
|
||||||
}
|
}
|
||||||
return _t("%(duration)ss", {duration: s});
|
return _t("%(duration)ss", { duration: s });
|
||||||
}
|
}
|
||||||
if (t < 60 * 60) {
|
if (t < 60 * 60) {
|
||||||
return _t("%(duration)sm", {duration: m});
|
return _t("%(duration)sm", { duration: m });
|
||||||
}
|
}
|
||||||
if (t < 24 * 60 * 60) {
|
if (t < 24 * 60 * 60) {
|
||||||
return _t("%(duration)sh", {duration: h});
|
return _t("%(duration)sh", { duration: h });
|
||||||
}
|
}
|
||||||
return _t("%(duration)sd", {duration: d});
|
return _t("%(duration)sd", { duration: d });
|
||||||
}
|
}
|
||||||
|
|
||||||
getPrettyPresence(presence, activeAgo, currentlyActive) {
|
private getPrettyPresence(presence: string, activeAgo: number, currentlyActive: boolean): string {
|
||||||
if (!currentlyActive && activeAgo !== undefined && activeAgo > 0) {
|
if (!currentlyActive && activeAgo !== undefined && activeAgo > 0) {
|
||||||
const duration = this.getDuration(activeAgo);
|
const duration = this.getDuration(activeAgo);
|
||||||
if (presence === "online") return _t("Online for %(duration)s", { duration: duration });
|
if (presence === "online") return _t("Online for %(duration)s", { duration: duration });
|
|
@ -45,7 +45,6 @@ import { objectShallowClone, objectWithOnly } from "../../../utils/objects";
|
||||||
import { IconizedContextMenuOption, IconizedContextMenuOptionList } from "../context_menus/IconizedContextMenu";
|
import { IconizedContextMenuOption, IconizedContextMenuOptionList } from "../context_menus/IconizedContextMenu";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
|
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
|
||||||
import CallHandler from "../../../CallHandler";
|
|
||||||
import SpaceStore, {ISuggestedRoom, SUGGESTED_ROOMS} from "../../../stores/SpaceStore";
|
import SpaceStore, {ISuggestedRoom, SUGGESTED_ROOMS} from "../../../stores/SpaceStore";
|
||||||
import {showAddExistingRooms, showCreateNewRoom, showSpaceInvite} from "../../../utils/space";
|
import {showAddExistingRooms, showCreateNewRoom, showSpaceInvite} from "../../../utils/space";
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||||
|
@ -103,38 +102,6 @@ interface ITagAestheticsMap {
|
||||||
[tagId: TagID]: ITagAesthetics;
|
[tagId: TagID]: ITagAesthetics;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have no dialer support, we just show the create chat dialog
|
|
||||||
const dmOnAddRoom = (dispatcher?: Dispatcher<ActionPayload>) => {
|
|
||||||
(dispatcher || defaultDispatcher).dispatch({action: 'view_create_chat'});
|
|
||||||
};
|
|
||||||
|
|
||||||
// If we have dialer support, show a context menu so the user can pick between
|
|
||||||
// the dialer and the create chat dialog
|
|
||||||
const dmAddRoomContextMenu = (onFinished: () => void) => {
|
|
||||||
return <IconizedContextMenuOptionList first>
|
|
||||||
<IconizedContextMenuOption
|
|
||||||
label={_t("Start a Conversation")}
|
|
||||||
iconClassName="mx_RoomList_iconPlus"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
onFinished();
|
|
||||||
defaultDispatcher.dispatch({action: "view_create_chat"});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<IconizedContextMenuOption
|
|
||||||
label={_t("Open dial pad")}
|
|
||||||
iconClassName="mx_RoomList_iconDialpad"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
onFinished();
|
|
||||||
defaultDispatcher.fire(Action.OpenDialPad);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</IconizedContextMenuOptionList>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const TAG_AESTHETICS: ITagAestheticsMap = {
|
const TAG_AESTHETICS: ITagAestheticsMap = {
|
||||||
[DefaultTagID.Invite]: {
|
[DefaultTagID.Invite]: {
|
||||||
sectionLabel: _td("Invites"),
|
sectionLabel: _td("Invites"),
|
||||||
|
@ -151,8 +118,9 @@ const TAG_AESTHETICS: ITagAestheticsMap = {
|
||||||
isInvite: false,
|
isInvite: false,
|
||||||
defaultHidden: false,
|
defaultHidden: false,
|
||||||
addRoomLabel: _td("Start chat"),
|
addRoomLabel: _td("Start chat"),
|
||||||
// Either onAddRoom or addRoomContextMenu are set depending on whether we
|
onAddRoom: (dispatcher?: Dispatcher<ActionPayload>) => {
|
||||||
// have dialer support.
|
(dispatcher || defaultDispatcher).dispatch({action: 'view_create_chat'});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
[DefaultTagID.Untagged]: {
|
[DefaultTagID.Untagged]: {
|
||||||
sectionLabel: _td("Rooms"),
|
sectionLabel: _td("Rooms"),
|
||||||
|
@ -271,7 +239,6 @@ function customTagAesthetics(tagId: TagID): ITagAesthetics {
|
||||||
export default class RoomList extends React.PureComponent<IProps, IState> {
|
export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
private dispatcherRef;
|
private dispatcherRef;
|
||||||
private customTagStoreRef;
|
private customTagStoreRef;
|
||||||
private tagAesthetics: ITagAestheticsMap;
|
|
||||||
private roomStoreToken: fbEmitter.EventSubscription;
|
private roomStoreToken: fbEmitter.EventSubscription;
|
||||||
|
|
||||||
constructor(props: IProps) {
|
constructor(props: IProps) {
|
||||||
|
@ -282,10 +249,6 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
isNameFiltering: !!RoomListStore.instance.getFirstNameFilterCondition(),
|
isNameFiltering: !!RoomListStore.instance.getFirstNameFilterCondition(),
|
||||||
suggestedRooms: SpaceStore.instance.suggestedRooms,
|
suggestedRooms: SpaceStore.instance.suggestedRooms,
|
||||||
};
|
};
|
||||||
|
|
||||||
// shallow-copy from the template as we need to make modifications to it
|
|
||||||
this.tagAesthetics = objectShallowClone(TAG_AESTHETICS);
|
|
||||||
this.updateDmAddRoomAction();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
@ -311,17 +274,6 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private updateDmAddRoomAction() {
|
|
||||||
const dmTagAesthetics = objectShallowClone(TAG_AESTHETICS[DefaultTagID.DM]);
|
|
||||||
if (CallHandler.sharedInstance().getSupportsPstnProtocol()) {
|
|
||||||
dmTagAesthetics.addRoomContextMenu = dmAddRoomContextMenu;
|
|
||||||
} else {
|
|
||||||
dmTagAesthetics.onAddRoom = dmOnAddRoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.tagAesthetics[DefaultTagID.DM] = dmTagAesthetics;
|
|
||||||
}
|
|
||||||
|
|
||||||
private onAction = (payload: ActionPayload) => {
|
private onAction = (payload: ActionPayload) => {
|
||||||
if (payload.action === Action.ViewRoomDelta) {
|
if (payload.action === Action.ViewRoomDelta) {
|
||||||
const viewRoomDeltaPayload = payload as ViewRoomDeltaPayload;
|
const viewRoomDeltaPayload = payload as ViewRoomDeltaPayload;
|
||||||
|
@ -335,7 +287,6 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (payload.action === Action.PstnSupportUpdated) {
|
} else if (payload.action === Action.PstnSupportUpdated) {
|
||||||
this.updateDmAddRoomAction();
|
|
||||||
this.updateLists();
|
this.updateLists();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -524,7 +475,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
const aesthetics: ITagAesthetics = isCustomTag(orderedTagId)
|
const aesthetics: ITagAesthetics = isCustomTag(orderedTagId)
|
||||||
? customTagAesthetics(orderedTagId)
|
? customTagAesthetics(orderedTagId)
|
||||||
: this.tagAesthetics[orderedTagId];
|
: TAG_AESTHETICS[orderedTagId];
|
||||||
if (!aesthetics) throw new Error(`Tag ${orderedTagId} does not have aesthetics`);
|
if (!aesthetics) throw new Error(`Tag ${orderedTagId} does not have aesthetics`);
|
||||||
|
|
||||||
// The cost of mounting/unmounting this component offsets the cost
|
// The cost of mounting/unmounting this component offsets the cost
|
||||||
|
|
|
@ -65,12 +65,13 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.state.recorder.stop();
|
await this.state.recorder.stop();
|
||||||
const mxc = await this.state.recorder.upload();
|
const upload = await this.state.recorder.upload(this.props.room.roomId);
|
||||||
MatrixClientPeg.get().sendMessage(this.props.room.roomId, {
|
MatrixClientPeg.get().sendMessage(this.props.room.roomId, {
|
||||||
"body": "Voice message",
|
"body": "Voice message",
|
||||||
//"msgtype": "org.matrix.msc2516.voice",
|
//"msgtype": "org.matrix.msc2516.voice",
|
||||||
"msgtype": MsgType.Audio,
|
"msgtype": MsgType.Audio,
|
||||||
"url": mxc,
|
"url": upload.mxc,
|
||||||
|
"file": upload.encrypted,
|
||||||
"info": {
|
"info": {
|
||||||
duration: Math.round(this.state.recorder.durationSeconds * 1000),
|
duration: Math.round(this.state.recorder.durationSeconds * 1000),
|
||||||
mimetype: this.state.recorder.contentType,
|
mimetype: this.state.recorder.contentType,
|
||||||
|
@ -81,7 +82,8 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
||||||
// https://github.com/matrix-org/matrix-doc/pull/3245
|
// https://github.com/matrix-org/matrix-doc/pull/3245
|
||||||
"org.matrix.msc1767.text": "Voice message",
|
"org.matrix.msc1767.text": "Voice message",
|
||||||
"org.matrix.msc1767.file": {
|
"org.matrix.msc1767.file": {
|
||||||
url: mxc,
|
url: upload.mxc,
|
||||||
|
file: upload.encrypted,
|
||||||
name: "Voice message.ogg",
|
name: "Voice message.ogg",
|
||||||
mimetype: this.state.recorder.contentType,
|
mimetype: this.state.recorder.contentType,
|
||||||
size: this.state.recorder.contentLength,
|
size: this.state.recorder.contentLength,
|
||||||
|
|
|
@ -79,8 +79,8 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||||
async _getUpdatedStatus() {
|
async _getUpdatedStatus() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const pkCache = cli.getCrossSigningCacheCallbacks();
|
const pkCache = cli.getCrossSigningCacheCallbacks();
|
||||||
const crossSigning = cli.crypto._crossSigningInfo;
|
const crossSigning = cli.crypto.crossSigningInfo;
|
||||||
const secretStorage = cli.crypto._secretStorage;
|
const secretStorage = cli.crypto.secretStorage;
|
||||||
const crossSigningPublicKeysOnDevice = crossSigning.getId();
|
const crossSigningPublicKeysOnDevice = crossSigning.getId();
|
||||||
const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage);
|
const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage);
|
||||||
const masterPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("master"));
|
const masterPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("master"));
|
||||||
|
|
|
@ -131,7 +131,7 @@ export default class SecureBackupPanel extends React.PureComponent {
|
||||||
|
|
||||||
async _getUpdatedDiagnostics() {
|
async _getUpdatedDiagnostics() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const secretStorage = cli.crypto._secretStorage;
|
const secretStorage = cli.crypto.secretStorage;
|
||||||
|
|
||||||
const backupKeyStored = !!(await cli.isKeyBackupKeyStored());
|
const backupKeyStored = !!(await cli.isKeyBackupKeyStored());
|
||||||
const backupKeyFromCache = await cli.crypto.getSessionBackupPrivateKey();
|
const backupKeyFromCache = await cli.crypto.getSessionBackupPrivateKey();
|
||||||
|
|
|
@ -33,6 +33,9 @@ export enum JoinRule {
|
||||||
Public = "public",
|
Public = "public",
|
||||||
Knock = "knock",
|
Knock = "knock",
|
||||||
Invite = "invite",
|
Invite = "invite",
|
||||||
|
/**
|
||||||
|
* @deprecated Reserved. Should not be used.
|
||||||
|
*/
|
||||||
Private = "private",
|
Private = "private",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,9 @@ const SpaceCreateMenu = ({ onFinished }) => {
|
||||||
events_default: 100,
|
events_default: 100,
|
||||||
...Visibility.Public ? { invite: 0 } : {},
|
...Visibility.Public ? { invite: 0 } : {},
|
||||||
},
|
},
|
||||||
room_alias_name: alias ? alias.substr(1, alias.indexOf(":") - 1) : undefined,
|
room_alias_name: visibility === Visibility.Public && alias
|
||||||
|
? alias.substr(1, alias.indexOf(":") - 1)
|
||||||
|
: undefined,
|
||||||
topic,
|
topic,
|
||||||
},
|
},
|
||||||
spinner: false,
|
spinner: false,
|
||||||
|
|
|
@ -62,9 +62,9 @@ const SpaceSettingsVisibilityTab = ({ matrixClient: cli, space }: IProps) => {
|
||||||
const userId = cli.getUserId();
|
const userId = cli.getUserId();
|
||||||
|
|
||||||
const [visibility, setVisibility] = useLocalEcho<SpaceVisibility>(
|
const [visibility, setVisibility] = useLocalEcho<SpaceVisibility>(
|
||||||
() => space.getJoinRule() === JoinRule.Private ? SpaceVisibility.Private : SpaceVisibility.Unlisted,
|
() => space.getJoinRule() === JoinRule.Invite ? SpaceVisibility.Private : SpaceVisibility.Unlisted,
|
||||||
visibility => cli.sendStateEvent(space.roomId, EventType.RoomJoinRules, {
|
visibility => cli.sendStateEvent(space.roomId, EventType.RoomJoinRules, {
|
||||||
join_rule: visibility === SpaceVisibility.Unlisted ? JoinRule.Public : JoinRule.Private,
|
join_rule: visibility === SpaceVisibility.Unlisted ? JoinRule.Public : JoinRule.Invite,
|
||||||
}, ""),
|
}, ""),
|
||||||
() => setError(_t("Failed to update the visibility of this space")),
|
() => setError(_t("Failed to update the visibility of this space")),
|
||||||
);
|
);
|
||||||
|
|
|
@ -50,7 +50,7 @@ const GenericToast: React.FC<XOR<IPropsExtended, IProps>> = ({
|
||||||
{detailContent}
|
{detailContent}
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_Toast_buttons" aria-live="off">
|
<div className="mx_Toast_buttons" aria-live="off">
|
||||||
{onReject && rejectLabel && <AccessibleButton kind="danger" onClick={onReject}>
|
{onReject && rejectLabel && <AccessibleButton kind="danger_outline" onClick={onReject}>
|
||||||
{ rejectLabel }
|
{ rejectLabel }
|
||||||
</AccessibleButton> }
|
</AccessibleButton> }
|
||||||
<AccessibleButton onClick={onAccept} kind="primary">
|
<AccessibleButton onClick={onAccept} kind="primary">
|
||||||
|
|
|
@ -27,6 +27,11 @@ export interface SetRightPanelPhasePayload extends ActionPayload {
|
||||||
|
|
||||||
phase: RightPanelPhases;
|
phase: RightPanelPhases;
|
||||||
refireParams?: SetRightPanelPhaseRefireParams;
|
refireParams?: SetRightPanelPhaseRefireParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default SetRightPanelPhase can close the panel, this allows overriding that behaviour
|
||||||
|
*/
|
||||||
|
allowClose?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SetRightPanelPhaseRefireParams {
|
export interface SetRightPanelPhaseRefireParams {
|
||||||
|
|
|
@ -396,7 +396,8 @@
|
||||||
"Failed to invite": "Failed to invite",
|
"Failed to invite": "Failed to invite",
|
||||||
"Operation failed": "Operation failed",
|
"Operation failed": "Operation failed",
|
||||||
"Failed to invite users to the room:": "Failed to invite users to the room:",
|
"Failed to invite users to the room:": "Failed to invite users to the room:",
|
||||||
"Failed to invite the following users to the %(roomName)s room:": "Failed to invite the following users to the %(roomName)s room:",
|
"We sent the others, but the below people couldn't be invited to <RoomName/>": "We sent the others, but the below people couldn't be invited to <RoomName/>",
|
||||||
|
"Some invites couldn't be sent": "Some invites couldn't be sent",
|
||||||
"You need to be logged in.": "You need to be logged in.",
|
"You need to be logged in.": "You need to be logged in.",
|
||||||
"You need to be able to invite users to do that.": "You need to be able to invite users to do that.",
|
"You need to be able to invite users to do that.": "You need to be able to invite users to do that.",
|
||||||
"Unable to create widget.": "Unable to create widget.",
|
"Unable to create widget.": "Unable to create widget.",
|
||||||
|
@ -489,24 +490,27 @@
|
||||||
"Converts the room to a DM": "Converts the room to a DM",
|
"Converts the room to a DM": "Converts the room to a DM",
|
||||||
"Converts the DM to a room": "Converts the DM to a room",
|
"Converts the DM to a room": "Converts the DM to a room",
|
||||||
"Displays action": "Displays action",
|
"Displays action": "Displays action",
|
||||||
"Reason": "Reason",
|
"%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s accepted the invitation for %(displayName)s",
|
||||||
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",
|
"%(targetName)s accepted an invitation": "%(targetName)s accepted an invitation",
|
||||||
"%(targetName)s accepted an invitation.": "%(targetName)s accepted an invitation.",
|
"%(senderName)s invited %(targetName)s": "%(senderName)s invited %(targetName)s",
|
||||||
"%(senderName)s invited %(targetName)s.": "%(senderName)s invited %(targetName)s.",
|
"%(senderName)s banned %(targetName)s: %(reason)s": "%(senderName)s banned %(targetName)s: %(reason)s",
|
||||||
"%(senderName)s banned %(targetName)s.": "%(senderName)s banned %(targetName)s.",
|
"%(senderName)s banned %(targetName)s": "%(senderName)s banned %(targetName)s",
|
||||||
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s changed their display name to %(displayName)s.",
|
"%(oldDisplayName)s changed their display name to %(displayName)s": "%(oldDisplayName)s changed their display name to %(displayName)s",
|
||||||
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s set their display name to %(displayName)s.",
|
"%(senderName)s set their display name to %(displayName)s": "%(senderName)s set their display name to %(displayName)s",
|
||||||
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s removed their display name (%(oldDisplayName)s).",
|
"%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s removed their display name (%(oldDisplayName)s)",
|
||||||
"%(senderName)s removed their profile picture.": "%(senderName)s removed their profile picture.",
|
"%(senderName)s removed their profile picture": "%(senderName)s removed their profile picture",
|
||||||
"%(senderName)s changed their profile picture.": "%(senderName)s changed their profile picture.",
|
"%(senderName)s changed their profile picture": "%(senderName)s changed their profile picture",
|
||||||
"%(senderName)s set a profile picture.": "%(senderName)s set a profile picture.",
|
"%(senderName)s set a profile picture": "%(senderName)s set a profile picture",
|
||||||
"%(senderName)s made no change.": "%(senderName)s made no change.",
|
"%(senderName)s made no change": "%(senderName)s made no change",
|
||||||
"%(targetName)s joined the room.": "%(targetName)s joined the room.",
|
"%(targetName)s joined the room": "%(targetName)s joined the room",
|
||||||
"%(targetName)s rejected the invitation.": "%(targetName)s rejected the invitation.",
|
"%(targetName)s rejected the invitation": "%(targetName)s rejected the invitation",
|
||||||
"%(targetName)s left the room.": "%(targetName)s left the room.",
|
"%(targetName)s left the room: %(reason)s": "%(targetName)s left the room: %(reason)s",
|
||||||
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s unbanned %(targetName)s.",
|
"%(targetName)s left the room": "%(targetName)s left the room",
|
||||||
"%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.",
|
"%(senderName)s unbanned %(targetName)s": "%(senderName)s unbanned %(targetName)s",
|
||||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s kicked %(targetName)s.",
|
"%(senderName)s withdrew %(targetName)s's invitation: %(reason)s": "%(senderName)s withdrew %(targetName)s's invitation: %(reason)s",
|
||||||
|
"%(senderName)s withdrew %(targetName)s's invitation": "%(senderName)s withdrew %(targetName)s's invitation",
|
||||||
|
"%(senderName)s kicked %(targetName)s: %(reason)s": "%(senderName)s kicked %(targetName)s: %(reason)s",
|
||||||
|
"%(senderName)s kicked %(targetName)s": "%(senderName)s kicked %(targetName)s",
|
||||||
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s changed the topic to \"%(topic)s\".",
|
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s changed the topic to \"%(topic)s\".",
|
||||||
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s removed the room name.",
|
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s removed the room name.",
|
||||||
"%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.",
|
"%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.",
|
||||||
|
@ -558,6 +562,7 @@
|
||||||
"%(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).",
|
||||||
"%(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",
|
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s",
|
||||||
|
"%(senderName)s changed the <a>pinned messages</a> for the room.": "%(senderName)s changed the <a>pinned messages</a> 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.": "%(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",
|
||||||
|
@ -1410,6 +1415,7 @@
|
||||||
"Failed to unban": "Failed to unban",
|
"Failed to unban": "Failed to unban",
|
||||||
"Unban": "Unban",
|
"Unban": "Unban",
|
||||||
"Banned by %(displayName)s": "Banned by %(displayName)s",
|
"Banned by %(displayName)s": "Banned by %(displayName)s",
|
||||||
|
"Reason": "Reason",
|
||||||
"Error changing power level requirement": "Error changing power level requirement",
|
"Error changing power level requirement": "Error changing power level requirement",
|
||||||
"An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.": "An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.",
|
"An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.": "An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.",
|
||||||
"Error changing power level": "Error changing power level",
|
"Error changing power level": "Error changing power level",
|
||||||
|
@ -1576,8 +1582,6 @@
|
||||||
"Search": "Search",
|
"Search": "Search",
|
||||||
"Voice call": "Voice call",
|
"Voice call": "Voice call",
|
||||||
"Video call": "Video call",
|
"Video call": "Video call",
|
||||||
"Start a Conversation": "Start a Conversation",
|
|
||||||
"Open dial pad": "Open dial pad",
|
|
||||||
"Invites": "Invites",
|
"Invites": "Invites",
|
||||||
"Favourites": "Favourites",
|
"Favourites": "Favourites",
|
||||||
"People": "People",
|
"People": "People",
|
||||||
|
@ -2277,7 +2281,6 @@
|
||||||
"Confirm to continue": "Confirm to continue",
|
"Confirm to continue": "Confirm to continue",
|
||||||
"Click the button below to confirm your identity.": "Click the button below to confirm your identity.",
|
"Click the button below to confirm your identity.": "Click the button below to confirm your identity.",
|
||||||
"Invite by email": "Invite by email",
|
"Invite by email": "Invite by email",
|
||||||
"Failed to invite the following users to chat: %(csvUsers)s": "Failed to invite the following users to chat: %(csvUsers)s",
|
|
||||||
"We couldn't create your DM.": "We couldn't create your DM.",
|
"We couldn't create your DM.": "We couldn't create your DM.",
|
||||||
"Something went wrong trying to invite the users.": "Something went wrong trying to invite the users.",
|
"Something went wrong trying to invite the users.": "Something went wrong trying to invite the users.",
|
||||||
"We couldn't invite those users. Please check the users you want to invite and try again.": "We couldn't invite those users. Please check the users you want to invite and try again.",
|
"We couldn't invite those users. Please check the users you want to invite and try again.": "We couldn't invite those users. Please check the users you want to invite and try again.",
|
||||||
|
@ -2657,6 +2660,7 @@
|
||||||
"Explore Public Rooms": "Explore Public Rooms",
|
"Explore Public Rooms": "Explore Public Rooms",
|
||||||
"Create a Group Chat": "Create a Group Chat",
|
"Create a Group Chat": "Create a Group Chat",
|
||||||
"Upgrade to %(hostSignupBrand)s": "Upgrade to %(hostSignupBrand)s",
|
"Upgrade to %(hostSignupBrand)s": "Upgrade to %(hostSignupBrand)s",
|
||||||
|
"Open dial pad": "Open dial pad",
|
||||||
"Failed to reject invitation": "Failed to reject invitation",
|
"Failed to reject invitation": "Failed to reject invitation",
|
||||||
"Cannot create rooms in this community": "Cannot create rooms in this community",
|
"Cannot create rooms in this community": "Cannot create rooms in this community",
|
||||||
"You do not have permission to create rooms in this community.": "You do not have permission to create rooms in this community.",
|
"You do not have permission to create rooms in this community.": "You do not have permission to create rooms in this community.",
|
||||||
|
@ -2763,6 +2767,7 @@
|
||||||
"<inviter/> invites you": "<inviter/> invites you",
|
"<inviter/> invites you": "<inviter/> invites you",
|
||||||
"To view %(spaceName)s, turn on the <a>Spaces beta</a>": "To view %(spaceName)s, turn on the <a>Spaces beta</a>",
|
"To view %(spaceName)s, turn on the <a>Spaces beta</a>": "To view %(spaceName)s, turn on the <a>Spaces beta</a>",
|
||||||
"To join %(spaceName)s, turn on the <a>Spaces beta</a>": "To join %(spaceName)s, turn on the <a>Spaces beta</a>",
|
"To join %(spaceName)s, turn on the <a>Spaces beta</a>": "To join %(spaceName)s, turn on the <a>Spaces beta</a>",
|
||||||
|
"To view %(spaceName)s, you need an invite": "To view %(spaceName)s, you need an invite",
|
||||||
"Welcome to <name/>": "Welcome to <name/>",
|
"Welcome to <name/>": "Welcome to <name/>",
|
||||||
"Random": "Random",
|
"Random": "Random",
|
||||||
"Support": "Support",
|
"Support": "Support",
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
// The following interfaces take their names and member names from seshat and the spec
|
// The following interfaces take their names and member names from seshat and the spec
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
|
|
||||||
export interface MatrixEvent {
|
export interface IMatrixEvent {
|
||||||
type: string;
|
type: string;
|
||||||
sender: string;
|
sender: string;
|
||||||
content: {};
|
content: {};
|
||||||
|
@ -27,37 +27,37 @@ export interface MatrixEvent {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MatrixProfile {
|
export interface IMatrixProfile {
|
||||||
avatar_url: string;
|
avatar_url: string;
|
||||||
displayname: string;
|
displayname: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CrawlerCheckpoint {
|
export interface ICrawlerCheckpoint {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
token: string;
|
token: string;
|
||||||
fullCrawl?: boolean;
|
fullCrawl?: boolean;
|
||||||
direction: string;
|
direction: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResultContext {
|
export interface IResultContext {
|
||||||
events_before: [MatrixEvent];
|
events_before: [IMatrixEvent];
|
||||||
events_after: [MatrixEvent];
|
events_after: [IMatrixEvent];
|
||||||
profile_info: Map<string, MatrixProfile>;
|
profile_info: Map<string, IMatrixProfile>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResultsElement {
|
export interface IResultsElement {
|
||||||
rank: number;
|
rank: number;
|
||||||
result: MatrixEvent;
|
result: IMatrixEvent;
|
||||||
context: ResultContext;
|
context: IResultContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchResult {
|
export interface ISearchResult {
|
||||||
count: number;
|
count: number;
|
||||||
results: [ResultsElement];
|
results: [IResultsElement];
|
||||||
highlights: [string];
|
highlights: [string];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchArgs {
|
export interface ISearchArgs {
|
||||||
search_term: string;
|
search_term: string;
|
||||||
before_limit: number;
|
before_limit: number;
|
||||||
after_limit: number;
|
after_limit: number;
|
||||||
|
@ -65,19 +65,19 @@ export interface SearchArgs {
|
||||||
room_id?: string;
|
room_id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventAndProfile {
|
export interface IEventAndProfile {
|
||||||
event: MatrixEvent;
|
event: IMatrixEvent;
|
||||||
profile: MatrixProfile;
|
profile: IMatrixProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoadArgs {
|
export interface ILoadArgs {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
limit: number;
|
limit: number;
|
||||||
fromEvent?: string;
|
fromEvent?: string;
|
||||||
direction?: string;
|
direction?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IndexStats {
|
export interface IIndexStats {
|
||||||
size: number;
|
size: number;
|
||||||
eventCount: number;
|
eventCount: number;
|
||||||
roomCount: number;
|
roomCount: number;
|
||||||
|
@ -119,13 +119,13 @@ export default abstract class BaseEventIndexManager {
|
||||||
* Queue up an event to be added to the index.
|
* Queue up an event to be added to the index.
|
||||||
*
|
*
|
||||||
* @param {MatrixEvent} ev The event that should be added to the index.
|
* @param {MatrixEvent} ev The event that should be added to the index.
|
||||||
* @param {MatrixProfile} profile The profile of the event sender at the
|
* @param {IMatrixProfile} profile The profile of the event sender at the
|
||||||
* time of the event receival.
|
* time of the event receival.
|
||||||
*
|
*
|
||||||
* @return {Promise} A promise that will resolve when the was queued up for
|
* @return {Promise} A promise that will resolve when the was queued up for
|
||||||
* addition.
|
* addition.
|
||||||
*/
|
*/
|
||||||
async addEventToIndex(ev: MatrixEvent, profile: MatrixProfile): Promise<void> {
|
async addEventToIndex(ev: IMatrixEvent, profile: IMatrixProfile): Promise<void> {
|
||||||
throw new Error("Unimplemented");
|
throw new Error("Unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,10 +160,10 @@ export default abstract class BaseEventIndexManager {
|
||||||
/**
|
/**
|
||||||
* Get statistical information of the index.
|
* Get statistical information of the index.
|
||||||
*
|
*
|
||||||
* @return {Promise<IndexStats>} A promise that will resolve to the index
|
* @return {Promise<IIndexStats>} A promise that will resolve to the index
|
||||||
* statistics.
|
* statistics.
|
||||||
*/
|
*/
|
||||||
async getStats(): Promise<IndexStats> {
|
async getStats(): Promise<IIndexStats> {
|
||||||
throw new Error("Unimplemented");
|
throw new Error("Unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,13 +203,13 @@ export default abstract class BaseEventIndexManager {
|
||||||
/**
|
/**
|
||||||
* Search the event index using the given term for matching events.
|
* Search the event index using the given term for matching events.
|
||||||
*
|
*
|
||||||
* @param {SearchArgs} searchArgs The search configuration for the search,
|
* @param {ISearchArgs} searchArgs The search configuration for the search,
|
||||||
* sets the search term and determines the search result contents.
|
* sets the search term and determines the search result contents.
|
||||||
*
|
*
|
||||||
* @return {Promise<[SearchResult]>} A promise that will resolve to an array
|
* @return {Promise<[ISearchResult]>} A promise that will resolve to an array
|
||||||
* of search results once the search is done.
|
* of search results once the search is done.
|
||||||
*/
|
*/
|
||||||
async searchEventIndex(searchArgs: SearchArgs): Promise<SearchResult> {
|
async searchEventIndex(searchArgs: ISearchArgs): Promise<ISearchResult> {
|
||||||
throw new Error("Unimplemented");
|
throw new Error("Unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,12 +218,12 @@ export default abstract class BaseEventIndexManager {
|
||||||
*
|
*
|
||||||
* This is used to add a batch of events to the index.
|
* This is used to add a batch of events to the index.
|
||||||
*
|
*
|
||||||
* @param {[EventAndProfile]} events The list of events and profiles that
|
* @param {[IEventAndProfile]} events The list of events and profiles that
|
||||||
* should be added to the event index.
|
* should be added to the event index.
|
||||||
* @param {[CrawlerCheckpoint]} checkpoint A new crawler checkpoint that
|
* @param {[ICrawlerCheckpoint]} checkpoint A new crawler checkpoint that
|
||||||
* should be stored in the index which should be used to continue crawling
|
* should be stored in the index which should be used to continue crawling
|
||||||
* the room.
|
* the room.
|
||||||
* @param {[CrawlerCheckpoint]} oldCheckpoint The checkpoint that was used
|
* @param {[ICrawlerCheckpoint]} oldCheckpoint The checkpoint that was used
|
||||||
* to fetch the current batch of events. This checkpoint will be removed
|
* to fetch the current batch of events. This checkpoint will be removed
|
||||||
* from the index.
|
* from the index.
|
||||||
*
|
*
|
||||||
|
@ -231,9 +231,9 @@ export default abstract class BaseEventIndexManager {
|
||||||
* were already added to the index, false otherwise.
|
* were already added to the index, false otherwise.
|
||||||
*/
|
*/
|
||||||
async addHistoricEvents(
|
async addHistoricEvents(
|
||||||
events: [EventAndProfile],
|
events: IEventAndProfile[],
|
||||||
checkpoint: CrawlerCheckpoint | null,
|
checkpoint: ICrawlerCheckpoint | null,
|
||||||
oldCheckpoint: CrawlerCheckpoint | null,
|
oldCheckpoint: ICrawlerCheckpoint | null,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
throw new Error("Unimplemented");
|
throw new Error("Unimplemented");
|
||||||
}
|
}
|
||||||
|
@ -241,36 +241,36 @@ export default abstract class BaseEventIndexManager {
|
||||||
/**
|
/**
|
||||||
* Add a new crawler checkpoint to the index.
|
* Add a new crawler checkpoint to the index.
|
||||||
*
|
*
|
||||||
* @param {CrawlerCheckpoint} checkpoint The checkpoint that should be added
|
* @param {ICrawlerCheckpoint} checkpoint The checkpoint that should be added
|
||||||
* to the index.
|
* to the index.
|
||||||
*
|
*
|
||||||
* @return {Promise} A promise that will resolve once the checkpoint has
|
* @return {Promise} A promise that will resolve once the checkpoint has
|
||||||
* been stored.
|
* been stored.
|
||||||
*/
|
*/
|
||||||
async addCrawlerCheckpoint(checkpoint: CrawlerCheckpoint): Promise<void> {
|
async addCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise<void> {
|
||||||
throw new Error("Unimplemented");
|
throw new Error("Unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new crawler checkpoint to the index.
|
* Add a new crawler checkpoint to the index.
|
||||||
*
|
*
|
||||||
* @param {CrawlerCheckpoint} checkpoint The checkpoint that should be
|
* @param {ICrawlerCheckpoint} checkpoint The checkpoint that should be
|
||||||
* removed from the index.
|
* removed from the index.
|
||||||
*
|
*
|
||||||
* @return {Promise} A promise that will resolve once the checkpoint has
|
* @return {Promise} A promise that will resolve once the checkpoint has
|
||||||
* been removed.
|
* been removed.
|
||||||
*/
|
*/
|
||||||
async removeCrawlerCheckpoint(checkpoint: CrawlerCheckpoint): Promise<void> {
|
async removeCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise<void> {
|
||||||
throw new Error("Unimplemented");
|
throw new Error("Unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the stored checkpoints from the index.
|
* Load the stored checkpoints from the index.
|
||||||
*
|
*
|
||||||
* @return {Promise<[CrawlerCheckpoint]>} A promise that will resolve to an
|
* @return {Promise<[ICrawlerCheckpoint]>} A promise that will resolve to an
|
||||||
* array of crawler checkpoints once they have been loaded from the index.
|
* array of crawler checkpoints once they have been loaded from the index.
|
||||||
*/
|
*/
|
||||||
async loadCheckpoints(): Promise<[CrawlerCheckpoint]> {
|
async loadCheckpoints(): Promise<ICrawlerCheckpoint[]> {
|
||||||
throw new Error("Unimplemented");
|
throw new Error("Unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,11 +286,11 @@ export default abstract class BaseEventIndexManager {
|
||||||
* @param {string} args.direction The direction to which we should continue
|
* @param {string} args.direction The direction to which we should continue
|
||||||
* loading events from. This is used only if fromEvent is used as well.
|
* loading events from. This is used only if fromEvent is used as well.
|
||||||
*
|
*
|
||||||
* @return {Promise<[EventAndProfile]>} A promise that will resolve to an
|
* @return {Promise<[IEventAndProfile]>} A promise that will resolve to an
|
||||||
* array of Matrix events that contain mxc URLs accompanied with the
|
* array of Matrix events that contain mxc URLs accompanied with the
|
||||||
* historic profile of the sender.
|
* historic profile of the sender.
|
||||||
*/
|
*/
|
||||||
async loadFileEvents(args: LoadArgs): Promise<[EventAndProfile]> {
|
async loadFileEvents(args: ILoadArgs): Promise<IEventAndProfile[]> {
|
||||||
throw new Error("Unimplemented");
|
throw new Error("Unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||||
import { sleep } from "../utils/promise";
|
import { sleep } from "../utils/promise";
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import { SettingLevel } from "../settings/SettingLevel";
|
import { SettingLevel } from "../settings/SettingLevel";
|
||||||
import {CrawlerCheckpoint, LoadArgs, SearchArgs} from "./BaseEventIndexManager";
|
import { ICrawlerCheckpoint, ILoadArgs, ISearchArgs } from "./BaseEventIndexManager";
|
||||||
|
|
||||||
// The time in ms that the crawler will wait loop iterations if there
|
// The time in ms that the crawler will wait loop iterations if there
|
||||||
// have not been any checkpoints to consume in the last iteration.
|
// have not been any checkpoints to consume in the last iteration.
|
||||||
|
@ -45,9 +45,9 @@ interface ICrawler {
|
||||||
* Event indexing class that wraps the platform specific event indexing.
|
* Event indexing class that wraps the platform specific event indexing.
|
||||||
*/
|
*/
|
||||||
export default class EventIndex extends EventEmitter {
|
export default class EventIndex extends EventEmitter {
|
||||||
private crawlerCheckpoints: CrawlerCheckpoint[] = [];
|
private crawlerCheckpoints: ICrawlerCheckpoint[] = [];
|
||||||
private crawler: ICrawler = null;
|
private crawler: ICrawler = null;
|
||||||
private currentCheckpoint: CrawlerCheckpoint = null;
|
private currentCheckpoint: ICrawlerCheckpoint = null;
|
||||||
|
|
||||||
public async init() {
|
public async init() {
|
||||||
const indexManager = PlatformPeg.get().getEventIndexingManager();
|
const indexManager = PlatformPeg.get().getEventIndexingManager();
|
||||||
|
@ -111,14 +111,14 @@ export default class EventIndex extends EventEmitter {
|
||||||
const timeline = room.getLiveTimeline();
|
const timeline = room.getLiveTimeline();
|
||||||
const token = timeline.getPaginationToken("b");
|
const token = timeline.getPaginationToken("b");
|
||||||
|
|
||||||
const backCheckpoint: CrawlerCheckpoint = {
|
const backCheckpoint: ICrawlerCheckpoint = {
|
||||||
roomId: room.roomId,
|
roomId: room.roomId,
|
||||||
token: token,
|
token: token,
|
||||||
direction: "b",
|
direction: "b",
|
||||||
fullCrawl: true,
|
fullCrawl: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const forwardCheckpoint: CrawlerCheckpoint = {
|
const forwardCheckpoint: ICrawlerCheckpoint = {
|
||||||
roomId: room.roomId,
|
roomId: room.roomId,
|
||||||
token: token,
|
token: token,
|
||||||
direction: "f",
|
direction: "f",
|
||||||
|
@ -668,13 +668,13 @@ export default class EventIndex extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* Search the event index using the given term for matching events.
|
* Search the event index using the given term for matching events.
|
||||||
*
|
*
|
||||||
* @param {SearchArgs} searchArgs The search configuration for the search,
|
* @param {ISearchArgs} searchArgs The search configuration for the search,
|
||||||
* sets the search term and determines the search result contents.
|
* sets the search term and determines the search result contents.
|
||||||
*
|
*
|
||||||
* @return {Promise<[SearchResult]>} A promise that will resolve to an array
|
* @return {Promise<[SearchResult]>} A promise that will resolve to an array
|
||||||
* of search results once the search is done.
|
* of search results once the search is done.
|
||||||
*/
|
*/
|
||||||
public async search(searchArgs: SearchArgs) {
|
public async search(searchArgs: ISearchArgs) {
|
||||||
const indexManager = PlatformPeg.get().getEventIndexingManager();
|
const indexManager = PlatformPeg.get().getEventIndexingManager();
|
||||||
return indexManager.searchEventIndex(searchArgs);
|
return indexManager.searchEventIndex(searchArgs);
|
||||||
}
|
}
|
||||||
|
@ -709,7 +709,7 @@ export default class EventIndex extends EventEmitter {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const indexManager = PlatformPeg.get().getEventIndexingManager();
|
const indexManager = PlatformPeg.get().getEventIndexingManager();
|
||||||
|
|
||||||
const loadArgs: LoadArgs = {
|
const loadArgs: ILoadArgs = {
|
||||||
roomId: room.roomId,
|
roomId: room.roomId,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
};
|
};
|
||||||
|
|
|
@ -86,8 +86,8 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true) {
|
||||||
body.append('cross_signing_key', client.getCrossSigningId());
|
body.append('cross_signing_key', client.getCrossSigningId());
|
||||||
|
|
||||||
// add cross-signing status information
|
// add cross-signing status information
|
||||||
const crossSigning = client.crypto._crossSigningInfo;
|
const crossSigning = client.crypto.crossSigningInfo;
|
||||||
const secretStorage = client.crypto._secretStorage;
|
const secretStorage = client.crypto.secretStorage;
|
||||||
|
|
||||||
body.append("cross_signing_ready", String(await client.isCrossSigningReady()));
|
body.append("cross_signing_ready", String(await client.isCrossSigningReady()));
|
||||||
body.append("cross_signing_supported_by_hs",
|
body.append("cross_signing_supported_by_hs",
|
||||||
|
|
|
@ -161,6 +161,7 @@ export default class RightPanelStore extends Store<ActionPayload> {
|
||||||
case Action.SetRightPanelPhase: {
|
case Action.SetRightPanelPhase: {
|
||||||
let targetPhase = payload.phase;
|
let targetPhase = payload.phase;
|
||||||
let refireParams = payload.refireParams;
|
let refireParams = payload.refireParams;
|
||||||
|
const allowClose = payload.allowClose ?? true;
|
||||||
// redirect to EncryptionPanel if there is an ongoing verification request
|
// redirect to EncryptionPanel if there is an ongoing verification request
|
||||||
if (targetPhase === RightPanelPhases.RoomMemberInfo && payload.refireParams) {
|
if (targetPhase === RightPanelPhases.RoomMemberInfo && payload.refireParams) {
|
||||||
const {member} = payload.refireParams;
|
const {member} = payload.refireParams;
|
||||||
|
@ -192,7 +193,7 @@ export default class RightPanelStore extends Store<ActionPayload> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (targetPhase === this.state.lastRoomPhase && !refireParams) {
|
if (targetPhase === this.state.lastRoomPhase && !refireParams && allowClose) {
|
||||||
this.setState({
|
this.setState({
|
||||||
showRoomPanel: !this.state.showRoomPanel,
|
showRoomPanel: !this.state.showRoomPanel,
|
||||||
previousPhase: null,
|
previousPhase: null,
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { diff_match_patch as DiffMatchPatch } from 'diff-match-patch';
|
import { diff_match_patch as DiffMatchPatch } from 'diff-match-patch';
|
||||||
import { Action, DiffDOM, IDiff } from "diff-dom";
|
import { DiffDOM, IDiff } from "diff-dom";
|
||||||
import { IContent } from "matrix-js-sdk/src/models/event";
|
import { IContent } from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
import { bodyToHtml, checkBlockNode, IOptsReturnString } from "../HtmlUtils";
|
import { bodyToHtml, checkBlockNode, IOptsReturnString } from "../HtmlUtils";
|
||||||
|
@ -149,7 +149,7 @@ function stringAsTextNode(string: string): Text {
|
||||||
function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatch: DiffMatchPatch): void {
|
function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatch: DiffMatchPatch): void {
|
||||||
const {refNode, refParentNode} = findRefNodes(originalRootNode, diff.route);
|
const {refNode, refParentNode} = findRefNodes(originalRootNode, diff.route);
|
||||||
switch (diff.action) {
|
switch (diff.action) {
|
||||||
case Action.ReplaceElement: {
|
case "replaceElement": {
|
||||||
const container = document.createElement("span");
|
const container = document.createElement("span");
|
||||||
const delNode = wrapDeletion(diffTreeToDOM(diff.oldValue));
|
const delNode = wrapDeletion(diffTreeToDOM(diff.oldValue));
|
||||||
const insNode = wrapInsertion(diffTreeToDOM(diff.newValue));
|
const insNode = wrapInsertion(diffTreeToDOM(diff.newValue));
|
||||||
|
@ -158,17 +158,17 @@ function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatc
|
||||||
refNode.parentNode.replaceChild(container, refNode);
|
refNode.parentNode.replaceChild(container, refNode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Action.RemoveTextElement: {
|
case "removeTextElement": {
|
||||||
const delNode = wrapDeletion(stringAsTextNode(diff.value));
|
const delNode = wrapDeletion(stringAsTextNode(diff.value));
|
||||||
refNode.parentNode.replaceChild(delNode, refNode);
|
refNode.parentNode.replaceChild(delNode, refNode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Action.RemoveElement: {
|
case "removeElement": {
|
||||||
const delNode = wrapDeletion(diffTreeToDOM(diff.element));
|
const delNode = wrapDeletion(diffTreeToDOM(diff.element));
|
||||||
refNode.parentNode.replaceChild(delNode, refNode);
|
refNode.parentNode.replaceChild(delNode, refNode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Action.ModifyTextElement: {
|
case "modifyTextElement": {
|
||||||
const textDiffs = diffMathPatch.diff_main(diff.oldValue, diff.newValue);
|
const textDiffs = diffMathPatch.diff_main(diff.oldValue, diff.newValue);
|
||||||
diffMathPatch.diff_cleanupSemantic(textDiffs);
|
diffMathPatch.diff_cleanupSemantic(textDiffs);
|
||||||
const container = document.createElement("span");
|
const container = document.createElement("span");
|
||||||
|
@ -184,12 +184,12 @@ function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatc
|
||||||
refNode.parentNode.replaceChild(container, refNode);
|
refNode.parentNode.replaceChild(container, refNode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Action.AddElement: {
|
case "addElement": {
|
||||||
const insNode = wrapInsertion(diffTreeToDOM(diff.element));
|
const insNode = wrapInsertion(diffTreeToDOM(diff.element));
|
||||||
insertBefore(refParentNode, refNode, insNode);
|
insertBefore(refParentNode, refNode, insNode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Action.AddTextElement: {
|
case "addTextElement": {
|
||||||
// XXX: sometimes diffDOM says insert a newline when there shouldn't be one
|
// XXX: sometimes diffDOM says insert a newline when there shouldn't be one
|
||||||
// but we must insert the node anyway so that we don't break the route child IDs.
|
// but we must insert the node anyway so that we don't break the route child IDs.
|
||||||
// See https://github.com/fiduswriter/diffDOM/issues/100
|
// See https://github.com/fiduswriter/diffDOM/issues/100
|
||||||
|
@ -199,9 +199,9 @@ function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatc
|
||||||
}
|
}
|
||||||
// e.g. when changing a the href of a link,
|
// e.g. when changing a the href of a link,
|
||||||
// show the link with old href as removed and with the new href as added
|
// show the link with old href as removed and with the new href as added
|
||||||
case Action.RemoveAttribute:
|
case "removeAttribute":
|
||||||
case Action.AddAttribute:
|
case "addAttribute":
|
||||||
case Action.ModifyAttribute: {
|
case "modifyAttribute": {
|
||||||
const delNode = wrapDeletion(refNode.cloneNode(true));
|
const delNode = wrapDeletion(refNode.cloneNode(true));
|
||||||
const updatedNode = refNode.cloneNode(true) as HTMLElement;
|
const updatedNode = refNode.cloneNode(true) as HTMLElement;
|
||||||
if (diff.action === "addAttribute" || diff.action === "modifyAttribute") {
|
if (diff.action === "addAttribute" || diff.action === "modifyAttribute") {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,23 +14,51 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {MatrixClientPeg} from '../MatrixClientPeg';
|
import { MatrixError } from "matrix-js-sdk/src/http-api";
|
||||||
import {getAddressType} from '../UserAddress';
|
|
||||||
|
import { MatrixClientPeg } from '../MatrixClientPeg';
|
||||||
|
import { AddressType, getAddressType } from '../UserAddress';
|
||||||
import GroupStore from '../stores/GroupStore';
|
import GroupStore from '../stores/GroupStore';
|
||||||
import {_t} from "../languageHandler";
|
import { _t } from "../languageHandler";
|
||||||
import * as sdk from "../index";
|
|
||||||
import Modal from "../Modal";
|
import Modal from "../Modal";
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import {defer} from "./promise";
|
import { defer, IDeferred } from "./promise";
|
||||||
|
import AskInviteAnywayDialog from "../components/views/dialogs/AskInviteAnywayDialog";
|
||||||
|
|
||||||
|
export enum InviteState {
|
||||||
|
Invited = "invited",
|
||||||
|
Error = "error",
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IError {
|
||||||
|
errorText: string;
|
||||||
|
errcode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UNKNOWN_PROFILE_ERRORS = ['M_NOT_FOUND', 'M_USER_NOT_FOUND', 'M_PROFILE_UNDISCLOSED', 'M_PROFILE_NOT_FOUND'];
|
||||||
|
|
||||||
|
export type CompletionStates = Record<string, InviteState>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invites multiple addresses to a room or group, handling rate limiting from the server
|
* Invites multiple addresses to a room or group, handling rate limiting from the server
|
||||||
*/
|
*/
|
||||||
export default class MultiInviter {
|
export default class MultiInviter {
|
||||||
|
private readonly roomId?: string;
|
||||||
|
private readonly groupId?: string;
|
||||||
|
|
||||||
|
private canceled = false;
|
||||||
|
private addresses: string[] = [];
|
||||||
|
private busy = false;
|
||||||
|
private _fatal = false;
|
||||||
|
private completionStates: CompletionStates = {}; // State of each address (invited or error)
|
||||||
|
private errors: Record<string, IError> = {}; // { address: {errorText, errcode} }
|
||||||
|
private deferred: IDeferred<CompletionStates> = null;
|
||||||
|
private reason: string = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} targetId The ID of the room or group to invite to
|
* @param {string} targetId The ID of the room or group to invite to
|
||||||
*/
|
*/
|
||||||
constructor(targetId) {
|
constructor(targetId: string) {
|
||||||
if (targetId[0] === '+') {
|
if (targetId[0] === '+') {
|
||||||
this.roomId = null;
|
this.roomId = null;
|
||||||
this.groupId = targetId;
|
this.groupId = targetId;
|
||||||
|
@ -39,41 +66,38 @@ export default class MultiInviter {
|
||||||
this.roomId = targetId;
|
this.roomId = targetId;
|
||||||
this.groupId = null;
|
this.groupId = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.canceled = false;
|
public get fatal() {
|
||||||
this.addrs = [];
|
return this._fatal;
|
||||||
this.busy = false;
|
|
||||||
this.completionStates = {}; // State of each address (invited or error)
|
|
||||||
this.errors = {}; // { address: {errorText, errcode} }
|
|
||||||
this.deferred = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invite users to this room. This may only be called once per
|
* Invite users to this room. This may only be called once per
|
||||||
* instance of the class.
|
* instance of the class.
|
||||||
*
|
*
|
||||||
* @param {array} addrs Array of addresses to invite
|
* @param {array} addresses Array of addresses to invite
|
||||||
* @param {string} reason Reason for inviting (optional)
|
* @param {string} reason Reason for inviting (optional)
|
||||||
* @returns {Promise} Resolved when all invitations in the queue are complete
|
* @returns {Promise} Resolved when all invitations in the queue are complete
|
||||||
*/
|
*/
|
||||||
invite(addrs, reason) {
|
public invite(addresses, reason?: string): Promise<CompletionStates> {
|
||||||
if (this.addrs.length > 0) {
|
if (this.addresses.length > 0) {
|
||||||
throw new Error("Already inviting/invited");
|
throw new Error("Already inviting/invited");
|
||||||
}
|
}
|
||||||
this.addrs.push(...addrs);
|
this.addresses.push(...addresses);
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
|
|
||||||
for (const addr of this.addrs) {
|
for (const addr of this.addresses) {
|
||||||
if (getAddressType(addr) === null) {
|
if (getAddressType(addr) === null) {
|
||||||
this.completionStates[addr] = 'error';
|
this.completionStates[addr] = InviteState.Error;
|
||||||
this.errors[addr] = {
|
this.errors[addr] = {
|
||||||
errcode: 'M_INVALID',
|
errcode: 'M_INVALID',
|
||||||
errorText: _t('Unrecognised address'),
|
errorText: _t('Unrecognised address'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.deferred = defer();
|
this.deferred = defer<CompletionStates>();
|
||||||
this._inviteMore(0);
|
this.inviteMore(0);
|
||||||
|
|
||||||
return this.deferred.promise;
|
return this.deferred.promise;
|
||||||
}
|
}
|
||||||
|
@ -81,33 +105,36 @@ export default class MultiInviter {
|
||||||
/**
|
/**
|
||||||
* Stops inviting. Causes promises returned by invite() to be rejected.
|
* Stops inviting. Causes promises returned by invite() to be rejected.
|
||||||
*/
|
*/
|
||||||
cancel() {
|
public cancel(): void {
|
||||||
if (!this.busy) return;
|
if (!this.busy) return;
|
||||||
|
|
||||||
this._canceled = true;
|
this.canceled = true;
|
||||||
this.deferred.reject(new Error('canceled'));
|
this.deferred.reject(new Error('canceled'));
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompletionState(addr) {
|
public getCompletionState(addr: string): InviteState {
|
||||||
return this.completionStates[addr];
|
return this.completionStates[addr];
|
||||||
}
|
}
|
||||||
|
|
||||||
getErrorText(addr) {
|
public getErrorText(addr: string): string {
|
||||||
return this.errors[addr] ? this.errors[addr].errorText : null;
|
return this.errors[addr] ? this.errors[addr].errorText : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _inviteToRoom(roomId, addr, ignoreProfile) {
|
private async inviteToRoom(roomId: string, addr: string, ignoreProfile = false): Promise<{}> {
|
||||||
const addrType = getAddressType(addr);
|
const addrType = getAddressType(addr);
|
||||||
|
|
||||||
if (addrType === 'email') {
|
if (addrType === AddressType.Email) {
|
||||||
return MatrixClientPeg.get().inviteByEmail(roomId, addr);
|
return MatrixClientPeg.get().inviteByEmail(roomId, addr);
|
||||||
} else if (addrType === 'mx-user-id') {
|
} else if (addrType === AddressType.MatrixUserId) {
|
||||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
if (!room) throw new Error("Room not found");
|
if (!room) throw new Error("Room not found");
|
||||||
|
|
||||||
const member = room.getMember(addr);
|
const member = room.getMember(addr);
|
||||||
if (member && ['join', 'invite'].includes(member.membership)) {
|
if (member && ['join', 'invite'].includes(member.membership)) {
|
||||||
throw {errcode: "RIOT.ALREADY_IN_ROOM", error: "Member already invited"};
|
throw new new MatrixError({
|
||||||
|
errcode: "RIOT.ALREADY_IN_ROOM",
|
||||||
|
error: "Member already invited",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignoreProfile && SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) {
|
if (!ignoreProfile && SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) {
|
||||||
|
@ -124,28 +151,28 @@ export default class MultiInviter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_doInvite(address, ignoreProfile) {
|
private doInvite(address: string, ignoreProfile = false): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
console.log(`Inviting ${address}`);
|
console.log(`Inviting ${address}`);
|
||||||
|
|
||||||
let doInvite;
|
let doInvite;
|
||||||
if (this.groupId !== null) {
|
if (this.groupId !== null) {
|
||||||
doInvite = GroupStore.inviteUserToGroup(this.groupId, address);
|
doInvite = GroupStore.inviteUserToGroup(this.groupId, address);
|
||||||
} else {
|
} else {
|
||||||
doInvite = this._inviteToRoom(this.roomId, address, ignoreProfile);
|
doInvite = this.inviteToRoom(this.roomId, address, ignoreProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
doInvite.then(() => {
|
doInvite.then(() => {
|
||||||
if (this._canceled) {
|
if (this.canceled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.completionStates[address] = 'invited';
|
this.completionStates[address] = InviteState.Invited;
|
||||||
delete this.errors[address];
|
delete this.errors[address];
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
if (this._canceled) {
|
if (this.canceled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +188,7 @@ export default class MultiInviter {
|
||||||
} else if (err.errcode === 'M_LIMIT_EXCEEDED') {
|
} else if (err.errcode === 'M_LIMIT_EXCEEDED') {
|
||||||
// we're being throttled so wait a bit & try again
|
// we're being throttled so wait a bit & try again
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this._doInvite(address, ignoreProfile).then(resolve, reject);
|
this.doInvite(address, ignoreProfile).then(resolve, reject);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
return;
|
return;
|
||||||
} else if (['M_NOT_FOUND', 'M_USER_NOT_FOUND'].includes(err.errcode)) {
|
} else if (['M_NOT_FOUND', 'M_USER_NOT_FOUND'].includes(err.errcode)) {
|
||||||
|
@ -171,7 +198,7 @@ export default class MultiInviter {
|
||||||
} else if (err.errcode === 'M_PROFILE_NOT_FOUND' && !ignoreProfile) {
|
} else if (err.errcode === 'M_PROFILE_NOT_FOUND' && !ignoreProfile) {
|
||||||
// Invite without the profile check
|
// Invite without the profile check
|
||||||
console.warn(`User ${address} does not have a profile - inviting anyways automatically`);
|
console.warn(`User ${address} does not have a profile - inviting anyways automatically`);
|
||||||
this._doInvite(address, true).then(resolve, reject);
|
this.doInvite(address, true).then(resolve, reject);
|
||||||
} else if (err.errcode === "M_BAD_STATE") {
|
} else if (err.errcode === "M_BAD_STATE") {
|
||||||
errorText = _t("The user must be unbanned before they can be invited.");
|
errorText = _t("The user must be unbanned before they can be invited.");
|
||||||
} else if (err.errcode === "M_UNSUPPORTED_ROOM_VERSION") {
|
} else if (err.errcode === "M_UNSUPPORTED_ROOM_VERSION") {
|
||||||
|
@ -180,14 +207,14 @@ export default class MultiInviter {
|
||||||
errorText = _t('Unknown server error');
|
errorText = _t('Unknown server error');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.completionStates[address] = 'error';
|
this.completionStates[address] = InviteState.Error;
|
||||||
this.errors[address] = {errorText, errcode: err.errcode};
|
this.errors[address] = { errorText, errcode: err.errcode };
|
||||||
|
|
||||||
this.busy = !fatal;
|
this.busy = !fatal;
|
||||||
this.fatal = fatal;
|
this._fatal = fatal;
|
||||||
|
|
||||||
if (fatal) {
|
if (fatal) {
|
||||||
reject();
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
|
@ -195,22 +222,22 @@ export default class MultiInviter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_inviteMore(nextIndex, ignoreProfile) {
|
private inviteMore(nextIndex: number, ignoreProfile = false): void {
|
||||||
if (this._canceled) {
|
if (this.canceled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextIndex === this.addrs.length) {
|
if (nextIndex === this.addresses.length) {
|
||||||
this.busy = false;
|
this.busy = false;
|
||||||
if (Object.keys(this.errors).length > 0 && !this.groupId) {
|
if (Object.keys(this.errors).length > 0 && !this.groupId) {
|
||||||
// There were problems inviting some people - see if we can invite them
|
// There were problems inviting some people - see if we can invite them
|
||||||
// without caring if they exist or not.
|
// without caring if they exist or not.
|
||||||
const unknownProfileErrors = ['M_NOT_FOUND', 'M_USER_NOT_FOUND', 'M_PROFILE_UNDISCLOSED', 'M_PROFILE_NOT_FOUND'];
|
const unknownProfileUsers = Object.keys(this.errors)
|
||||||
const unknownProfileUsers = Object.keys(this.errors).filter(a => unknownProfileErrors.includes(this.errors[a].errcode));
|
.filter(a => UNKNOWN_PROFILE_ERRORS.includes(this.errors[a].errcode));
|
||||||
|
|
||||||
if (unknownProfileUsers.length > 0) {
|
if (unknownProfileUsers.length > 0) {
|
||||||
const inviteUnknowns = () => {
|
const inviteUnknowns = () => {
|
||||||
const promises = unknownProfileUsers.map(u => this._doInvite(u, true));
|
const promises = unknownProfileUsers.map(u => this.doInvite(u, true));
|
||||||
Promise.all(promises).then(() => this.deferred.resolve(this.completionStates));
|
Promise.all(promises).then(() => this.deferred.resolve(this.completionStates));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -219,15 +246,17 @@ export default class MultiInviter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AskInviteAnywayDialog = sdk.getComponent("dialogs.AskInviteAnywayDialog");
|
|
||||||
console.log("Showing failed to invite dialog...");
|
console.log("Showing failed to invite dialog...");
|
||||||
Modal.createTrackedDialog('Failed to invite', '', AskInviteAnywayDialog, {
|
Modal.createTrackedDialog('Failed to invite', '', AskInviteAnywayDialog, {
|
||||||
unknownProfileUsers: unknownProfileUsers.map(u => {return {userId: u, errorText: this.errors[u].errorText};}),
|
unknownProfileUsers: unknownProfileUsers.map(u => ({
|
||||||
|
userId: u,
|
||||||
|
errorText: this.errors[u].errorText,
|
||||||
|
})),
|
||||||
onInviteAnyways: () => inviteUnknowns(),
|
onInviteAnyways: () => inviteUnknowns(),
|
||||||
onGiveUp: () => {
|
onGiveUp: () => {
|
||||||
// Fake all the completion states because we already warned the user
|
// Fake all the completion states because we already warned the user
|
||||||
for (const addr of unknownProfileUsers) {
|
for (const addr of unknownProfileUsers) {
|
||||||
this.completionStates[addr] = 'invited';
|
this.completionStates[addr] = InviteState.Invited;
|
||||||
}
|
}
|
||||||
this.deferred.resolve(this.completionStates);
|
this.deferred.resolve(this.completionStates);
|
||||||
},
|
},
|
||||||
|
@ -239,25 +268,25 @@ export default class MultiInviter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addr = this.addrs[nextIndex];
|
const addr = this.addresses[nextIndex];
|
||||||
|
|
||||||
// don't try to invite it if it's an invalid address
|
// don't try to invite it if it's an invalid address
|
||||||
// (it will already be marked as an error though,
|
// (it will already be marked as an error though,
|
||||||
// so no need to do so again)
|
// so no need to do so again)
|
||||||
if (getAddressType(addr) === null) {
|
if (getAddressType(addr) === null) {
|
||||||
this._inviteMore(nextIndex + 1);
|
this.inviteMore(nextIndex + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't re-invite (there's no way in the UI to do this, but
|
// don't re-invite (there's no way in the UI to do this, but
|
||||||
// for sanity's sake)
|
// for sanity's sake)
|
||||||
if (this.completionStates[addr] === 'invited') {
|
if (this.completionStates[addr] === InviteState.Invited) {
|
||||||
this._inviteMore(nextIndex + 1);
|
this.inviteMore(nextIndex + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._doInvite(addr, ignoreProfile).then(() => {
|
this.doInvite(addr, ignoreProfile).then(() => {
|
||||||
this._inviteMore(nextIndex + 1, ignoreProfile);
|
this.inviteMore(nextIndex + 1, ignoreProfile);
|
||||||
}).catch(() => this.deferred.resolve(this.completionStates));
|
}).catch(() => this.deferred.resolve(this.completionStates));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,6 +27,8 @@ import {PayloadEvent, WORKLET_NAME} from "./consts";
|
||||||
import {UPDATE_EVENT} from "../stores/AsyncStore";
|
import {UPDATE_EVENT} from "../stores/AsyncStore";
|
||||||
import {Playback} from "./Playback";
|
import {Playback} from "./Playback";
|
||||||
import {createAudioContext} from "./compat";
|
import {createAudioContext} from "./compat";
|
||||||
|
import { IEncryptedFile } from "matrix-js-sdk/src/@types/event";
|
||||||
|
import { uploadFile } from "../ContentMessages";
|
||||||
|
|
||||||
const CHANNELS = 1; // stereo isn't important
|
const CHANNELS = 1; // stereo isn't important
|
||||||
export const SAMPLE_RATE = 48000; // 48khz is what WebRTC uses. 12khz is where we lose quality.
|
export const SAMPLE_RATE = 48000; // 48khz is what WebRTC uses. 12khz is where we lose quality.
|
||||||
|
@ -49,6 +51,11 @@ export enum RecordingState {
|
||||||
Uploaded = "uploaded",
|
Uploaded = "uploaded",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IUpload {
|
||||||
|
mxc?: string; // for unencrypted uploads
|
||||||
|
encrypted?: IEncryptedFile;
|
||||||
|
}
|
||||||
|
|
||||||
export class VoiceRecording extends EventEmitter implements IDestroyable {
|
export class VoiceRecording extends EventEmitter implements IDestroyable {
|
||||||
private recorder: Recorder;
|
private recorder: Recorder;
|
||||||
private recorderContext: AudioContext;
|
private recorderContext: AudioContext;
|
||||||
|
@ -58,7 +65,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
||||||
private recorderWorklet: AudioWorkletNode;
|
private recorderWorklet: AudioWorkletNode;
|
||||||
private recorderProcessor: ScriptProcessorNode;
|
private recorderProcessor: ScriptProcessorNode;
|
||||||
private buffer = new Uint8Array(0); // use this.audioBuffer to access
|
private buffer = new Uint8Array(0); // use this.audioBuffer to access
|
||||||
private mxc: string;
|
private lastUpload: IUpload;
|
||||||
private recording = false;
|
private recording = false;
|
||||||
private observable: SimpleObservable<IRecordingUpdate>;
|
private observable: SimpleObservable<IRecordingUpdate>;
|
||||||
private amplitudes: number[] = []; // at each second mark, generated
|
private amplitudes: number[] = []; // at each second mark, generated
|
||||||
|
@ -214,13 +221,6 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
||||||
return this.buffer.length > 0;
|
return this.buffer.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get mxcUri(): string {
|
|
||||||
if (!this.mxc) {
|
|
||||||
throw new Error("Recording has not been uploaded yet");
|
|
||||||
}
|
|
||||||
return this.mxc;
|
|
||||||
}
|
|
||||||
|
|
||||||
private onAudioProcess = (ev: AudioProcessingEvent) => {
|
private onAudioProcess = (ev: AudioProcessingEvent) => {
|
||||||
this.processAudioUpdate(ev.playbackTime);
|
this.processAudioUpdate(ev.playbackTime);
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
||||||
};
|
};
|
||||||
|
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
if (this.mxc || this.hasRecording) {
|
if (this.lastUpload || this.hasRecording) {
|
||||||
throw new Error("Recording already prepared");
|
throw new Error("Recording already prepared");
|
||||||
}
|
}
|
||||||
if (this.recording) {
|
if (this.recording) {
|
||||||
|
@ -362,20 +362,19 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
||||||
this.observable.close();
|
this.observable.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async upload(): Promise<string> {
|
public async upload(inRoomId: string): Promise<IUpload> {
|
||||||
if (!this.hasRecording) {
|
if (!this.hasRecording) {
|
||||||
throw new Error("No recording available to upload");
|
throw new Error("No recording available to upload");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.mxc) return this.mxc;
|
if (this.lastUpload) return this.lastUpload;
|
||||||
|
|
||||||
this.emit(RecordingState.Uploading);
|
this.emit(RecordingState.Uploading);
|
||||||
this.mxc = await this.client.uploadContent(new Blob([this.audioBuffer], {
|
const { url: mxc, file: encrypted } = await uploadFile(this.client, inRoomId, new Blob([this.audioBuffer], {
|
||||||
type: this.contentType,
|
type: this.contentType,
|
||||||
}), {
|
}));
|
||||||
onlyContentUri: false, // to stop the warnings in the console
|
this.lastUpload = { mxc, encrypted };
|
||||||
}).then(r => r['content_uri']);
|
|
||||||
this.emit(RecordingState.Uploaded);
|
this.emit(RecordingState.Uploaded);
|
||||||
return this.mxc;
|
return this.lastUpload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,36 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
||||||
|
|
||||||
|
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 React from 'react';
|
||||||
import ReactTestUtils from 'react-dom/test-utils';
|
import ReactTestUtils from 'react-dom/test-utils';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import * as TestUtils from '../../../test-utils';
|
import * as TestUtils from '../../../test-utils';
|
||||||
|
|
||||||
import {MatrixClientPeg} from '../../../../src/MatrixClientPeg';
|
|
||||||
import sdk from '../../../skinned-sdk';
|
import sdk from '../../../skinned-sdk';
|
||||||
|
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||||
import {Room, RoomMember, User} from 'matrix-js-sdk';
|
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||||
|
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
|
||||||
|
import { User } from "matrix-js-sdk/src/models/user";
|
||||||
import { compare } from "../../../../src/utils/strings";
|
import { compare } from "../../../../src/utils/strings";
|
||||||
|
import MemberList from "../../../../src/components/views/rooms/MemberList";
|
||||||
|
|
||||||
function generateRoomId() {
|
function generateRoomId() {
|
||||||
return '!' + Math.random().toString().slice(2, 10) + ':domain';
|
return '!' + Math.random().toString().slice(2, 10) + ':domain';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
describe('MemberList', () => {
|
describe('MemberList', () => {
|
||||||
function createRoom(opts) {
|
function createRoom(opts) {
|
||||||
const room = new Room(generateRoomId(), null, client.getUserId());
|
const room = new Room(generateRoomId(), null, client.getUserId());
|
||||||
|
@ -97,13 +112,19 @@ describe('MemberList', () => {
|
||||||
memberListRoom.currentState.members[member.userId] = member;
|
memberListRoom.currentState.members[member.userId] = member;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemberList = sdk.getComponent('views.rooms.MemberList');
|
|
||||||
const WrappedMemberList = TestUtils.wrapInMatrixClientContext(MemberList);
|
const WrappedMemberList = TestUtils.wrapInMatrixClientContext(MemberList);
|
||||||
const gatherWrappedRef = (r) => {
|
const gatherWrappedRef = (r) => {
|
||||||
memberList = r;
|
memberList = r;
|
||||||
};
|
};
|
||||||
root = ReactDOM.render(<WrappedMemberList roomId={memberListRoom.roomId}
|
root = ReactDOM.render(
|
||||||
wrappedRef={gatherWrappedRef} />, parentDiv);
|
(
|
||||||
|
<WrappedMemberList
|
||||||
|
roomId={memberListRoom.roomId}
|
||||||
|
wrappedRef={gatherWrappedRef}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
parentDiv,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach((done) => {
|
afterEach((done) => {
|
||||||
|
@ -213,8 +234,8 @@ describe('MemberList', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bypass all the event listeners and skip to the good part
|
// Bypass all the event listeners and skip to the good part
|
||||||
memberList._showPresence = enablePresence;
|
memberList.showPresence = enablePresence;
|
||||||
memberList._updateListNow();
|
memberList.updateListNow();
|
||||||
|
|
||||||
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
||||||
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
||||||
|
@ -225,7 +246,7 @@ describe('MemberList', () => {
|
||||||
|
|
||||||
// Bypass all the event listeners and skip to the good part
|
// Bypass all the event listeners and skip to the good part
|
||||||
memberList._showPresence = enablePresence;
|
memberList._showPresence = enablePresence;
|
||||||
memberList._updateListNow();
|
memberList.updateListNow();
|
||||||
|
|
||||||
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
||||||
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
||||||
|
@ -254,8 +275,8 @@ describe('MemberList', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bypass all the event listeners and skip to the good part
|
// Bypass all the event listeners and skip to the good part
|
||||||
memberList._showPresence = enablePresence;
|
memberList.showPresence = enablePresence;
|
||||||
memberList._updateListNow();
|
memberList.updateListNow();
|
||||||
|
|
||||||
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
||||||
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
||||||
|
@ -273,8 +294,8 @@ describe('MemberList', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bypass all the event listeners and skip to the good part
|
// Bypass all the event listeners and skip to the good part
|
||||||
memberList._showPresence = enablePresence;
|
memberList.showPresence = enablePresence;
|
||||||
memberList._updateListNow();
|
memberList.updateListNow();
|
||||||
|
|
||||||
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
||||||
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
|
@ -11,7 +11,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cheerio": "^1.0.0-rc.2",
|
"cheerio": "^1.0.0-rc.2",
|
||||||
"commander": "^2.19.0",
|
"commander": "^2.19.0",
|
||||||
"puppeteer": "^1.14.0",
|
"puppeteer": "10.0.0",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"request-promise-native": "^1.0.7",
|
"request-promise-native": "^1.0.7",
|
||||||
"uuid": "^3.3.2"
|
"uuid": "^3.3.2"
|
||||||
|
|
|
@ -40,7 +40,7 @@ async function acceptToast(session, expectedTitle) {
|
||||||
|
|
||||||
async function rejectToast(session, expectedTitle) {
|
async function rejectToast(session, expectedTitle) {
|
||||||
await assertToast(session, expectedTitle);
|
await assertToast(session, expectedTitle);
|
||||||
const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger');
|
const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger_outline');
|
||||||
await btn.click();
|
await btn.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,19 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.12.1.tgz#d90123f6c61fdf2f7cddd286ddae891586dd3488"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.12.1.tgz#d90123f6c61fdf2f7cddd286ddae891586dd3488"
|
||||||
integrity sha512-sKDlqv6COJrR7ar0+GqqhrXQDzQlMcqMnF2iEU6m9hLo8kxozoAGUazwPyELHlRVmjsbvlnGXjnzyptSXVmceA==
|
integrity sha512-sKDlqv6COJrR7ar0+GqqhrXQDzQlMcqMnF2iEU6m9hLo8kxozoAGUazwPyELHlRVmjsbvlnGXjnzyptSXVmceA==
|
||||||
|
|
||||||
agent-base@^4.3.0:
|
"@types/yauzl@^2.9.1":
|
||||||
version "4.3.0"
|
version "2.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
|
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af"
|
||||||
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
|
integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==
|
||||||
dependencies:
|
dependencies:
|
||||||
es6-promisify "^5.0.0"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
agent-base@6:
|
||||||
|
version "6.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||||
|
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||||
|
dependencies:
|
||||||
|
debug "4"
|
||||||
|
|
||||||
ajv@^6.5.5:
|
ajv@^6.5.5:
|
||||||
version "6.10.0"
|
version "6.10.0"
|
||||||
|
@ -36,11 +43,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
||||||
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
|
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
|
||||||
|
|
||||||
async-limiter@~1.0.0:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
|
||||||
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
|
||||||
|
|
||||||
asynckit@^0.4.0:
|
asynckit@^0.4.0:
|
||||||
version "0.4.0"
|
version "0.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
|
@ -61,6 +63,11 @@ balanced-match@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
|
base64-js@^1.3.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
|
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||||
|
|
||||||
bcrypt-pbkdf@^1.0.0:
|
bcrypt-pbkdf@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
|
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
|
||||||
|
@ -68,6 +75,15 @@ bcrypt-pbkdf@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
tweetnacl "^0.14.3"
|
tweetnacl "^0.14.3"
|
||||||
|
|
||||||
|
bl@^4.0.3:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
|
||||||
|
integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
|
||||||
|
dependencies:
|
||||||
|
buffer "^5.5.0"
|
||||||
|
inherits "^2.0.4"
|
||||||
|
readable-stream "^3.4.0"
|
||||||
|
|
||||||
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"
|
||||||
|
@ -86,10 +102,13 @@ buffer-crc32@~0.2.3:
|
||||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||||
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||||
|
|
||||||
buffer-from@^1.0.0:
|
buffer@^5.2.1, buffer@^5.5.0:
|
||||||
version "1.1.1"
|
version "5.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.3.1"
|
||||||
|
ieee754 "^1.1.13"
|
||||||
|
|
||||||
caseless@~0.12.0:
|
caseless@~0.12.0:
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
|
@ -108,6 +127,11 @@ cheerio@^1.0.0-rc.2:
|
||||||
lodash "^4.15.0"
|
lodash "^4.15.0"
|
||||||
parse5 "^3.0.1"
|
parse5 "^3.0.1"
|
||||||
|
|
||||||
|
chownr@^1.1.1:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||||
|
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
||||||
|
|
||||||
combined-stream@^1.0.6, combined-stream@~1.0.6:
|
combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||||
version "1.0.7"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
|
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
|
||||||
|
@ -125,17 +149,7 @@ concat-map@0.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||||
|
|
||||||
concat-stream@^1.6.2:
|
core-util-is@1.0.2:
|
||||||
version "1.6.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
|
|
||||||
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
|
|
||||||
dependencies:
|
|
||||||
buffer-from "^1.0.0"
|
|
||||||
inherits "^2.0.3"
|
|
||||||
readable-stream "^2.2.2"
|
|
||||||
typedarray "^0.0.6"
|
|
||||||
|
|
||||||
core-util-is@1.0.2, core-util-is@~1.0.0:
|
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||||
|
@ -162,21 +176,7 @@ dashdash@^1.12.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
assert-plus "^1.0.0"
|
assert-plus "^1.0.0"
|
||||||
|
|
||||||
debug@^2.6.9:
|
debug@4, debug@4.3.1, debug@^4.1.1:
|
||||||
version "2.6.9"
|
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
|
||||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
|
||||||
dependencies:
|
|
||||||
ms "2.0.0"
|
|
||||||
|
|
||||||
debug@^3.1.0:
|
|
||||||
version "3.2.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
|
||||||
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
|
|
||||||
dependencies:
|
|
||||||
ms "^2.1.1"
|
|
||||||
|
|
||||||
debug@^4.1.0:
|
|
||||||
version "4.3.1"
|
version "4.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
||||||
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
||||||
|
@ -188,6 +188,11 @@ delayed-stream@~1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||||
|
|
||||||
|
devtools-protocol@0.0.883894:
|
||||||
|
version "0.0.883894"
|
||||||
|
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.883894.tgz#d403f2c75cd6d71c916aee8dde9258da988a4da9"
|
||||||
|
integrity sha512-33idhm54QJzf3Q7QofMgCvIVSd2o9H3kQPWaKT/fhoZh+digc+WSiMhbkeG3iN79WY4Hwr9G05NpbhEVrsOYAg==
|
||||||
|
|
||||||
dom-serializer@0, dom-serializer@~0.1.0:
|
dom-serializer@0, dom-serializer@~0.1.0:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
|
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
|
||||||
|
@ -232,37 +237,33 @@ ecc-jsbn@~0.1.1:
|
||||||
jsbn "~0.1.0"
|
jsbn "~0.1.0"
|
||||||
safer-buffer "^2.1.0"
|
safer-buffer "^2.1.0"
|
||||||
|
|
||||||
|
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||||
|
version "1.4.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||||
|
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||||
|
dependencies:
|
||||||
|
once "^1.4.0"
|
||||||
|
|
||||||
entities@^1.1.1, entities@~1.1.1:
|
entities@^1.1.1, entities@~1.1.1:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
|
||||||
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
|
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
|
||||||
|
|
||||||
es6-promise@^4.0.3:
|
|
||||||
version "4.2.8"
|
|
||||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
|
||||||
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
|
|
||||||
|
|
||||||
es6-promisify@^5.0.0:
|
|
||||||
version "5.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
|
|
||||||
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
|
|
||||||
dependencies:
|
|
||||||
es6-promise "^4.0.3"
|
|
||||||
|
|
||||||
extend@~3.0.2:
|
extend@~3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||||
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
||||||
|
|
||||||
extract-zip@^1.6.6:
|
extract-zip@2.0.1:
|
||||||
version "1.7.0"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
|
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
|
||||||
integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==
|
integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
|
||||||
dependencies:
|
dependencies:
|
||||||
concat-stream "^1.6.2"
|
debug "^4.1.1"
|
||||||
debug "^2.6.9"
|
get-stream "^5.1.0"
|
||||||
mkdirp "^0.5.4"
|
|
||||||
yauzl "^2.10.0"
|
yauzl "^2.10.0"
|
||||||
|
optionalDependencies:
|
||||||
|
"@types/yauzl" "^2.9.1"
|
||||||
|
|
||||||
extsprintf@1.3.0:
|
extsprintf@1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
|
@ -291,6 +292,14 @@ fd-slicer@~1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
pend "~1.2.0"
|
pend "~1.2.0"
|
||||||
|
|
||||||
|
find-up@^4.0.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
|
||||||
|
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
|
||||||
|
dependencies:
|
||||||
|
locate-path "^5.0.0"
|
||||||
|
path-exists "^4.0.0"
|
||||||
|
|
||||||
forever-agent@~0.6.1:
|
forever-agent@~0.6.1:
|
||||||
version "0.6.1"
|
version "0.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||||
|
@ -305,11 +314,23 @@ form-data@~2.3.2:
|
||||||
combined-stream "^1.0.6"
|
combined-stream "^1.0.6"
|
||||||
mime-types "^2.1.12"
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
|
fs-constants@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||||
|
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||||
|
|
||||||
fs.realpath@^1.0.0:
|
fs.realpath@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||||
|
|
||||||
|
get-stream@^5.1.0:
|
||||||
|
version "5.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
|
||||||
|
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
|
||||||
|
dependencies:
|
||||||
|
pump "^3.0.0"
|
||||||
|
|
||||||
getpass@^0.1.1:
|
getpass@^0.1.1:
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
|
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
|
||||||
|
@ -363,13 +384,18 @@ http-signature@~1.2.0:
|
||||||
jsprim "^1.2.2"
|
jsprim "^1.2.2"
|
||||||
sshpk "^1.7.0"
|
sshpk "^1.7.0"
|
||||||
|
|
||||||
https-proxy-agent@^2.2.1:
|
https-proxy-agent@5.0.0:
|
||||||
version "2.2.4"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
|
||||||
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
|
integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base "^4.3.0"
|
agent-base "6"
|
||||||
debug "^3.1.0"
|
debug "4"
|
||||||
|
|
||||||
|
ieee754@^1.1.13:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
|
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||||
|
|
||||||
inflight@^1.0.4:
|
inflight@^1.0.4:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
|
@ -379,7 +405,7 @@ inflight@^1.0.4:
|
||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@2, inherits@^2.0.3, inherits@~2.0.3:
|
inherits@2, inherits@^2.0.3, inherits@^2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
@ -394,11 +420,6 @@ is-typedarray@~1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||||
|
|
||||||
isarray@~1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
|
||||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
|
||||||
|
|
||||||
isstream@~0.1.2:
|
isstream@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||||
|
@ -434,6 +455,13 @@ jsprim@^1.2.2:
|
||||||
json-schema "0.2.3"
|
json-schema "0.2.3"
|
||||||
verror "1.10.0"
|
verror "1.10.0"
|
||||||
|
|
||||||
|
locate-path@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
|
||||||
|
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
|
||||||
|
dependencies:
|
||||||
|
p-locate "^4.1.0"
|
||||||
|
|
||||||
lodash@^4.15.0, lodash@^4.17.11:
|
lodash@^4.15.0, lodash@^4.17.11:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
|
@ -451,11 +479,6 @@ mime-types@^2.1.12, mime-types@~2.1.19:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-db "~1.38.0"
|
mime-db "~1.38.0"
|
||||||
|
|
||||||
mime@^2.0.3:
|
|
||||||
version "2.5.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe"
|
|
||||||
integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==
|
|
||||||
|
|
||||||
minimatch@^3.0.4:
|
minimatch@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||||
|
@ -468,27 +491,22 @@ minimist@^1.2.5:
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||||
|
|
||||||
mkdirp@^0.5.4:
|
mkdirp@^0.5.1:
|
||||||
version "0.5.5"
|
version "0.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
||||||
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
|
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
|
|
||||||
ms@2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
|
||||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
|
||||||
|
|
||||||
ms@2.1.2:
|
ms@2.1.2:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
ms@^2.1.1:
|
node-fetch@2.6.1:
|
||||||
version "2.1.3"
|
version "2.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||||
|
|
||||||
nth-check@~1.0.1:
|
nth-check@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
|
@ -502,13 +520,32 @@ oauth-sign@~0.9.0:
|
||||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
||||||
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
|
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
|
||||||
|
|
||||||
once@^1.3.0:
|
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||||
dependencies:
|
dependencies:
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
|
p-limit@^2.2.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
|
||||||
|
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
|
||||||
|
dependencies:
|
||||||
|
p-try "^2.0.0"
|
||||||
|
|
||||||
|
p-locate@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
|
||||||
|
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
|
||||||
|
dependencies:
|
||||||
|
p-limit "^2.2.0"
|
||||||
|
|
||||||
|
p-try@^2.0.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||||
|
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
||||||
|
|
||||||
parse5@^3.0.1:
|
parse5@^3.0.1:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
|
resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
|
||||||
|
@ -516,6 +553,11 @@ parse5@^3.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
path-exists@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||||
|
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
|
||||||
|
|
||||||
path-is-absolute@^1.0.0:
|
path-is-absolute@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||||
|
@ -531,17 +573,19 @@ performance-now@^2.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||||
|
|
||||||
process-nextick-args@~2.0.0:
|
pkg-dir@4.2.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
|
||||||
|
integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
|
||||||
|
dependencies:
|
||||||
|
find-up "^4.0.0"
|
||||||
|
|
||||||
|
progress@2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.1.tgz#c9242169342b1c29d275889c95734621b1952e31"
|
||||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
integrity sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==
|
||||||
|
|
||||||
progress@^2.0.1:
|
proxy-from-env@1.1.0:
|
||||||
version "2.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
|
||||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
|
||||||
|
|
||||||
proxy-from-env@^1.0.0:
|
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||||
|
@ -551,6 +595,14 @@ psl@^1.1.24, psl@^1.1.28:
|
||||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184"
|
resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184"
|
||||||
integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
|
integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
|
||||||
|
|
||||||
|
pump@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||||
|
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
|
||||||
|
dependencies:
|
||||||
|
end-of-stream "^1.1.0"
|
||||||
|
once "^1.3.1"
|
||||||
|
|
||||||
punycode@^1.4.1:
|
punycode@^1.4.1:
|
||||||
version "1.4.1"
|
version "1.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
|
||||||
|
@ -561,38 +613,29 @@ punycode@^2.1.0, punycode@^2.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||||
|
|
||||||
puppeteer@^1.14.0:
|
puppeteer@10.0.0:
|
||||||
version "1.20.0"
|
version "10.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.20.0.tgz#e3d267786f74e1d87cf2d15acc59177f471bbe38"
|
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-10.0.0.tgz#1b597c956103e2d989ca17f41ba4693b20a3640c"
|
||||||
integrity sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ==
|
integrity sha512-AxHvCb9IWmmP3gMW+epxdj92Gglii+6Z4sb+W+zc2hTTu10HF0yg6hGXot5O74uYkVqG3lfDRLfnRpi6WOwi5A==
|
||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.1.0"
|
debug "4.3.1"
|
||||||
extract-zip "^1.6.6"
|
devtools-protocol "0.0.883894"
|
||||||
https-proxy-agent "^2.2.1"
|
extract-zip "2.0.1"
|
||||||
mime "^2.0.3"
|
https-proxy-agent "5.0.0"
|
||||||
progress "^2.0.1"
|
node-fetch "2.6.1"
|
||||||
proxy-from-env "^1.0.0"
|
pkg-dir "4.2.0"
|
||||||
rimraf "^2.6.1"
|
progress "2.0.1"
|
||||||
ws "^6.1.0"
|
proxy-from-env "1.1.0"
|
||||||
|
rimraf "3.0.2"
|
||||||
|
tar-fs "2.0.0"
|
||||||
|
unbzip2-stream "1.3.3"
|
||||||
|
ws "7.4.6"
|
||||||
|
|
||||||
qs@~6.5.2:
|
qs@~6.5.2:
|
||||||
version "6.5.2"
|
version "6.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||||
|
|
||||||
readable-stream@^2.2.2:
|
|
||||||
version "2.3.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
|
||||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
|
||||||
dependencies:
|
|
||||||
core-util-is "~1.0.0"
|
|
||||||
inherits "~2.0.3"
|
|
||||||
isarray "~1.0.0"
|
|
||||||
process-nextick-args "~2.0.0"
|
|
||||||
safe-buffer "~5.1.1"
|
|
||||||
string_decoder "~1.1.1"
|
|
||||||
util-deprecate "~1.0.1"
|
|
||||||
|
|
||||||
readable-stream@^3.1.1:
|
readable-stream@^3.1.1:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d"
|
||||||
|
@ -602,6 +645,15 @@ readable-stream@^3.1.1:
|
||||||
string_decoder "^1.1.1"
|
string_decoder "^1.1.1"
|
||||||
util-deprecate "^1.0.1"
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
|
readable-stream@^3.4.0:
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||||
|
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||||
|
dependencies:
|
||||||
|
inherits "^2.0.3"
|
||||||
|
string_decoder "^1.1.1"
|
||||||
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
request-promise-core@1.1.2:
|
request-promise-core@1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346"
|
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346"
|
||||||
|
@ -644,14 +696,14 @@ request@^2.88.0:
|
||||||
tunnel-agent "^0.6.0"
|
tunnel-agent "^0.6.0"
|
||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
|
|
||||||
rimraf@^2.6.1:
|
rimraf@3.0.2:
|
||||||
version "2.7.1"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
|
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||||
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
|
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
||||||
dependencies:
|
dependencies:
|
||||||
glob "^7.1.3"
|
glob "^7.1.3"
|
||||||
|
|
||||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0:
|
||||||
version "5.1.2"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||||
|
@ -688,12 +740,31 @@ string_decoder@^1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "~5.1.0"
|
safe-buffer "~5.1.0"
|
||||||
|
|
||||||
string_decoder@~1.1.1:
|
tar-fs@2.0.0:
|
||||||
version "1.1.1"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad"
|
||||||
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "~5.1.0"
|
chownr "^1.1.1"
|
||||||
|
mkdirp "^0.5.1"
|
||||||
|
pump "^3.0.0"
|
||||||
|
tar-stream "^2.0.0"
|
||||||
|
|
||||||
|
tar-stream@^2.0.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||||
|
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
||||||
|
dependencies:
|
||||||
|
bl "^4.0.3"
|
||||||
|
end-of-stream "^1.4.1"
|
||||||
|
fs-constants "^1.0.0"
|
||||||
|
inherits "^2.0.3"
|
||||||
|
readable-stream "^3.1.1"
|
||||||
|
|
||||||
|
through@^2.3.8:
|
||||||
|
version "2.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||||
|
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||||
|
|
||||||
tough-cookie@^2.3.3:
|
tough-cookie@^2.3.3:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
|
@ -723,10 +794,13 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
||||||
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
||||||
|
|
||||||
typedarray@^0.0.6:
|
unbzip2-stream@1.3.3:
|
||||||
version "0.0.6"
|
version "1.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==
|
||||||
|
dependencies:
|
||||||
|
buffer "^5.2.1"
|
||||||
|
through "^2.3.8"
|
||||||
|
|
||||||
uri-js@^4.2.2:
|
uri-js@^4.2.2:
|
||||||
version "4.2.2"
|
version "4.2.2"
|
||||||
|
@ -735,7 +809,7 @@ uri-js@^4.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode "^2.1.0"
|
punycode "^2.1.0"
|
||||||
|
|
||||||
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
util-deprecate@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||||
|
@ -759,12 +833,10 @@ wrappy@1:
|
||||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||||
|
|
||||||
ws@^6.1.0:
|
ws@7.4.6:
|
||||||
version "6.2.2"
|
version "7.4.6"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
|
||||||
integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==
|
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
|
||||||
dependencies:
|
|
||||||
async-limiter "~1.0.0"
|
|
||||||
|
|
||||||
yauzl@^2.10.0:
|
yauzl@^2.10.0:
|
||||||
version "2.10.0"
|
version "2.10.0"
|
||||||
|
|
Loading…
Reference in a new issue