Merge branch 'develop' into bwindels/make-reply-not-overlay

This commit is contained in:
Bruno Windels 2020-08-18 13:39:06 +02:00
commit 5d98805d77
278 changed files with 6448 additions and 3045 deletions

View file

@ -58,7 +58,7 @@ export enum ChevronFace {
None = "none",
}
interface IProps extends IPosition {
export interface IProps extends IPosition {
menuWidth?: number;
menuHeight?: number;

View file

@ -210,6 +210,11 @@ const FilePanel = createReactClass({
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
const Loader = sdk.getComponent("elements.Spinner");
const emptyState = (<div className="mx_RightPanel_empty mx_FilePanel_empty">
<h2>{_t('No files visible in this room')}</h2>
<p>{_t('Attach files from chat or just drag and drop them anywhere in a room.')}</p>
</div>);
if (this.state.timelineSet) {
// console.log("rendering TimelinePanel for timelineSet " + this.state.timelineSet.room.roomId + " " +
// "(" + this.state.timelineSet._timelines.join(", ") + ")" + " with key " + this.props.roomId);
@ -223,7 +228,7 @@ const FilePanel = createReactClass({
onPaginationRequest={this.onPaginationRequest}
tileShape="file_grid"
resizeNotifier={this.props.resizeNotifier}
empty={_t('There are no visible files in this room')}
empty={emptyState}
/>
</div>
);

View file

@ -46,8 +46,8 @@ const HomePage = () => {
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
return <AutoHideScrollbar className="mx_HomePage mx_HomePage_default">
<div className="mx_HomePage_default_wrapper">
<img src={logoUrl} alt={config.brand || "Riot"} />
<h1>{ _t("Welcome to %(appName)s", { appName: config.brand || "Riot" }) }</h1>
<img src={logoUrl} alt={config.brand || "Element"} />
<h1>{ _t("Welcome to %(appName)s", { appName: config.brand || "Element" }) }</h1>
<h4>{ _t("Liberate your communication") }</h4>
<div className="mx_HomePage_default_buttons">
<AccessibleButton onClick={onClickSendDm} className="mx_HomePage_button_sendDm">

View file

@ -158,7 +158,7 @@ export default class IndicatorScrollbar extends React.Component {
}
// don't mess with the horizontal scroll for trackpad users
// See https://github.com/vector-im/riot-web/issues/10005
// See https://github.com/vector-im/element-web/issues/10005
if (this._likelyTrackpadUser) {
return;
}

View file

@ -203,7 +203,7 @@ export default createReactClass({
// the UI layer, so we ignore this signal and show a spinner until
// there's a new screen to show the user. This is implemented by setting
// `busy: false` in `_authStateUpdated`.
// See also https://github.com/vector-im/riot-web/issues/12546
// See also https://github.com/vector-im/element-web/issues/12546
},
_setFocus: function() {

View file

@ -54,6 +54,7 @@ import LeftPanel from "./LeftPanel";
import CallContainer from '../views/voip/CallContainer';
import { ViewRoomDeltaPayload } from "../../dispatcher/payloads/ViewRoomDeltaPayload";
import RoomListStore from "../../stores/room-list/RoomListStore";
import NonUrgentToastContainer from "./NonUrgentToastContainer";
import { ToggleRightPanelPayload } from "../../dispatcher/payloads/ToggleRightPanelPayload";
// We need to fetch each pinned message individually (if we don't already have it)
@ -595,7 +596,7 @@ class LoggedInView extends React.Component<IProps, IState> {
const maxRadius = 5; // People shouldn't be straying too far, hopefully
// Note: we track how far the user moved their mouse to help
// combat against https://github.com/vector-im/riot-web/issues/7158
// combat against https://github.com/vector-im/element-web/issues/7158
if (distance < maxRadius) {
// This is probably a real click, and not a drag
@ -688,6 +689,7 @@ class LoggedInView extends React.Component<IProps, IState> {
</DragDropContext>
</div>
<CallContainer />
<NonUrgentToastContainer />
</MatrixClientContext.Provider>
);
}

View file

@ -51,7 +51,7 @@ import { getHomePageUrl } from '../../utils/pages';
import createRoom from "../../createRoom";
import {_t, _td, getCurrentLanguage} from '../../languageHandler';
import SettingsStore, { SettingLevel } from "../../settings/SettingsStore";
import SettingsStore from "../../settings/SettingsStore";
import ThemeController from "../../settings/controllers/ThemeController";
import { startAnyRegistrationFlow } from "../../Registration.js";
import { messageForSyncError } from '../../utils/ErrorUtils';
@ -75,6 +75,7 @@ import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificat
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
import ErrorDialog from "../views/dialogs/ErrorDialog";
import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore";
import { SettingLevel } from "../../settings/SettingLevel";
/** constants for MatrixChat.state.view */
export enum Views {
@ -414,7 +415,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
return;
}
this.pageChanging = true;
performance.mark('riot_MatrixChat_page_change_start');
performance.mark('element_MatrixChat_page_change_start');
}
stopPageChangeTimer() {
@ -426,15 +427,15 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
return;
}
this.pageChanging = false;
performance.mark('riot_MatrixChat_page_change_stop');
performance.mark('element_MatrixChat_page_change_stop');
performance.measure(
'riot_MatrixChat_page_change_delta',
'riot_MatrixChat_page_change_start',
'riot_MatrixChat_page_change_stop',
'element_MatrixChat_page_change_delta',
'element_MatrixChat_page_change_start',
'element_MatrixChat_page_change_stop',
);
performance.clearMarks('riot_MatrixChat_page_change_start');
performance.clearMarks('riot_MatrixChat_page_change_stop');
const measurement = performance.getEntriesByName('riot_MatrixChat_page_change_delta').pop();
performance.clearMarks('element_MatrixChat_page_change_start');
performance.clearMarks('element_MatrixChat_page_change_stop');
const measurement = performance.getEntriesByName('element_MatrixChat_page_change_delta').pop();
// In practice, sometimes the entries list is empty, so we get no measurement
if (!measurement) return null;
@ -1322,7 +1323,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// state (each of which can be 10s of MBs) for each DISJOINT timeline. This is
// particularly noticeable when there are lots of 'limited' /sync responses
// such as when laptops unsleep.
// https://github.com/vector-im/riot-web/issues/3307#issuecomment-282895568
// https://github.com/vector-im/element-web/issues/3307#issuecomment-282895568
cli.setCanResetTimelineCallback((roomId) => {
console.log("Request to reset timeline in room ", roomId, " viewing:", this.state.currentRoomId);
if (roomId !== this.state.currentRoomId) {
@ -1660,7 +1661,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// of the app, we coerce the eventId to be undefined where applicable.
if (!eventId) eventId = undefined;
// TODO: Handle encoded room/event IDs: https://github.com/vector-im/riot-web/issues/9149
// TODO: Handle encoded room/event IDs: https://github.com/vector-im/element-web/issues/9149
// FIXME: sort_out caseConsistency
const thirdPartyInvite = {
@ -1934,7 +1935,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
let fragmentAfterLogin = "";
const initialScreenAfterLogin = this.props.initialScreenAfterLogin;
if (initialScreenAfterLogin &&
// XXX: workaround for https://github.com/vector-im/riot-web/issues/11643 causing a login-loop
// XXX: workaround for https://github.com/vector-im/element-web/issues/11643 causing a login-loop
!["welcome", "login", "register", "start_sso", "start_cas"].includes(initialScreenAfterLogin.screen)
) {
fragmentAfterLogin = `/${initialScreenAfterLogin.screen}`;

View file

@ -0,0 +1,63 @@
/*
Copyright 2020 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 * as React from "react";
import { ComponentClass } from "../../@types/common";
import NonUrgentToastStore from "../../stores/NonUrgentToastStore";
import { UPDATE_EVENT } from "../../stores/AsyncStore";
interface IProps {
}
interface IState {
toasts: ComponentClass[],
}
export default class NonUrgentToastContainer extends React.PureComponent<IProps, IState> {
public constructor(props, context) {
super(props, context);
this.state = {
toasts: NonUrgentToastStore.instance.components,
};
NonUrgentToastStore.instance.on(UPDATE_EVENT, this.onUpdateToasts);
}
public componentWillUnmount() {
NonUrgentToastStore.instance.off(UPDATE_EVENT, this.onUpdateToasts);
}
private onUpdateToasts = () => {
this.setState({toasts: NonUrgentToastStore.instance.components});
};
public render() {
const toasts = this.state.toasts.map((t, i) => {
return (
<div className="mx_NonUrgentToastContainer_toast" key={`toast-${i}`}>
{React.createElement(t, {})}
</div>
);
});
return (
<div className="mx_NonUrgentToastContainer" role="alert">
{toasts}
</div>
);
}
}

View file

@ -36,6 +36,11 @@ const NotificationPanel = createReactClass({
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
const Loader = sdk.getComponent("elements.Spinner");
const emptyState = (<div className="mx_RightPanel_empty mx_NotificationPanel_empty">
<h2>{_t('Youre all caught up')}</h2>
<p>{_t('You have no visible notifications in this room.')}</p>
</div>);
const timelineSet = MatrixClientPeg.get().getNotifTimelineSet();
if (timelineSet) {
return (
@ -46,7 +51,7 @@ const NotificationPanel = createReactClass({
timelineSet={timelineSet}
showUrlPreview={false}
tileShape="notif"
empty={_t('You have no visible notifications')}
empty={emptyState}
/>
</div>
);

View file

@ -48,7 +48,7 @@ import RightPanel from './RightPanel';
import RoomViewStore from '../../stores/RoomViewStore';
import RoomScrollStateStore from '../../stores/RoomScrollStateStore';
import WidgetEchoStore from '../../stores/WidgetEchoStore';
import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
import SettingsStore from "../../settings/SettingsStore";
import AccessibleButton from "../views/elements/AccessibleButton";
import RightPanelStore from "../../stores/RightPanelStore";
import {haveTileForEvent} from "../views/rooms/EventTile";
@ -56,6 +56,7 @@ import RoomContext from "../../contexts/RoomContext";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import { shieldStatusForRoom } from '../../utils/ShieldUtils';
import {Action} from "../../dispatcher/actions";
import {SettingLevel} from "../../settings/SettingLevel";
const DEBUG = false;
let debuglog = function() {};
@ -250,7 +251,7 @@ export default createReactClass({
this.context.stopPeeking();
}
// Temporary logging to diagnose https://github.com/vector-im/riot-web/issues/4307
// Temporary logging to diagnose https://github.com/vector-im/element-web/issues/4307
console.log(
'RVS update:',
newState.roomId,
@ -1077,7 +1078,7 @@ export default createReactClass({
// Do this by indicating our intention to join
// XXX: ILAG is disabled for now,
// see https://github.com/vector-im/riot-web/issues/8222
// see https://github.com/vector-im/element-web/issues/8222
dis.dispatch({action: 'require_registration'});
// dis.dispatch({
// action: 'will_join',

View file

@ -141,7 +141,7 @@ const TagPanel = createReactClass({
<AutoHideScrollbar
className="mx_TagPanel_scroller"
// XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273
// instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6253
// instead of onClick. Otherwise we experience https://github.com/vector-im/element-web/issues/6253
onMouseDown={this.onMouseDown}
>
<Droppable

View file

@ -1186,7 +1186,7 @@ const TimelinePanel = createReactClass({
if (!timeline) {
// Somehow, it seems to be possible for live events to not have
// a timeline, even though that should not happen. :(
// https://github.com/vector-im/riot-web/issues/12120
// https://github.com/vector-im/element-web/issues/12120
console.warn(
`Event ${events[i].getId()} in room ${room.roomId} is live, ` +
`but it does not have a timeline`,

View file

@ -20,13 +20,13 @@ import defaultDispatcher from "../../dispatcher/dispatcher";
import { ActionPayload } from "../../dispatcher/payloads";
import { Action } from "../../dispatcher/actions";
import { _t } from "../../languageHandler";
import { ChevronFace, ContextMenu, ContextMenuButton, MenuItem } from "./ContextMenu";
import { ContextMenuButton } from "./ContextMenu";
import {USER_NOTIFICATIONS_TAB, USER_SECURITY_TAB} from "../views/dialogs/UserSettingsDialog";
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
import RedesignFeedbackDialog from "../views/dialogs/RedesignFeedbackDialog";
import Modal from "../../Modal";
import LogoutDialog from "../views/dialogs/LogoutDialog";
import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
import SettingsStore from "../../settings/SettingsStore";
import {getCustomTheme} from "../../theme";
import {getHostingLink} from "../../utils/HostingLink";
import {ButtonEvent} from "../views/elements/AccessibleButton";
@ -37,6 +37,11 @@ import { UPDATE_EVENT } from "../../stores/AsyncStore";
import BaseAvatar from '../views/avatars/BaseAvatar';
import classNames from "classnames";
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import { SettingLevel } from "../../settings/SettingLevel";
import IconizedContextMenu, {
IconizedContextMenuOption,
IconizedContextMenuOptionList
} from "../views/context_menus/IconizedContextMenu";
interface IProps {
isMinimized: boolean;
@ -49,19 +54,6 @@ interface IState {
isDarkTheme: boolean;
}
interface IMenuButtonProps {
iconClassName: string;
label: string;
onClick(ev: ButtonEvent);
}
const MenuButton: React.FC<IMenuButtonProps> = ({iconClassName, label, onClick}) => {
return <MenuItem label={label} onClick={onClick}>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
<span className="mx_IconizedContextMenu_label">{label}</span>
</MenuItem>;
};
export default class UserMenu extends React.Component<IProps, IState> {
private dispatcherRef: string;
private themeWatcherRef: string;
@ -169,7 +161,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
ev.preventDefault();
ev.stopPropagation();
// TODO: Archived room view: https://github.com/vector-im/riot-web/issues/14038
// TODO: Archived room view: https://github.com/vector-im/element-web/issues/14038
// Note: You'll need to uncomment the button too.
console.log("TODO: Show archived rooms");
};
@ -225,7 +217,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
let homeButton = null;
if (this.hasHomePage) {
homeButton = (
<MenuButton
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconHome"
label={_t("Home")}
onClick={this.onHomeClick}
@ -233,75 +225,71 @@ export default class UserMenu extends React.Component<IProps, IState> {
);
}
return (
<ContextMenu
chevronFace={ChevronFace.None}
// -20 to overlap the context menu by just over the width of the `...` icon and make it look connected
left={this.state.contextMenuPosition.width + this.state.contextMenuPosition.left - 20}
top={this.state.contextMenuPosition.top + this.state.contextMenuPosition.height}
onFinished={this.onCloseMenu}
>
<div className="mx_IconizedContextMenu mx_UserMenu_contextMenu">
<div className="mx_UserMenu_contextMenu_header">
<div className="mx_UserMenu_contextMenu_name">
<span className="mx_UserMenu_contextMenu_displayName">
{OwnProfileStore.instance.displayName}
</span>
<span className="mx_UserMenu_contextMenu_userId">
{MatrixClientPeg.get().getUserId()}
</span>
</div>
<AccessibleTooltipButton
className="mx_UserMenu_contextMenu_themeButton"
onClick={this.onSwitchThemeClick}
title={this.state.isDarkTheme ? _t("Switch to light mode") : _t("Switch to dark mode")}
>
<img
src={require("../../../res/img/element-icons/roomlist/dark-light-mode.svg")}
alt={_t("Switch theme")}
width={16}
/>
</AccessibleTooltipButton>
</div>
{hostingLink}
<div className="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst">
{homeButton}
<MenuButton
iconClassName="mx_UserMenu_iconBell"
label={_t("Notification settings")}
onClick={(e) => this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}
/>
<MenuButton
iconClassName="mx_UserMenu_iconLock"
label={_t("Security & privacy")}
onClick={(e) => this.onSettingsOpen(e, USER_SECURITY_TAB)}
/>
<MenuButton
iconClassName="mx_UserMenu_iconSettings"
label={_t("All settings")}
onClick={(e) => this.onSettingsOpen(e, null)}
/>
{/* <MenuButton
iconClassName="mx_UserMenu_iconArchive"
label={_t("Archived rooms")}
onClick={this.onShowArchived}
/> */}
<MenuButton
iconClassName="mx_UserMenu_iconMessage"
label={_t("Feedback")}
onClick={this.onProvideFeedback}
/>
</div>
<div className="mx_IconizedContextMenu_optionList mx_UserMenu_contextMenu_redRow">
<MenuButton
iconClassName="mx_UserMenu_iconSignOut"
label={_t("Sign out")}
onClick={this.onSignOutClick}
/>
</div>
return <IconizedContextMenu
// -20 to overlap the context menu by just over the width of the `...` icon and make it look connected
left={this.state.contextMenuPosition.width + this.state.contextMenuPosition.left - 20}
top={this.state.contextMenuPosition.top + this.state.contextMenuPosition.height}
onFinished={this.onCloseMenu}
className="mx_UserMenu_contextMenu"
>
<div className="mx_UserMenu_contextMenu_header">
<div className="mx_UserMenu_contextMenu_name">
<span className="mx_UserMenu_contextMenu_displayName">
{OwnProfileStore.instance.displayName}
</span>
<span className="mx_UserMenu_contextMenu_userId">
{MatrixClientPeg.get().getUserId()}
</span>
</div>
</ContextMenu>
);
<AccessibleTooltipButton
className="mx_UserMenu_contextMenu_themeButton"
onClick={this.onSwitchThemeClick}
title={this.state.isDarkTheme ? _t("Switch to light mode") : _t("Switch to dark mode")}
>
<img
src={require("../../../res/img/element-icons/roomlist/dark-light-mode.svg")}
alt={_t("Switch theme")}
width={16}
/>
</AccessibleTooltipButton>
</div>
{hostingLink}
<IconizedContextMenuOptionList>
{homeButton}
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconBell"
label={_t("Notification settings")}
onClick={(e) => this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconLock"
label={_t("Security & privacy")}
onClick={(e) => this.onSettingsOpen(e, USER_SECURITY_TAB)}
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconSettings"
label={_t("All settings")}
onClick={(e) => this.onSettingsOpen(e, null)}
/>
{/* <IconizedContextMenuOption
iconClassName="mx_UserMenu_iconArchive"
label={_t("Archived rooms")}
onClick={this.onShowArchived}
/> */}
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconMessage"
label={_t("Feedback")}
onClick={this.onProvideFeedback}
/>
</IconizedContextMenuOptionList>
<IconizedContextMenuOptionList red>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconSignOut"
label={_t("Sign out")}
onClick={this.onSignOutClick}
/>
</IconizedContextMenuOptionList>
</IconizedContextMenu>;
};
public render() {

View file

@ -285,7 +285,7 @@ export default createReactClass({
// We'd like to rely on new props coming in via `onServerConfigChange`
// so that we know the servers have definitely updated before clearing
// the busy state. In the case of a full MXID that resolves to the same
// HS as Riot's default HS though, there may not be any server change.
// HS as Element's default HS though, there may not be any server change.
// To avoid this trap, we clear busy here. For cases where the server
// actually has changed, `_initLoginLogic` will be called and manages
// busy state for its own liveness check.
@ -615,8 +615,8 @@ export default createReactClass({
}
// XXX: This link does *not* have a target="_blank" because single sign-on relies on
// redirecting the user back to a URI once they're logged in. On the web, this means
// we use the same window and redirect back to riot. On electron, this actually
// opens the SSO page in the electron app itself due to
// we use the same window and redirect back to Element. On Electron, this actually
// opens the SSO page in the Electron app itself due to
// https://github.com/electron/electron/issues/8841 and so happens to work.
// If this bug gets fixed, it will break SSO since it will open the SSO page in the
// user's browser, let them log into their SSO provider, then redirect their browser

View file

@ -262,7 +262,7 @@ export default createReactClass({
// the user off to the login page to figure their account out.
try {
const loginLogic = new Login(hsUrl, isUrl, null, {
defaultDeviceDisplayName: "riot login check", // We shouldn't ever be used
defaultDeviceDisplayName: "Element login check", // We shouldn't ever be used
});
const flows = await loginLogic.getFlows();
const hasSsoFlow = flows.find(f => f.type === 'm.login.sso' || f.type === 'm.login.cas');

View file

@ -152,7 +152,7 @@ export default class SetupEncryptionBody extends React.Component {
</div>
<div className="mx_CompleteSecurity_clients_mobile">
<div>{_t("%(brand)s iOS", { brand })}</div>
<div>{_t("%(brand)s X for Android", { brand })}</div>
<div>{_t("%(brand)s Android", { brand })}</div>
</div>
<p>{_t("or another cross-signing capable Matrix client")}</p>
</div>