mirror of https://github.com/vector-im/riot-web
Merge pull request #5383 from matrix-org/t3chguy/fix/15604
Tweaks to toasts and post-registration landingpull/21833/head
commit
5c9acc364f
|
@ -50,6 +50,44 @@ limitations under the License.
|
|||
color: $muted-fg-color;
|
||||
}
|
||||
|
||||
.mx_HomePage_userAvatar {
|
||||
position: relative;
|
||||
width: min-content;
|
||||
margin: 0 auto;
|
||||
|
||||
&::before, &::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
|
||||
right: -6px;
|
||||
bottom: -6px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: $primary-bg-color;
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background-color: $secondary-fg-color;
|
||||
mask-position: center;
|
||||
mask-repeat: no-repeat;
|
||||
mask-image: url('$(res)/img/element-icons/camera.svg');
|
||||
mask-size: 16px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.mx_HomePage_userAvatar_busy::after {
|
||||
background: url("$(res)/img/spinner.gif") no-repeat center;
|
||||
background-size: 80%;
|
||||
mask: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_HomePage_default_buttons {
|
||||
margin: 80px auto 0;
|
||||
width: fit-content;
|
||||
|
@ -57,49 +95,43 @@ limitations under the License.
|
|||
.mx_AccessibleButton {
|
||||
padding: 73px 8px 15px; // top: 20px top padding + 40px icon + 13px margin
|
||||
|
||||
width: 104px; // 120px - 2* 8px
|
||||
margin: 0 39px; // 55px - 2* 8px
|
||||
width: 160px;
|
||||
height: 132px;
|
||||
margin: 0 20px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
border-radius: 8px;
|
||||
vertical-align: top;
|
||||
word-break: break-word;
|
||||
box-sizing: border-box;
|
||||
|
||||
font-weight: 600;
|
||||
font-size: $font-15px;
|
||||
line-height: $font-20px;
|
||||
color: $muted-fg-color;
|
||||
|
||||
&:hover {
|
||||
color: $accent-color;
|
||||
background: rgba($accent-color, 0.06);
|
||||
|
||||
&::before {
|
||||
background-color: $accent-color;
|
||||
}
|
||||
}
|
||||
color: #fff; // on all themes
|
||||
background-color: $accent-color;
|
||||
|
||||
&::before {
|
||||
top: 20px;
|
||||
left: 40px; // (120px-40px)/2
|
||||
left: 60px; // (160px-40px)/2
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: $muted-fg-color;
|
||||
background-color: #fff; // on all themes
|
||||
mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
}
|
||||
|
||||
&.mx_HomePage_button_sendDm::before {
|
||||
mask-image: url('$(res)/img/feather-customised/message-circle.svg');
|
||||
mask-image: url('$(res)/img/element-icons/feedback.svg');
|
||||
}
|
||||
&.mx_HomePage_button_explore::before {
|
||||
mask-image: url('$(res)/img/feather-customised/explore.svg');
|
||||
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');
|
||||
}
|
||||
&.mx_HomePage_button_createGroup::before {
|
||||
mask-image: url('$(res)/img/feather-customised/group.svg');
|
||||
mask-image: url('$(res)/img/element-icons/community-members.svg');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -383,3 +383,22 @@ limitations under the License.
|
|||
.mx_RoomSublist_addRoomTooltip {
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
.mx_RoomSublist_skeletonUI {
|
||||
position: relative;
|
||||
margin-left: 4px;
|
||||
height: 288px;
|
||||
|
||||
&::before {
|
||||
background: $roomsublist-skeleton-ui-bg;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
content: '';
|
||||
position: absolute;
|
||||
mask-repeat: repeat-y;
|
||||
mask-size: auto 48px;
|
||||
mask-image: url('$(res)/img/element-icons/roomlist/skeleton-ui.svg');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.4896 2.5C9.04778 2.5 7.827 3.52171 7.54879 4.90624C7.50711 5.11367 7.42679 5.31408 7.28726 5.47312L6.6851 6.15949C6.49523 6.37591 6.22129 6.5 5.93338 6.5H2.75C1.64543 6.5 0.75 7.39543 0.75 8.5V19.5C0.75 20.6046 1.64543 21.5 2.75 21.5H22.75C23.8546 21.5 24.75 20.6046 24.75 19.5V8.5C24.75 7.39543 23.8546 6.5 22.75 6.5H19.5666C19.2787 6.5 19.0048 6.37591 18.8149 6.15949L18.2127 5.47312C18.0732 5.31408 17.9929 5.11366 17.9512 4.90623C17.673 3.5217 16.4522 2.5 15.0104 2.5H10.4896ZM16.75 13.5C16.75 15.7091 14.9591 17.5 12.75 17.5C10.5409 17.5 8.75 15.7091 8.75 13.5C8.75 11.2909 10.5409 9.5 12.75 9.5C14.9591 9.5 16.75 11.2909 16.75 13.5ZM3.25 5C2.97386 5 2.75 5.22386 2.75 5.5C2.75 5.77614 2.97386 6 3.25 6H5.25C5.52614 6 5.75 5.77614 5.75 5.5C5.75 5.22386 5.52614 5 5.25 5H3.25Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="24" height="24" fill="white" transform="translate(0.75)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,5 @@
|
|||
<svg width="228" height="48" viewBox="0 0 228 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="16" cy="16" r="16" fill="#D4D4D4"/>
|
||||
<rect x="39" width="189" height="12" rx="6" fill="#D4D4D4"/>
|
||||
<rect x="39" y="20" width="143" height="12" rx="6" fill="#D4D4D4"/>
|
||||
</svg>
|
After Width: | Height: | Size: 282 B |
|
@ -117,6 +117,7 @@ $roomlist-filter-active-bg-color: $bg-color;
|
|||
$roomlist-bg-color: rgba(33, 38, 44, 0.90);
|
||||
$roomlist-header-color: $tertiary-fg-color;
|
||||
$roomsublist-divider-color: $primary-fg-color;
|
||||
$roomsublist-skeleton-ui-bg: linear-gradient(180deg, #3e444c 0%, #3e444c00 100%);
|
||||
|
||||
$groupFilterPanel-divider-color: $roomlist-header-color;
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ $roomlist-filter-active-bg-color: $roomlist-button-bg-color;
|
|||
$roomlist-bg-color: $header-panel-bg-color;
|
||||
|
||||
$roomsublist-divider-color: $primary-fg-color;
|
||||
$roomsublist-skeleton-ui-bg: linear-gradient(180deg, #3e444c 0%, #3e444c00 100%);
|
||||
|
||||
$groupFilterPanel-divider-color: $roomlist-header-color;
|
||||
|
||||
|
|
|
@ -181,6 +181,7 @@ $roomlist-filter-active-bg-color: $roomlist-button-bg-color;
|
|||
$roomlist-bg-color: $header-panel-bg-color;
|
||||
$roomlist-header-color: $primary-fg-color;
|
||||
$roomsublist-divider-color: $primary-fg-color;
|
||||
$roomsublist-skeleton-ui-bg: linear-gradient(180deg, #ffffff 0%, #ffffff00 100%);
|
||||
|
||||
$groupFilterPanel-divider-color: $roomlist-header-color;
|
||||
|
||||
|
|
|
@ -175,6 +175,7 @@ $roomlist-filter-active-bg-color: #ffffff;
|
|||
$roomlist-bg-color: rgba(245, 245, 245, 0.90);
|
||||
$roomlist-header-color: $tertiary-fg-color;
|
||||
$roomsublist-divider-color: $primary-fg-color;
|
||||
$roomsublist-skeleton-ui-bg: linear-gradient(180deg, #ffffff 0%, #ffffff00 100%);
|
||||
|
||||
$groupFilterPanel-divider-color: $roomlist-header-color;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import {ActionPayload} from "./dispatcher/payloads";
|
|||
import {CheckUpdatesPayload} from "./dispatcher/payloads/CheckUpdatesPayload";
|
||||
import {Action} from "./dispatcher/actions";
|
||||
import {hideToast as hideUpdateToast} from "./toasts/UpdateToast";
|
||||
import {MatrixClientPeg} from "./MatrixClientPeg";
|
||||
|
||||
export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url";
|
||||
export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_url";
|
||||
|
@ -105,6 +106,9 @@ export default abstract class BasePlatform {
|
|||
* @param newVersion the version string to check
|
||||
*/
|
||||
protected shouldShowUpdate(newVersion: string): boolean {
|
||||
// If the user registered on this client in the last 24 hours then do not show them the update toast
|
||||
if (MatrixClientPeg.userRegisteredWithinLastHours(24)) return false;
|
||||
|
||||
try {
|
||||
const [version, deferUntil] = JSON.parse(localStorage.getItem(UPDATE_DEFER_KEY));
|
||||
return newVersion !== version || Date.now() > deferUntil;
|
||||
|
|
|
@ -100,6 +100,12 @@ export interface IMatrixClientPeg {
|
|||
*/
|
||||
currentUserIsJustRegistered(): boolean;
|
||||
|
||||
/**
|
||||
* If the current user has been registered by this device then this
|
||||
* returns a boolean of whether it was within the last N hours given.
|
||||
*/
|
||||
userRegisteredWithinLastHours(hours: number): boolean;
|
||||
|
||||
/**
|
||||
* Replace this MatrixClientPeg's client with a client instance that has
|
||||
* homeserver / identity server URLs and active credentials
|
||||
|
@ -150,6 +156,9 @@ class _MatrixClientPeg implements IMatrixClientPeg {
|
|||
|
||||
public setJustRegisteredUserId(uid: string): void {
|
||||
this.justRegisteredUserId = uid;
|
||||
if (uid) {
|
||||
window.localStorage.setItem("mx_registration_time", String(new Date().getTime()));
|
||||
}
|
||||
}
|
||||
|
||||
public currentUserIsJustRegistered(): boolean {
|
||||
|
@ -159,6 +168,15 @@ class _MatrixClientPeg implements IMatrixClientPeg {
|
|||
);
|
||||
}
|
||||
|
||||
public userRegisteredWithinLastHours(hours: number): boolean {
|
||||
try {
|
||||
const date = new Date(window.localStorage.getItem("mx_registration_time"));
|
||||
return ((new Date().getTime() - date.getTime()) / 36e5) <= hours;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public replaceUsingCreds(creds: IMatrixClientCreds): void {
|
||||
this.currentClientCreds = creds;
|
||||
this.createClient(creds);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -332,6 +332,8 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
return p;
|
||||
}, [] as TagID[]);
|
||||
|
||||
const showSkeleton = tagOrder.every(tag => !this.state.sublists[tag]?.length);
|
||||
|
||||
for (const orderedTagId of tagOrder) {
|
||||
const orderedRooms = this.state.sublists[orderedTagId] || [];
|
||||
const extraTiles = orderedTagId === DefaultTagID.Invite ? this.renderCommunityInvites() : null;
|
||||
|
@ -356,6 +358,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
addRoomContextMenu={aesthetics.addRoomContextMenu}
|
||||
isMinimized={this.props.isMinimized}
|
||||
onResize={this.props.onResize}
|
||||
showSkeleton={showSkeleton}
|
||||
extraBadTilesThatShouldntExist={extraTiles}
|
||||
/>);
|
||||
}
|
||||
|
@ -365,13 +368,28 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
|
||||
public render() {
|
||||
let explorePrompt: JSX.Element;
|
||||
if (!this.props.isMinimized && RoomListStore.instance.getFirstNameFilterCondition()) {
|
||||
explorePrompt = <div className="mx_RoomList_explorePrompt">
|
||||
<div>{_t("Can't see what you’re looking for?")}</div>
|
||||
<AccessibleButton kind="link" onClick={this.onExplore}>
|
||||
{_t("Explore all public rooms")}
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
if (!this.props.isMinimized) {
|
||||
if (RoomListStore.instance.getFirstNameFilterCondition()) {
|
||||
explorePrompt = <div className="mx_RoomList_explorePrompt">
|
||||
<div>{_t("Can't see what you’re looking for?")}</div>
|
||||
<AccessibleButton kind="link" onClick={this.onExplore}>
|
||||
{_t("Explore all public rooms")}
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
} else if (Object.values(this.state.sublists).some(list => list.length > 0)) {
|
||||
const unfilteredLists = RoomListStore.instance.unfilteredLists
|
||||
const unfilteredRooms = unfilteredLists[DefaultTagID.Untagged] || [];
|
||||
const unfilteredHistorical = unfilteredLists[DefaultTagID.Archived] || [];
|
||||
// show a prompt to join/create rooms if the user is in 0 rooms and no historical
|
||||
if (unfilteredRooms.length < 1 && unfilteredHistorical < 1) {
|
||||
explorePrompt = <div className="mx_RoomList_explorePrompt">
|
||||
<div>{_t("Use the + to make a new room or explore existing ones below")}</div>
|
||||
<AccessibleButton kind="link" onClick={this.onExplore}>
|
||||
{_t("Explore all public rooms")}
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sublists = this.renderSublists();
|
||||
|
|
|
@ -71,6 +71,7 @@ interface IProps {
|
|||
isMinimized: boolean;
|
||||
tagId: TagID;
|
||||
onResize: () => void;
|
||||
showSkeleton?: boolean;
|
||||
|
||||
// TODO: Don't use this. It's for community invites, and community invites shouldn't be here.
|
||||
// You should feel bad if you use this.
|
||||
|
@ -877,6 +878,8 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
</Resizable>
|
||||
</React.Fragment>
|
||||
);
|
||||
} else if (this.props.showSkeleton && this.state.isExpanded) {
|
||||
content = <div className="mx_RoomSublist_skeletonUI" />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -403,10 +403,10 @@
|
|||
"Verify all your sessions to ensure your account & messages are safe": "Verify all your sessions to ensure your account & messages are safe",
|
||||
"Review": "Review",
|
||||
"Later": "Later",
|
||||
"Don't miss a reply": "Don't miss a reply",
|
||||
"Notifications": "Notifications",
|
||||
"You are not receiving desktop notifications": "You are not receiving desktop notifications",
|
||||
"Enable them now": "Enable them now",
|
||||
"Close": "Close",
|
||||
"Enable desktop notifications": "Enable desktop notifications",
|
||||
"Enable": "Enable",
|
||||
"Your homeserver has exceeded its user limit.": "Your homeserver has exceeded its user limit.",
|
||||
"Your homeserver has exceeded one of its resource limits.": "Your homeserver has exceeded one of its resource limits.",
|
||||
"Contact your <a>server admin</a>.": "Contact your <a>server admin</a>.",
|
||||
|
@ -424,9 +424,8 @@
|
|||
"What's new?": "What's new?",
|
||||
"What's New": "What's New",
|
||||
"Update": "Update",
|
||||
"Restart": "Restart",
|
||||
"Upgrade your %(brand)s": "Upgrade your %(brand)s",
|
||||
"A new version of %(brand)s is available!": "A new version of %(brand)s is available!",
|
||||
"Update %(brand)s": "Update %(brand)s",
|
||||
"New version of %(brand)s is available": "New version of %(brand)s is available",
|
||||
"Guest": "Guest",
|
||||
"There was an error joining the room": "There was an error joining the room",
|
||||
"Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.",
|
||||
|
@ -689,7 +688,6 @@
|
|||
"rooms.": "rooms.",
|
||||
"Manage": "Manage",
|
||||
"Securely cache encrypted messages locally for them to appear in search results.": "Securely cache encrypted messages locally for them to appear in search results.",
|
||||
"Enable": "Enable",
|
||||
"%(brand)s is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom %(brand)s Desktop with <nativeLink>search components added</nativeLink>.": "%(brand)s is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom %(brand)s Desktop with <nativeLink>search components added</nativeLink>.",
|
||||
"%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use <desktopLink>%(brand)s Desktop</desktopLink> for encrypted messages to appear in search results.": "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use <desktopLink>%(brand)s Desktop</desktopLink> for encrypted messages to appear in search results.",
|
||||
"Connecting to integration manager...": "Connecting to integration manager...",
|
||||
|
@ -872,6 +870,7 @@
|
|||
"Ban list rules - %(roomName)s": "Ban list rules - %(roomName)s",
|
||||
"Server rules": "Server rules",
|
||||
"User rules": "User rules",
|
||||
"Close": "Close",
|
||||
"You have not ignored anyone.": "You have not ignored anyone.",
|
||||
"You are currently ignoring:": "You are currently ignoring:",
|
||||
"You are not subscribed to any lists": "You are not subscribed to any lists",
|
||||
|
@ -1132,6 +1131,7 @@
|
|||
"Custom Tag": "Custom Tag",
|
||||
"Can't see what you’re looking for?": "Can't see what you’re looking for?",
|
||||
"Explore all public rooms": "Explore all public rooms",
|
||||
"Use the + to make a new room or explore existing ones below": "Use the + to make a new room or explore existing ones below",
|
||||
"%(count)s results|other": "%(count)s results",
|
||||
"%(count)s results|one": "%(count)s result",
|
||||
"This room": "This room",
|
||||
|
@ -2053,6 +2053,10 @@
|
|||
"Community %(groupId)s not found": "Community %(groupId)s not found",
|
||||
"This homeserver does not support communities": "This homeserver does not support communities",
|
||||
"Failed to load %(groupId)s": "Failed to load %(groupId)s",
|
||||
"Great, that'll help people know it's you": "Great, that'll help people know it's you",
|
||||
"Add a photo so people know it's you.": "Add a photo so people know it's you.",
|
||||
"Welcome %(name)s": "Welcome %(name)s",
|
||||
"Now, lets help you get started": "Now, lets help you get started",
|
||||
"Welcome to %(appName)s": "Welcome to %(appName)s",
|
||||
"Liberate your communication": "Liberate your communication",
|
||||
"Send a Direct Message": "Send a Direct Message",
|
||||
|
|
|
@ -29,15 +29,15 @@ const onReject = () => {
|
|||
|
||||
const TOAST_KEY = "desktopnotifications";
|
||||
|
||||
export const showToast = () => {
|
||||
export const showToast = (fromMessageSend: boolean) => {
|
||||
ToastStore.sharedInstance().addOrReplaceToast({
|
||||
key: TOAST_KEY,
|
||||
title: _t("Notifications"),
|
||||
title: fromMessageSend ? _t("Don't miss a reply") : _t("Notifications"),
|
||||
props: {
|
||||
description: _t("You are not receiving desktop notifications"),
|
||||
acceptLabel: _t("Enable them now"),
|
||||
description: _t("Enable desktop notifications"),
|
||||
acceptLabel: _t("Enable"),
|
||||
onAccept,
|
||||
rejectLabel: _t("Close"),
|
||||
rejectLabel: _t("Dismiss"),
|
||||
onReject,
|
||||
},
|
||||
component: GenericToast,
|
||||
|
|
|
@ -74,18 +74,18 @@ export const showToast = (version: string, newVersion: string, releaseNotes?: st
|
|||
};
|
||||
} else {
|
||||
onAccept = installUpdate;
|
||||
acceptLabel = _t("Restart");
|
||||
acceptLabel = _t("Update");
|
||||
}
|
||||
|
||||
const brand = SdkConfig.get().brand;
|
||||
ToastStore.sharedInstance().addOrReplaceToast({
|
||||
key: TOAST_KEY,
|
||||
title: _t("Upgrade your %(brand)s", { brand }),
|
||||
title: _t("Update %(brand)s", { brand }),
|
||||
props: {
|
||||
description: _t("A new version of %(brand)s is available!", { brand }),
|
||||
description: _t("New version of %(brand)s is available", { brand }),
|
||||
acceptLabel,
|
||||
onAccept,
|
||||
rejectLabel: _t("Later"),
|
||||
rejectLabel: _t("Dismiss"),
|
||||
onReject,
|
||||
},
|
||||
component: GenericToast,
|
||||
|
|
Loading…
Reference in New Issue