Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into develop

This commit is contained in:
RiotRobot 2021-04-12 14:51:22 +01:00
commit 736507b812
48 changed files with 407 additions and 213 deletions

View file

@ -123,6 +123,7 @@
@import "./views/elements/_ImageView.scss"; @import "./views/elements/_ImageView.scss";
@import "./views/elements/_InfoTooltip.scss"; @import "./views/elements/_InfoTooltip.scss";
@import "./views/elements/_InlineSpinner.scss"; @import "./views/elements/_InlineSpinner.scss";
@import "./views/elements/_InviteReason.scss";
@import "./views/elements/_ManageIntegsButton.scss"; @import "./views/elements/_ManageIntegsButton.scss";
@import "./views/elements/_MiniAvatarUploader.scss"; @import "./views/elements/_MiniAvatarUploader.scss";
@import "./views/elements/_PowerSelector.scss"; @import "./views/elements/_PowerSelector.scss";
@ -250,6 +251,7 @@
@import "./views/voice_messages/_Waveform.scss"; @import "./views/voice_messages/_Waveform.scss";
@import "./views/voip/_CallContainer.scss"; @import "./views/voip/_CallContainer.scss";
@import "./views/voip/_CallView.scss"; @import "./views/voip/_CallView.scss";
@import "./views/voip/_CallViewForRoom.scss";
@import "./views/voip/_DialPad.scss"; @import "./views/voip/_DialPad.scss";
@import "./views/voip/_DialPadContextMenu.scss"; @import "./views/voip/_DialPadContextMenu.scss";
@import "./views/voip/_DialPadModal.scss"; @import "./views/voip/_DialPadModal.scss";

View file

@ -276,15 +276,17 @@ $activeBorderColor: $secondary-fg-color;
.mx_SpaceButton:hover, .mx_SpaceButton:hover,
.mx_SpaceButton:focus-within, .mx_SpaceButton:focus-within,
.mx_SpaceButton_hasMenuOpen { .mx_SpaceButton_hasMenuOpen {
// Hide the badge container on hover because it'll be a menu button &:not(.mx_SpaceButton_home) {
.mx_SpacePanel_badgeContainer { // Hide the badge container on hover because it'll be a menu button
width: 0; .mx_SpacePanel_badgeContainer {
height: 0; width: 0;
display: none; height: 0;
} display: none;
}
.mx_SpaceButton_menuButton { .mx_SpaceButton_menuButton {
display: block; display: block;
}
} }
} }
} }

View file

@ -0,0 +1,54 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_InviteReason {
position: relative;
margin-bottom: 1em;
.mx_InviteReason_reason {
visibility: visible;
}
.mx_InviteReason_view {
display: none;
position: absolute;
inset: 0;
justify-content: center;
align-items: center;
cursor: pointer;
color: $secondary-fg-color;
&::before {
content: "";
margin-right: 8px;
background-color: $secondary-fg-color;
mask-image: url('$(res)/img/feather-customised/eye.svg');
display: inline-block;
width: 18px;
height: 14px;
}
}
}
.mx_InviteReason_hidden {
.mx_InviteReason_reason {
visibility: hidden;
}
.mx_InviteReason_view {
display: flex;
}
}

View file

@ -40,35 +40,6 @@ limitations under the License.
word-break: break-word; word-break: break-word;
} }
.mx_RoomPreviewBar_reason {
text-align: left;
background-color: $primary-bg-color;
border: 1px solid $invite-reason-border-color;
border-radius: 10px;
padding: 0 16px 12px 16px;
margin: 5px 0 20px 0;
div {
pointer-events: none;
}
.mx_EventTile_msgOption {
display: none;
}
.mx_MatrixChat_useCompactLayout & {
padding-top: 9px;
}
&.mx_EventTilePreview_faded {
cursor: pointer;
.mx_SenderProfile, .mx_EventTile_avatar {
opacity: 0.3;
}
}
}
.mx_Spinner { .mx_Spinner {
width: auto; width: auto;
height: auto; height: auto;

View file

@ -27,9 +27,12 @@ limitations under the License.
.mx_CallView_large { .mx_CallView_large {
padding-bottom: 10px; padding-bottom: 10px;
margin: 5px 5px 5px 18px; margin: 5px 5px 5px 18px;
display: flex;
flex-direction: column;
flex: 1;
.mx_CallView_voice { .mx_CallView_voice {
height: 360px; flex: 1;
} }
} }
@ -104,6 +107,7 @@ limitations under the License.
.mx_CallView_video { .mx_CallView_video {
width: 100%; width: 100%;
height: 100%;
position: relative; position: relative;
z-index: 30; z-index: 30;
border-radius: 8px; border-radius: 8px;
@ -177,6 +181,7 @@ limitations under the License.
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: left; justify-content: left;
flex-shrink: 0;
} }
.mx_CallView_header_callType { .mx_CallView_header_callType {

View file

@ -0,0 +1,46 @@
/*
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.
*/
.mx_CallViewForRoom {
overflow: hidden;
.mx_CallViewForRoom_ResizeWrapper {
display: flex;
margin-bottom: 8px;
&:hover .mx_CallViewForRoom_ResizeHandle {
// Need to use important to override element style attributes
// set by re-resizable
width: 100% !important;
display: flex;
justify-content: center;
&::after {
content: '';
margin-top: 3px;
border-radius: 4px;
height: 4px;
width: 100%;
max-width: 64px;
background-color: $primary-fg-color;
}
}
}
}

View file

@ -16,7 +16,7 @@ limitations under the License.
.mx_VideoFeed_remote { .mx_VideoFeed_remote {
width: 100%; width: 100%;
max-height: 100%; height: 100%;
background-color: #000; background-color: #000;
z-index: 50; z-index: 50;
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -209,8 +209,6 @@ $message-body-panel-fg-color: $primary-fg-color;
// Appearance tab colors // Appearance tab colors
$appearance-tab-border-color: $room-highlight-color; $appearance-tab-border-color: $room-highlight-color;
$invite-reason-border-color: $room-highlight-color;
// blur amounts for left left panel (only for element theme, used in _mods.scss) // blur amounts for left left panel (only for element theme, used in _mods.scss)
$roomlist-background-blur-amount: 60px; $roomlist-background-blur-amount: 60px;
$groupFilterPanel-background-blur-amount: 30px; $groupFilterPanel-background-blur-amount: 30px;

View file

@ -204,8 +204,6 @@ $message-body-panel-fg-color: $primary-fg-color;
// Appearance tab colors // Appearance tab colors
$appearance-tab-border-color: $room-highlight-color; $appearance-tab-border-color: $room-highlight-color;
$invite-reason-border-color: $room-highlight-color;
$composer-shadow-color: tranparent; $composer-shadow-color: tranparent;
// ***** Mixins! ***** // ***** Mixins! *****

View file

@ -333,8 +333,6 @@ $message-body-panel-fg-color: $muted-fg-color;
// FontSlider colors // FontSlider colors
$appearance-tab-border-color: $input-darker-bg-color; $appearance-tab-border-color: $input-darker-bg-color;
$invite-reason-border-color: $input-darker-bg-color;
$composer-shadow-color: tranparent; $composer-shadow-color: tranparent;
// ***** Mixins! ***** // ***** Mixins! *****

View file

@ -15,8 +15,8 @@ $inter-unicode-range: U+0000-20e2,U+20e4-23ce,U+23d0-24c1,U+24c3-259f,U+25c2-266
font-weight: 400; font-weight: 400;
font-display: swap; font-display: swap;
unicode-range: $inter-unicode-range; unicode-range: $inter-unicode-range;
src: url("$(res)/fonts/Inter/Inter-Regular.woff2?v=3.13") format("woff2"), src: url("$(res)/fonts/Inter/Inter-Regular.woff2?v=3.18") format("woff2"),
url("$(res)/fonts/Inter/Inter-Regular.woff?v=3.13") format("woff"); url("$(res)/fonts/Inter/Inter-Regular.woff?v=3.18") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
@ -24,8 +24,8 @@ $inter-unicode-range: U+0000-20e2,U+20e4-23ce,U+23d0-24c1,U+24c3-259f,U+25c2-266
font-weight: 400; font-weight: 400;
font-display: swap; font-display: swap;
unicode-range: $inter-unicode-range; unicode-range: $inter-unicode-range;
src: url("$(res)/fonts/Inter/Inter-Italic.woff2?v=3.13") format("woff2"), src: url("$(res)/fonts/Inter/Inter-Italic.woff2?v=3.18") format("woff2"),
url("$(res)/fonts/Inter/Inter-Italic.woff?v=3.13") format("woff"); url("$(res)/fonts/Inter/Inter-Italic.woff?v=3.18") format("woff");
} }
@font-face { @font-face {
@ -34,8 +34,8 @@ $inter-unicode-range: U+0000-20e2,U+20e4-23ce,U+23d0-24c1,U+24c3-259f,U+25c2-266
font-weight: 500; font-weight: 500;
font-display: swap; font-display: swap;
unicode-range: $inter-unicode-range; unicode-range: $inter-unicode-range;
src: url("$(res)/fonts/Inter/Inter-Medium.woff2?v=3.13") format("woff2"), src: url("$(res)/fonts/Inter/Inter-Medium.woff2?v=3.18") format("woff2"),
url("$(res)/fonts/Inter/Inter-Medium.woff?v=3.13") format("woff"); url("$(res)/fonts/Inter/Inter-Medium.woff?v=3.18") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
@ -43,8 +43,8 @@ $inter-unicode-range: U+0000-20e2,U+20e4-23ce,U+23d0-24c1,U+24c3-259f,U+25c2-266
font-weight: 500; font-weight: 500;
font-display: swap; font-display: swap;
unicode-range: $inter-unicode-range; unicode-range: $inter-unicode-range;
src: url("$(res)/fonts/Inter/Inter-MediumItalic.woff2?v=3.13") format("woff2"), src: url("$(res)/fonts/Inter/Inter-MediumItalic.woff2?v=3.18") format("woff2"),
url("$(res)/fonts/Inter/Inter-MediumItalic.woff?v=3.13") format("woff"); url("$(res)/fonts/Inter/Inter-MediumItalic.woff?v=3.18") format("woff");
} }
@font-face { @font-face {
@ -53,8 +53,8 @@ $inter-unicode-range: U+0000-20e2,U+20e4-23ce,U+23d0-24c1,U+24c3-259f,U+25c2-266
font-weight: 600; font-weight: 600;
font-display: swap; font-display: swap;
unicode-range: $inter-unicode-range; unicode-range: $inter-unicode-range;
src: url("$(res)/fonts/Inter/Inter-SemiBold.woff2?v=3.13") format("woff2"), src: url("$(res)/fonts/Inter/Inter-SemiBold.woff2?v=3.18") format("woff2"),
url("$(res)/fonts/Inter/Inter-SemiBold.woff?v=3.13") format("woff"); url("$(res)/fonts/Inter/Inter-SemiBold.woff?v=3.18") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
@ -62,8 +62,8 @@ $inter-unicode-range: U+0000-20e2,U+20e4-23ce,U+23d0-24c1,U+24c3-259f,U+25c2-266
font-weight: 600; font-weight: 600;
font-display: swap; font-display: swap;
unicode-range: $inter-unicode-range; unicode-range: $inter-unicode-range;
src: url("$(res)/fonts/Inter/Inter-SemiBoldItalic.woff2?v=3.13") format("woff2"), src: url("$(res)/fonts/Inter/Inter-SemiBoldItalic.woff2?v=3.18") format("woff2"),
url("$(res)/fonts/Inter/Inter-SemiBoldItalic.woff?v=3.13") format("woff"); url("$(res)/fonts/Inter/Inter-SemiBoldItalic.woff?v=3.18") format("woff");
} }
@font-face { @font-face {
@ -72,8 +72,8 @@ $inter-unicode-range: U+0000-20e2,U+20e4-23ce,U+23d0-24c1,U+24c3-259f,U+25c2-266
font-weight: 700; font-weight: 700;
font-display: swap; font-display: swap;
unicode-range: $inter-unicode-range; unicode-range: $inter-unicode-range;
src: url("$(res)/fonts/Inter/Inter-Bold.woff2?v=3.13") format("woff2"), src: url("$(res)/fonts/Inter/Inter-Bold.woff2?v=3.18") format("woff2"),
url("$(res)/fonts/Inter/Inter-Bold.woff?v=3.13") format("woff"); url("$(res)/fonts/Inter/Inter-Bold.woff?v=3.18") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
@ -81,8 +81,8 @@ $inter-unicode-range: U+0000-20e2,U+20e4-23ce,U+23d0-24c1,U+24c3-259f,U+25c2-266
font-weight: 700; font-weight: 700;
font-display: swap; font-display: swap;
unicode-range: $inter-unicode-range; unicode-range: $inter-unicode-range;
src: url("$(res)/fonts/Inter/Inter-BoldItalic.woff2?v=3.13") format("woff2"), src: url("$(res)/fonts/Inter/Inter-BoldItalic.woff2?v=3.18") format("woff2"),
url("$(res)/fonts/Inter/Inter-BoldItalic.woff?v=3.13") format("woff"); url("$(res)/fonts/Inter/Inter-BoldItalic.woff?v=3.18") format("woff");
} }
/* latin-ext */ /* latin-ext */

View file

@ -331,8 +331,6 @@ $message-body-panel-fg-color: $muted-fg-color;
// FontSlider colors // FontSlider colors
$appearance-tab-border-color: $input-darker-bg-color; $appearance-tab-border-color: $input-darker-bg-color;
$invite-reason-border-color: $input-darker-bg-color;
// blur amounts for left left panel (only for element theme, used in _mods.scss) // blur amounts for left left panel (only for element theme, used in _mods.scss)
$roomlist-background-blur-amount: 40px; $roomlist-background-blur-amount: 40px;
$groupFilterPanel-background-blur-amount: 20px; $groupFilterPanel-background-blur-amount: 20px;

View file

@ -22,15 +22,26 @@ clone() {
} }
# Try the PR author's branch in case it exists on the deps as well. # Try the PR author's branch in case it exists on the deps as well.
# If BUILDKITE_BRANCH is set, it will contain either: # First we check if BUILDKITE_BRANCH is defined,
# if it isn't we can assume this is a Netlify build
if [ -z ${BUILDKITE_BRANCH+x} ]; then
# Netlify doesn't give us info about the fork so we have to get it from GitHub API
apiEndpoint="https://api.github.com/repos/matrix-org/matrix-react-sdk/pulls/"
apiEndpoint+=$REVIEW_ID
head=$(curl $apiEndpoint | jq -r '.head.label')
else
head=$BUILDKITE_BRANCH
fi
# If head is set, it will contain either:
# * "branch" when the author's branch and target branch are in the same repo # * "branch" when the author's branch and target branch are in the same repo
# * "author:branch" when the author's branch is in their fork # * "fork:branch" when the author's branch is in their fork or if this is a Netlify build
# We can split on `:` into an array to check. # We can split on `:` into an array to check.
BUILDKITE_BRANCH_ARRAY=(${BUILDKITE_BRANCH//:/ }) BRANCH_ARRAY=(${head//:/ })
if [[ "${#BUILDKITE_BRANCH_ARRAY[@]}" == "1" ]]; then if [[ "${#BRANCH_ARRAY[@]}" == "1" ]]; then
clone $deforg $defrepo $BUILDKITE_BRANCH clone $deforg $defrepo $BUILDKITE_BRANCH
elif [[ "${#BUILDKITE_BRANCH_ARRAY[@]}" == "2" ]]; then elif [[ "${#BRANCH_ARRAY[@]}" == "2" ]]; then
clone ${BUILDKITE_BRANCH_ARRAY[0]} $defrepo ${BUILDKITE_BRANCH_ARRAY[1]} clone ${BRANCH_ARRAY[0]} $defrepo ${BRANCH_ARRAY[1]}
fi fi
# Try the target branch of the push or PR. # Try the target branch of the push or PR.
clone $deforg $defrepo $BUILDKITE_PULL_REQUEST_BASE_BRANCH clone $deforg $defrepo $BUILDKITE_PULL_REQUEST_BASE_BRANCH

View file

@ -95,9 +95,10 @@ function textForMemberEvent(ev) {
senderName, senderName,
targetName, targetName,
}) + ' ' + reason; }) + ' ' + reason;
} else { } else if (prevContent.membership === "join") {
// sender is not target and made the target leave, if not from invite/ban then this is a kick
return _t('%(senderName)s kicked %(targetName)s.', {senderName, targetName}) + ' ' + reason; return _t('%(senderName)s kicked %(targetName)s.', {senderName, targetName}) + ' ' + reason;
} else {
return "";
} }
} }
} }

View file

@ -84,6 +84,7 @@ import {replaceableComponent} from "../../utils/replaceableComponent";
import RoomListStore from "../../stores/room-list/RoomListStore"; import RoomListStore from "../../stores/room-list/RoomListStore";
import {RoomUpdateCause} from "../../stores/room-list/models"; import {RoomUpdateCause} from "../../stores/room-list/models";
import defaultDispatcher from "../../dispatcher/dispatcher"; import defaultDispatcher from "../../dispatcher/dispatcher";
import SecurityCustomisations from "../../customisations/Security";
/** constants for MatrixChat.state.view */ /** constants for MatrixChat.state.view */
export enum Views { export enum Views {
@ -395,7 +396,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId()); const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId());
if (crossSigningIsSetUp) { if (crossSigningIsSetUp) {
this.setStateForNewView({ view: Views.COMPLETE_SECURITY }); if (SecurityCustomisations.SHOW_ENCRYPTION_SETUP_UI === false) {
this.onLoggedIn();
} else {
this.setStateForNewView({view: Views.COMPLETE_SECURITY});
}
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) { } else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
this.setStateForNewView({ view: Views.E2E_SETUP }); this.setStateForNewView({ view: Views.E2E_SETUP });
} else { } else {

View file

@ -1137,10 +1137,16 @@ export default class RoomView extends React.Component<IProps, IState> {
ev.stopPropagation(); ev.stopPropagation();
ev.preventDefault(); ev.preventDefault();
this.setState({ // We always increment the counter no matter the types, because dragging is
dragCounter: this.state.dragCounter + 1, // still happening. If we didn't, the drag counter would get out of sync.
draggingFile: true, this.setState({dragCounter: this.state.dragCounter + 1});
});
// See:
// https://docs.w3cub.com/dom/datatransfer/types
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
if (ev.dataTransfer.types.includes("Files") || ev.dataTransfer.types.includes("application/x-moz-file")) {
this.setState({draggingFile: true});
}
}; };
private onDragLeave = ev => { private onDragLeave = ev => {
@ -1164,6 +1170,9 @@ export default class RoomView extends React.Component<IProps, IState> {
ev.dataTransfer.dropEffect = 'none'; ev.dataTransfer.dropEffect = 'none';
// See:
// https://docs.w3cub.com/dom/datatransfer/types
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
if (ev.dataTransfer.types.includes("Files") || ev.dataTransfer.types.includes("application/x-moz-file")) { if (ev.dataTransfer.types.includes("Files") || ev.dataTransfer.types.includes("application/x-moz-file")) {
ev.dataTransfer.dropEffect = 'copy'; ev.dataTransfer.dropEffect = 'copy';
} }

View file

@ -712,8 +712,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
this.props.onFinished(); this.props.onFinished();
} }
if (cli.isRoomEncrypted(this.props.roomId) && if (cli.isRoomEncrypted(this.props.roomId)) {
SettingsStore.getValue("feature_room_history_key_sharing")) {
const visibilityEvent = room.currentState.getStateEvents( const visibilityEvent = room.currentState.getStateEvents(
"m.room.history_visibility", "", "m.room.history_visibility", "",
); );
@ -1344,8 +1343,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
buttonText = _t("Invite"); buttonText = _t("Invite");
goButtonFn = this._inviteUsers; goButtonFn = this._inviteUsers;
if (SettingsStore.getValue("feature_room_history_key_sharing") && if (cli.isRoomEncrypted(this.props.roomId)) {
cli.isRoomEncrypted(this.props.roomId)) {
const room = cli.getRoom(this.props.roomId); const room = cli.getRoom(this.props.roomId);
const visibilityEvent = room.currentState.getStateEvents( const visibilityEvent = room.currentState.getStateEvents(
"m.room.history_visibility", "", "m.room.history_visibility", "",

View file

@ -55,22 +55,10 @@ interface IProps {
* The mxc:// avatar URL of the displayed user * The mxc:// avatar URL of the displayed user
*/ */
avatarUrl?: string; avatarUrl?: string;
/**
* Whether the EventTile should appear faded
*/
faded?: boolean;
/**
* Callback for when the component is clicked
*/
onClick?: () => void;
} }
interface IState { interface IState {
message: string; message: string;
faded: boolean;
eventTileKey: number;
} }
const AVATAR_SIZE = 32; const AVATAR_SIZE = 32;
@ -81,23 +69,9 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
super(props); super(props);
this.state = { this.state = {
message: props.message, message: props.message,
faded: !!props.faded,
eventTileKey: 0,
}; };
} }
changeMessage(message: string) {
this.setState({
message,
// Change the EventTile key to force React to create a new instance
eventTileKey: this.state.eventTileKey + 1,
});
}
unfade() {
this.setState({ faded: false });
}
private fakeEvent({message}: IState) { private fakeEvent({message}: IState) {
// Fake it till we make it // Fake it till we make it
/* eslint-disable quote-props */ /* eslint-disable quote-props */
@ -147,12 +121,10 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
const className = classnames(this.props.className, { const className = classnames(this.props.className, {
"mx_IRCLayout": this.props.layout == Layout.IRC, "mx_IRCLayout": this.props.layout == Layout.IRC,
"mx_GroupLayout": this.props.layout == Layout.Group, "mx_GroupLayout": this.props.layout == Layout.Group,
"mx_EventTilePreview_faded": this.state.faded,
}); });
return <div className={className} onClick={this.props.onClick}> return <div className={className}>
<EventTile <EventTile
key={this.state.eventTileKey}
mxEvent={event} mxEvent={event}
layout={this.props.layout} layout={this.props.layout}
enableFlair={SettingsStore.getValue(UIFeature.Flair)} enableFlair={SettingsStore.getValue(UIFeature.Flair)}

View file

@ -0,0 +1,62 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import classNames from "classnames";
import React from "react";
import { _t } from "../../../languageHandler";
import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
reason: string;
}
interface IState {
hidden: boolean;
}
@replaceableComponent("views.elements.InviteReason")
export default class InviteReason extends React.PureComponent<IProps, IState> {
constructor(props) {
super(props);
this.state = {
// We hide the reason for invitation by default, since it can be a
// vector for spam/harassment.
hidden: true,
};
}
onViewClick = () => {
this.setState({
hidden: false,
});
}
render() {
const classes = classNames({
"mx_InviteReason": true,
"mx_InviteReason_hidden": this.state.hidden,
});
return <div className={classes}>
<div className="mx_InviteReason_reason">{this.props.reason}</div>
<div className="mx_InviteReason_view"
onClick={this.onViewClick}
>
{_t("View message")}
</div>
</div>;
}
}

View file

@ -1494,7 +1494,7 @@ const UserInfoHeader: React.FC<{
e2eIcon = <E2EIcon size={18} status={e2eStatus} isUser={true} />; e2eIcon = <E2EIcon size={18} status={e2eStatus} isUser={true} />;
} }
const displayName = member.name || member.displayname; const displayName = member.rawDisplayName || member.displayname;
return <React.Fragment> return <React.Fragment>
{ avatarElement } { avatarElement }

View file

@ -149,8 +149,8 @@ export default class AuxPanel extends React.Component<IProps, IState> {
const callView = ( const callView = (
<CallViewForRoom <CallViewForRoom
roomId={this.props.room.roomId} roomId={this.props.room.roomId}
onResize={this.props.onResize}
maxVideoHeight={this.props.maxHeight} maxVideoHeight={this.props.maxHeight}
resizeNotifier={this.props.resizeNotifier}
/> />
); );

View file

@ -1,7 +1,5 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015-2021 The Matrix.org Foundation C.I.C.
Copyright 2017 Vector Creations Ltd
Copyright 2019, 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.
@ -25,10 +23,10 @@ import classNames from 'classnames';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import SdkConfig from "../../../SdkConfig"; import SdkConfig from "../../../SdkConfig";
import IdentityAuthClient from '../../../IdentityAuthClient'; import IdentityAuthClient from '../../../IdentityAuthClient';
import SettingsStore from "../../../settings/SettingsStore";
import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore"; import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore";
import {UPDATE_EVENT} from "../../../stores/AsyncStore"; import {UPDATE_EVENT} from "../../../stores/AsyncStore";
import {replaceableComponent} from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import InviteReason from "../elements/InviteReason";
const MessageCase = Object.freeze({ const MessageCase = Object.freeze({
NotLoggedIn: "NotLoggedIn", NotLoggedIn: "NotLoggedIn",
@ -303,7 +301,6 @@ export default class RoomPreviewBar extends React.Component {
const brand = SdkConfig.get().brand; const brand = SdkConfig.get().brand;
const Spinner = sdk.getComponent('elements.Spinner'); const Spinner = sdk.getComponent('elements.Spinner');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const EventTilePreview = sdk.getComponent('elements.EventTilePreview');
let showSpinner = false; let showSpinner = false;
let title; let title;
@ -497,24 +494,7 @@ export default class RoomPreviewBar extends React.Component {
const myUserId = MatrixClientPeg.get().getUserId(); const myUserId = MatrixClientPeg.get().getUserId();
const reason = this.props.room.currentState.getMember(myUserId).events.member.event.content.reason; const reason = this.props.room.currentState.getMember(myUserId).events.member.event.content.reason;
if (reason) { if (reason) {
this.reasonElement = React.createRef(); reasonElement = <InviteReason reason={reason} />;
// We hide the reason for invitation by default, since it can be a
// vector for spam/harassment.
const showReason = () => {
this.reasonElement.current.unfade();
this.reasonElement.current.changeMessage(reason);
};
reasonElement = <EventTilePreview
ref={this.reasonElement}
onClick={showReason}
className="mx_RoomPreviewBar_reason"
message={_t("Invite messages are hidden by default. Click to show the message.")}
layout={SettingsStore.getValue("layout")}
userId={inviteMember.userId}
displayName={inviteMember.rawDisplayName}
avatarUrl={inviteMember.events.member.event.content.avatar_url}
faded={true}
/>;
} }
primaryActionHandler = this.props.onJoinClick; primaryActionHandler = this.props.onJoinClick;

View file

@ -40,9 +40,6 @@ interface IProps {
// Another ongoing call to display information about // Another ongoing call to display information about
secondaryCall?: MatrixCall, secondaryCall?: MatrixCall,
// maxHeight style attribute for the video panel
maxVideoHeight?: number;
// a callback which is called when the content in the callview changes // a callback which is called when the content in the callview changes
// in a way that is likely to cause a resize. // in a way that is likely to cause a resize.
onResize?: any; onResize?: any;
@ -96,9 +93,6 @@ function exitFullscreen() {
const CONTROLS_HIDE_DELAY = 1000; const CONTROLS_HIDE_DELAY = 1000;
// Height of the header duplicated from CSS because we need to subtract it from our max // Height of the header duplicated from CSS because we need to subtract it from our max
// height to get the max height of the video // height to get the max height of the video
const HEADER_HEIGHT = 44;
const BOTTOM_PADDING = 10;
const BOTTOM_MARGIN_TOP_BOTTOM = 10; // top margin plus bottom margin
const CONTEXT_MENU_VPADDING = 8; // How far the context menu sits above the button (px) const CONTEXT_MENU_VPADDING = 8; // How far the context menu sits above the button (px)
@replaceableComponent("views.voip.CallView") @replaceableComponent("views.voip.CallView")
@ -548,20 +542,9 @@ export default class CallView extends React.Component<IProps, IState> {
localVideoFeed = <VideoFeed type={VideoFeedType.Local} call={this.props.call} />; localVideoFeed = <VideoFeed type={VideoFeedType.Local} call={this.props.call} />;
} }
// if we're fullscreen, we don't want to set a maxHeight on the video element. contentView = <div className={containerClasses} ref={this.contentRef} onMouseMove={this.onMouseMove}>
const maxVideoHeight = getFullScreenElement() || !this.props.maxVideoHeight ? null : (
this.props.maxVideoHeight - (HEADER_HEIGHT + BOTTOM_PADDING + BOTTOM_MARGIN_TOP_BOTTOM)
);
contentView = <div className={containerClasses}
ref={this.contentRef} onMouseMove={this.onMouseMove}
// Put the max height on here too because this div is ended up 4px larger than the content
// and is causing it to scroll, and I am genuinely baffled as to why.
style={{maxHeight: maxVideoHeight}}
>
{onHoldBackground} {onHoldBackground}
<VideoFeed type={VideoFeedType.Remote} call={this.props.call} onResize={this.props.onResize} <VideoFeed type={VideoFeedType.Remote} call={this.props.call} onResize={this.props.onResize} />
maxHeight={maxVideoHeight}
/>
{localVideoFeed} {localVideoFeed}
{holdTransferContent} {holdTransferContent}
{callControls} {callControls}

View file

@ -19,6 +19,8 @@ import React from 'react';
import CallHandler from '../../../CallHandler'; import CallHandler from '../../../CallHandler';
import CallView from './CallView'; import CallView from './CallView';
import dis from '../../../dispatcher/dispatcher'; import dis from '../../../dispatcher/dispatcher';
import {Resizable} from "re-resizable";
import ResizeNotifier from "../../../utils/ResizeNotifier";
import {replaceableComponent} from "../../../utils/replaceableComponent"; import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps { interface IProps {
@ -28,9 +30,7 @@ interface IProps {
// maxHeight style attribute for the video panel // maxHeight style attribute for the video panel
maxVideoHeight?: number; maxVideoHeight?: number;
// a callback which is called when the content in the callview changes resizeNotifier: ResizeNotifier,
// in a way that is likely to cause a resize.
onResize?: any;
} }
interface IState { interface IState {
@ -79,11 +79,50 @@ export default class CallViewForRoom extends React.Component<IProps, IState> {
return call; return call;
} }
private onResizeStart = () => {
this.props.resizeNotifier.startResizing();
};
private onResize = () => {
this.props.resizeNotifier.notifyTimelineHeightChanged();
};
private onResizeStop = () => {
this.props.resizeNotifier.stopResizing();
};
public render() { public render() {
if (!this.state.call) return null; if (!this.state.call) return null;
// We subtract 8 as it the margin-bottom of the mx_CallViewForRoom_ResizeWrapper
const maxHeight = this.props.maxVideoHeight - 8;
return <CallView call={this.state.call} pipMode={false} return (
onResize={this.props.onResize} maxVideoHeight={this.props.maxVideoHeight} <div className="mx_CallViewForRoom">
/>; <Resizable
minHeight={380}
maxHeight={maxHeight}
enable={{
top: false,
right: false,
bottom: true,
left: false,
topRight: false,
bottomRight: false,
bottomLeft: false,
topLeft: false,
}}
onResizeStart={this.onResizeStart}
onResize={this.onResize}
onResizeStop={this.onResizeStop}
className="mx_CallViewForRoom_ResizeWrapper"
handleClasses={{bottom: "mx_CallViewForRoom_ResizeHandle"}}
>
<CallView
call={this.state.call}
pipMode={false}
/>
</Resizable>
</div>
);
} }
} }

View file

@ -30,9 +30,6 @@ interface IProps {
type: VideoFeedType, type: VideoFeedType,
// maxHeight style attribute for the video element
maxHeight?: number,
// a callback which is called when the video element is resized // a callback which is called when the video element is resized
// due to a change in video metadata // due to a change in video metadata
onResize?: (e: Event) => void, onResize?: (e: Event) => void,
@ -82,9 +79,6 @@ export default class VideoFeed extends React.Component<IProps> {
), ),
}; };
let videoStyle = {}; return <video className={classnames(videoClasses)} ref={this.vid} />;
if (this.props.maxHeight) videoStyle = { maxHeight: this.props.maxHeight };
return <video className={classnames(videoClasses)} ref={this.vid} style={videoStyle} />;
} }
} }

View file

@ -74,8 +74,20 @@ export interface ISecurityCustomisations {
catchAccessSecretStorageError?: typeof catchAccessSecretStorageError, catchAccessSecretStorageError?: typeof catchAccessSecretStorageError,
setupEncryptionNeeded?: typeof setupEncryptionNeeded, setupEncryptionNeeded?: typeof setupEncryptionNeeded,
getDehydrationKey?: typeof getDehydrationKey, getDehydrationKey?: typeof getDehydrationKey,
/**
* When false, disables the post-login UI from showing. If there's
* an error during setup, that will be shown to the user.
*
* Note: when this is set to false then the app will assume the user's
* encryption is set up some other way which would circumvent the default
* UI, such as by presenting alternative UI.
*/
SHOW_ENCRYPTION_SETUP_UI?: boolean, // default true
} }
// A real customisation module will define and export one or more of the // A real customisation module will define and export one or more of the
// customisation points that make up `ISecurityCustomisations`. // customisation points that make up `ISecurityCustomisations`.
export default {} as ISecurityCustomisations; export default {
SHOW_ENCRYPTION_SETUP_UI: true,
} as ISecurityCustomisations;

View file

@ -143,11 +143,11 @@ function parseElement(n: HTMLElement, partCreator: PartCreator, lastNode: HTMLEl
// math nodes are translated back into delimited latex strings // math nodes are translated back into delimited latex strings
if (n.hasAttribute("data-mx-maths")) { if (n.hasAttribute("data-mx-maths")) {
const delimLeft = (n.nodeName == "SPAN") ? const delimLeft = (n.nodeName == "SPAN") ?
(SdkConfig.get()['latex_maths_delims'] || {})['inline_left'] || "$" : ((SdkConfig.get()['latex_maths_delims'] || {})['inline'] || {})['left'] || "\\(" :
(SdkConfig.get()['latex_maths_delims'] || {})['display_left'] || "$$"; ((SdkConfig.get()['latex_maths_delims'] || {})['display'] || {})['left'] || "\\[";
const delimRight = (n.nodeName == "SPAN") ? const delimRight = (n.nodeName == "SPAN") ?
(SdkConfig.get()['latex_maths_delims'] || {})['inline_right'] || "$" : ((SdkConfig.get()['latex_maths_delims'] || {})['inline'] || {})['right'] || "\\)" :
(SdkConfig.get()['latex_maths_delims'] || {})['display_right'] || "$$"; ((SdkConfig.get()['latex_maths_delims'] || {})['display'] || {})['right'] || "\\]";
const tex = n.getAttribute("data-mx-maths"); const tex = n.getAttribute("data-mx-maths");
return partCreator.plain(delimLeft + tex + delimRight); return partCreator.plain(delimLeft + tex + delimRight);
} else if (!checkDescendInto(n)) { } else if (!checkDescendInto(n)) {

View file

@ -47,21 +47,65 @@ export function mdSerialize(model: EditorModel) {
export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) {
let md = mdSerialize(model); let md = mdSerialize(model);
// copy of raw input to remove unwanted math later
const orig = md;
if (SettingsStore.getValue("feature_latex_maths")) { if (SettingsStore.getValue("feature_latex_maths")) {
const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || const patternNames = ['tex', 'latex'];
"\\$\\$(([^$]|\\\\\\$)*)\\$\\$"; const patternTypes = ['display', 'inline'];
const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || const patternDefaults = {
"\\$(([^$]|\\\\\\$)*)\\$"; "tex": {
// detect math with tex delimiters, inline: $...$, display $$...$$
// preferably use negative lookbehinds, not supported in all major browsers:
// const displayPattern = "^(?<!\\\\)\\$\\$(?![ \\t])(([^$]|\\\\\\$)+?)\\$\\$$";
// const inlinePattern = "(?:^|\\s)(?<!\\\\)\\$(?!\\s)(([^$]|\\\\\\$)+?)(?<!\\\\|\\s)\\$";
md = md.replace(RegExp(displayPattern, "gm"), function(m, p1) { // conditions for display math detection $$...$$:
const p1e = AllHtmlEntities.encode(p1); // - pattern starts at beginning of line or is not prefixed with backslash or dollar
return `<div data-mx-maths="${p1e}">\n\n</div>\n\n`; // - left delimiter ($$) is not escaped by backslash
}); "display": "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$",
md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1) { // conditions for inline math detection $...$:
const p1e = AllHtmlEntities.encode(p1); // - pattern starts at beginning of line, follows whitespace character or punctuation
return `<span data-mx-maths="${p1e}"></span>`; // - pattern is on a single line
// - left and right delimiters ($) are not escaped by backslashes
// - left delimiter is not followed by whitespace character
// - right delimiter is not prefixed with whitespace character
"inline":
"(^|\\s|[.,!?:;])(?!\\\\)\\$(?!\\s)(([^$\\n]|\\\\\\$)*([^\\\\\\s\\$]|\\\\\\$)(?:\\\\\\$)?)\\$",
},
"latex": {
// detect math with latex delimiters, inline: \(...\), display \[...\]
// conditions for display math detection \[...\]:
// - pattern starts at beginning of line or is not prefixed with backslash
// - pattern is not empty
"display": "(^|[^\\\\])\\\\\\[(?!\\\\\\])(.*?)\\\\\\]",
// conditions for inline math detection \(...\):
// - pattern starts at beginning of line or is not prefixed with backslash
// - pattern is not empty
"inline": "(^|[^\\\\])\\\\\\((?!\\\\\\))(.*?)\\\\\\)",
},
};
patternNames.forEach(function(patternName) {
patternTypes.forEach(function(patternType) {
// get the regex replace pattern from config or use the default
const pattern = (((SdkConfig.get()["latex_maths_delims"] ||
{})[patternType] || {})["pattern"] || {})[patternName] ||
patternDefaults[patternName][patternType];
md = md.replace(RegExp(pattern, "gms"), function(m, p1, p2) {
const p2e = AllHtmlEntities.encode(p2);
switch (patternType) {
case "display":
return `${p1}<div data-mx-maths="${p2e}">\n\n</div>\n\n`;
case "inline":
return `${p1}<span data-mx-maths="${p2e}"></span>`;
}
});
});
}); });
// make sure div tags always start on a new line, otherwise it will confuse // make sure div tags always start on a new line, otherwise it will confuse
@ -73,15 +117,29 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} =
if (!parser.isPlainText() || forceHTML) { if (!parser.isPlainText() || forceHTML) {
// feed Markdown output to HTML parser // feed Markdown output to HTML parser
const phtml = cheerio.load(parser.toHTML(), const phtml = cheerio.load(parser.toHTML(),
{ _useHtmlParser2: true, decodeEntities: false }) { _useHtmlParser2: true, decodeEntities: false });
// add fallback output for latex math, which should not be interpreted as markdown if (SettingsStore.getValue("feature_latex_maths")) {
phtml('div, span').each(function(i, e) { // original Markdown without LaTeX replacements
const tex = phtml(e).attr('data-mx-maths') const parserOrig = new Markdown(orig);
if (tex) { const phtmlOrig = cheerio.load(parserOrig.toHTML(),
phtml(e).html(`<code>${tex}</code>`) { _useHtmlParser2: true, decodeEntities: false });
}
}); // since maths delimiters are handled before Markdown,
// code blocks could contain mangled content.
// replace code blocks with original content
phtmlOrig('code').each(function(i) {
phtml('code').eq(i).text(phtmlOrig('code').eq(i).text());
});
// add fallback output for latex math, which should not be interpreted as markdown
phtml('div, span').each(function(i, e) {
const tex = phtml(e).attr('data-mx-maths')
if (tex) {
phtml(e).html(`<code>${tex}</code>`)
}
});
}
return phtml.html(); return phtml.html();
} }
// ensure removal of escape backslashes in non-Markdown messages // ensure removal of escape backslashes in non-Markdown messages

View file

@ -800,7 +800,6 @@
"Show message previews for reactions in DMs": "Show message previews for reactions in DMs", "Show message previews for reactions in DMs": "Show message previews for reactions in DMs",
"Show message previews for reactions in all rooms": "Show message previews for reactions in all rooms", "Show message previews for reactions in all rooms": "Show message previews for reactions in all rooms",
"Offline encrypted messaging using dehydrated devices": "Offline encrypted messaging using dehydrated devices", "Offline encrypted messaging using dehydrated devices": "Offline encrypted messaging using dehydrated devices",
"Share decryption keys for room history when inviting users": "Share decryption keys for room history when inviting users",
"Enable advanced debugging for the room list": "Enable advanced debugging for the room list", "Enable advanced debugging for the room list": "Enable advanced debugging for the room list",
"Show info about bridges in room settings": "Show info about bridges in room settings", "Show info about bridges in room settings": "Show info about bridges in room settings",
"Font size": "Font size", "Font size": "Font size",
@ -1578,7 +1577,6 @@
"Start chatting": "Start chatting", "Start chatting": "Start chatting",
"Do you want to join %(roomName)s?": "Do you want to join %(roomName)s?", "Do you want to join %(roomName)s?": "Do you want to join %(roomName)s?",
"<userName/> invited you": "<userName/> invited you", "<userName/> invited you": "<userName/> invited you",
"Invite messages are hidden by default. Click to show the message.": "Invite messages are hidden by default. Click to show the message.",
"Reject": "Reject", "Reject": "Reject",
"Reject & Ignore user": "Reject & Ignore user", "Reject & Ignore user": "Reject & Ignore user",
"You're previewing %(roomName)s. Want to join it?": "You're previewing %(roomName)s. Want to join it?", "You're previewing %(roomName)s. Want to join it?": "You're previewing %(roomName)s. Want to join it?",
@ -1925,6 +1923,7 @@
"Rotate clockwise": "Rotate clockwise", "Rotate clockwise": "Rotate clockwise",
"Download this file": "Download this file", "Download this file": "Download this file",
"Information": "Information", "Information": "Information",
"View message": "View message",
"Language Dropdown": "Language Dropdown", "Language Dropdown": "Language Dropdown",
"%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s", "%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s",
"%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)sjoined %(count)s times", "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)sjoined %(count)s times",

View file

@ -220,12 +220,6 @@ export const SETTINGS: {[setting: string]: ISetting} = {
supportedLevels: LEVELS_FEATURE, supportedLevels: LEVELS_FEATURE,
default: false, default: false,
}, },
"feature_room_history_key_sharing": {
isFeature: true,
displayName: _td("Share decryption keys for room history when inviting users"),
supportedLevels: LEVELS_FEATURE,
default: false,
},
"advancedRoomListLogging": { "advancedRoomListLogging": {
// TODO: Remove flag before launch: https://github.com/vector-im/element-web/issues/14231 // TODO: Remove flag before launch: https://github.com/vector-im/element-web/issues/14231
displayName: _td("Enable advanced debugging for the room list"), displayName: _td("Enable advanced debugging for the room list"),

View file

@ -376,16 +376,16 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
this.onRoomsUpdate(); this.onRoomsUpdate();
} }
// if the user was looking at the room and then joined select that space
if (room.getMyMembership() === "join" && room.roomId === RoomViewStore.getRoomId()) {
this.setActiveSpace(room);
}
if (room.getMyMembership() === "join") { if (room.getMyMembership() === "join") {
const numSuggestedRooms = this._suggestedRooms.length; if (!room.isSpaceRoom()) {
this._suggestedRooms = this._suggestedRooms.filter(r => r.room_id !== room.roomId); const numSuggestedRooms = this._suggestedRooms.length;
if (numSuggestedRooms !== this._suggestedRooms.length) { this._suggestedRooms = this._suggestedRooms.filter(r => r.room_id !== room.roomId);
this.emit(SUGGESTED_ROOMS, this._suggestedRooms); if (numSuggestedRooms !== this._suggestedRooms.length) {
this.emit(SUGGESTED_ROOMS, this._suggestedRooms);
}
} else if (room.roomId === RoomViewStore.getRoomId()) {
// if the user was looking at the space and then joined: select that space
this.setActiveSpace(room);
} }
} }
}; };

View file

@ -65,12 +65,17 @@ export class NameFilterCondition extends EventEmitter implements IFilterConditio
return this.matches(room.name); return this.matches(room.name);
} }
public matches(val: string): boolean { private normalize(val: string): string {
// Note: we have to match the filter with the removeHiddenChars() room name because the // Note: we have to match the filter with the removeHiddenChars() room name because the
// function strips spaces and other characters (M becomes RN for example, in lowercase). // function strips spaces and other characters (M becomes RN for example, in lowercase).
// We also doubly convert to lowercase to work around oddities of the library. return removeHiddenChars(val.toLowerCase())
const noSecretsFilter = removeHiddenChars(this.search.toLowerCase()).toLowerCase(); // Strip all punctuation
const noSecretsName = removeHiddenChars(val.toLowerCase()).toLowerCase(); .replace(/[\\'!"#$%&()*+,\-./:;<=>?@[\]^_`{|}~\u2000-\u206f\u2e00-\u2e7f]/g, "")
return noSecretsName.includes(noSecretsFilter); // We also doubly convert to lowercase to work around oddities of the library.
.toLowerCase();
}
public matches(val: string): boolean {
return this.normalize(val).includes(this.normalize(this.search));
} }
} }