1
0
mirror of https://github.com/matrix-org/matrix-react-sdk.git synced 2025-12-07 03:42:20 +03:00

Move new search experience to a Beta (#7718)

This commit is contained in:
Michael Telatynski
2022-02-08 14:02:36 +00:00
committed by GitHub
parent 5201c9b285
commit ed185240a5
16 changed files with 329 additions and 116 deletions

View File

@@ -91,7 +91,7 @@ import { RoomUpdateCause } from "../../stores/room-list/models";
import SecurityCustomisations from "../../customisations/Security";
import Spinner from "../views/elements/Spinner";
import QuestionDialog from "../views/dialogs/QuestionDialog";
import UserSettingsDialog from '../views/dialogs/UserSettingsDialog';
import UserSettingsDialog, { UserTab } from '../views/dialogs/UserSettingsDialog';
import CreateGroupDialog from '../views/dialogs/CreateGroupDialog';
import CreateRoomDialog from '../views/dialogs/CreateRoomDialog';
import RoomDirectory from './RoomDirectory';
@@ -117,6 +117,7 @@ import { showSpaceInvite } from "../../utils/space";
import AccessibleButton from "../views/elements/AccessibleButton";
import { ActionPayload } from "../../dispatcher/payloads";
import { SummarizedNotificationState } from "../../stores/notifications/SummarizedNotificationState";
import GenericToast from '../views/toasts/GenericToast';
/** constants for MatrixChat.state.view */
export enum Views {
@@ -1551,6 +1552,42 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
showNotificationsToast(false);
}
if (!localStorage.getItem("mx_seen_feature_spotlight_toast")) {
setTimeout(() => {
// Skip the toast if the beta is already enabled or the user has changed the setting from default
if (SettingsStore.getValue("feature_spotlight") ||
SettingsStore.getValue("feature_spotlight", null, true) !== null) {
return;
}
const key = "BETA_SPOTLIGHT_TOAST";
ToastStore.sharedInstance().addOrReplaceToast({
key,
title: _t("New search beta available"),
props: {
description: _t("We're testing a new search to make finding what you want quicker.\n"),
acceptLabel: _t("Learn more"),
onAccept: () => {
dis.dispatch({
action: Action.ViewUserSettings,
initialTabId: UserTab.Labs,
});
localStorage.setItem("mx_seen_feature_spotlight_toast", "true");
ToastStore.sharedInstance().dismissToast(key);
},
rejectLabel: _t("Dismiss"),
onReject: () => {
localStorage.setItem("mx_seen_feature_spotlight_toast", "true");
ToastStore.sharedInstance().dismissToast(key);
},
},
icon: "labs",
component: GenericToast,
priority: 9,
});
}, 5 * 60 * 1000); // show after 5 minutes to not overload user with toasts on launch
}
dis.fire(Action.FocusSendMessageComposer);
this.setState({
ready: true,

View File

@@ -34,6 +34,7 @@ import SettingsStore from "../../settings/SettingsStore";
import Modal from "../../Modal";
import SpotlightDialog from "../views/dialogs/SpotlightDialog";
import { ALTERNATE_KEY_NAME, KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
import ToastStore from "../../stores/ToastStore";
interface IProps {
isMinimized: boolean;
@@ -46,11 +47,13 @@ interface IProps {
interface IState {
query: string;
focused: boolean;
spotlightBetaEnabled: boolean;
}
@replaceableComponent("structures.RoomSearch")
export default class RoomSearch extends React.PureComponent<IProps, IState> {
private readonly dispatcherRef: string;
private readonly betaRef: string;
private inputRef: React.RefObject<HTMLInputElement> = createRef();
private searchFilter: NameFilterCondition = new NameFilterCondition();
@@ -60,11 +63,13 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
this.state = {
query: "",
focused: false,
spotlightBetaEnabled: SettingsStore.getValue("feature_spotlight"),
};
this.dispatcherRef = defaultDispatcher.register(this.onAction);
// clear filter when changing spaces, in future we may wish to maintain a filter per-space
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.clearInput);
this.betaRef = SettingsStore.watchSetting("feature_spotlight", null, this.onSpotlightChange);
}
public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>): void {
@@ -85,8 +90,18 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
public componentWillUnmount() {
defaultDispatcher.unregister(this.dispatcherRef);
SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.clearInput);
SettingsStore.unwatchSetting(this.betaRef);
}
private onSpotlightChange = () => {
const spotlightBetaEnabled = SettingsStore.getValue("feature_spotlight");
if (this.state.spotlightBetaEnabled !== spotlightBetaEnabled) {
this.setState({ spotlightBetaEnabled });
}
// in case the user was in settings at the 5-minute mark, dismiss the toast
ToastStore.sharedInstance().dismissToast("BETA_SPOTLIGHT_TOAST");
};
private openSpotlight() {
Modal.createTrackedDialog("Spotlight", "", SpotlightDialog, {}, "mx_SpotlightDialog_wrapper", false, true);
}
@@ -95,11 +110,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
if (payload.action === Action.ViewRoom && payload.clear_search) {
this.clearInput();
} else if (payload.action === 'focus_room_filter') {
if (SettingsStore.getValue("feature_spotlight")) {
this.openSpotlight();
} else {
this.inputRef.current?.focus();
}
this.focus();
}
};
@@ -110,10 +121,10 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
};
private openSearch = () => {
if (SettingsStore.getValue("feature_spotlight")) {
if (this.state.spotlightBetaEnabled) {
this.openSpotlight();
} else {
defaultDispatcher.dispatch({ action: "show_left_panel" });
// dispatched as it needs handling by MatrixChat too
defaultDispatcher.dispatch({ action: "focus_room_filter" });
}
};
@@ -124,14 +135,8 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
};
private onFocus = (ev: React.FocusEvent<HTMLInputElement>) => {
if (SettingsStore.getValue("feature_spotlight")) {
ev.preventDefault();
ev.stopPropagation();
this.openSpotlight();
} else {
this.setState({ focused: true });
ev.target.select();
}
this.setState({ focused: true });
ev.target.select();
};
private onBlur = (ev: React.FocusEvent<HTMLInputElement>) => {
@@ -159,7 +164,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
};
public focus = (): void => {
if (SettingsStore.getValue("feature_spotlight")) {
if (this.state.spotlightBetaEnabled) {
this.openSpotlight();
} else {
this.inputRef.current?.focus();
@@ -172,6 +177,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
'mx_RoomSearch_hasQuery': this.state.query,
'mx_RoomSearch_focused': this.state.focused,
'mx_RoomSearch_minimized': this.props.isMinimized,
'mx_RoomSearch_spotlightTrigger': this.state.spotlightBetaEnabled,
});
const inputClasses = classNames({
@@ -180,8 +186,9 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
});
let icon = (
<div className="mx_RoomSearch_icon" onClick={this.focus} />
<div className="mx_RoomSearch_icon" />
);
let input = (
<input
type="text"
@@ -192,10 +199,11 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
onBlur={this.onBlur}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
placeholder={SettingsStore.getValue("feature_spotlight") ? _t("Search") : _t("Filter")}
placeholder={_t("Filter")}
autoComplete="off"
/>
);
let clearButton = (
<AccessibleButton
tabIndex={-1}
@@ -204,7 +212,8 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
onClick={this.clearInput}
/>
);
let shortcutPrompt = <div className="mx_RoomSearch_shortcutPrompt" onClick={this.focus}>
let shortcutPrompt = <div className="mx_RoomSearch_shortcutPrompt">
{ isMac ? "⌘ K" : _t(ALTERNATE_KEY_NAME[Key.CONTROL]) + " K" }
</div>;
@@ -221,8 +230,18 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
shortcutPrompt = null;
}
if (this.state.spotlightBetaEnabled) {
return <AccessibleButton onClick={this.openSpotlight} className={classes}>
{ icon }
{ input && <div className="mx_RoomSearch_spotlightTriggerText">
{ _t("Search") }
</div> }
{ shortcutPrompt }
</AccessibleButton>;
}
return (
<div className={classes}>
<div className={classes} onClick={this.focus}>
{ icon }
{ input }
{ shortcutPrompt }