Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/socials
This commit is contained in:
commit
499f7e8af7
38 changed files with 267 additions and 238 deletions
|
@ -393,14 +393,14 @@ export default class CallHandler {
|
|||
title = _t("Unable to access microphone");
|
||||
description = <div>
|
||||
{_t(
|
||||
"Call failed because no microphone could not be accessed. " +
|
||||
"Call failed because microphone could not be accessed. " +
|
||||
"Check that a microphone is plugged in and set up correctly.",
|
||||
)}
|
||||
</div>;
|
||||
} else if (call.type === CallType.Video) {
|
||||
title = _t("Unable to access webcam / microphone");
|
||||
description = <div>
|
||||
{_t("Call failed because no webcam or microphone could not be accessed. Check that:")}
|
||||
{_t("Call failed because webcam or microphone could not be accessed. Check that:")}
|
||||
<ul>
|
||||
<li>{_t("A microphone and webcam are plugged in and set up correctly")}</li>
|
||||
<li>{_t("Permission is granted to use the webcam")}</li>
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function tsOfNewestEvent(room) {
|
||||
if (room.timeline.length) {
|
||||
return room.timeline[room.timeline.length - 1].getTs();
|
||||
} else {
|
||||
return Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
}
|
||||
|
||||
export function mostRecentActivityFirst(roomList) {
|
||||
return roomList.sort(function(a, b) {
|
||||
return tsOfNewestEvent(b) - tsOfNewestEvent(a);
|
||||
});
|
||||
}
|
|
@ -34,7 +34,6 @@ import { DecryptionFailureTracker } from "../../DecryptionFailureTracker";
|
|||
import { MatrixClientPeg, IMatrixClientCreds } from "../../MatrixClientPeg";
|
||||
import PlatformPeg from "../../PlatformPeg";
|
||||
import SdkConfig from "../../SdkConfig";
|
||||
import * as RoomListSorter from "../../RoomListSorter";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import Notifier from '../../Notifier';
|
||||
|
||||
|
@ -48,7 +47,6 @@ import * as Lifecycle from '../../Lifecycle';
|
|||
// LifecycleStore is not used but does listen to and dispatch actions
|
||||
import '../../stores/LifecycleStore';
|
||||
import PageTypes from '../../PageTypes';
|
||||
import { getHomePageUrl } from '../../utils/pages';
|
||||
|
||||
import createRoom from "../../createRoom";
|
||||
import {_t, _td, getCurrentLanguage} from '../../languageHandler';
|
||||
|
@ -591,7 +589,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
MatrixClientPeg.get().leave(payload.room_id).then(() => {
|
||||
modal.close();
|
||||
if (this.state.currentRoomId === payload.room_id) {
|
||||
dis.dispatch({action: 'view_next_room'});
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
}
|
||||
}, (err) => {
|
||||
modal.close();
|
||||
|
@ -620,9 +618,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 'view_next_room':
|
||||
this.viewNextRoom(1);
|
||||
break;
|
||||
case Action.ViewUserSettings: {
|
||||
const tabPayload = payload as OpenToTabPayload;
|
||||
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
|
||||
|
@ -802,35 +797,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
this.notifyNewScreen('register');
|
||||
}
|
||||
|
||||
// TODO: Move to RoomViewStore
|
||||
private viewNextRoom(roomIndexDelta: number) {
|
||||
const allRooms = RoomListSorter.mostRecentActivityFirst(
|
||||
MatrixClientPeg.get().getRooms(),
|
||||
);
|
||||
// If there are 0 rooms or 1 room, view the home page because otherwise
|
||||
// if there are 0, we end up trying to index into an empty array, and
|
||||
// if there is 1, we end up viewing the same room.
|
||||
if (allRooms.length < 2) {
|
||||
dis.dispatch({
|
||||
action: 'view_home_page',
|
||||
});
|
||||
return;
|
||||
}
|
||||
let roomIndex = -1;
|
||||
for (let i = 0; i < allRooms.length; ++i) {
|
||||
if (allRooms[i].roomId === this.state.currentRoomId) {
|
||||
roomIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
roomIndex = (roomIndex + roomIndexDelta) % allRooms.length;
|
||||
if (roomIndex < 0) roomIndex = allRooms.length - 1;
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: allRooms[roomIndex].roomId,
|
||||
});
|
||||
}
|
||||
|
||||
// switch view to the given room
|
||||
//
|
||||
// @param {Object} roomInfo Object containing data about the room to be joined
|
||||
|
@ -1097,9 +1063,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
|
||||
private forgetRoom(roomId: string) {
|
||||
MatrixClientPeg.get().forget(roomId).then(() => {
|
||||
// Switch to another room view if we're currently viewing the historical room
|
||||
// Switch to home page if we're currently viewing the forgotten room
|
||||
if (this.state.currentRoomId === roomId) {
|
||||
dis.dispatch({ action: "view_next_room" });
|
||||
dis.dispatch({ action: "view_home_page" });
|
||||
}
|
||||
}).catch((err) => {
|
||||
const errCode = err.errcode || _td("unknown error code");
|
||||
|
@ -1233,12 +1199,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
} else {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
dis.dispatch({action: 'view_welcome_page'});
|
||||
} else if (getHomePageUrl(this.props.config)) {
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
} else {
|
||||
this.firstSyncPromise.promise.then(() => {
|
||||
dis.dispatch({action: 'view_next_room'});
|
||||
});
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1332,7 +1332,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
rejecting: true,
|
||||
});
|
||||
this.context.leave(this.state.roomId).then(() => {
|
||||
dis.dispatch({ action: 'view_next_room' });
|
||||
dis.dispatch({ action: 'view_home_page' });
|
||||
this.setState({
|
||||
rejecting: false,
|
||||
});
|
||||
|
@ -1366,7 +1366,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
await this.context.setIgnoredUsers(ignoredUsers);
|
||||
|
||||
await this.context.leave(this.state.roomId);
|
||||
dis.dispatch({ action: 'view_next_room' });
|
||||
dis.dispatch({ action: 'view_home_page' });
|
||||
this.setState({
|
||||
rejecting: false,
|
||||
});
|
||||
|
|
|
@ -149,7 +149,7 @@ export default class MessageContextMenu extends React.Component {
|
|||
onRedactClick = () => {
|
||||
const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog");
|
||||
Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, {
|
||||
onFinished: async (proceed) => {
|
||||
onFinished: async (proceed, reason) => {
|
||||
if (!proceed) return;
|
||||
|
||||
const cli = MatrixClientPeg.get();
|
||||
|
@ -157,6 +157,8 @@ export default class MessageContextMenu extends React.Component {
|
|||
await cli.redactEvent(
|
||||
this.props.mxEvent.getRoomId(),
|
||||
this.props.mxEvent.getId(),
|
||||
undefined,
|
||||
reason ? { reason } : {},
|
||||
);
|
||||
} catch (e) {
|
||||
const code = e.errcode || e.statusCode;
|
||||
|
|
|
@ -23,15 +23,17 @@ import { _t } from '../../../languageHandler';
|
|||
*/
|
||||
export default class ConfirmRedactDialog extends React.Component {
|
||||
render() {
|
||||
const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog');
|
||||
const TextInputDialog = sdk.getComponent('views.dialogs.TextInputDialog');
|
||||
return (
|
||||
<QuestionDialog onFinished={this.props.onFinished}
|
||||
<TextInputDialog onFinished={this.props.onFinished}
|
||||
title={_t("Confirm Removal")}
|
||||
description={
|
||||
_t("Are you sure you wish to remove (delete) this event? " +
|
||||
"Note that if you delete a room name or topic change, it could undo the change.")}
|
||||
placeholder={_t("Reason (optional)")}
|
||||
focus
|
||||
button={_t("Remove")}>
|
||||
</QuestionDialog>
|
||||
</TextInputDialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,9 +53,9 @@ export default class RoomSettingsDialog extends React.Component {
|
|||
}
|
||||
|
||||
_onAction = (payload) => {
|
||||
// When room changes below us, close the room settings
|
||||
// When view changes below us, close the room settings
|
||||
// whilst the modal is open this can only be triggered when someone hits Leave Room
|
||||
if (payload.action === 'view_next_room') {
|
||||
if (payload.action === 'view_home_page') {
|
||||
this.props.onFinished();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -175,7 +175,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
|
|||
>
|
||||
<form className="mx_Dialog_content" id="mx_ServerPickerDialog" onSubmit={this.onSubmit}>
|
||||
<p>
|
||||
{_t("We call the places you where you can host your account ‘homeservers’.")} {text}
|
||||
{_t("We call the places where you can host your account ‘homeservers’.")} {text}
|
||||
</p>
|
||||
|
||||
<StyledRadioButton
|
||||
|
|
|
@ -47,9 +47,9 @@
|
|||
"Try using turn.matrix.org": "Try using turn.matrix.org",
|
||||
"OK": "OK",
|
||||
"Unable to access microphone": "Unable to access microphone",
|
||||
"Call failed because no microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "Call failed because no microphone could not be accessed. Check that a microphone is plugged in and set up correctly.",
|
||||
"Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.",
|
||||
"Unable to access webcam / microphone": "Unable to access webcam / microphone",
|
||||
"Call failed because no webcam or microphone could not be accessed. Check that:": "Call failed because no webcam or microphone could not be accessed. Check that:",
|
||||
"Call failed because webcam or microphone could not be accessed. Check that:": "Call failed because webcam or microphone could not be accessed. Check that:",
|
||||
"A microphone and webcam are plugged in and set up correctly": "A microphone and webcam are plugged in and set up correctly",
|
||||
"Permission is granted to use the webcam": "Permission is granted to use the webcam",
|
||||
"No other application is using the webcam": "No other application is using the webcam",
|
||||
|
@ -1962,6 +1962,7 @@
|
|||
"Removing…": "Removing…",
|
||||
"Confirm Removal": "Confirm Removal",
|
||||
"Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.",
|
||||
"Reason (optional)": "Reason (optional)",
|
||||
"Clear all data in this session?": "Clear all data in this session?",
|
||||
"Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.",
|
||||
"Clear all data": "Clear all data",
|
||||
|
@ -2160,7 +2161,7 @@
|
|||
"Specify a homeserver": "Specify a homeserver",
|
||||
"Matrix.org is the biggest public homeserver in the world, so it’s a good place for many.": "Matrix.org is the biggest public homeserver in the world, so it’s a good place for many.",
|
||||
"Sign into your homeserver": "Sign into your homeserver",
|
||||
"We call the places you where you can host your account ‘homeservers’.": "We call the places you where you can host your account ‘homeservers’.",
|
||||
"We call the places where you can host your account ‘homeservers’.": "We call the places where you can host your account ‘homeservers’.",
|
||||
"Other homeserver": "Other homeserver",
|
||||
"Use your preferred Matrix homeserver if you have one, or host your own.": "Use your preferred Matrix homeserver if you have one, or host your own.",
|
||||
"Learn more": "Learn more",
|
||||
|
|
|
@ -29,6 +29,8 @@ import WidgetUtils from "../utils/WidgetUtils";
|
|||
import {SettingLevel} from "../settings/SettingLevel";
|
||||
import {WidgetType} from "../widgets/WidgetType";
|
||||
import {UPDATE_EVENT} from "./AsyncStore";
|
||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
import { arrayDiff, arrayHasDiff, arrayUnion } from "../utils/arrays";
|
||||
|
||||
interface IState {}
|
||||
|
||||
|
@ -48,13 +50,16 @@ interface IRoomWidgets {
|
|||
|
||||
export const MAX_PINNED = 3;
|
||||
|
||||
function widgetUid(app: IApp): string {
|
||||
return `${app.roomId ?? MatrixClientPeg.get().getUserId()}::${app.id}`;
|
||||
}
|
||||
|
||||
// TODO consolidate WidgetEchoStore into this
|
||||
// TODO consolidate ActiveWidgetStore into this
|
||||
export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
||||
private static internalInstance = new WidgetStore();
|
||||
|
||||
// TODO: Come up with a unique key for widgets as their IDs are not globally unique, but can exist anywhere
|
||||
private widgetMap = new Map<string, IApp>(); // Key is widget ID
|
||||
private widgetMap = new Map<string, IApp>(); // Key is widget Unique ID (UID)
|
||||
private roomMap = new Map<string, IRoomWidgets>(); // Key is room ID
|
||||
|
||||
private constructor() {
|
||||
|
@ -129,14 +134,12 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
// first clean out old widgets from the map which originate from this room
|
||||
// otherwise we are out of sync with the rest of the app with stale widget events during removal
|
||||
Array.from(this.widgetMap.values()).forEach(app => {
|
||||
if (app.roomId === room.roomId) {
|
||||
this.widgetMap.delete(app.id);
|
||||
}
|
||||
this.widgetMap.delete(widgetUid(app));
|
||||
});
|
||||
|
||||
this.generateApps(room).forEach(app => {
|
||||
// Sanity check for https://github.com/vector-im/element-web/issues/15705
|
||||
const existingApp = this.widgetMap.get(app.id);
|
||||
const existingApp = this.widgetMap.get(widgetUid(app));
|
||||
if (existingApp) {
|
||||
console.warn(
|
||||
`Possible widget ID conflict for ${app.id} - wants to store in room ${app.roomId} ` +
|
||||
|
@ -144,7 +147,7 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
);
|
||||
}
|
||||
|
||||
this.widgetMap.set(app.id, app);
|
||||
this.widgetMap.set(widgetUid(app), app);
|
||||
roomInfo.widgets.push(app);
|
||||
});
|
||||
this.emit(room.roomId);
|
||||
|
@ -158,19 +161,6 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
this.emit(UPDATE_EVENT);
|
||||
};
|
||||
|
||||
public getRoomId = (widgetId: string) => {
|
||||
const app = this.widgetMap.get(widgetId);
|
||||
if (!app) return null;
|
||||
|
||||
// Sanity check for https://github.com/vector-im/element-web/issues/15705
|
||||
const roomInfo = this.getRoom(app.roomId);
|
||||
if (!roomInfo.widgets?.some(w => w.id === app.id)) {
|
||||
throw new Error(`Widget ${app.id} says it is in ${app.roomId} but was not found there`);
|
||||
}
|
||||
|
||||
return app.roomId;
|
||||
}
|
||||
|
||||
public getRoom = (roomId: string) => {
|
||||
return this.roomMap.get(roomId);
|
||||
};
|
||||
|
@ -220,7 +210,7 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
this.setPinned(roomId, widgetId, true);
|
||||
|
||||
// Show the apps drawer upon the user pinning a widget
|
||||
if (RoomViewStore.getRoomId() === this.getRoomId(widgetId)) {
|
||||
if (RoomViewStore.getRoomId() === roomId) {
|
||||
defaultDispatcher.dispatch({
|
||||
action: "appsDrawer",
|
||||
show: true,
|
||||
|
@ -295,12 +285,33 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
});
|
||||
|
||||
const order = Object.keys(roomInfo.pinned).filter(k => roomInfo.pinned[k]);
|
||||
let apps = order.map(wId => this.widgetMap.get(wId)).filter(Boolean);
|
||||
apps = apps.slice(0, priorityWidget ? MAX_PINNED - 1 : MAX_PINNED);
|
||||
const apps = order
|
||||
.map(wId => Array.from(this.widgetMap.values())
|
||||
.find(w2 => w2.roomId === roomId && w2.id === wId))
|
||||
.filter(Boolean)
|
||||
.slice(0, priorityWidget ? MAX_PINNED - 1 : MAX_PINNED);
|
||||
if (priorityWidget) {
|
||||
apps.push(priorityWidget);
|
||||
}
|
||||
|
||||
// Sanity check for https://github.com/vector-im/element-web/issues/15705
|
||||
// We union the app IDs the above generated with the roomInfo's known widgets to
|
||||
// get a list of IDs which both exist. We then diff that against the generated app
|
||||
// IDs above to ensure that all of the app IDs are captured by the union with the
|
||||
// room - if we grabbed a widget that wasn't part of the roomInfo's list, it wouldn't
|
||||
// be in the union and thus result in a diff.
|
||||
const appIds = apps.map(a => widgetUid(a));
|
||||
const roomAppIds = roomInfo.widgets.map(a => widgetUid(a));
|
||||
const roomAppIdsUnion = arrayUnion(appIds, roomAppIds);
|
||||
const missingSomeApps = arrayHasDiff(roomAppIdsUnion, appIds);
|
||||
if (missingSomeApps) {
|
||||
const diff = arrayDiff(roomAppIdsUnion, appIds);
|
||||
console.warn(
|
||||
`${roomId} appears to have a conflict for which widgets belong to it. ` +
|
||||
`Widget UIDs are: `, [...diff.added, ...diff.removed],
|
||||
);
|
||||
}
|
||||
|
||||
return apps;
|
||||
}
|
||||
|
||||
|
|
|
@ -140,6 +140,6 @@ export async function leaveRoomBehaviour(roomId: string) {
|
|||
}
|
||||
|
||||
if (RoomViewStore.getRoomId() === roomId) {
|
||||
dis.dispatch({action: 'view_next_room'});
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue