Merge pull request #5418 from matrix-org/t3chguy/fix/muggle-hustle
Small delight tweaks to improve rough corners in the app
This commit is contained in:
commit
9b2143742a
15 changed files with 299 additions and 191 deletions
|
@ -14,6 +14,35 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.mx_InteractiveAuthEntryComponents_emailWrapper {
|
||||||
|
padding-right: 60px;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
|
||||||
|
&::before, &::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 116px;
|
||||||
|
height: 116px;
|
||||||
|
content: "";
|
||||||
|
right: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
background-color: rgba(244, 246, 250, 0.91);
|
||||||
|
border-radius: 50%;
|
||||||
|
top: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background-image: url('$(res)/img/element-icons/email-prompt.svg');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: contain;
|
||||||
|
top: -25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mx_InteractiveAuthEntryComponents_msisdnWrapper {
|
.mx_InteractiveAuthEntryComponents_msisdnWrapper {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ limitations under the License.
|
||||||
|
|
||||||
div:first-child {
|
div:first-child {
|
||||||
font-weight: $font-semi-bold;
|
font-weight: $font-semi-bold;
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
.mx_AccessibleButton {
|
||||||
|
@ -41,6 +40,7 @@ limitations under the License.
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0 0 0 24px;
|
padding: 0 0 0 24px;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: '';
|
||||||
|
@ -53,6 +53,13 @@ limitations under the License.
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_RoomList_explorePrompt_startChat::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/feedback.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_RoomList_explorePrompt_explore::before {
|
||||||
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');
|
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
res/img/element-icons/email-prompt.svg
Normal file
13
res/img/element-icons/email-prompt.svg
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<svg width="57" height="77" viewBox="0 0 57 77" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.55298 38.9352H4C1.79086 38.9352 0 40.726 0 42.9352V72.0304C0 74.2396 1.79086 76.0304 4 76.0304H53C55.2091 76.0304 57 74.2396 57 72.0304V42.9352C57 40.726 55.2091 38.9352 53 38.9352H51.365V41.6473H5.55298V38.9352ZM26.9753 61.3068L3.10141 43.4482C2.33137 42.8721 2.73876 41.6474 3.70041 41.6474H28.459H53.3841C54.3282 41.6474 54.7464 42.8352 54.0107 43.4268L31.8776 61.2212C30.4545 62.3653 28.4374 62.4005 26.9753 61.3068Z" fill="#8A8C8E"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M49.5885 33.0898C48.9384 33.2156 48.2703 33.2911 47.5885 33.3119V34.706V44.4238V54.1415H49.5885V44.4238V34.706V33.0898ZM36.5604 14.2706H13.7177C10.9562 14.2706 8.71765 16.5092 8.71765 19.2706V34.706V44.4238V54.1415H10.7177V44.4238V34.706V19.2706C10.7177 17.6138 12.0608 16.2706 13.7177 16.2706H35.5616C35.8354 15.571 36.1706 14.9022 36.5604 14.2706Z" fill="#8A8C8E"/>
|
||||||
|
<path d="M16.6589 30.5414H37.4826" stroke="#8A8C8E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<line x1="16.2706" y1="37.8708" x2="40.6473" y2="37.8708" stroke="#8A8C8E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<line x1="16.2706" y1="44.812" x2="40.6473" y2="44.812" stroke="#8A8C8E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="47.2003" cy="20.8237" r="9.71771" fill="#FE2928"/>
|
||||||
|
<rect x="45.812" y="14.5765" width="2.77649" height="8.32946" rx="1" fill="white"/>
|
||||||
|
<rect x="45.812" y="24.2943" width="2.77649" height="2.77649" rx="1" fill="white"/>
|
||||||
|
<line x1="27.3766" y1="1" x2="27.3766" y2="10.106" stroke="#8A8C8E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<line x1="34.3179" y1="6.55298" x2="34.3179" y2="10.106" stroke="#8A8C8E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<line x1="20.4354" y1="6.55298" x2="20.4354" y2="10.106" stroke="#8A8C8E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
|
@ -40,11 +40,11 @@ export function inviteMultipleToRoom(roomId, addrs) {
|
||||||
return inviter.invite(addrs).then(states => Promise.resolve({states, inviter}));
|
return inviter.invite(addrs).then(states => Promise.resolve({states, inviter}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showStartChatInviteDialog() {
|
export function showStartChatInviteDialog(initialText) {
|
||||||
// This dialog handles the room creation internally - we don't need to worry about it.
|
// This dialog handles the room creation internally - we don't need to worry about it.
|
||||||
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
|
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
|
||||||
Modal.createTrackedDialog(
|
Modal.createTrackedDialog(
|
||||||
'Start DM', '', InviteDialog, {kind: KIND_DM},
|
'Start DM', '', InviteDialog, {kind: KIND_DM, initialText},
|
||||||
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
|
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -653,8 +653,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
case Action.ViewRoomDirectory: {
|
case Action.ViewRoomDirectory: {
|
||||||
const RoomDirectory = sdk.getComponent("structures.RoomDirectory");
|
const RoomDirectory = sdk.getComponent("structures.RoomDirectory");
|
||||||
Modal.createTrackedDialog('Room directory', '', RoomDirectory, {},
|
Modal.createTrackedDialog('Room directory', '', RoomDirectory, {
|
||||||
'mx_RoomDirectory_dialogWrapper', false, true);
|
initialText: payload.initialText,
|
||||||
|
}, 'mx_RoomDirectory_dialogWrapper', false, true);
|
||||||
|
|
||||||
// View the welcome or home page if we need something to look at
|
// View the welcome or home page if we need something to look at
|
||||||
this.viewSomethingBehindModal();
|
this.viewSomethingBehindModal();
|
||||||
|
@ -677,7 +678,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
this.chatCreateOrReuse(payload.user_id);
|
this.chatCreateOrReuse(payload.user_id);
|
||||||
break;
|
break;
|
||||||
case 'view_create_chat':
|
case 'view_create_chat':
|
||||||
showStartChatInviteDialog();
|
showStartChatInviteDialog(payload.initialText || "");
|
||||||
break;
|
break;
|
||||||
case 'view_invite':
|
case 'view_invite':
|
||||||
showRoomInviteDialog(payload.roomId);
|
showRoomInviteDialog(payload.roomId);
|
||||||
|
|
|
@ -44,6 +44,7 @@ function track(action) {
|
||||||
|
|
||||||
export default class RoomDirectory extends React.Component {
|
export default class RoomDirectory extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
initialText: PropTypes.string,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ export default class RoomDirectory extends React.Component {
|
||||||
error: null,
|
error: null,
|
||||||
instanceId: undefined,
|
instanceId: undefined,
|
||||||
roomServer: MatrixClientPeg.getHomeserverName(),
|
roomServer: MatrixClientPeg.getHomeserverName(),
|
||||||
filterString: null,
|
filterString: this.props.initialText || "",
|
||||||
selectedCommunityId: SettingsStore.getValue("feature_communities_v2_prototypes")
|
selectedCommunityId: SettingsStore.getValue("feature_communities_v2_prototypes")
|
||||||
? selectedCommunityId
|
? selectedCommunityId
|
||||||
: null,
|
: null,
|
||||||
|
@ -686,6 +687,7 @@ export default class RoomDirectory extends React.Component {
|
||||||
onJoinClick={this.onJoinFromSearchClick}
|
onJoinClick={this.onJoinFromSearchClick}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
showJoinButton={showJoinButton}
|
showJoinButton={showJoinButton}
|
||||||
|
initialText={this.props.initialText}
|
||||||
/>
|
/>
|
||||||
{dropdown}
|
{dropdown}
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -148,7 +148,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
|
||||||
onBlur={this.onBlur}
|
onBlur={this.onBlur}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
placeholder={_t("Search")}
|
placeholder={_t("Filter")}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -164,7 +164,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
|
||||||
if (this.props.isMinimized) {
|
if (this.props.isMinimized) {
|
||||||
icon = (
|
icon = (
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
title={_t("Search rooms")}
|
title={_t("Filter rooms and people")}
|
||||||
className="mx_RoomSearch_icon mx_RoomSearch_minimizedHandle"
|
className="mx_RoomSearch_icon mx_RoomSearch_minimizedHandle"
|
||||||
onClick={this.openSearch}
|
onClick={this.openSearch}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -502,6 +502,11 @@ export default class Registration extends React.Component {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide the server picker once the user is doing UI Auth unless encountered a fatal server error
|
||||||
|
if (this.state.phase !== PHASE_SERVER_DETAILS && this.state.doingUIAuth && !this.state.serverErrorIsFatal) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// If we're on a different phase, we only show the server type selector,
|
// If we're on a different phase, we only show the server type selector,
|
||||||
// which is always shown if we allow custom URLs at all.
|
// which is always shown if we allow custom URLs at all.
|
||||||
// (if there's a fatal server error, we need to show the full server
|
// (if there's a fatal server error, we need to show the full server
|
||||||
|
@ -582,17 +587,6 @@ export default class Registration extends React.Component {
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>;
|
</div>;
|
||||||
} else if (this.state.flows.length) {
|
} else if (this.state.flows.length) {
|
||||||
let onEditServerDetailsClick = null;
|
|
||||||
// If custom URLs are allowed and we haven't selected the Free server type, wire
|
|
||||||
// up the server details edit link.
|
|
||||||
if (
|
|
||||||
PHASES_ENABLED &&
|
|
||||||
!SdkConfig.get()['disable_custom_urls'] &&
|
|
||||||
this.state.serverType !== ServerType.FREE
|
|
||||||
) {
|
|
||||||
onEditServerDetailsClick = this.onEditServerDetailsClick;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <RegistrationForm
|
return <RegistrationForm
|
||||||
defaultUsername={this.state.formVals.username}
|
defaultUsername={this.state.formVals.username}
|
||||||
defaultEmail={this.state.formVals.email}
|
defaultEmail={this.state.formVals.email}
|
||||||
|
@ -600,7 +594,6 @@ export default class Registration extends React.Component {
|
||||||
defaultPhoneNumber={this.state.formVals.phoneNumber}
|
defaultPhoneNumber={this.state.formVals.phoneNumber}
|
||||||
defaultPassword={this.state.formVals.password}
|
defaultPassword={this.state.formVals.password}
|
||||||
onRegisterClick={this.onFormSubmit}
|
onRegisterClick={this.onFormSubmit}
|
||||||
onEditServerDetailsClick={onEditServerDetailsClick}
|
|
||||||
flows={this.state.flows}
|
flows={this.state.flows}
|
||||||
serverConfig={this.props.serverConfig}
|
serverConfig={this.props.serverConfig}
|
||||||
canSubmit={!this.state.serverErrorIsFatal}
|
canSubmit={!this.state.serverErrorIsFatal}
|
||||||
|
@ -686,11 +679,48 @@ export default class Registration extends React.Component {
|
||||||
{ regDoneText }
|
{ regDoneText }
|
||||||
</div>;
|
</div>;
|
||||||
} else {
|
} else {
|
||||||
|
let yourMatrixAccountText = _t('Create your Matrix account on %(serverName)s', {
|
||||||
|
serverName: this.props.serverConfig.hsName,
|
||||||
|
});
|
||||||
|
if (this.props.serverConfig.hsNameIsDifferent) {
|
||||||
|
const TextWithTooltip = sdk.getComponent("elements.TextWithTooltip");
|
||||||
|
|
||||||
|
yourMatrixAccountText = _t('Create your Matrix account on <underlinedServerName />', {}, {
|
||||||
|
'underlinedServerName': () => {
|
||||||
|
return <TextWithTooltip
|
||||||
|
class="mx_Login_underlinedServerName"
|
||||||
|
tooltip={this.props.serverConfig.hsUrl}
|
||||||
|
>
|
||||||
|
{this.props.serverConfig.hsName}
|
||||||
|
</TextWithTooltip>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If custom URLs are allowed, user is not doing UIA flows and they haven't selected the Free server type,
|
||||||
|
// wire up the server details edit link.
|
||||||
|
let editLink = null;
|
||||||
|
if (PHASES_ENABLED &&
|
||||||
|
!SdkConfig.get()['disable_custom_urls'] &&
|
||||||
|
this.state.serverType !== ServerType.FREE &&
|
||||||
|
!this.state.doingUIAuth
|
||||||
|
) {
|
||||||
|
editLink = (
|
||||||
|
<a className="mx_AuthBody_editServerDetails" href="#" onClick={this.onEditServerDetailsClick}>
|
||||||
|
{_t('Change')}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
body = <div>
|
body = <div>
|
||||||
<h2>{ _t('Create your account') }</h2>
|
<h2>{ _t('Create your account') }</h2>
|
||||||
{ errorText }
|
{ errorText }
|
||||||
{ serverDeadSection }
|
{ serverDeadSection }
|
||||||
{ this.renderServerComponent() }
|
{ this.renderServerComponent() }
|
||||||
|
{ this.state.phase !== PHASE_SERVER_DETAILS && <h3>
|
||||||
|
{yourMatrixAccountText}
|
||||||
|
{editLink}
|
||||||
|
</h3> }
|
||||||
{ this.renderRegisterComponent() }
|
{ this.renderRegisterComponent() }
|
||||||
{ goBack }
|
{ goBack }
|
||||||
{ signIn }
|
{ signIn }
|
||||||
|
|
|
@ -102,6 +102,10 @@ export default class CaptchaForm extends React.Component {
|
||||||
console.log("Loaded recaptcha script.");
|
console.log("Loaded recaptcha script.");
|
||||||
try {
|
try {
|
||||||
this._renderRecaptcha(DIV_ID);
|
this._renderRecaptcha(DIV_ID);
|
||||||
|
// clear error if re-rendered
|
||||||
|
this.setState({
|
||||||
|
errorText: null,
|
||||||
|
});
|
||||||
CountlyAnalytics.instance.track("onboarding_grecaptcha_loaded");
|
CountlyAnalytics.instance.track("onboarding_grecaptcha_loaded");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|
|
@ -421,12 +421,12 @@ export class EmailIdentityAuthEntry extends React.Component {
|
||||||
return <Spinner />;
|
return <Spinner />;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="mx_InteractiveAuthEntryComponents_emailWrapper">
|
||||||
<p>{ _t("An email has been sent to %(emailAddress)s",
|
<p>{ _t("A confirmation email has been sent to %(emailAddress)s",
|
||||||
{ emailAddress: (sub) => <i>{ this.props.inputs.emailAddress }</i> },
|
{ emailAddress: (sub) => <b>{ this.props.inputs.emailAddress }</b> },
|
||||||
) }
|
) }
|
||||||
</p>
|
</p>
|
||||||
<p>{ _t("Please check your email to continue registration.") }</p>
|
<p>{ _t("Open the link in the email to continue registration.") }</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,6 @@ export default class RegistrationForm extends React.Component {
|
||||||
defaultUsername: PropTypes.string,
|
defaultUsername: PropTypes.string,
|
||||||
defaultPassword: PropTypes.string,
|
defaultPassword: PropTypes.string,
|
||||||
onRegisterClick: PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise
|
onRegisterClick: PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise
|
||||||
onEditServerDetailsClick: PropTypes.func,
|
|
||||||
flows: PropTypes.arrayOf(PropTypes.object).isRequired,
|
flows: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||||
canSubmit: PropTypes.bool,
|
canSubmit: PropTypes.bool,
|
||||||
|
@ -461,7 +460,7 @@ export default class RegistrationForm extends React.Component {
|
||||||
ref={field => this[FIELD_PASSWORD_CONFIRM] = field}
|
ref={field => this[FIELD_PASSWORD_CONFIRM] = field}
|
||||||
type="password"
|
type="password"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
label={_t("Confirm")}
|
label={_t("Confirm password")}
|
||||||
value={this.state.passwordConfirm}
|
value={this.state.passwordConfirm}
|
||||||
onChange={this.onPasswordConfirmChange}
|
onChange={this.onPasswordConfirmChange}
|
||||||
onValidate={this.onPasswordConfirmValidate}
|
onValidate={this.onPasswordConfirmValidate}
|
||||||
|
@ -513,33 +512,6 @@ export default class RegistrationForm extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let yourMatrixAccountText = _t('Create your Matrix account on %(serverName)s', {
|
|
||||||
serverName: this.props.serverConfig.hsName,
|
|
||||||
});
|
|
||||||
if (this.props.serverConfig.hsNameIsDifferent) {
|
|
||||||
const TextWithTooltip = sdk.getComponent("elements.TextWithTooltip");
|
|
||||||
|
|
||||||
yourMatrixAccountText = _t('Create your Matrix account on <underlinedServerName />', {}, {
|
|
||||||
'underlinedServerName': () => {
|
|
||||||
return <TextWithTooltip
|
|
||||||
class="mx_Login_underlinedServerName"
|
|
||||||
tooltip={this.props.serverConfig.hsUrl}
|
|
||||||
>
|
|
||||||
{this.props.serverConfig.hsName}
|
|
||||||
</TextWithTooltip>;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let editLink = null;
|
|
||||||
if (this.props.onEditServerDetailsClick) {
|
|
||||||
editLink = <a className="mx_AuthBody_editServerDetails"
|
|
||||||
href="#" onClick={this.props.onEditServerDetailsClick}
|
|
||||||
>
|
|
||||||
{_t('Change')}
|
|
||||||
</a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const registerButton = (
|
const registerButton = (
|
||||||
<input className="mx_Login_submit" type="submit" value={_t("Register")} disabled={!this.props.canSubmit} />
|
<input className="mx_Login_submit" type="submit" value={_t("Register")} disabled={!this.props.canSubmit} />
|
||||||
);
|
);
|
||||||
|
@ -575,10 +547,6 @@ export default class RegistrationForm extends React.Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3>
|
|
||||||
{yourMatrixAccountText}
|
|
||||||
{editLink}
|
|
||||||
</h3>
|
|
||||||
<form onSubmit={this.onSubmit}>
|
<form onSubmit={this.onSubmit}>
|
||||||
<div className="mx_AuthBody_fieldRow">
|
<div className="mx_AuthBody_fieldRow">
|
||||||
{this.renderUsername()}
|
{this.renderUsername()}
|
||||||
|
|
|
@ -309,10 +309,14 @@ export default class InviteDialog extends React.PureComponent {
|
||||||
|
|
||||||
// The room ID this dialog is for. Only required for KIND_INVITE.
|
// The room ID this dialog is for. Only required for KIND_INVITE.
|
||||||
roomId: PropTypes.string,
|
roomId: PropTypes.string,
|
||||||
|
|
||||||
|
// Initial value to populate the filter with
|
||||||
|
initialText: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
kind: KIND_DM,
|
kind: KIND_DM,
|
||||||
|
initialText: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
_debounceTimer: number = null;
|
_debounceTimer: number = null;
|
||||||
|
@ -339,7 +343,7 @@ export default class InviteDialog extends React.PureComponent {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
targets: [], // array of Member objects (see interface above)
|
targets: [], // array of Member objects (see interface above)
|
||||||
filterText: "",
|
filterText: this.props.initialText,
|
||||||
recents: InviteDialog.buildRecents(alreadyInvited),
|
recents: InviteDialog.buildRecents(alreadyInvited),
|
||||||
numRecentsShown: INITIAL_ROOMS_SHOWN,
|
numRecentsShown: INITIAL_ROOMS_SHOWN,
|
||||||
suggestions: this._buildSuggestions(alreadyInvited),
|
suggestions: this._buildSuggestions(alreadyInvited),
|
||||||
|
@ -357,6 +361,12 @@ export default class InviteDialog extends React.PureComponent {
|
||||||
this._editorRef = createRef();
|
this._editorRef = createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (this.props.initialText) {
|
||||||
|
this._updateSuggestions(this.props.initialText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static buildRecents(excludedTargetIds: Set<string>): {userId: string, user: RoomMember, lastActive: number} {
|
static buildRecents(excludedTargetIds: Set<string>): {userId: string, user: RoomMember, lastActive: number} {
|
||||||
const rooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals(); // map of userId => js-sdk Room
|
const rooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals(); // map of userId => js-sdk Room
|
||||||
|
|
||||||
|
@ -693,17 +703,7 @@ export default class InviteDialog extends React.PureComponent {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_updateFilter = (e) => {
|
_updateSuggestions = async (term) => {
|
||||||
const term = e.target.value;
|
|
||||||
this.setState({filterText: term});
|
|
||||||
|
|
||||||
// Debounce server lookups to reduce spam. We don't clear the existing server
|
|
||||||
// results because they might still be vaguely accurate, likewise for races which
|
|
||||||
// could happen here.
|
|
||||||
if (this._debounceTimer) {
|
|
||||||
clearTimeout(this._debounceTimer);
|
|
||||||
}
|
|
||||||
this._debounceTimer = setTimeout(async () => {
|
|
||||||
MatrixClientPeg.get().searchUserDirectory({term}).then(async r => {
|
MatrixClientPeg.get().searchUserDirectory({term}).then(async r => {
|
||||||
if (term !== this.state.filterText) {
|
if (term !== this.state.filterText) {
|
||||||
// Discard the results - we were probably too slow on the server-side to make
|
// Discard the results - we were probably too slow on the server-side to make
|
||||||
|
@ -810,6 +810,20 @@ export default class InviteDialog extends React.PureComponent {
|
||||||
this.setState({threepidResultsMixin: []}); // clear results because it's moderately fatal
|
this.setState({threepidResultsMixin: []}); // clear results because it's moderately fatal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_updateFilter = (e) => {
|
||||||
|
const term = e.target.value;
|
||||||
|
this.setState({filterText: term});
|
||||||
|
|
||||||
|
// Debounce server lookups to reduce spam. We don't clear the existing server
|
||||||
|
// results because they might still be vaguely accurate, likewise for races which
|
||||||
|
// could happen here.
|
||||||
|
if (this._debounceTimer) {
|
||||||
|
clearTimeout(this._debounceTimer);
|
||||||
|
}
|
||||||
|
this._debounceTimer = setTimeout(() => {
|
||||||
|
this._updateSuggestions(term);
|
||||||
}, 150); // 150ms debounce (human reaction time + some)
|
}, 150); // 150ms debounce (human reaction time + some)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
export default class DirectorySearchBox extends React.Component {
|
export default class DirectorySearchBox extends React.Component {
|
||||||
constructor() {
|
constructor(props) {
|
||||||
super();
|
super(props);
|
||||||
this._collectInput = this._collectInput.bind(this);
|
this._collectInput = this._collectInput.bind(this);
|
||||||
this._onClearClick = this._onClearClick.bind(this);
|
this._onClearClick = this._onClearClick.bind(this);
|
||||||
this._onChange = this._onChange.bind(this);
|
this._onChange = this._onChange.bind(this);
|
||||||
|
@ -31,7 +31,7 @@ export default class DirectorySearchBox extends React.Component {
|
||||||
this.input = null;
|
this.input = null;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
value: '',
|
value: this.props.initialText || '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,14 +90,19 @@ export default class DirectorySearchBox extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={`mx_DirectorySearchBox ${this.props.className} mx_textinput`}>
|
return <div className={`mx_DirectorySearchBox ${this.props.className} mx_textinput`}>
|
||||||
<input type="text" name="dirsearch" value={this.state.value}
|
<input
|
||||||
|
type="text"
|
||||||
|
name="dirsearch"
|
||||||
|
value={this.state.value}
|
||||||
className="mx_textinput_icon mx_textinput_search"
|
className="mx_textinput_icon mx_textinput_search"
|
||||||
ref={this._collectInput}
|
ref={this._collectInput}
|
||||||
onChange={this._onChange} onKeyUp={this._onKeyUp}
|
onChange={this._onChange}
|
||||||
placeholder={this.props.placeholder} autoFocus
|
onKeyUp={this._onKeyUp}
|
||||||
|
placeholder={this.props.placeholder}
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
{ joinButton }
|
{ joinButton }
|
||||||
<AccessibleButton className="mx_DirectorySearchBox_clear" onClick={this._onClearClick}></AccessibleButton>
|
<AccessibleButton className="mx_DirectorySearchBox_clear" onClick={this._onClearClick} />
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,4 +114,5 @@ DirectorySearchBox.propTypes = {
|
||||||
onJoinClick: PropTypes.func,
|
onJoinClick: PropTypes.func,
|
||||||
placeholder: PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
showJoinButton: PropTypes.bool,
|
showJoinButton: PropTypes.bool,
|
||||||
|
initialText: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
|
@ -58,6 +58,7 @@ interface IProps {
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
sublists: ITagMap;
|
sublists: ITagMap;
|
||||||
|
isNameFiltering: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TAG_ORDER: TagID[] = [
|
const TAG_ORDER: TagID[] = [
|
||||||
|
@ -183,6 +184,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
sublists: {},
|
sublists: {},
|
||||||
|
isNameFiltering: !!RoomListStore.instance.getFirstNameFilterCondition(),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||||
|
@ -253,7 +255,8 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
return CustomRoomTagStore.getTags()[t];
|
return CustomRoomTagStore.getTags()[t];
|
||||||
});
|
});
|
||||||
|
|
||||||
let doUpdate = arrayHasDiff(previousListIds, newListIds);
|
const isNameFiltering = !!RoomListStore.instance.getFirstNameFilterCondition();
|
||||||
|
let doUpdate = this.state.isNameFiltering !== isNameFiltering || arrayHasDiff(previousListIds, newListIds);
|
||||||
if (!doUpdate) {
|
if (!doUpdate) {
|
||||||
// so we didn't have the visible sublists change, but did the contents of those
|
// so we didn't have the visible sublists change, but did the contents of those
|
||||||
// sublists change significantly enough to break the sticky headers? Probably, so
|
// sublists change significantly enough to break the sticky headers? Probably, so
|
||||||
|
@ -275,14 +278,20 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
const newSublists = objectWithOnly(newLists, newListIds);
|
const newSublists = objectWithOnly(newLists, newListIds);
|
||||||
const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v));
|
const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v));
|
||||||
|
|
||||||
this.setState({sublists}, () => {
|
this.setState({sublists, isNameFiltering}, () => {
|
||||||
this.props.onResize();
|
this.props.onResize();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onStartChat = () => {
|
||||||
|
const initialText = RoomListStore.instance.getFirstNameFilterCondition()?.search;
|
||||||
|
dis.dispatch({ action: "view_create_chat", initialText });
|
||||||
|
};
|
||||||
|
|
||||||
private onExplore = () => {
|
private onExplore = () => {
|
||||||
dis.fire(Action.ViewRoomDirectory);
|
const initialText = RoomListStore.instance.getFirstNameFilterCondition()?.search;
|
||||||
|
dis.dispatch({ action: Action.ViewRoomDirectory, initialText });
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderCommunityInvites(): TemporaryTile[] {
|
private renderCommunityInvites(): TemporaryTile[] {
|
||||||
|
@ -332,8 +341,9 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
return p;
|
return p;
|
||||||
}, [] as TagID[]);
|
}, [] as TagID[]);
|
||||||
|
|
||||||
// show a skeleton UI if the user is in no rooms
|
// show a skeleton UI if the user is in no rooms and they are not filtering
|
||||||
const showSkeleton = Object.values(RoomListStore.instance.unfilteredLists).every(list => !list?.length);
|
const showSkeleton = !this.state.isNameFiltering &&
|
||||||
|
Object.values(RoomListStore.instance.unfilteredLists).every(list => !list?.length);
|
||||||
|
|
||||||
for (const orderedTagId of tagOrder) {
|
for (const orderedTagId of tagOrder) {
|
||||||
const orderedRooms = this.state.sublists[orderedTagId] || [];
|
const orderedRooms = this.state.sublists[orderedTagId] || [];
|
||||||
|
@ -370,10 +380,21 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
public render() {
|
public render() {
|
||||||
let explorePrompt: JSX.Element;
|
let explorePrompt: JSX.Element;
|
||||||
if (!this.props.isMinimized) {
|
if (!this.props.isMinimized) {
|
||||||
if (RoomListStore.instance.getFirstNameFilterCondition()) {
|
if (this.state.isNameFiltering) {
|
||||||
explorePrompt = <div className="mx_RoomList_explorePrompt">
|
explorePrompt = <div className="mx_RoomList_explorePrompt">
|
||||||
<div>{_t("Can't see what you’re looking for?")}</div>
|
<div>{_t("Can't see what you’re looking for?")}</div>
|
||||||
<AccessibleButton kind="link" onClick={this.onExplore}>
|
<AccessibleButton
|
||||||
|
className="mx_RoomList_explorePrompt_startChat"
|
||||||
|
kind="link"
|
||||||
|
onClick={this.onStartChat}
|
||||||
|
>
|
||||||
|
{_t("Start a new chat")}
|
||||||
|
</AccessibleButton>
|
||||||
|
<AccessibleButton
|
||||||
|
className="mx_RoomList_explorePrompt_explore"
|
||||||
|
kind="link"
|
||||||
|
onClick={this.onExplore}
|
||||||
|
>
|
||||||
{_t("Explore all public rooms")}
|
{_t("Explore all public rooms")}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -385,7 +406,18 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
if (unfilteredRooms.length < 1 && unfilteredHistorical < 1) {
|
if (unfilteredRooms.length < 1 && unfilteredHistorical < 1) {
|
||||||
explorePrompt = <div className="mx_RoomList_explorePrompt">
|
explorePrompt = <div className="mx_RoomList_explorePrompt">
|
||||||
<div>{_t("Use the + to make a new room or explore existing ones below")}</div>
|
<div>{_t("Use the + to make a new room or explore existing ones below")}</div>
|
||||||
<AccessibleButton kind="link" onClick={this.onExplore}>
|
<AccessibleButton
|
||||||
|
className="mx_RoomList_explorePrompt_startChat"
|
||||||
|
kind="link"
|
||||||
|
onClick={this.onStartChat}
|
||||||
|
>
|
||||||
|
{_t("Start a new chat")}
|
||||||
|
</AccessibleButton>
|
||||||
|
<AccessibleButton
|
||||||
|
className="mx_RoomList_explorePrompt_explore"
|
||||||
|
kind="link"
|
||||||
|
onClick={this.onExplore}
|
||||||
|
>
|
||||||
{_t("Explore all public rooms")}
|
{_t("Explore all public rooms")}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -1390,6 +1390,7 @@
|
||||||
"Historical": "Historical",
|
"Historical": "Historical",
|
||||||
"Custom Tag": "Custom Tag",
|
"Custom Tag": "Custom Tag",
|
||||||
"Can't see what you’re looking for?": "Can't see what you’re looking for?",
|
"Can't see what you’re looking for?": "Can't see what you’re looking for?",
|
||||||
|
"Start a new chat": "Start a new chat",
|
||||||
"Explore all public rooms": "Explore all public rooms",
|
"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",
|
"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|other": "%(count)s results",
|
||||||
|
@ -2208,8 +2209,8 @@
|
||||||
"Missing captcha public key in homeserver configuration. Please report this to your homeserver administrator.": "Missing captcha public key in homeserver configuration. Please report this to your homeserver administrator.",
|
"Missing captcha public key in homeserver configuration. Please report this to your homeserver administrator.": "Missing captcha public key in homeserver configuration. Please report this to your homeserver administrator.",
|
||||||
"Please review and accept all of the homeserver's policies": "Please review and accept all of the homeserver's policies",
|
"Please review and accept all of the homeserver's policies": "Please review and accept all of the homeserver's policies",
|
||||||
"Please review and accept the policies of this homeserver:": "Please review and accept the policies of this homeserver:",
|
"Please review and accept the policies of this homeserver:": "Please review and accept the policies of this homeserver:",
|
||||||
"An email has been sent to %(emailAddress)s": "An email has been sent to %(emailAddress)s",
|
"A confirmation email has been sent to %(emailAddress)s": "A confirmation email has been sent to %(emailAddress)s",
|
||||||
"Please check your email to continue registration.": "Please check your email to continue registration.",
|
"Open the link in the email to continue registration.": "Open the link in the email to continue registration.",
|
||||||
"Token incorrect": "Token incorrect",
|
"Token incorrect": "Token incorrect",
|
||||||
"A text message has been sent to %(msisdn)s": "A text message has been sent to %(msisdn)s",
|
"A text message has been sent to %(msisdn)s": "A text message has been sent to %(msisdn)s",
|
||||||
"Please enter the code it contains:": "Please enter the code it contains:",
|
"Please enter the code it contains:": "Please enter the code it contains:",
|
||||||
|
@ -2246,8 +2247,6 @@
|
||||||
"Enter username": "Enter username",
|
"Enter username": "Enter username",
|
||||||
"Email (optional)": "Email (optional)",
|
"Email (optional)": "Email (optional)",
|
||||||
"Phone (optional)": "Phone (optional)",
|
"Phone (optional)": "Phone (optional)",
|
||||||
"Create your Matrix account on %(serverName)s": "Create your Matrix account on %(serverName)s",
|
|
||||||
"Create your Matrix account on <underlinedServerName />": "Create your Matrix account on <underlinedServerName />",
|
|
||||||
"Register": "Register",
|
"Register": "Register",
|
||||||
"Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.",
|
"Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.",
|
||||||
"Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Set an email for account recovery. Use email to optionally be discoverable by existing contacts.",
|
"Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Set an email for account recovery. Use email to optionally be discoverable by existing contacts.",
|
||||||
|
@ -2370,8 +2369,9 @@
|
||||||
"Find a room… (e.g. %(exampleRoom)s)": "Find a room… (e.g. %(exampleRoom)s)",
|
"Find a room… (e.g. %(exampleRoom)s)": "Find a room… (e.g. %(exampleRoom)s)",
|
||||||
"If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.": "If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.",
|
"If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.": "If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.",
|
||||||
"Explore rooms in %(communityName)s": "Explore rooms in %(communityName)s",
|
"Explore rooms in %(communityName)s": "Explore rooms in %(communityName)s",
|
||||||
|
"Filter": "Filter",
|
||||||
"Clear filter": "Clear filter",
|
"Clear filter": "Clear filter",
|
||||||
"Search rooms": "Search rooms",
|
"Filter rooms and people": "Filter rooms and people",
|
||||||
"You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.": "You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.",
|
"You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.": "You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.",
|
||||||
"Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please <a>contact your service administrator</a> to continue using the service.": "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please <a>contact your service administrator</a> to continue using the service.",
|
"Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please <a>contact your service administrator</a> to continue using the service.": "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please <a>contact your service administrator</a> to continue using the service.",
|
||||||
"Your message wasn't sent because this homeserver has exceeded a resource limit. Please <a>contact your service administrator</a> to continue using the service.": "Your message wasn't sent because this homeserver has exceeded a resource limit. Please <a>contact your service administrator</a> to continue using the service.",
|
"Your message wasn't sent because this homeserver has exceeded a resource limit. Please <a>contact your service administrator</a> to continue using the service.": "Your message wasn't sent because this homeserver has exceeded a resource limit. Please <a>contact your service administrator</a> to continue using the service.",
|
||||||
|
@ -2470,6 +2470,8 @@
|
||||||
"<a>Log in</a> to your new account.": "<a>Log in</a> to your new account.",
|
"<a>Log in</a> to your new account.": "<a>Log in</a> to your new account.",
|
||||||
"You can now close this window or <a>log in</a> to your new account.": "You can now close this window or <a>log in</a> to your new account.",
|
"You can now close this window or <a>log in</a> to your new account.": "You can now close this window or <a>log in</a> to your new account.",
|
||||||
"Registration Successful": "Registration Successful",
|
"Registration Successful": "Registration Successful",
|
||||||
|
"Create your Matrix account on %(serverName)s": "Create your Matrix account on %(serverName)s",
|
||||||
|
"Create your Matrix account on <underlinedServerName />": "Create your Matrix account on <underlinedServerName />",
|
||||||
"Create your account": "Create your account",
|
"Create your account": "Create your account",
|
||||||
"Use Recovery Key or Passphrase": "Use Recovery Key or Passphrase",
|
"Use Recovery Key or Passphrase": "Use Recovery Key or Passphrase",
|
||||||
"Use Recovery Key": "Use Recovery Key",
|
"Use Recovery Key": "Use Recovery Key",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue