Merge pull request #5383 from matrix-org/t3chguy/fix/15604
Tweaks to toasts and post-registration landing
This commit is contained in:
commit
5c9acc364f
19 changed files with 284 additions and 60 deletions
|
@ -15,20 +15,105 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import {useContext, useRef, useState} from "react";
|
||||
|
||||
import AutoHideScrollbar from './AutoHideScrollbar';
|
||||
import { getHomePageUrl } from "../../utils/pages";
|
||||
import { _t } from "../../languageHandler";
|
||||
import {getHomePageUrl} from "../../utils/pages";
|
||||
import {_t} from "../../languageHandler";
|
||||
import SdkConfig from "../../SdkConfig";
|
||||
import * as sdk from "../../index";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
import {Action} from "../../dispatcher/actions";
|
||||
import {Transition} from "react-transition-group";
|
||||
import BaseAvatar from "../views/avatars/BaseAvatar";
|
||||
import {OwnProfileStore} from "../../stores/OwnProfileStore";
|
||||
import AccessibleButton from "../views/elements/AccessibleButton";
|
||||
import Tooltip from "../views/elements/Tooltip";
|
||||
import {UPDATE_EVENT} from "../../stores/AsyncStore";
|
||||
import {useEventEmitter} from "../../hooks/useEventEmitter";
|
||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||
import classNames from "classnames";
|
||||
import {ENTERING} from "react-transition-group/Transition";
|
||||
|
||||
const onClickSendDm = () => dis.dispatch({action: 'view_create_chat'});
|
||||
const onClickExplore = () => dis.fire(Action.ViewRoomDirectory);
|
||||
const onClickNewRoom = () => dis.dispatch({action: 'view_create_room'});
|
||||
|
||||
const HomePage = () => {
|
||||
interface IProps {
|
||||
justRegistered?: boolean;
|
||||
}
|
||||
|
||||
const avatarSize = 52;
|
||||
|
||||
const getOwnProfile = (userId: string) => ({
|
||||
displayName: OwnProfileStore.instance.displayName || userId,
|
||||
avatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(avatarSize),
|
||||
});
|
||||
|
||||
const UserWelcomeTop = () => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const userId = cli.getUserId();
|
||||
const [ownProfile, setOwnProfile] = useState(getOwnProfile(userId));
|
||||
useEventEmitter(OwnProfileStore.instance, UPDATE_EVENT, () => {
|
||||
setOwnProfile(getOwnProfile(userId));
|
||||
});
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const uploadRef = useRef<HTMLInputElement>();
|
||||
|
||||
return <div>
|
||||
<input
|
||||
type="file"
|
||||
ref={uploadRef}
|
||||
className="mx_ProfileSettings_avatarUpload"
|
||||
onChange={async (ev) => {
|
||||
if (!ev.target.files?.length) return;
|
||||
setBusy(true);
|
||||
const file = ev.target.files[0];
|
||||
const uri = await cli.uploadContent(file);
|
||||
await cli.setAvatarUrl(uri);
|
||||
setBusy(false);
|
||||
}}
|
||||
accept="image/*"
|
||||
/>
|
||||
|
||||
<AccessibleButton
|
||||
className={classNames("mx_HomePage_userAvatar", {
|
||||
mx_HomePage_userAvatar_busy: busy,
|
||||
})}
|
||||
disabled={busy}
|
||||
onClick={() => {
|
||||
uploadRef.current.click();
|
||||
}}
|
||||
>
|
||||
<BaseAvatar
|
||||
idName={userId}
|
||||
name={ownProfile.displayName}
|
||||
url={ownProfile.avatarUrl}
|
||||
width={avatarSize}
|
||||
height={avatarSize}
|
||||
resizeMethod="crop"
|
||||
/>
|
||||
|
||||
<Transition appear in timeout={3000}>
|
||||
{state => (
|
||||
<Tooltip
|
||||
label={ownProfile.avatarUrl || busy
|
||||
? _t("Great, that'll help people know it's you")
|
||||
: _t("Add a photo so people know it's you.")}
|
||||
visible={state !== ENTERING}
|
||||
forceOnRight
|
||||
/>
|
||||
)}
|
||||
</Transition>
|
||||
</AccessibleButton>
|
||||
|
||||
<h1>{ _t("Welcome %(name)s", { name: ownProfile.displayName }) }</h1>
|
||||
<h4>{ _t("Now, lets help you get started") }</h4>
|
||||
</div>;
|
||||
};
|
||||
|
||||
const HomePage: React.FC<IProps> = ({ justRegistered = false }) => {
|
||||
const config = SdkConfig.get();
|
||||
const pageUrl = getHomePageUrl(config);
|
||||
|
||||
|
@ -37,18 +122,27 @@ const HomePage = () => {
|
|||
return <EmbeddedPage className="mx_HomePage" url={pageUrl} scrollbar={true} />;
|
||||
}
|
||||
|
||||
const brandingConfig = config.branding;
|
||||
let logoUrl = "themes/element/img/logos/element-logo.svg";
|
||||
if (brandingConfig && brandingConfig.authHeaderLogoUrl) {
|
||||
logoUrl = brandingConfig.authHeaderLogoUrl;
|
||||
let introSection;
|
||||
if (justRegistered) {
|
||||
introSection = <UserWelcomeTop />;
|
||||
} else {
|
||||
const brandingConfig = config.branding;
|
||||
let logoUrl = "themes/element/img/logos/element-logo.svg";
|
||||
if (brandingConfig && brandingConfig.authHeaderLogoUrl) {
|
||||
logoUrl = brandingConfig.authHeaderLogoUrl;
|
||||
}
|
||||
|
||||
introSection = <React.Fragment>
|
||||
<img src={logoUrl} alt={config.brand} />
|
||||
<h1>{ _t("Welcome to %(appName)s", { appName: config.brand }) }</h1>
|
||||
<h4>{ _t("Liberate your communication") }</h4>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
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 || "Element"} />
|
||||
<h1>{ _t("Welcome to %(appName)s", { appName: config.brand || "Element" }) }</h1>
|
||||
<h4>{ _t("Liberate your communication") }</h4>
|
||||
{ introSection }
|
||||
<div className="mx_HomePage_default_buttons">
|
||||
<AccessibleButton onClick={onClickSendDm} className="mx_HomePage_button_sendDm">
|
||||
{ _t("Send a Direct Message") }
|
||||
|
|
|
@ -88,6 +88,7 @@ interface IProps {
|
|||
currentUserId?: string;
|
||||
currentGroupId?: string;
|
||||
currentGroupIsNew?: boolean;
|
||||
justRegistered?: boolean;
|
||||
}
|
||||
|
||||
interface IUsageLimit {
|
||||
|
@ -573,7 +574,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
break;
|
||||
|
||||
case PageTypes.HomePage:
|
||||
pageElement = <HomePage />;
|
||||
pageElement = <HomePage justRegistered={this.props.justRegistered} />;
|
||||
break;
|
||||
|
||||
case PageTypes.UserView:
|
||||
|
|
|
@ -62,7 +62,7 @@ import DMRoomMap from '../../utils/DMRoomMap';
|
|||
import ThemeWatcher from "../../settings/watchers/ThemeWatcher";
|
||||
import { FontWatcher } from '../../settings/watchers/FontWatcher';
|
||||
import { storeRoomAliasInCache } from '../../RoomAliasCache';
|
||||
import { defer, IDeferred } from "../../utils/promise";
|
||||
import { defer, IDeferred, sleep } from "../../utils/promise";
|
||||
import ToastStore from "../../stores/ToastStore";
|
||||
import * as StorageManager from "../../utils/StorageManager";
|
||||
import type LoggedInViewType from "./LoggedInView";
|
||||
|
@ -201,6 +201,7 @@ interface IState {
|
|||
roomOobData?: object;
|
||||
viaServers?: string[];
|
||||
pendingInitialSync?: boolean;
|
||||
justRegistered?: boolean;
|
||||
}
|
||||
|
||||
export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
|
@ -479,6 +480,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
const newState = {
|
||||
currentUserId: null,
|
||||
justRegistered: false,
|
||||
};
|
||||
Object.assign(newState, state);
|
||||
this.setState(newState);
|
||||
|
@ -669,7 +671,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
this.viewWelcome();
|
||||
break;
|
||||
case 'view_home_page':
|
||||
this.viewHome();
|
||||
this.viewHome(payload.justRegistered);
|
||||
break;
|
||||
case 'view_start_chat_or_reuse':
|
||||
this.chatCreateOrReuse(payload.user_id);
|
||||
|
@ -953,10 +955,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
this.themeWatcher.recheck();
|
||||
}
|
||||
|
||||
private viewHome() {
|
||||
private viewHome(justRegistered = false) {
|
||||
// The home page requires the "logged in" view, so we'll set that.
|
||||
this.setStateForNewView({
|
||||
view: Views.LOGGED_IN,
|
||||
justRegistered,
|
||||
});
|
||||
this.setPage(PageTypes.HomePage);
|
||||
this.notifyNewScreen('home');
|
||||
|
@ -1190,7 +1193,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
if (welcomeUserRoom === null) {
|
||||
// We didn't redirect to the welcome user room, so show
|
||||
// the homepage.
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
dis.dispatch({action: 'view_home_page', justRegistered: true});
|
||||
}
|
||||
} else if (ThreepidInviteStore.instance.pickBestInvite()) {
|
||||
// The user has a 3pid invite pending - show them that
|
||||
|
@ -1203,7 +1206,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
} else {
|
||||
// The user has just logged in after registering,
|
||||
// so show the homepage.
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
dis.dispatch({action: 'view_home_page', justRegistered: true});
|
||||
}
|
||||
} else {
|
||||
this.showScreenAfterLogin();
|
||||
|
@ -1211,6 +1214,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
|
||||
StorageManager.tryPersistStorage();
|
||||
|
||||
// defer the following actions by 30 seconds to not throw them at the user immediately
|
||||
await sleep(30);
|
||||
if (SettingsStore.getValue("showCookieBar") &&
|
||||
(Analytics.canEnable() || CountlyAnalytics.instance.canEnable())
|
||||
) {
|
||||
|
@ -1343,8 +1348,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
this.firstSyncComplete = true;
|
||||
this.firstSyncPromise.resolve();
|
||||
|
||||
if (Notifier.shouldShowPrompt()) {
|
||||
showNotificationsToast();
|
||||
if (Notifier.shouldShowPrompt() && !MatrixClientPeg.userRegisteredWithinLastHours(24)) {
|
||||
showNotificationsToast(false);
|
||||
}
|
||||
|
||||
dis.fire(Action.FocusComposer);
|
||||
|
|
|
@ -74,6 +74,8 @@ import { IThreepidInvite } from "../../stores/ThreepidInviteStore";
|
|||
import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call";
|
||||
import WidgetStore from "../../stores/WidgetStore";
|
||||
import {UPDATE_EVENT} from "../../stores/AsyncStore";
|
||||
import Notifier from "../../Notifier";
|
||||
import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast";
|
||||
|
||||
const DEBUG = false;
|
||||
let debuglog = function(msg: string) {};
|
||||
|
@ -1050,6 +1052,11 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
let joinedOrInvitedMemberCount = room.getJoinedMemberCount() + room.getInvitedMemberCount();
|
||||
if (countInfluence) joinedOrInvitedMemberCount += countInfluence;
|
||||
this.setState({isAlone: joinedOrInvitedMemberCount === 1});
|
||||
|
||||
// if they are not alone additionally prompt the user about notifications so they don't miss replies
|
||||
if (joinedOrInvitedMemberCount > 1 && Notifier.shouldShowPrompt()) {
|
||||
showNotificationsToast(true);
|
||||
}
|
||||
}
|
||||
|
||||
private updateDMState() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue