Merge branch 'develop' of https://github.com/matrix-org/matrix-react-sdk into travis/download-logs
This commit is contained in:
commit
df980dbf92
173 changed files with 1409 additions and 1502 deletions
|
@ -58,7 +58,7 @@ export enum ChevronFace {
|
|||
None = "none",
|
||||
}
|
||||
|
||||
interface IProps extends IPosition {
|
||||
export interface IProps extends IPosition {
|
||||
menuWidth?: number;
|
||||
menuHeight?: number;
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -596,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
|
||||
|
|
|
@ -415,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() {
|
||||
|
@ -427,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;
|
||||
|
@ -1323,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) {
|
||||
|
@ -1661,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 = {
|
||||
|
@ -1935,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}`;
|
||||
|
|
|
@ -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('You’re 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>
|
||||
);
|
||||
|
|
|
@ -251,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,
|
||||
|
@ -1078,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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`,
|
||||
|
|
|
@ -20,7 +20,7 @@ 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";
|
||||
|
@ -38,6 +38,10 @@ 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;
|
||||
|
@ -50,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;
|
||||
|
@ -170,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");
|
||||
};
|
||||
|
@ -226,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}
|
||||
|
@ -234,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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -109,7 +109,7 @@ export const PasswordAuthEntry = createReactClass({
|
|||
this.props.submitAuthDict({
|
||||
type: PasswordAuthEntry.LOGIN_TYPE,
|
||||
// TODO: Remove `user` once servers support proper UIA
|
||||
// See https://github.com/vector-im/riot-web/issues/10312
|
||||
// See https://github.com/vector-im/element-web/issues/10312
|
||||
user: this.props.matrixClient.credentials.userId,
|
||||
identifier: {
|
||||
type: "m.id.user",
|
||||
|
@ -538,7 +538,7 @@ export const MsisdnAuthEntry = createReactClass({
|
|||
this.props.submitAuthDict({
|
||||
type: MsisdnAuthEntry.LOGIN_TYPE,
|
||||
// TODO: Remove `threepid_creds` once servers support proper UIA
|
||||
// See https://github.com/vector-im/riot-web/issues/10312
|
||||
// See https://github.com/vector-im/element-web/issues/10312
|
||||
// See https://github.com/matrix-org/matrix-doc/issues/2220
|
||||
threepid_creds: creds,
|
||||
threepidCreds: creds,
|
||||
|
|
124
src/components/views/context_menus/IconizedContextMenu.tsx
Normal file
124
src/components/views/context_menus/IconizedContextMenu.tsx
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
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 React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
import {
|
||||
ChevronFace,
|
||||
ContextMenu,
|
||||
IProps as IContextMenuProps,
|
||||
MenuItem,
|
||||
MenuItemCheckbox, MenuItemRadio,
|
||||
} from "../../structures/ContextMenu";
|
||||
|
||||
interface IProps extends IContextMenuProps {
|
||||
className?: string;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
interface IOptionListProps {
|
||||
first?: boolean;
|
||||
red?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface IOptionProps extends React.ComponentProps<typeof MenuItem> {
|
||||
iconClassName: string;
|
||||
}
|
||||
|
||||
interface ICheckboxProps extends React.ComponentProps<typeof MenuItemCheckbox> {
|
||||
iconClassName: string;
|
||||
}
|
||||
|
||||
interface IRadioProps extends React.ComponentProps<typeof MenuItemRadio> {
|
||||
iconClassName: string;
|
||||
}
|
||||
|
||||
export const IconizedContextMenuRadio: React.FC<IRadioProps> = ({
|
||||
label,
|
||||
iconClassName,
|
||||
active,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
return <MenuItemRadio
|
||||
{...props}
|
||||
className={classNames(className, {
|
||||
mx_IconizedContextMenu_active: active,
|
||||
})}
|
||||
active={active}
|
||||
label={label}
|
||||
>
|
||||
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
|
||||
<span className="mx_IconizedContextMenu_label">{label}</span>
|
||||
{active && <span className="mx_IconizedContextMenu_icon mx_IconizedContextMenu_checked" />}
|
||||
</MenuItemRadio>;
|
||||
};
|
||||
|
||||
export const IconizedContextMenuCheckbox: React.FC<ICheckboxProps> = ({
|
||||
label,
|
||||
iconClassName,
|
||||
active,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
return <MenuItemCheckbox
|
||||
{...props}
|
||||
className={classNames(className, {
|
||||
mx_IconizedContextMenu_active: active,
|
||||
})}
|
||||
active={active}
|
||||
label={label}
|
||||
>
|
||||
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
|
||||
<span className="mx_IconizedContextMenu_label">{label}</span>
|
||||
{active && <span className="mx_IconizedContextMenu_icon mx_IconizedContextMenu_checked" />}
|
||||
</MenuItemCheckbox>;
|
||||
};
|
||||
|
||||
export const IconizedContextMenuOption: React.FC<IOptionProps> = ({label, iconClassName, ...props}) => {
|
||||
return <MenuItem {...props} label={label}>
|
||||
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
|
||||
<span className="mx_IconizedContextMenu_label">{label}</span>
|
||||
</MenuItem>;
|
||||
};
|
||||
|
||||
export const IconizedContextMenuOptionList: React.FC<IOptionListProps> = ({first, red, className, children}) => {
|
||||
const classes = classNames("mx_IconizedContextMenu_optionList", className, {
|
||||
mx_IconizedContextMenu_optionList_notFirst: !first,
|
||||
mx_IconizedContextMenu_optionList_red: red,
|
||||
});
|
||||
|
||||
return <div className={classes}>
|
||||
{children}
|
||||
</div>;
|
||||
};
|
||||
|
||||
const IconizedContextMenu: React.FC<IProps> = ({className, children, compact, ...props}) => {
|
||||
const classes = classNames("mx_IconizedContextMenu", className, {
|
||||
mx_IconizedContextMenu_compact: compact,
|
||||
});
|
||||
|
||||
return <ContextMenu chevronFace={ChevronFace.None} {...props}>
|
||||
<div className={classes}>
|
||||
{ children }
|
||||
</div>
|
||||
</ContextMenu>;
|
||||
};
|
||||
|
||||
export default IconizedContextMenu;
|
||||
|
|
@ -193,7 +193,7 @@ export default class BugReportDialog extends React.Component {
|
|||
{
|
||||
a: (sub) => <a
|
||||
target="_blank"
|
||||
href="https://github.com/vector-im/riot-web/issues/new"
|
||||
href="https://github.com/vector-im/element-web/issues/new"
|
||||
>
|
||||
{ sub }
|
||||
</a>,
|
||||
|
@ -211,7 +211,7 @@ export default class BugReportDialog extends React.Component {
|
|||
label={_t("GitHub issue")}
|
||||
onChange={this._onIssueUrlChange}
|
||||
value={this.state.issueUrl}
|
||||
placeholder="https://github.com/vector-im/riot-web/issues/..."
|
||||
placeholder="https://github.com/vector-im/element-web/issues/..."
|
||||
/>
|
||||
<Field
|
||||
className="mx_BugReportDialog_field_input"
|
||||
|
|
|
@ -21,7 +21,7 @@ import * as sdk from '../../../index';
|
|||
import request from 'browser-request';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
const REPOS = ['vector-im/riot-web', 'matrix-org/matrix-react-sdk', 'matrix-org/matrix-js-sdk'];
|
||||
const REPOS = ['vector-im/element-web', 'matrix-org/matrix-react-sdk', 'matrix-org/matrix-js-sdk'];
|
||||
|
||||
export default class ChangelogDialog extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
|
@ -349,7 +349,7 @@ export default class InviteDialog extends React.PureComponent {
|
|||
|
||||
// Also pull in all the rooms tagged as DefaultTagID.DM so we don't miss anything. Sometimes the
|
||||
// room list doesn't tag the room for the DMRoomMap, but does for the room list.
|
||||
const dmTaggedRooms = RoomListStore.instance.orderedLists[DefaultTagID.DM];
|
||||
const dmTaggedRooms = RoomListStore.instance.orderedLists[DefaultTagID.DM] || [];
|
||||
const myUserId = MatrixClientPeg.get().getUserId();
|
||||
for (const dmRoom of dmTaggedRooms) {
|
||||
const otherMembers = dmRoom.getJoinedMembers().filter(u => u.userId !== myUserId);
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
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 * as PropTypes from 'prop-types';
|
||||
import BaseDialog from './BaseDialog';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import DialogButtons from '../elements/DialogButtons';
|
||||
|
||||
export enum RebrandDialogKind {
|
||||
NAG,
|
||||
ONE_TIME,
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
onFinished: (bool) => void;
|
||||
kind: RebrandDialogKind;
|
||||
targetUrl?: string;
|
||||
}
|
||||
|
||||
export default class RebrandDialog extends React.PureComponent<IProps> {
|
||||
private onDoneClick = () => {
|
||||
this.props.onFinished(true);
|
||||
};
|
||||
|
||||
private onGoToElementClick = () => {
|
||||
this.props.onFinished(true);
|
||||
};
|
||||
|
||||
private onRemindMeLaterClick = () => {
|
||||
this.props.onFinished(false);
|
||||
};
|
||||
|
||||
private getPrettyTargetUrl() {
|
||||
const u = new URL(this.props.targetUrl);
|
||||
let ret = u.host;
|
||||
if (u.pathname !== '/') ret += u.pathname;
|
||||
return ret;
|
||||
}
|
||||
|
||||
getBodyText() {
|
||||
if (this.props.kind === RebrandDialogKind.NAG) {
|
||||
return _t(
|
||||
"Use your account to sign in to the latest version of the app at <a />", {},
|
||||
{
|
||||
a: sub => <a href={this.props.targetUrl} rel="noopener noreferrer" target="_blank">{this.getPrettyTargetUrl()}</a>,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return _t(
|
||||
"You’re already signed in and good to go here, but you can also grab the latest " +
|
||||
"versions of the app on all platforms at <a>element.io/get-started</a>.", {},
|
||||
{
|
||||
a: sub => <a href="https://element.io/get-started" rel="noopener noreferrer" target="_blank">{sub}</a>,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getDialogButtons() {
|
||||
if (this.props.kind === RebrandDialogKind.NAG) {
|
||||
return <DialogButtons primaryButton={_t("Go to Element")}
|
||||
primaryButtonClass='primary'
|
||||
onPrimaryButtonClick={this.onGoToElementClick}
|
||||
hasCancel={true}
|
||||
cancelButton={"Remind me later"}
|
||||
onCancel={this.onRemindMeLaterClick}
|
||||
focus={true}
|
||||
/>;
|
||||
} else {
|
||||
return <DialogButtons primaryButton={_t("Done")}
|
||||
primaryButtonClass='primary'
|
||||
hasCancel={false}
|
||||
onPrimaryButtonClick={this.onDoneClick}
|
||||
focus={true}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <BaseDialog title={_t("We’re excited to announce Riot is now Element!")}
|
||||
className='mx_RebrandDialog'
|
||||
contentId='mx_Dialog_content'
|
||||
onFinished={this.props.onFinished}
|
||||
hasCancel={false}
|
||||
>
|
||||
<div className="mx_RebrandDialog_body">{this.getBodyText()}</div>
|
||||
<div className="mx_RebrandDialog_logoContainer">
|
||||
<img className="mx_RebrandDialog_logo" src={require("../../../../res/img/riot-logo.svg")} alt="Riot Logo" />
|
||||
<span className="mx_RebrandDialog_chevron" />
|
||||
<img className="mx_RebrandDialog_logo" src={require("../../../../res/img/element-logo.svg")} alt="Element Logo" />
|
||||
</div>
|
||||
<div>
|
||||
{_t(
|
||||
"Learn more at <a>element.io/previously-riot</a>", {}, {
|
||||
a: sub => <a href="https://element.io/previously-riot" rel="noopener noreferrer" target="_blank">{sub}</a>,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
{this.getDialogButtons()}
|
||||
</BaseDialog>;
|
||||
}
|
||||
}
|
|
@ -19,9 +19,9 @@ import QuestionDialog from './QuestionDialog';
|
|||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default (props) => {
|
||||
const existingIssuesUrl = "https://github.com/vector-im/riot-web/issues" +
|
||||
const existingIssuesUrl = "https://github.com/vector-im/element-web/issues" +
|
||||
"?q=is%3Aopen+is%3Aissue+sort%3Areactions-%2B1-desc";
|
||||
const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new";
|
||||
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new";
|
||||
|
||||
const description1 =
|
||||
_t("If you run into any bugs or have feedback you'd like to share, " +
|
||||
|
|
|
@ -30,6 +30,7 @@ import * as ContextMenu from "../../structures/ContextMenu";
|
|||
import {toRightOf} from "../../structures/ContextMenu";
|
||||
import {copyPlaintext, selectText} from "../../../utils/strings";
|
||||
import StyledCheckbox from '../elements/StyledCheckbox';
|
||||
import AccessibleTooltipButton from '../elements/AccessibleTooltipButton';
|
||||
|
||||
const socials = [
|
||||
{
|
||||
|
@ -210,10 +211,11 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
|
|||
>
|
||||
{ matrixToUrl }
|
||||
</a>
|
||||
<a href={matrixToUrl} className="mx_ShareDialog_matrixto_copy" onClick={this.onCopyClick}>
|
||||
{ _t('COPY') }
|
||||
<div> </div>
|
||||
</a>
|
||||
<AccessibleTooltipButton
|
||||
title={_t("Copy")}
|
||||
onClick={this.onCopyClick}
|
||||
className="mx_ShareDialog_matrixto_copy"
|
||||
/>
|
||||
</div>
|
||||
{ checkbox }
|
||||
<hr />
|
||||
|
|
|
@ -314,13 +314,13 @@ export default class AppTile extends React.Component {
|
|||
if (SettingsStore.isFeatureEnabled("feature_many_integration_managers")) {
|
||||
IntegrationManagers.sharedInstance().openAll(
|
||||
this.props.room,
|
||||
'type_' + this.props.type,
|
||||
'type_' + this.props.app.type,
|
||||
this.props.app.id,
|
||||
);
|
||||
} else {
|
||||
IntegrationManagers.sharedInstance().getPrimaryManager().open(
|
||||
this.props.room,
|
||||
'type_' + this.props.type,
|
||||
'type_' + this.props.app.type,
|
||||
this.props.app.id,
|
||||
);
|
||||
}
|
||||
|
@ -361,14 +361,14 @@ export default class AppTile extends React.Component {
|
|||
return terminationPromise.finally(() => {
|
||||
// HACK: This is a really dirty way to ensure that Jitsi cleans up
|
||||
// its hold on the webcam. Without this, the widget holds a media
|
||||
// stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351
|
||||
// stream open, even after death. See https://github.com/vector-im/element-web/issues/7351
|
||||
if (this._appFrame.current) {
|
||||
// In practice we could just do `+= ''` to trick the browser
|
||||
// into thinking the URL changed, however I can foresee this
|
||||
// being optimized out by a browser. Instead, we'll just point
|
||||
// the iframe at a page that is reasonably safe to use in the
|
||||
// event the iframe doesn't wink away.
|
||||
// This is relative to where the Riot instance is located.
|
||||
// This is relative to where the Element instance is located.
|
||||
this._appFrame.current.src = 'about:blank';
|
||||
}
|
||||
|
||||
|
@ -727,7 +727,7 @@ export default class AppTile extends React.Component {
|
|||
|
||||
// Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
|
||||
// because that would allow the iframe to programmatically remove the sandbox attribute, but
|
||||
// this would only be for content hosted on the same origin as the riot client: anything
|
||||
// this would only be for content hosted on the same origin as the element client: anything
|
||||
// hosted on the same origin as the client will get the same access as if you clicked
|
||||
// a link to it.
|
||||
const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox "+
|
||||
|
@ -924,7 +924,7 @@ AppTile.propTypes = {
|
|||
// Optionally show the reload widget icon
|
||||
// This is not currently intended for use with production widgets. However
|
||||
// it can be useful when developing persistent widgets in order to avoid
|
||||
// having to reload all of riot to get new widget content.
|
||||
// having to reload all of Element to get new widget content.
|
||||
showReload: PropTypes.bool,
|
||||
// Widget capabilities to allow by default (without user confirmation)
|
||||
// NOTE -- Use with caution. This is intended to aid better integration / UX
|
||||
|
|
|
@ -72,7 +72,7 @@ export default class ErrorBoundary extends React.PureComponent {
|
|||
render() {
|
||||
if (this.state.error) {
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new";
|
||||
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new";
|
||||
return <div className="mx_ErrorBoundary">
|
||||
<div className="mx_ErrorBoundary_body">
|
||||
<h1>{_t("Something went wrong!")}</h1>
|
||||
|
|
|
@ -25,6 +25,7 @@ interface IDefinition<T extends string> {
|
|||
disabled?: boolean;
|
||||
label: React.ReactChild;
|
||||
description?: React.ReactChild;
|
||||
checked?: boolean; // If provided it will override the value comparison done in the group
|
||||
}
|
||||
|
||||
interface IProps<T extends string> {
|
||||
|
@ -33,7 +34,7 @@ interface IProps<T extends string> {
|
|||
definitions: IDefinition<T>[];
|
||||
value?: T; // if not provided no options will be selected
|
||||
outlined?: boolean;
|
||||
onChange(newValue: T);
|
||||
onChange(newValue: T): void;
|
||||
}
|
||||
|
||||
function StyledRadioGroup<T extends string>({name, definitions, value, className, outlined, onChange}: IProps<T>) {
|
||||
|
@ -46,7 +47,7 @@ function StyledRadioGroup<T extends string>({name, definitions, value, className
|
|||
<StyledRadioButton
|
||||
className={classNames(className, d.className)}
|
||||
onChange={_onChange}
|
||||
checked={d.value === value}
|
||||
checked={d.checked !== undefined ? d.checked : d.value === value}
|
||||
name={name}
|
||||
value={d.value}
|
||||
disabled={d.disabled}
|
||||
|
|
|
@ -126,7 +126,7 @@ const GroupTile = createReactClass({
|
|||
}
|
||||
|
||||
// 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/6156
|
||||
// instead of onClick. Otherwise we experience https://github.com/vector-im/element-web/issues/6156
|
||||
return <AccessibleButton className="mx_GroupTile" onMouseDown={this.onMouseDown} onClick={nop}>
|
||||
{ avatarElement }
|
||||
<div className="mx_GroupTile_profile">
|
||||
|
|
|
@ -39,6 +39,8 @@ interface IProps {
|
|||
title: string;
|
||||
}
|
||||
|
||||
// TODO: replace this, the composer buttons and the right panel buttons with a unified
|
||||
// representation
|
||||
export default class HeaderButton extends React.Component<IProps> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
|
|
@ -550,7 +550,9 @@ const RedactMessagesButton = ({member}) => {
|
|||
let eventsToRedact = [];
|
||||
while (timeline) {
|
||||
eventsToRedact = timeline.getEvents().reduce((events, event) => {
|
||||
if (event.getSender() === userId && !event.isRedacted() && !event.isRedaction()) {
|
||||
if (event.getSender() === userId && !event.isRedacted() && !event.isRedaction() &&
|
||||
event.getType() !== "m.room.create"
|
||||
) {
|
||||
return events.concat(event);
|
||||
} else {
|
||||
return events;
|
||||
|
|
|
@ -208,7 +208,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
});
|
||||
let body: JSX.Element;
|
||||
if (this.state.reciprocateQREvent) {
|
||||
// riot web doesn't support scanning yet, so assume here we're the client being scanned.
|
||||
// Element Web doesn't support scanning yet, so assume here we're the client being scanned.
|
||||
//
|
||||
// we're passing both a label and a child string to Button as
|
||||
// FormButton and AccessibleButton expect this differently
|
||||
|
|
|
@ -233,7 +233,7 @@ export default class AliasSettings extends React.Component {
|
|||
onLocalAliasDeleted = (index) => {
|
||||
const alias = this.state.localAliases[index];
|
||||
// TODO: In future, we should probably be making sure that the alias actually belongs
|
||||
// to this room. See https://github.com/vector-im/riot-web/issues/7353
|
||||
// to this room. See https://github.com/vector-im/element-web/issues/7353
|
||||
MatrixClientPeg.get().deleteAlias(alias).then(() => {
|
||||
const localAliases = this.state.localAliases.filter(a => a !== alias);
|
||||
this.setState({localAliases});
|
||||
|
|
|
@ -39,7 +39,7 @@ const ROOM_COLORS = [
|
|||
|
||||
// Dev note: this component is not attached anywhere, but is left here as it
|
||||
// has a high possibility of being used in the nearish future.
|
||||
// Ref: https://github.com/vector-im/riot-web/issues/8421
|
||||
// Ref: https://github.com/vector-im/element-web/issues/8421
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'ColorSettings',
|
||||
|
|
|
@ -242,7 +242,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
// so trigger a model update after the composition is done by calling the input handler.
|
||||
|
||||
// however, modifying the DOM (caused by the editor model update) from the compositionend handler seems
|
||||
// to confuse the IME in Chrome, likely causing https://github.com/vector-im/riot-web/issues/10913 ,
|
||||
// to confuse the IME in Chrome, likely causing https://github.com/vector-im/element-web/issues/10913 ,
|
||||
// so we do it async
|
||||
|
||||
// however, doing this async seems to break things in Safari for some reason, so browser sniff.
|
||||
|
@ -273,7 +273,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
const {model} = this.props;
|
||||
const range = getRangeForSelection(this.editorRef.current, model, selection);
|
||||
const selectedParts = range.parts.map(p => p.serialize());
|
||||
event.clipboardData.setData("application/x-riot-composer", JSON.stringify(selectedParts));
|
||||
event.clipboardData.setData("application/x-element-composer", JSON.stringify(selectedParts));
|
||||
event.clipboardData.setData("text/plain", text); // so plain copy/paste works
|
||||
if (type === "cut") {
|
||||
// Remove the text, updating the model as appropriate
|
||||
|
@ -301,7 +301,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
|
||||
const {model} = this.props;
|
||||
const {partCreator} = model;
|
||||
const partsText = event.clipboardData.getData("application/x-riot-composer");
|
||||
const partsText = event.clipboardData.getData("application/x-element-composer");
|
||||
let parts;
|
||||
if (partsText) {
|
||||
const serializedTextParts = JSON.parse(partsText);
|
||||
|
|
|
@ -60,7 +60,7 @@ const stateEventTileTypes = {
|
|||
'm.room.power_levels': 'messages.TextualEvent',
|
||||
'm.room.pinned_events': 'messages.TextualEvent',
|
||||
'm.room.server_acl': 'messages.TextualEvent',
|
||||
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
|
||||
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
|
||||
'im.vector.modular.widgets': 'messages.TextualEvent',
|
||||
'm.room.tombstone': 'messages.TextualEvent',
|
||||
'm.room.join_rules': 'messages.TextualEvent',
|
||||
|
@ -519,7 +519,7 @@ export default createReactClass({
|
|||
|
||||
onPermalinkClicked: function(e) {
|
||||
// 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.
|
||||
// matrix.to, but also for it to enable routing within Element when clicked.
|
||||
e.preventDefault();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
|
@ -595,11 +595,11 @@ export default createReactClass({
|
|||
}
|
||||
const eventId = this.props.mxEvent.getId();
|
||||
if (!eventId) {
|
||||
// XXX: Temporary diagnostic logging for https://github.com/vector-im/riot-web/issues/11120
|
||||
// XXX: Temporary diagnostic logging for https://github.com/vector-im/element-web/issues/11120
|
||||
console.error("EventTile attempted to get relations for an event without an ID");
|
||||
// Use event's special `toJSON` method to log key data.
|
||||
console.log(JSON.stringify(this.props.mxEvent, null, 4));
|
||||
console.trace("Stacktrace for https://github.com/vector-im/riot-web/issues/11120");
|
||||
console.trace("Stacktrace for https://github.com/vector-im/element-web/issues/11120");
|
||||
}
|
||||
return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction");
|
||||
},
|
||||
|
|
|
@ -15,6 +15,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
import React, {createRef} from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import CallHandler from '../../../CallHandler';
|
||||
|
@ -29,7 +30,6 @@ import E2EIcon from './E2EIcon';
|
|||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from "../../structures/ContextMenu";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
|
||||
function ComposerAvatar(props) {
|
||||
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
|
||||
return <div className="mx_MessageComposer_avatar">
|
||||
|
@ -117,9 +117,19 @@ const EmojiButton = ({addEmoji}) => {
|
|||
</ContextMenu>;
|
||||
}
|
||||
|
||||
const className = classNames(
|
||||
"mx_MessageComposer_button",
|
||||
"mx_MessageComposer_emoji",
|
||||
{
|
||||
"mx_MessageComposer_button_highlight": menuDisplayed,
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: replace ContextMenuTooltipButton with a unified representation of
|
||||
// the header buttons and the right panel buttons
|
||||
return <React.Fragment>
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_MessageComposer_button mx_MessageComposer_emoji"
|
||||
className={className}
|
||||
onClick={openMenu}
|
||||
isExpanded={menuDisplayed}
|
||||
title={_t('Emoji picker')}
|
||||
|
|
|
@ -97,7 +97,7 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
|
|||
// Don't show a badge if we don't need to
|
||||
if (notification.isIdle) return null;
|
||||
|
||||
// TODO: Update these booleans for FTUE Notifications: https://github.com/vector-im/riot-web/issues/14261
|
||||
// TODO: Update these booleans for FTUE Notifications: https://github.com/vector-im/element-web/issues/14261
|
||||
// As of writing, that is "if red, show count always" and "optionally show counts instead of dots".
|
||||
// See git diff for what that boolean state looks like.
|
||||
// XXX: We ignore this.state.showCounts (the setting which controls counts vs dots).
|
||||
|
|
|
@ -73,7 +73,7 @@ export default class ReplyPreview extends React.Component {
|
|||
return <div className="mx_ReplyPreview">
|
||||
<div className="mx_ReplyPreview_section">
|
||||
<div className="mx_ReplyPreview_header mx_ReplyPreview_title">
|
||||
{ '💬 ' + _t('Replying') }
|
||||
{ _t('Replying') }
|
||||
</div>
|
||||
<div className="mx_ReplyPreview_header mx_ReplyPreview_cancel">
|
||||
<img className="mx_filterFlipColor" src={require("../../../../res/img/cancel.svg")} width="18" height="18"
|
||||
|
|
|
@ -128,7 +128,7 @@ const TAG_AESTHETICS: {
|
|||
defaultHidden: false,
|
||||
},
|
||||
|
||||
// TODO: Replace with archived view: https://github.com/vector-im/riot-web/issues/14038
|
||||
// TODO: Replace with archived view: https://github.com/vector-im/element-web/issues/14038
|
||||
[DefaultTagID.Archived]: {
|
||||
sectionLabel: _td("Historical"),
|
||||
isInvite: false,
|
||||
|
@ -215,7 +215,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
private updateLists = () => {
|
||||
const newLists = RoomListStore.instance.orderedLists;
|
||||
if (SettingsStore.getValue("advancedRoomListLogging")) {
|
||||
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
|
||||
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
|
||||
console.log("new lists", newLists);
|
||||
}
|
||||
|
||||
|
@ -245,6 +245,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
if (doUpdate) {
|
||||
// We have to break our reference to the room list store if we want to be able to
|
||||
// diff the object for changes, so do that.
|
||||
// @ts-ignore - ITagMap is ts-ignored so this will have to be too
|
||||
const newSublists = objectWithOnly(newLists, newListIds);
|
||||
const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v));
|
||||
|
||||
|
@ -256,7 +257,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
|
||||
private renderCommunityInvites(): TemporaryTile[] {
|
||||
// TODO: Put community invites in a more sensible place (not in the room list)
|
||||
// See https://github.com/vector-im/riot-web/issues/14456
|
||||
// See https://github.com/vector-im/element-web/issues/14456
|
||||
return MatrixClientPeg.get().getGroups().filter(g => {
|
||||
return g.myMembership === 'invite';
|
||||
}).map(g => {
|
||||
|
|
|
@ -511,7 +511,7 @@ export default createReactClass({
|
|||
"If you think you're seeing this message in error, please " +
|
||||
"<issueLink>submit a bug report</issueLink>.",
|
||||
{ errcode: this.props.error.errcode },
|
||||
{ issueLink: label => <a href="https://github.com/vector-im/riot-web/issues/new/choose"
|
||||
{ issueLink: label => <a href="https://github.com/vector-im/element-web/issues/new/choose"
|
||||
target="_blank" rel="noreferrer noopener">{ label }</a> },
|
||||
),
|
||||
];
|
||||
|
|
|
@ -74,7 +74,7 @@ interface IProps {
|
|||
// You should feel bad if you use this.
|
||||
extraBadTilesThatShouldntExist?: TemporaryTile[];
|
||||
|
||||
// TODO: Account for https://github.com/vector-im/riot-web/issues/14179
|
||||
// TODO: Account for https://github.com/vector-im/element-web/issues/14179
|
||||
}
|
||||
|
||||
// TODO: Use re-resizer's NumberSize when it is exposed as the type
|
||||
|
@ -703,7 +703,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
|
||||
private onScrollPrevent(e: React.UIEvent<HTMLDivElement>) {
|
||||
// the RoomTile calls scrollIntoView and the browser may scroll a div we do not wish to be scrollable
|
||||
// this fixes https://github.com/vector-im/riot-web/issues/14413
|
||||
// this fixes https://github.com/vector-im/element-web/issues/14413
|
||||
(e.target as HTMLDivElement).scrollTop = 0;
|
||||
}
|
||||
|
||||
|
@ -738,14 +738,20 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
const nonPaddedHeight = this.state.height - RESIZE_HANDLE_HEIGHT - SHOW_N_BUTTON_HEIGHT;
|
||||
const amountFullyShown = Math.floor(nonPaddedHeight / this.layout.tileHeight);
|
||||
const numMissing = this.numTiles - amountFullyShown;
|
||||
const label = _t("Show %(count)s more", {count: numMissing});
|
||||
let showMoreText = (
|
||||
<span className='mx_RoomSublist_showNButtonText'>
|
||||
{_t("Show %(count)s more", {count: numMissing})}
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
if (this.props.isMinimized) showMoreText = null;
|
||||
showNButton = (
|
||||
<RovingAccessibleButton onClick={this.onShowAllClick} className={showMoreBtnClasses}>
|
||||
<RovingAccessibleButton
|
||||
role="treeitem"
|
||||
onClick={this.onShowAllClick}
|
||||
className={showMoreBtnClasses}
|
||||
aria-label={label}
|
||||
>
|
||||
<span className='mx_RoomSublist_showMoreButtonChevron mx_RoomSublist_showNButtonChevron'>
|
||||
{/* set by CSS masking */}
|
||||
</span>
|
||||
|
@ -754,14 +760,20 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
);
|
||||
} else if (this.numTiles > this.layout.defaultVisibleTiles) {
|
||||
// we have all tiles visible - add a button to show less
|
||||
const label = _t("Show less");
|
||||
let showLessText = (
|
||||
<span className='mx_RoomSublist_showNButtonText'>
|
||||
{_t("Show less")}
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
if (this.props.isMinimized) showLessText = null;
|
||||
showNButton = (
|
||||
<RovingAccessibleButton onClick={this.onShowLessClick} className={showMoreBtnClasses}>
|
||||
<RovingAccessibleButton
|
||||
role="treeitem"
|
||||
onClick={this.onShowLessClick}
|
||||
className={showMoreBtnClasses}
|
||||
aria-label={label}
|
||||
>
|
||||
<span className='mx_RoomSublist_showLessButtonChevron mx_RoomSublist_showNButtonChevron'>
|
||||
{/* set by CSS masking */}
|
||||
</span>
|
||||
|
|
|
@ -27,14 +27,7 @@ import defaultDispatcher from '../../../dispatcher/dispatcher';
|
|||
import { Key } from "../../../Keyboard";
|
||||
import ActiveRoomObserver from "../../../ActiveRoomObserver";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import {
|
||||
ChevronFace,
|
||||
ContextMenu,
|
||||
ContextMenuTooltipButton,
|
||||
MenuItem,
|
||||
MenuItemCheckbox,
|
||||
MenuItemRadio,
|
||||
} from "../../structures/ContextMenu";
|
||||
import { ChevronFace, ContextMenuTooltipButton, MenuItemRadio } from "../../structures/ContextMenu";
|
||||
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
|
||||
import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore";
|
||||
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
||||
|
@ -51,6 +44,11 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
|||
import { EchoChamber } from "../../../stores/local-echo/EchoChamber";
|
||||
import { CachedRoomKey, RoomEchoChamber } from "../../../stores/local-echo/RoomEchoChamber";
|
||||
import { PROPERTY_UPDATED } from "../../../stores/local-echo/GenericEchoChamber";
|
||||
import IconizedContextMenu, {
|
||||
IconizedContextMenuCheckbox,
|
||||
IconizedContextMenuOption,
|
||||
IconizedContextMenuOptionList, IconizedContextMenuRadio
|
||||
} from "../context_menus/IconizedContextMenu";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
|
@ -78,32 +76,6 @@ const contextMenuBelow = (elementRect: PartialDOMRect) => {
|
|||
return {left, top, chevronFace};
|
||||
};
|
||||
|
||||
interface INotifOptionProps {
|
||||
active: boolean;
|
||||
iconClassName: string;
|
||||
label: string;
|
||||
onClick(ev: ButtonEvent);
|
||||
}
|
||||
|
||||
const NotifOption: React.FC<INotifOptionProps> = ({active, onClick, iconClassName, label}) => {
|
||||
const classes = classNames({
|
||||
mx_RoomTile_contextMenu_activeRow: active,
|
||||
});
|
||||
|
||||
let activeIcon;
|
||||
if (active) {
|
||||
activeIcon = <span className="mx_IconizedContextMenu_icon mx_RoomTile_iconCheck" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuItemRadio className={classes} onClick={onClick} active={active} label={label}>
|
||||
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
|
||||
<span className="mx_IconizedContextMenu_label">{ label }</span>
|
||||
{ activeIcon }
|
||||
</MenuItemRadio>
|
||||
);
|
||||
};
|
||||
|
||||
export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||
private dispatcherRef: string;
|
||||
private roomTileRef = createRef<HTMLDivElement>();
|
||||
|
@ -148,6 +120,12 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
return !this.props.isMinimized && this.props.showMessagePreview;
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>) {
|
||||
if (prevProps.showMessagePreview !== this.props.showMessagePreview && this.showMessagePreview) {
|
||||
this.setState({messagePreview: this.generatePreview()});
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
// when we're first rendered (or our sublist is expanded) make sure we are visible if we're active
|
||||
if (this.state.selected) {
|
||||
|
@ -335,38 +313,39 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
|
||||
let contextMenu = null;
|
||||
if (this.state.notificationsMenuPosition) {
|
||||
contextMenu = (
|
||||
<ContextMenu {...contextMenuBelow(this.state.notificationsMenuPosition)} onFinished={this.onCloseNotificationsMenu}>
|
||||
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile_contextMenu">
|
||||
<div className="mx_IconizedContextMenu_optionList">
|
||||
<NotifOption
|
||||
label={_t("Use default")}
|
||||
active={state === ALL_MESSAGES}
|
||||
iconClassName="mx_RoomTile_iconBell"
|
||||
onClick={this.onClickAllNotifs}
|
||||
/>
|
||||
<NotifOption
|
||||
label={_t("All messages")}
|
||||
active={state === ALL_MESSAGES_LOUD}
|
||||
iconClassName="mx_RoomTile_iconBellDot"
|
||||
onClick={this.onClickAlertMe}
|
||||
/>
|
||||
<NotifOption
|
||||
label={_t("Mentions & Keywords")}
|
||||
active={state === MENTIONS_ONLY}
|
||||
iconClassName="mx_RoomTile_iconBellMentions"
|
||||
onClick={this.onClickMentions}
|
||||
/>
|
||||
<NotifOption
|
||||
label={_t("None")}
|
||||
active={state === MUTE}
|
||||
iconClassName="mx_RoomTile_iconBellCrossed"
|
||||
onClick={this.onClickMute}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ContextMenu>
|
||||
);
|
||||
contextMenu = <IconizedContextMenu
|
||||
{...contextMenuBelow(this.state.notificationsMenuPosition)}
|
||||
onFinished={this.onCloseNotificationsMenu}
|
||||
className="mx_RoomTile_contextMenu"
|
||||
compact
|
||||
>
|
||||
<IconizedContextMenuOptionList first>
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("Use default")}
|
||||
active={state === ALL_MESSAGES}
|
||||
iconClassName="mx_RoomTile_iconBell"
|
||||
onClick={this.onClickAllNotifs}
|
||||
/>
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("All messages")}
|
||||
active={state === ALL_MESSAGES_LOUD}
|
||||
iconClassName="mx_RoomTile_iconBellDot"
|
||||
onClick={this.onClickAlertMe}
|
||||
/>
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("Mentions & Keywords")}
|
||||
active={state === MENTIONS_ONLY}
|
||||
iconClassName="mx_RoomTile_iconBellMentions"
|
||||
onClick={this.onClickMentions}
|
||||
/>
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("None")}
|
||||
active={state === MUTE}
|
||||
iconClassName="mx_RoomTile_iconBellCrossed"
|
||||
onClick={this.onClickMute}
|
||||
/>
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>;
|
||||
}
|
||||
|
||||
const classes = classNames("mx_RoomTile_notificationsButton", {
|
||||
|
@ -400,18 +379,20 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
|
||||
let contextMenu = null;
|
||||
if (this.state.generalMenuPosition && this.props.tag === DefaultTagID.Archived) {
|
||||
contextMenu = (
|
||||
<ContextMenu {...contextMenuBelow(this.state.generalMenuPosition)} onFinished={this.onCloseGeneralMenu}>
|
||||
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile_contextMenu">
|
||||
<div className="mx_IconizedContextMenu_optionList mx_RoomTile_contextMenu_redRow">
|
||||
<MenuItem onClick={this.onForgetRoomClick} label={_t("Leave Room")}>
|
||||
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconSignOut" />
|
||||
<span className="mx_IconizedContextMenu_label">{_t("Forget Room")}</span>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</div>
|
||||
</ContextMenu>
|
||||
);
|
||||
contextMenu = <IconizedContextMenu
|
||||
{...contextMenuBelow(this.state.generalMenuPosition)}
|
||||
onFinished={this.onCloseGeneralMenu}
|
||||
className="mx_RoomTile_contextMenu"
|
||||
compact
|
||||
>
|
||||
<IconizedContextMenuOptionList red>
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_RoomTile_iconSignOut"
|
||||
label={_t("Forget Room")}
|
||||
onClick={this.onForgetRoomClick}
|
||||
/>
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>;
|
||||
} else if (this.state.generalMenuPosition) {
|
||||
const roomTags = RoomListStore.instance.getTagsForRoom(this.props.room);
|
||||
|
||||
|
@ -421,42 +402,40 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
const isLowPriority = roomTags.includes(DefaultTagID.LowPriority);
|
||||
const lowPriorityLabel = _t("Low Priority");
|
||||
|
||||
contextMenu = (
|
||||
<ContextMenu {...contextMenuBelow(this.state.generalMenuPosition)} onFinished={this.onCloseGeneralMenu}>
|
||||
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile_contextMenu">
|
||||
<div className="mx_IconizedContextMenu_optionList">
|
||||
<MenuItemCheckbox
|
||||
className={isFavorite ? "mx_RoomTile_contextMenu_activeRow" : ""}
|
||||
onClick={(e) => this.onTagRoom(e, DefaultTagID.Favourite)}
|
||||
active={isFavorite}
|
||||
label={favouriteLabel}
|
||||
>
|
||||
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconStar" />
|
||||
<span className="mx_IconizedContextMenu_label">{favouriteLabel}</span>
|
||||
</MenuItemCheckbox>
|
||||
<MenuItemCheckbox
|
||||
className={isLowPriority ? "mx_RoomTile_contextMenu_activeRow" : ""}
|
||||
onClick={(e) => this.onTagRoom(e, DefaultTagID.LowPriority)}
|
||||
active={isLowPriority}
|
||||
label={lowPriorityLabel}
|
||||
>
|
||||
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconArrowDown" />
|
||||
<span className="mx_IconizedContextMenu_label">{lowPriorityLabel}</span>
|
||||
</MenuItemCheckbox>
|
||||
<MenuItem onClick={this.onOpenRoomSettings} label={_t("Settings")}>
|
||||
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconSettings" />
|
||||
<span className="mx_IconizedContextMenu_label">{_t("Settings")}</span>
|
||||
</MenuItem>
|
||||
</div>
|
||||
<div className="mx_IconizedContextMenu_optionList mx_RoomTile_contextMenu_redRow">
|
||||
<MenuItem onClick={this.onLeaveRoomClick} label={_t("Leave Room")}>
|
||||
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconSignOut" />
|
||||
<span className="mx_IconizedContextMenu_label">{_t("Leave Room")}</span>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</div>
|
||||
</ContextMenu>
|
||||
);
|
||||
contextMenu = <IconizedContextMenu
|
||||
{...contextMenuBelow(this.state.generalMenuPosition)}
|
||||
onFinished={this.onCloseGeneralMenu}
|
||||
className="mx_RoomTile_contextMenu"
|
||||
compact
|
||||
>
|
||||
<IconizedContextMenuOptionList>
|
||||
<IconizedContextMenuCheckbox
|
||||
onClick={(e) => this.onTagRoom(e, DefaultTagID.Favourite)}
|
||||
active={isFavorite}
|
||||
label={favouriteLabel}
|
||||
iconClassName="mx_RoomTile_iconStar"
|
||||
/>
|
||||
<IconizedContextMenuCheckbox
|
||||
onClick={(e) => this.onTagRoom(e, DefaultTagID.LowPriority)}
|
||||
active={isLowPriority}
|
||||
label={lowPriorityLabel}
|
||||
iconClassName="mx_RoomTile_iconArrowDown"
|
||||
/>
|
||||
|
||||
<IconizedContextMenuOption
|
||||
onClick={this.onOpenRoomSettings}
|
||||
label={_t("Settings")}
|
||||
iconClassName="mx_RoomTile_iconSettings"
|
||||
/>
|
||||
</IconizedContextMenuOptionList>
|
||||
<IconizedContextMenuOptionList red>
|
||||
<IconizedContextMenuOption
|
||||
onClick={this.onLeaveRoomClick}
|
||||
label={_t("Leave Room")}
|
||||
iconClassName="mx_RoomTile_iconSignOut"
|
||||
/>
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import {_t, _td} from '../../../languageHandler';
|
||||
import AppTile from '../elements/AppTile';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
|
@ -380,14 +381,21 @@ export default class Stickerpicker extends React.Component {
|
|||
render() {
|
||||
let stickerPicker;
|
||||
let stickersButton;
|
||||
const className = classNames(
|
||||
"mx_MessageComposer_button",
|
||||
"mx_MessageComposer_stickers",
|
||||
"mx_Stickers_hideStickers",
|
||||
"mx_MessageComposer_button_highlight",
|
||||
);
|
||||
if (this.state.showStickers) {
|
||||
// Show hide-stickers button
|
||||
stickersButton =
|
||||
<AccessibleButton
|
||||
id='stickersButton'
|
||||
key="controls_hide_stickers"
|
||||
className="mx_MessageComposer_button mx_MessageComposer_stickers mx_Stickers_hideStickers"
|
||||
className={className}
|
||||
onClick={this._onHideStickersClick}
|
||||
active={this.state.showStickers}
|
||||
title={_t("Hide Stickers")}
|
||||
>
|
||||
</AccessibleButton>;
|
||||
|
|
|
@ -38,7 +38,7 @@ interface IState {
|
|||
hover: boolean;
|
||||
}
|
||||
|
||||
// TODO: Remove with community invites in the room list: https://github.com/vector-im/riot-web/issues/14456
|
||||
// TODO: Remove with community invites in the room list: https://github.com/vector-im/element-web/issues/14456
|
||||
export default class TemporaryTile extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
|
|
@ -119,8 +119,8 @@ export default createReactClass({
|
|||
'In future this will be improved.',
|
||||
) }
|
||||
{' '}
|
||||
<a href="https://github.com/vector-im/riot-web/issues/2671" target="_blank" rel="noreferrer noopener">
|
||||
https://github.com/vector-im/riot-web/issues/2671
|
||||
<a href="https://github.com/vector-im/element-web/issues/2671" target="_blank" rel="noreferrer noopener">
|
||||
https://github.com/vector-im/element-web/issues/2671
|
||||
</a>
|
||||
</div>,
|
||||
button: _t("Continue"),
|
||||
|
|
|
@ -32,6 +32,7 @@ export default class CrossSigningPanel extends React.PureComponent {
|
|||
error: null,
|
||||
crossSigningPublicKeysOnDevice: false,
|
||||
crossSigningPrivateKeysInStorage: false,
|
||||
masterPrivateKeyCached: false,
|
||||
selfSigningPrivateKeyCached: false,
|
||||
userSigningPrivateKeyCached: false,
|
||||
sessionBackupKeyCached: false,
|
||||
|
@ -78,6 +79,7 @@ export default class CrossSigningPanel extends React.PureComponent {
|
|||
const secretStorage = cli._crypto._secretStorage;
|
||||
const crossSigningPublicKeysOnDevice = crossSigning.getId();
|
||||
const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage);
|
||||
const masterPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("master"));
|
||||
const selfSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing"));
|
||||
const userSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing"));
|
||||
const sessionBackupKeyFromCache = await cli._crypto.getSessionBackupPrivateKey();
|
||||
|
@ -91,6 +93,7 @@ export default class CrossSigningPanel extends React.PureComponent {
|
|||
this.setState({
|
||||
crossSigningPublicKeysOnDevice,
|
||||
crossSigningPrivateKeysInStorage,
|
||||
masterPrivateKeyCached,
|
||||
selfSigningPrivateKeyCached,
|
||||
userSigningPrivateKeyCached,
|
||||
sessionBackupKeyCached,
|
||||
|
@ -140,6 +143,7 @@ export default class CrossSigningPanel extends React.PureComponent {
|
|||
error,
|
||||
crossSigningPublicKeysOnDevice,
|
||||
crossSigningPrivateKeysInStorage,
|
||||
masterPrivateKeyCached,
|
||||
selfSigningPrivateKeyCached,
|
||||
userSigningPrivateKeyCached,
|
||||
sessionBackupKeyCached,
|
||||
|
@ -235,6 +239,10 @@ export default class CrossSigningPanel extends React.PureComponent {
|
|||
<td>{_t("Cross-signing private keys:")}</td>
|
||||
<td>{crossSigningPrivateKeysInStorage ? _t("in secret storage") : _t("not found")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{_t("Master private key:")}</td>
|
||||
<td>{masterPrivateKeyCached ? _t("cached locally") : _t("not found locally")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{_t("Self signing private key:")}</td>
|
||||
<td>{selfSigningPrivateKeyCached ? _t("cached locally") : _t("not found locally")}</td>
|
||||
|
|
|
@ -160,7 +160,7 @@ export default class EventIndexPanel extends React.Component {
|
|||
);
|
||||
} else if (EventIndexPeg.platformHasSupport() && !EventIndexPeg.supportIsInstalled()) {
|
||||
const nativeLink = (
|
||||
"https://github.com/vector-im/riot-web/blob/develop/" +
|
||||
"https://github.com/vector-im/element-web/blob/develop/" +
|
||||
"docs/native-node-modules.md#" +
|
||||
"adding-seshat-for-search-in-e2e-encrypted-rooms"
|
||||
);
|
||||
|
@ -194,7 +194,7 @@ export default class EventIndexPanel extends React.Component {
|
|||
brand,
|
||||
},
|
||||
{
|
||||
'desktopLink': (sub) => <a href="https://riot.im/download/desktop"
|
||||
'desktopLink': (sub) => <a href="https://element.io/get-started"
|
||||
target="_blank" rel="noreferrer noopener">{sub}</a>,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2019, 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.
|
||||
|
@ -14,8 +14,10 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from "react";
|
||||
import {Room} from "matrix-js-sdk/src/models/room";
|
||||
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
||||
|
||||
import {_t} from "../../../../../languageHandler";
|
||||
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
||||
import BridgeTile from "../../BridgeTile";
|
||||
|
@ -27,28 +29,26 @@ const BRIDGE_EVENT_TYPES = [
|
|||
|
||||
const BRIDGES_LINK = "https://matrix.org/bridges/";
|
||||
|
||||
export default class BridgeSettingsTab extends React.Component {
|
||||
static propTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
};
|
||||
interface IProps {
|
||||
roomId: string;
|
||||
}
|
||||
|
||||
_renderBridgeCard(event, room) {
|
||||
export default class BridgeSettingsTab extends React.Component<IProps> {
|
||||
private renderBridgeCard(event: MatrixEvent, room: Room) {
|
||||
const content = event.getContent();
|
||||
if (!content || !content.channel || !content.protocol) {
|
||||
return null;
|
||||
}
|
||||
return <BridgeTile room={room} ev={event}></BridgeTile>;
|
||||
return <BridgeTile key={event.getId()} room={room} ev={event} />;
|
||||
}
|
||||
|
||||
static getBridgeStateEvents(roomId) {
|
||||
static getBridgeStateEvents(roomId: string) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const roomState = (client.getRoom(roomId)).currentState;
|
||||
const roomState = client.getRoom(roomId).currentState;
|
||||
|
||||
const bridgeEvents = [].concat(...BRIDGE_EVENT_TYPES.map((typeName) =>
|
||||
Object.values(roomState.events[typeName] || {}),
|
||||
return [].concat(...BRIDGE_EVENT_TYPES.map((typeName) =>
|
||||
Array.from(roomState.events.get(typeName).values()),
|
||||
));
|
||||
|
||||
return bridgeEvents;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -58,8 +58,7 @@ export default class BridgeSettingsTab extends React.Component {
|
|||
const client = MatrixClientPeg.get();
|
||||
const room = client.getRoom(this.props.roomId);
|
||||
|
||||
let content = null;
|
||||
|
||||
let content: JSX.Element;
|
||||
if (bridgeEvents.length > 0) {
|
||||
content = <div>
|
||||
<p>{_t(
|
||||
|
@ -72,7 +71,7 @@ export default class BridgeSettingsTab extends React.Component {
|
|||
},
|
||||
)}</p>
|
||||
<ul className="mx_RoomSettingsDialog_BridgeList">
|
||||
{ bridgeEvents.map((event) => this._renderBridgeCard(event, room)) }
|
||||
{ bridgeEvents.map((event) => this.renderBridgeCard(event, room)) }
|
||||
</ul>
|
||||
</div>;
|
||||
} else {
|
|
@ -33,7 +33,7 @@ const plEventsToLabels = {
|
|||
"m.room.tombstone": _td("Upgrade the room"),
|
||||
"m.room.encryption": _td("Enable room encryption"),
|
||||
|
||||
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
|
||||
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
|
||||
"im.vector.modular.widgets": _td("Modify widgets"),
|
||||
};
|
||||
|
||||
|
@ -48,7 +48,7 @@ const plEventsToShow = {
|
|||
"m.room.tombstone": {isState: true},
|
||||
"m.room.encryption": {isState: true},
|
||||
|
||||
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
|
||||
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
|
||||
"im.vector.modular.widgets": {isState: true},
|
||||
};
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import * as sdk from "../../../../..";
|
|||
import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
|
||||
import Modal from "../../../../../Modal";
|
||||
import QuestionDialog from "../../../dialogs/QuestionDialog";
|
||||
import StyledRadioGroup from '../../../elements/StyledRadioGroup';
|
||||
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
||||
|
||||
export default class SecurityRoomSettingsTab extends React.Component {
|
||||
|
@ -99,7 +100,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
|
|||
{
|
||||
'a': (sub) => {
|
||||
return <a rel='noreferrer noopener' target='_blank'
|
||||
href='https://about.riot.im/help#end-to-end-encryption'>{sub}</a>;
|
||||
href='https://element.io/help#encryption'>{sub}</a>;
|
||||
},
|
||||
},
|
||||
),
|
||||
|
@ -144,7 +145,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
_onRoomAccessRadioToggle = (ev) => {
|
||||
_onRoomAccessRadioToggle = (roomAccess) => {
|
||||
// join_rule
|
||||
// INVITE | PUBLIC
|
||||
// ----------------------+----------------
|
||||
|
@ -161,7 +162,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
|
|||
let joinRule = "invite";
|
||||
let guestAccess = "can_join";
|
||||
|
||||
switch (ev.target.value) {
|
||||
switch (roomAccess) {
|
||||
case "invite_only":
|
||||
// no change - use defaults above
|
||||
break;
|
||||
|
@ -190,11 +191,11 @@ export default class SecurityRoomSettingsTab extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
_onHistoryRadioToggle = (ev) => {
|
||||
_onHistoryRadioToggle = (history) => {
|
||||
const beforeHistory = this.state.history;
|
||||
this.setState({history: ev.target.value});
|
||||
this.setState({history: history});
|
||||
MatrixClientPeg.get().sendStateEvent(this.props.roomId, "m.room.history_visibility", {
|
||||
history_visibility: ev.target.value,
|
||||
history_visibility: history,
|
||||
}, "").catch((e) => {
|
||||
console.error(e);
|
||||
this.setState({history: beforeHistory});
|
||||
|
@ -257,27 +258,31 @@ export default class SecurityRoomSettingsTab extends React.Component {
|
|||
<div>
|
||||
{guestWarning}
|
||||
{aliasWarning}
|
||||
<label>
|
||||
<input type="radio" name="roomVis" value="invite_only"
|
||||
disabled={!canChangeAccess}
|
||||
onChange={this._onRoomAccessRadioToggle}
|
||||
checked={joinRule !== "public"} />
|
||||
{_t('Only people who have been invited')}
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="roomVis" value="public_no_guests"
|
||||
disabled={!canChangeAccess}
|
||||
onChange={this._onRoomAccessRadioToggle}
|
||||
checked={joinRule === "public" && guestAccess !== "can_join"} />
|
||||
{_t('Anyone who knows the room\'s link, apart from guests')}
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="roomVis" value="public_with_guests"
|
||||
disabled={!canChangeAccess}
|
||||
onChange={this._onRoomAccessRadioToggle}
|
||||
checked={joinRule === "public" && guestAccess === "can_join"} />
|
||||
{_t("Anyone who knows the room's link, including guests")}
|
||||
</label>
|
||||
<StyledRadioGroup
|
||||
name="roomVis"
|
||||
value={joinRule}
|
||||
onChange={this._onRoomAccessRadioToggle}
|
||||
definitions={[
|
||||
{
|
||||
value: "invite_only",
|
||||
disabled: !canChangeAccess,
|
||||
label: _t('Only people who have been invited'),
|
||||
checked: joinRule !== "public",
|
||||
},
|
||||
{
|
||||
value: "public_no_guests",
|
||||
disabled: !canChangeAccess,
|
||||
label: _t('Anyone who knows the room\'s link, apart from guests'),
|
||||
checked: joinRule === "public" && guestAccess !== "can_join",
|
||||
},
|
||||
{
|
||||
value: "public_with_guests",
|
||||
disabled: !canChangeAccess,
|
||||
label: _t("Anyone who knows the room's link, including guests"),
|
||||
checked: joinRule === "public" && guestAccess === "can_join",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -294,34 +299,33 @@ export default class SecurityRoomSettingsTab extends React.Component {
|
|||
{_t('Changes to who can read history will only apply to future messages in this room. ' +
|
||||
'The visibility of existing history will be unchanged.')}
|
||||
</div>
|
||||
<label>
|
||||
<input type="radio" name="historyVis" value="world_readable"
|
||||
disabled={!canChangeHistory}
|
||||
checked={history === "world_readable"}
|
||||
onChange={this._onHistoryRadioToggle} />
|
||||
{_t("Anyone")}
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="historyVis" value="shared"
|
||||
disabled={!canChangeHistory}
|
||||
checked={history === "shared"}
|
||||
onChange={this._onHistoryRadioToggle} />
|
||||
{_t('Members only (since the point in time of selecting this option)')}
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="historyVis" value="invited"
|
||||
disabled={!canChangeHistory}
|
||||
checked={history === "invited"}
|
||||
onChange={this._onHistoryRadioToggle} />
|
||||
{_t('Members only (since they were invited)')}
|
||||
</label>
|
||||
<label >
|
||||
<input type="radio" name="historyVis" value="joined"
|
||||
disabled={!canChangeHistory}
|
||||
checked={history === "joined"}
|
||||
onChange={this._onHistoryRadioToggle} />
|
||||
{_t('Members only (since they joined)')}
|
||||
</label>
|
||||
<StyledRadioGroup
|
||||
name="historyVis"
|
||||
value={history}
|
||||
onChange={this._onHistoryRadioToggle}
|
||||
definitions={[
|
||||
{
|
||||
value: "world_readable",
|
||||
disabled: !canChangeHistory,
|
||||
label: _t("Anyone"),
|
||||
},
|
||||
{
|
||||
value: "shared",
|
||||
disabled: !canChangeHistory,
|
||||
label: _t('Members only (since the point in time of selecting this option)'),
|
||||
},
|
||||
{
|
||||
value: "invited",
|
||||
disabled: !canChangeHistory,
|
||||
label: _t('Members only (since they were invited)'),
|
||||
},
|
||||
{
|
||||
value: "joined",
|
||||
disabled: !canChangeHistory,
|
||||
label: _t('Members only (since they joined)'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ export default class LabsUserSettingsTab extends React.Component {
|
|||
_t('Customise your experience with experimental labs features. ' +
|
||||
'<a>Learn more</a>.', {}, {
|
||||
'a': (sub) => {
|
||||
return <a href="https://github.com/vector-im/riot-web/blob/develop/docs/labs.md"
|
||||
return <a href="https://github.com/vector-im/element-web/blob/develop/docs/labs.md"
|
||||
rel='noreferrer noopener' target='_blank'>{sub}</a>;
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue