Merge remote-tracking branch 'upstream/develop' into feature/muting
This commit is contained in:
commit
cf973cdb95
20 changed files with 250 additions and 135 deletions
|
@ -250,7 +250,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
|||
|
||||
{ _t("You can change this at any time from room settings.") }
|
||||
</p>;
|
||||
} else if (this.state.joinRule === JoinRule.Public) {
|
||||
} else if (this.state.joinRule === JoinRule.Public && this.props.parentSpace) {
|
||||
publicPrivateLabel = <p>
|
||||
{ _t(
|
||||
"Anyone will be able to find and join this room, not just members of <SpaceName/>.", {}, {
|
||||
|
@ -260,6 +260,12 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
|||
|
||||
{ _t("You can change this at any time from room settings.") }
|
||||
</p>;
|
||||
} else if (this.state.joinRule === JoinRule.Public) {
|
||||
publicPrivateLabel = <p>
|
||||
{ _t("Anyone will be able to find and join this room.") }
|
||||
|
||||
{ _t("You can change this at any time from room settings.") }
|
||||
</p>;
|
||||
} else if (this.state.joinRule === JoinRule.Invite) {
|
||||
publicPrivateLabel = <p>
|
||||
{ _t(
|
||||
|
|
|
@ -67,15 +67,21 @@ export default class ReplyTile extends React.PureComponent<IProps> {
|
|||
};
|
||||
|
||||
private onClick = (e: React.MouseEvent): void => {
|
||||
// This allows the permalink to be opened in a new tab/window or copied as
|
||||
// matrix.to, but also for it to enable routing within Riot when clicked.
|
||||
e.preventDefault();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
event_id: this.props.mxEvent.getId(),
|
||||
highlighted: true,
|
||||
room_id: this.props.mxEvent.getRoomId(),
|
||||
});
|
||||
const clickTarget = e.target as HTMLElement;
|
||||
// Following a link within a reply should not dispatch the `view_room` action
|
||||
// so that the browser can direct the user to the correct location
|
||||
// The exception being the link wrapping the reply
|
||||
if (clickTarget.tagName.toLowerCase() !== "a" || clickTarget.closest("a") === null) {
|
||||
// This allows the permalink to be opened in a new tab/window or copied as
|
||||
// matrix.to, but also for it to enable routing within Riot when clicked.
|
||||
e.preventDefault();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
event_id: this.props.mxEvent.getId(),
|
||||
highlighted: true,
|
||||
room_id: this.props.mxEvent.getRoomId(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -67,6 +67,7 @@ interface IState {
|
|||
screensharing: boolean;
|
||||
callState: CallState;
|
||||
controlsVisible: boolean;
|
||||
hoveringControls: boolean;
|
||||
showMoreMenu: boolean;
|
||||
showDialpad: boolean;
|
||||
primaryFeed: CallFeed;
|
||||
|
@ -102,7 +103,7 @@ function exitFullscreen() {
|
|||
if (exitMethod) exitMethod.call(document);
|
||||
}
|
||||
|
||||
const CONTROLS_HIDE_DELAY = 1000;
|
||||
const CONTROLS_HIDE_DELAY = 2000;
|
||||
// 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
|
||||
const CONTEXT_MENU_VPADDING = 8; // How far the context menu sits above the button (px)
|
||||
|
@ -128,6 +129,7 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
screensharing: this.props.call.isScreensharing(),
|
||||
callState: this.props.call.state,
|
||||
controlsVisible: true,
|
||||
hoveringControls: false,
|
||||
showMoreMenu: false,
|
||||
showDialpad: false,
|
||||
primaryFeed: primary,
|
||||
|
@ -244,6 +246,7 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
private onControlsHideTimer = () => {
|
||||
if (this.state.hoveringControls || this.state.showDialpad || this.state.showMoreMenu) return;
|
||||
this.controlsHideTimer = null;
|
||||
this.setState({
|
||||
controlsVisible: false,
|
||||
|
@ -293,24 +296,10 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
|
||||
private onDialpadClick = (): void => {
|
||||
if (!this.state.showDialpad) {
|
||||
if (this.controlsHideTimer) {
|
||||
clearTimeout(this.controlsHideTimer);
|
||||
this.controlsHideTimer = null;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
showDialpad: true,
|
||||
controlsVisible: true,
|
||||
});
|
||||
this.setState({ showDialpad: true });
|
||||
this.showControls();
|
||||
} else {
|
||||
if (this.controlsHideTimer !== null) {
|
||||
clearTimeout(this.controlsHideTimer);
|
||||
}
|
||||
this.controlsHideTimer = window.setTimeout(this.onControlsHideTimer, CONTROLS_HIDE_DELAY);
|
||||
|
||||
this.setState({
|
||||
showDialpad: false,
|
||||
});
|
||||
this.setState({ showDialpad: false });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -345,29 +334,16 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
private onMoreClick = (): void => {
|
||||
if (this.controlsHideTimer) {
|
||||
clearTimeout(this.controlsHideTimer);
|
||||
this.controlsHideTimer = null;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
showMoreMenu: true,
|
||||
controlsVisible: true,
|
||||
});
|
||||
this.setState({ showMoreMenu: true });
|
||||
this.showControls();
|
||||
};
|
||||
|
||||
private closeDialpad = (): void => {
|
||||
this.setState({
|
||||
showDialpad: false,
|
||||
});
|
||||
this.controlsHideTimer = window.setTimeout(this.onControlsHideTimer, CONTROLS_HIDE_DELAY);
|
||||
this.setState({ showDialpad: false });
|
||||
};
|
||||
|
||||
private closeContextMenu = (): void => {
|
||||
this.setState({
|
||||
showMoreMenu: false,
|
||||
});
|
||||
this.controlsHideTimer = window.setTimeout(this.onControlsHideTimer, CONTROLS_HIDE_DELAY);
|
||||
this.setState({ showMoreMenu: false });
|
||||
};
|
||||
|
||||
// we register global shortcuts here, they *must not conflict* with local shortcuts elsewhere or both will fire
|
||||
|
@ -403,6 +379,15 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
}
|
||||
};
|
||||
|
||||
private onCallControlsMouseEnter = (): void => {
|
||||
this.setState({ hoveringControls: true });
|
||||
this.showControls();
|
||||
};
|
||||
|
||||
private onCallControlsMouseLeave = (): void => {
|
||||
this.setState({ hoveringControls: false });
|
||||
};
|
||||
|
||||
private onRoomAvatarClick = (): void => {
|
||||
const userFacingRoomId = CallHandler.sharedInstance().roomIdForCall(this.props.call);
|
||||
dis.dispatch({
|
||||
|
@ -537,8 +522,6 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
// The dial pad & 'more' button actions are only relevant in a connected call
|
||||
// When not connected, we have to put something there to make the flexbox alignment correct
|
||||
let dialpadButton;
|
||||
let contextMenuButton;
|
||||
if (this.state.callState === CallState.Connected) {
|
||||
contextMenuButton = (
|
||||
|
@ -549,6 +532,9 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
isExpanded={this.state.showMoreMenu}
|
||||
/>
|
||||
);
|
||||
}
|
||||
let dialpadButton;
|
||||
if (this.state.callState === CallState.Connected && this.props.call.opponentSupportsDTMF()) {
|
||||
dialpadButton = (
|
||||
<ContextMenuButton
|
||||
className="mx_CallView_callControls_button mx_CallView_callControls_dialpad"
|
||||
|
@ -560,7 +546,11 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={callControlsClasses}>
|
||||
<div
|
||||
className={callControlsClasses}
|
||||
onMouseEnter={this.onCallControlsMouseEnter}
|
||||
onMouseLeave={this.onCallControlsMouseLeave}
|
||||
>
|
||||
{ dialpadButton }
|
||||
<AccessibleButton
|
||||
className={micClasses}
|
||||
|
@ -821,10 +811,15 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
{ expandButton }
|
||||
</div>;
|
||||
|
||||
const callTypeIconClassName = classNames("mx_CallView_header_callTypeIcon", {
|
||||
"mx_CallView_header_callTypeIcon_voice": !isVideoCall,
|
||||
"mx_CallView_header_callTypeIcon_video": isVideoCall,
|
||||
});
|
||||
|
||||
let header: React.ReactNode;
|
||||
if (!this.props.pipMode) {
|
||||
header = <div className="mx_CallView_header">
|
||||
<div className="mx_CallView_header_phoneIcon" />
|
||||
<div className={callTypeIconClassName} />
|
||||
<span className="mx_CallView_header_callType">{ callTypeText }</span>
|
||||
{ headerControls }
|
||||
</div>;
|
||||
|
|
53
src/customisations/WidgetVariables.ts
Normal file
53
src/customisations/WidgetVariables.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Populate this class with the details of your customisations when copying it.
|
||||
import { ITemplateParams } from "matrix-widget-api";
|
||||
|
||||
/**
|
||||
* Provides a partial set of the variables needed to render any widget. If
|
||||
* variables are missing or not provided then they will be filled with the
|
||||
* application-determined defaults.
|
||||
*
|
||||
* This will not be called until after isReady() resolves.
|
||||
* @returns {Partial<Omit<ITemplateParams, "widgetRoomId">>} The variables.
|
||||
*/
|
||||
function provideVariables(): Partial<Omit<ITemplateParams, "widgetRoomId">> {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to whether or not the customisation point is ready for variables
|
||||
* to be provided. This will block widgets being rendered.
|
||||
* @returns {Promise<boolean>} Resolves when ready.
|
||||
*/
|
||||
async function isReady(): Promise<void> {
|
||||
return; // default no waiting
|
||||
}
|
||||
|
||||
// This interface summarises all available customisation points and also marks
|
||||
// them all as optional. This allows customisers to only define and export the
|
||||
// customisations they need while still maintaining type safety.
|
||||
export interface IWidgetVariablesCustomisations {
|
||||
provideVariables?: typeof provideVariables;
|
||||
|
||||
// If not provided, the app will assume that the customisation is always ready.
|
||||
isReady?: typeof isReady;
|
||||
}
|
||||
|
||||
// A real customisation module will define and export one or more of the
|
||||
// customisation points that make up the interface above.
|
||||
export const WidgetVariableCustomisations: IWidgetVariablesCustomisations = {};
|
|
@ -797,9 +797,6 @@
|
|||
"Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.": "Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.",
|
||||
"Your feedback will help make spaces better. The more detail you can go into, the better.": "Your feedback will help make spaces better. The more detail you can go into, the better.",
|
||||
"Show all rooms in Home": "Show all rooms in Home",
|
||||
"Show people in spaces": "Show people in spaces",
|
||||
"If disabled, you can still add Direct Messages to Personal Spaces. If enabled, you'll automatically see everyone who is a member of the Space.": "If disabled, you can still add Direct Messages to Personal Spaces. If enabled, you'll automatically see everyone who is a member of the Space.",
|
||||
"Show notification badges for People in Spaces": "Show notification badges for People in Spaces",
|
||||
"Show options to enable 'Do not disturb' mode": "Show options to enable 'Do not disturb' mode",
|
||||
"Send and receive voice messages": "Send and receive voice messages",
|
||||
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
|
||||
|
@ -2200,6 +2197,7 @@
|
|||
"Everyone in <SpaceName/> will be able to find and join this room.": "Everyone in <SpaceName/> will be able to find and join this room.",
|
||||
"You can change this at any time from room settings.": "You can change this at any time from room settings.",
|
||||
"Anyone will be able to find and join this room, not just members of <SpaceName/>.": "Anyone will be able to find and join this room, not just members of <SpaceName/>.",
|
||||
"Anyone will be able to find and join this room.": "Anyone will be able to find and join this room.",
|
||||
"Only people invited will be able to find and join this room.": "Only people invited will be able to find and join this room.",
|
||||
"You can’t disable this later. Bridges & most bots won’t work yet.": "You can’t disable this later. Bridges & most bots won’t work yet.",
|
||||
"Your server requires encryption to be enabled in private rooms.": "Your server requires encryption to be enabled in private rooms.",
|
||||
|
|
|
@ -181,8 +181,6 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
|||
feedbackLabel: "spaces-feedback",
|
||||
extraSettings: [
|
||||
"feature_spaces.all_rooms",
|
||||
"feature_spaces.space_member_dms",
|
||||
"feature_spaces.space_dm_badges",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
@ -192,20 +190,6 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
|||
default: true,
|
||||
controller: new ReloadOnChangeController(),
|
||||
},
|
||||
"feature_spaces.space_member_dms": {
|
||||
displayName: _td("Show people in spaces"),
|
||||
description: _td("If disabled, you can still add Direct Messages to Personal Spaces. " +
|
||||
"If enabled, you'll automatically see everyone who is a member of the Space."),
|
||||
supportedLevels: LEVELS_FEATURE,
|
||||
default: true,
|
||||
controller: new ReloadOnChangeController(),
|
||||
},
|
||||
"feature_spaces.space_dm_badges": {
|
||||
displayName: _td("Show notification badges for People in Spaces"),
|
||||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
controller: new ReloadOnChangeController(),
|
||||
},
|
||||
"feature_dnd": {
|
||||
isFeature: true,
|
||||
displayName: _td("Show options to enable 'Do not disturb' mode"),
|
||||
|
|
|
@ -72,8 +72,6 @@ const MAX_SUGGESTED_ROOMS = 20;
|
|||
// All of these settings cause the page to reload and can be costly if read frequently, so read them here only
|
||||
const spacesEnabled = SettingsStore.getValue("feature_spaces");
|
||||
const spacesTweakAllRoomsEnabled = SettingsStore.getValue("feature_spaces.all_rooms");
|
||||
const spacesTweakSpaceMemberDMsEnabled = SettingsStore.getValue("feature_spaces.space_member_dms");
|
||||
const spacesTweakSpaceDMBadgesEnabled = SettingsStore.getValue("feature_spaces.space_dm_badges");
|
||||
|
||||
const homeSpaceKey = spacesTweakAllRoomsEnabled ? "ALL_ROOMS" : "HOME_SPACE";
|
||||
const getSpaceContextKey = (space?: Room) => `mx_space_context_${space?.roomId || homeSpaceKey}`;
|
||||
|
@ -535,15 +533,13 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
const roomIds = new Set(childRooms.map(r => r.roomId));
|
||||
const space = this.matrixClient?.getRoom(spaceId);
|
||||
|
||||
if (spacesTweakSpaceMemberDMsEnabled) {
|
||||
// Add relevant DMs
|
||||
space?.getMembers().forEach(member => {
|
||||
if (member.membership !== "join" && member.membership !== "invite") return;
|
||||
DMRoomMap.shared().getDMRoomsForUserId(member.userId).forEach(roomId => {
|
||||
roomIds.add(roomId);
|
||||
});
|
||||
// Add relevant DMs
|
||||
space?.getMembers().forEach(member => {
|
||||
if (member.membership !== "join" && member.membership !== "invite") return;
|
||||
DMRoomMap.shared().getDMRoomsForUserId(member.userId).forEach(roomId => {
|
||||
roomIds.add(roomId);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const newPath = new Set(parentPath).add(spaceId);
|
||||
childSpaces.forEach(childSpace => {
|
||||
|
@ -568,14 +564,13 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
this.spaceFilteredRooms.forEach((roomIds, s) => {
|
||||
// Update NotificationStates
|
||||
this.getNotificationState(s)?.setRooms(visibleRooms.filter(room => {
|
||||
if (roomIds.has(room.roomId)) {
|
||||
if (s !== HOME_SPACE && spacesTweakSpaceDMBadgesEnabled) return true;
|
||||
if (!roomIds.has(room.roomId)) return false;
|
||||
|
||||
return !DMRoomMap.shared().getUserIdForRoomId(room.roomId)
|
||||
|| RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite);
|
||||
if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) {
|
||||
return s === HOME_SPACE;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}));
|
||||
});
|
||||
}, 100, { trailing: true, leading: true });
|
||||
|
@ -878,8 +873,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
export default class SpaceStore {
|
||||
public static spacesEnabled = spacesEnabled;
|
||||
public static spacesTweakAllRoomsEnabled = spacesTweakAllRoomsEnabled;
|
||||
public static spacesTweakSpaceMemberDMsEnabled = spacesTweakSpaceMemberDMsEnabled;
|
||||
public static spacesTweakSpaceDMBadgesEnabled = spacesTweakSpaceDMBadgesEnabled;
|
||||
|
||||
private static internalInstance = new SpaceStoreClass();
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import { NOTIFICATION_STATE_UPDATE, NotificationState } from "./NotificationStat
|
|||
import { FetchRoomFn } from "./ListNotificationState";
|
||||
|
||||
export class SpaceNotificationState extends NotificationState {
|
||||
private rooms: Room[] = [];
|
||||
public rooms: Room[] = []; // exposed only for tests
|
||||
private states: { [spaceId: string]: RoomNotificationState } = {};
|
||||
|
||||
constructor(private spaceId: string | symbol, private getRoomFn: FetchRoomFn) {
|
||||
|
|
|
@ -54,6 +54,7 @@ import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
|
|||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { ELEMENT_CLIENT_ID } from "../../identifiers";
|
||||
import { getUserLanguage } from "../../languageHandler";
|
||||
import { WidgetVariableCustomisations } from "../../customisations/WidgetVariables";
|
||||
|
||||
// TODO: Destroy all of this code
|
||||
|
||||
|
@ -191,7 +192,8 @@ export class StopGapWidget extends EventEmitter {
|
|||
}
|
||||
|
||||
private runUrlTemplate(opts = { asPopout: false }): string {
|
||||
const templated = this.mockWidget.getCompleteUrl({
|
||||
const fromCustomisation = WidgetVariableCustomisations?.provideVariables?.() ?? {};
|
||||
const defaults: ITemplateParams = {
|
||||
widgetRoomId: this.roomId,
|
||||
currentUserId: MatrixClientPeg.get().getUserId(),
|
||||
userDisplayName: OwnProfileStore.instance.displayName,
|
||||
|
@ -199,7 +201,8 @@ export class StopGapWidget extends EventEmitter {
|
|||
clientId: ELEMENT_CLIENT_ID,
|
||||
clientTheme: SettingsStore.getValue("theme"),
|
||||
clientLanguage: getUserLanguage(),
|
||||
}, opts?.asPopout);
|
||||
};
|
||||
const templated = this.mockWidget.getCompleteUrl(Object.assign(defaults, fromCustomisation), opts?.asPopout);
|
||||
|
||||
const parsed = new URL(templated);
|
||||
|
||||
|
@ -363,6 +366,9 @@ export class StopGapWidget extends EventEmitter {
|
|||
}
|
||||
|
||||
public async prepare(): Promise<void> {
|
||||
// Ensure the variables are ready for us to be rendered before continuing
|
||||
await (WidgetVariableCustomisations?.isReady?.() ?? Promise.resolve());
|
||||
|
||||
if (this.scalarToken) return;
|
||||
const existingMessaging = WidgetMessagingStore.instance.getMessaging(this.mockWidget);
|
||||
if (existingMessaging) this.messaging = existingMessaging;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue