Merge branch 'develop' into sort-imports
This commit is contained in:
commit
bc1dd6fedf
14 changed files with 246 additions and 63 deletions
|
@ -33,7 +33,7 @@ import FeedbackDialog from "../views/dialogs/FeedbackDialog";
|
|||
import Modal from "../../Modal";
|
||||
import LogoutDialog from "../views/dialogs/LogoutDialog";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import { getCustomTheme } from "../../theme";
|
||||
import { findHighContrastTheme, getCustomTheme, isHighContrastTheme } from "../../theme";
|
||||
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
|
||||
import SdkConfig from "../../SdkConfig";
|
||||
import { getHomePageUrl } from "../../utils/pages";
|
||||
|
@ -70,6 +70,7 @@ type PartialDOMRect = Pick<DOMRect, "width" | "left" | "top" | "height">;
|
|||
interface IState {
|
||||
contextMenuPosition: PartialDOMRect;
|
||||
isDarkTheme: boolean;
|
||||
isHighContrast: boolean;
|
||||
selectedSpace?: Room;
|
||||
pendingRoomJoin: Set<string>;
|
||||
}
|
||||
|
@ -88,6 +89,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
this.state = {
|
||||
contextMenuPosition: null,
|
||||
isDarkTheme: this.isUserOnDarkTheme(),
|
||||
isHighContrast: this.isUserOnHighContrastTheme(),
|
||||
pendingRoomJoin: new Set<string>(),
|
||||
};
|
||||
|
||||
|
@ -143,6 +145,18 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
private isUserOnHighContrastTheme(): boolean {
|
||||
if (SettingsStore.getValue("use_system_theme")) {
|
||||
return window.matchMedia("(prefers-contrast: more)").matches;
|
||||
} else {
|
||||
const theme = SettingsStore.getValue("theme");
|
||||
if (theme.startsWith("custom-")) {
|
||||
return false;
|
||||
}
|
||||
return isHighContrastTheme(theme);
|
||||
}
|
||||
}
|
||||
|
||||
private onProfileUpdate = async () => {
|
||||
// the store triggered an update, so force a layout update. We don't
|
||||
// have any state to store here for that to magically happen.
|
||||
|
@ -154,7 +168,11 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
private onThemeChanged = () => {
|
||||
this.setState({ isDarkTheme: this.isUserOnDarkTheme() });
|
||||
this.setState(
|
||||
{
|
||||
isDarkTheme: this.isUserOnDarkTheme(),
|
||||
isHighContrast: this.isUserOnHighContrastTheme(),
|
||||
});
|
||||
};
|
||||
|
||||
private onAction = (ev: ActionPayload) => {
|
||||
|
@ -222,7 +240,13 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
// Disable system theme matching if the user hits this button
|
||||
SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, false);
|
||||
|
||||
const newTheme = this.state.isDarkTheme ? "light" : "dark";
|
||||
let newTheme = this.state.isDarkTheme ? "light" : "dark";
|
||||
if (this.state.isHighContrast) {
|
||||
const hcTheme = findHighContrastTheme(newTheme);
|
||||
if (hcTheme) {
|
||||
newTheme = hcTheme;
|
||||
}
|
||||
}
|
||||
SettingsStore.setValue("theme", null, SettingLevel.DEVICE, newTheme); // set at same level as Appearance tab
|
||||
};
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ interface IState {
|
|||
error: Error;
|
||||
menuDisplayed: boolean;
|
||||
widgetPageTitle: string;
|
||||
requiresClient: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.elements.AppTile")
|
||||
|
@ -114,8 +115,10 @@ export default class AppTile extends React.Component<IProps, IState> {
|
|||
this.persistKey = getPersistKey(this.props.app.id);
|
||||
try {
|
||||
this.sgWidget = new StopGapWidget(this.props);
|
||||
this.sgWidget.on("preparing", this.onWidgetPrepared);
|
||||
this.sgWidget.on("preparing", this.onWidgetPreparing);
|
||||
this.sgWidget.on("ready", this.onWidgetReady);
|
||||
// emits when the capabilites have been setup or changed
|
||||
this.sgWidget.on("capabilitiesNotified", this.onWidgetCapabilitiesNotified);
|
||||
} catch (e) {
|
||||
logger.log("Failed to construct widget", e);
|
||||
this.sgWidget = null;
|
||||
|
@ -155,6 +158,10 @@ export default class AppTile extends React.Component<IProps, IState> {
|
|||
error: null,
|
||||
menuDisplayed: false,
|
||||
widgetPageTitle: this.props.widgetPageTitle,
|
||||
// requiresClient is initially set to true. This avoids the broken state of the popout
|
||||
// button being visible (for an instance) and then disappearing when the widget is loaded.
|
||||
// requiresClient <-> hide the popout button
|
||||
requiresClient: true,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -216,7 +223,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
|||
}
|
||||
try {
|
||||
this.sgWidget = new StopGapWidget(newProps);
|
||||
this.sgWidget.on("preparing", this.onWidgetPrepared);
|
||||
this.sgWidget.on("preparing", this.onWidgetPreparing);
|
||||
this.sgWidget.on("ready", this.onWidgetReady);
|
||||
this.startWidget();
|
||||
} catch (e) {
|
||||
|
@ -287,7 +294,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
|||
if (this.sgWidget) this.sgWidget.stop({ forceDestroy: true });
|
||||
}
|
||||
|
||||
private onWidgetPrepared = (): void => {
|
||||
private onWidgetPreparing = (): void => {
|
||||
this.setState({ loading: false });
|
||||
};
|
||||
|
||||
|
@ -297,6 +304,12 @@ export default class AppTile extends React.Component<IProps, IState> {
|
|||
}
|
||||
};
|
||||
|
||||
private onWidgetCapabilitiesNotified = (): void => {
|
||||
this.setState({
|
||||
requiresClient: this.sgWidget.widgetApi.hasCapability(MatrixCapabilities.RequiresClient),
|
||||
});
|
||||
};
|
||||
|
||||
private onAction = (payload): void => {
|
||||
if (payload.widgetId === this.props.app.id) {
|
||||
switch (payload.action) {
|
||||
|
@ -512,7 +525,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
|||
{ this.props.showTitle && this.getTileTitle() }
|
||||
</span>
|
||||
<span className="mx_AppTileMenuBarWidgets">
|
||||
{ this.props.showPopout && <AccessibleButton
|
||||
{ (this.props.showPopout && !this.state.requiresClient) && <AccessibleButton
|
||||
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_popout"
|
||||
title={_t('Popout widget')}
|
||||
onClick={this.onPopoutWidgetClick}
|
||||
|
|
|
@ -62,6 +62,7 @@ import MKeyVerificationConclusion from "../messages/MKeyVerificationConclusion";
|
|||
import { dispatchShowThreadEvent } from '../../../dispatcher/dispatch-actions/threads';
|
||||
import { MessagePreviewStore } from '../../../stores/room-list/MessagePreviewStore';
|
||||
import { TimelineRenderingType } from "../../../contexts/RoomContext";
|
||||
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
||||
|
||||
const eventTileTypes = {
|
||||
[EventType.RoomMessage]: 'messages.MessageEvent',
|
||||
|
@ -993,6 +994,12 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
const EventTileType = sdk.getComponent(tileHandler);
|
||||
const isProbablyMedia = MediaEventHelper.isEligible(this.props.mxEvent);
|
||||
|
||||
const lineClasses = classNames({
|
||||
mx_EventTile_line: true,
|
||||
mx_EventTile_mediaLine: isProbablyMedia,
|
||||
});
|
||||
|
||||
const isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1);
|
||||
const isRedacted = isMessageEvent(this.props.mxEvent) && this.props.isRedacted;
|
||||
|
@ -1208,7 +1215,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
{ timestamp }
|
||||
</a>
|
||||
</div>,
|
||||
<div className="mx_EventTile_line" key="mx_EventTile_line">
|
||||
<div className={lineClasses} key="mx_EventTile_line">
|
||||
<EventTileType ref={this.tile}
|
||||
mxEvent={this.props.mxEvent}
|
||||
highlights={this.props.highlights}
|
||||
|
@ -1256,7 +1263,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
{ timestamp }
|
||||
</a>
|
||||
</div>,
|
||||
<div className="mx_EventTile_line" key="mx_EventTile_line">
|
||||
<div className={lineClasses} key="mx_EventTile_line">
|
||||
{ replyChain }
|
||||
<EventTileType ref={this.tile}
|
||||
mxEvent={this.props.mxEvent}
|
||||
|
@ -1280,7 +1287,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
"aria-atomic": true,
|
||||
"data-scroll-tokens": scrollToken,
|
||||
}, [
|
||||
<div className="mx_EventTile_line" key="mx_EventTile_line">
|
||||
<div className={lineClasses} key="mx_EventTile_line">
|
||||
<EventTileType ref={this.tile}
|
||||
mxEvent={this.props.mxEvent}
|
||||
highlights={this.props.highlights}
|
||||
|
@ -1340,7 +1347,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
{ sender }
|
||||
{ ircPadlock }
|
||||
{ avatar }
|
||||
<div className="mx_EventTile_line" key="mx_EventTile_line">
|
||||
<div className={lineClasses} key="mx_EventTile_line">
|
||||
{ groupTimestamp }
|
||||
{ groupPadlock }
|
||||
{ replyChain }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue