MM-49865 - add telemetry to account creation screen (#22702)
* MM-49865 - add telemetry to account creation screen * fix snapshots
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
529ab959e2
Коммит
b892fe0555
@@ -58,6 +58,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
disabled={false}
|
disabled={false}
|
||||||
inputSize="large"
|
inputSize="large"
|
||||||
name="email"
|
name="email"
|
||||||
|
onBlur={[Function]}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
placeholder="Email address"
|
placeholder="Email address"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -75,6 +76,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
disabled={false}
|
disabled={false}
|
||||||
inputSize="large"
|
inputSize="large"
|
||||||
name="name"
|
name="name"
|
||||||
|
onBlur={[Function]}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
placeholder="Choose a Username"
|
placeholder="Choose a Username"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -87,6 +89,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
error=""
|
error=""
|
||||||
info="Must be 5-64 characters long."
|
info="Must be 5-64 characters long."
|
||||||
inputSize="large"
|
inputSize="large"
|
||||||
|
onBlur={[Function]}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
@@ -206,6 +209,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
disabled={false}
|
disabled={false}
|
||||||
inputSize="large"
|
inputSize="large"
|
||||||
name="email"
|
name="email"
|
||||||
|
onBlur={[Function]}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
placeholder="Email address"
|
placeholder="Email address"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -223,6 +227,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
disabled={false}
|
disabled={false}
|
||||||
inputSize="large"
|
inputSize="large"
|
||||||
name="name"
|
name="name"
|
||||||
|
onBlur={[Function]}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
placeholder="Choose a Username"
|
placeholder="Choose a Username"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -235,6 +240,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
error=""
|
error=""
|
||||||
info="Must be 5-64 characters long."
|
info="Must be 5-64 characters long."
|
||||||
inputSize="large"
|
inputSize="large"
|
||||||
|
onBlur={[Function]}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {useState, useEffect, useRef, useCallback} from 'react';
|
import React, {useState, useEffect, useRef, useCallback, FocusEvent} from 'react';
|
||||||
|
|
||||||
import {useIntl} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
import {useLocation, useHistory} from 'react-router-dom';
|
import {useLocation, useHistory} from 'react-router-dom';
|
||||||
import {useSelector, useDispatch} from 'react-redux';
|
import {useSelector, useDispatch} from 'react-redux';
|
||||||
@@ -455,16 +456,25 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function sendSignUpTelemetryEvents(telemetryId: string, props?: any) {
|
||||||
|
trackEvent('signup', telemetryId, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
type TelemetryErrorList = {errors: Array<{field: string; rule: string}>; success: boolean};
|
||||||
|
|
||||||
const isUserValid = () => {
|
const isUserValid = () => {
|
||||||
let isValid = true;
|
let isValid = true;
|
||||||
|
|
||||||
const providedEmail = emailInput.current?.value.trim();
|
const providedEmail = emailInput.current?.value.trim();
|
||||||
|
const telemetryEvents: TelemetryErrorList = {errors: [], success: true};
|
||||||
|
|
||||||
if (!providedEmail) {
|
if (!providedEmail) {
|
||||||
setEmailError(formatMessage({id: 'signup_user_completed.required', defaultMessage: 'This field is required'}));
|
setEmailError(formatMessage({id: 'signup_user_completed.required', defaultMessage: 'This field is required'}));
|
||||||
|
telemetryEvents.errors.push({field: 'email', rule: 'not_provided'});
|
||||||
isValid = false;
|
isValid = false;
|
||||||
} else if (!isEmail(providedEmail)) {
|
} else if (!isEmail(providedEmail)) {
|
||||||
setEmailError(formatMessage({id: 'signup_user_completed.validEmail', defaultMessage: 'Please enter a valid email address'}));
|
setEmailError(formatMessage({id: 'signup_user_completed.validEmail', defaultMessage: 'Please enter a valid email address'}));
|
||||||
|
telemetryEvents.errors.push({field: 'email', rule: 'invalid_email'});
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,10 +484,11 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
const usernameError = isValidUsername(providedUsername);
|
const usernameError = isValidUsername(providedUsername);
|
||||||
|
|
||||||
if (usernameError) {
|
if (usernameError) {
|
||||||
setNameError(usernameError.id === ValidationErrors.RESERVED_NAME ? (
|
let nameError = '';
|
||||||
formatMessage({id: 'signup_user_completed.reserved', defaultMessage: 'This username is reserved, please choose a new one.'})
|
if (usernameError.id === ValidationErrors.RESERVED_NAME) {
|
||||||
) : (
|
nameError = formatMessage({id: 'signup_user_completed.reserved', defaultMessage: 'This username is reserved, please choose a new one.'});
|
||||||
formatMessage(
|
} else {
|
||||||
|
nameError = formatMessage(
|
||||||
{
|
{
|
||||||
id: 'signup_user_completed.usernameLength',
|
id: 'signup_user_completed.usernameLength',
|
||||||
defaultMessage: 'Usernames have to begin with a lowercase letter and be {min}-{max} characters long. You can use lowercase letters, numbers, periods, dashes, and underscores.',
|
defaultMessage: 'Usernames have to begin with a lowercase letter and be {min}-{max} characters long. You can use lowercase letters, numbers, periods, dashes, and underscores.',
|
||||||
@@ -486,23 +497,33 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
min: Constants.MIN_USERNAME_LENGTH,
|
min: Constants.MIN_USERNAME_LENGTH,
|
||||||
max: Constants.MAX_USERNAME_LENGTH,
|
max: Constants.MAX_USERNAME_LENGTH,
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
));
|
}
|
||||||
|
telemetryEvents.errors.push({field: 'username', rule: usernameError.id.toLowerCase()});
|
||||||
|
setNameError(nameError);
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setNameError(formatMessage({id: 'signup_user_completed.required', defaultMessage: 'This field is required'}));
|
setNameError(formatMessage({id: 'signup_user_completed.required', defaultMessage: 'This field is required'}));
|
||||||
|
telemetryEvents.errors.push({field: 'username', rule: 'not_provided'});
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const providedPassword = passwordInput.current?.value ?? '';
|
const providedPassword = passwordInput.current?.value ?? '';
|
||||||
const {error} = isValidPassword(providedPassword, getPasswordConfig(config), intl);
|
const {error, telemetryErrorIds} = isValidPassword(providedPassword, getPasswordConfig(config), intl);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
setPasswordError(error as string);
|
setPasswordError(error as string);
|
||||||
|
telemetryEvents.errors = [...telemetryEvents.errors, ...telemetryErrorIds];
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (telemetryEvents.errors.length) {
|
||||||
|
telemetryEvents.success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendSignUpTelemetryEvents('validate_user', telemetryEvents);
|
||||||
|
|
||||||
return isValid;
|
return isValid;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -512,7 +533,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
|
|
||||||
const handleSubmit = async (e: React.MouseEvent | React.KeyboardEvent) => {
|
const handleSubmit = async (e: React.MouseEvent | React.KeyboardEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
trackEvent('signup_email', 'click_create_account', getRoleFromTrackFlow());
|
sendSignUpTelemetryEvents('click_create_account', getRoleFromTrackFlow());
|
||||||
setIsWaiting(true);
|
setIsWaiting(true);
|
||||||
|
|
||||||
if (isUserValid()) {
|
if (isUserValid()) {
|
||||||
@@ -550,6 +571,14 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
|
|
||||||
const handleReturnButtonOnClick = () => history.replace('/');
|
const handleReturnButtonOnClick = () => history.replace('/');
|
||||||
|
|
||||||
|
const handleOnBlur = (e: FocusEvent<HTMLInputElement>, inputId: string) => {
|
||||||
|
const text = e.target.value;
|
||||||
|
if (!text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendSignUpTelemetryEvents(`typed_input_${inputId}`);
|
||||||
|
};
|
||||||
|
|
||||||
const getContent = () => {
|
const getContent = () => {
|
||||||
if (!enableSignUpWithEmail && !enableExternalSignup) {
|
if (!enableSignUpWithEmail && !enableExternalSignup) {
|
||||||
return (
|
return (
|
||||||
@@ -671,6 +700,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
disabled={isWaiting || Boolean(parsedEmail)}
|
disabled={isWaiting || Boolean(parsedEmail)}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
customMessage={emailCustomLabelForInput}
|
customMessage={emailCustomLabelForInput}
|
||||||
|
onBlur={(e) => handleOnBlur(e, 'email')}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
ref={nameInput}
|
ref={nameInput}
|
||||||
@@ -692,6 +722,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
value: formatMessage({id: 'signup_user_completed.userHelp', defaultMessage: 'You can use lowercase letters, numbers, periods, dashes, and underscores.'}),
|
value: formatMessage({id: 'signup_user_completed.userHelp', defaultMessage: 'You can use lowercase letters, numbers, periods, dashes, and underscores.'}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onBlur={(e) => handleOnBlur(e, 'username')}
|
||||||
/>
|
/>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
ref={passwordInput}
|
ref={passwordInput}
|
||||||
@@ -703,6 +734,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
createMode={true}
|
createMode={true}
|
||||||
info={passwordInfo as string}
|
info={passwordInfo as string}
|
||||||
error={passwordError}
|
error={passwordError}
|
||||||
|
onBlur={(e) => handleOnBlur(e, 'password')}
|
||||||
/>
|
/>
|
||||||
<SaveButton
|
<SaveButton
|
||||||
extraClasses='signup-body-card-form-button-submit large'
|
extraClasses='signup-body-card-form-button-submit large'
|
||||||
|
|||||||
@@ -1366,11 +1366,13 @@ export function getPasswordConfig(config: Partial<ClientConfig>) {
|
|||||||
|
|
||||||
export function isValidPassword(password: string, passwordConfig: ReturnType<typeof getPasswordConfig>, intl?: IntlShape) {
|
export function isValidPassword(password: string, passwordConfig: ReturnType<typeof getPasswordConfig>, intl?: IntlShape) {
|
||||||
let errorId = t('user.settings.security.passwordError');
|
let errorId = t('user.settings.security.passwordError');
|
||||||
|
const telemetryErrorIds = [];
|
||||||
let valid = true;
|
let valid = true;
|
||||||
const minimumLength = passwordConfig.minimumLength || Constants.MIN_PASSWORD_LENGTH;
|
const minimumLength = passwordConfig.minimumLength || Constants.MIN_PASSWORD_LENGTH;
|
||||||
|
|
||||||
if (password.length < minimumLength || password.length > Constants.MAX_PASSWORD_LENGTH) {
|
if (password.length < minimumLength || password.length > Constants.MAX_PASSWORD_LENGTH) {
|
||||||
valid = false;
|
valid = false;
|
||||||
|
telemetryErrorIds.push({field: 'password', rule: 'error_length'});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passwordConfig.requireLowercase) {
|
if (passwordConfig.requireLowercase) {
|
||||||
@@ -1379,6 +1381,7 @@ export function isValidPassword(password: string, passwordConfig: ReturnType<typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorId += 'Lowercase';
|
errorId += 'Lowercase';
|
||||||
|
telemetryErrorIds.push({field: 'password', rule: 'lowercase'});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passwordConfig.requireUppercase) {
|
if (passwordConfig.requireUppercase) {
|
||||||
@@ -1387,6 +1390,7 @@ export function isValidPassword(password: string, passwordConfig: ReturnType<typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorId += 'Uppercase';
|
errorId += 'Uppercase';
|
||||||
|
telemetryErrorIds.push({field: 'password', rule: 'uppercase'});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passwordConfig.requireNumber) {
|
if (passwordConfig.requireNumber) {
|
||||||
@@ -1395,6 +1399,7 @@ export function isValidPassword(password: string, passwordConfig: ReturnType<typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorId += 'Number';
|
errorId += 'Number';
|
||||||
|
telemetryErrorIds.push({field: 'password', rule: 'number'});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passwordConfig.requireSymbol) {
|
if (passwordConfig.requireSymbol) {
|
||||||
@@ -1403,6 +1408,7 @@ export function isValidPassword(password: string, passwordConfig: ReturnType<typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorId += 'Symbol';
|
errorId += 'Symbol';
|
||||||
|
telemetryErrorIds.push({field: 'password', rule: 'symbol'});
|
||||||
}
|
}
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
@@ -1430,7 +1436,7 @@ export function isValidPassword(password: string, passwordConfig: ReturnType<typ
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {valid, error};
|
return {valid, error, telemetryErrorIds};
|
||||||
}
|
}
|
||||||
|
|
||||||
function isChannelOrPermalink(link: string) {
|
function isChannelOrPermalink(link: string) {
|
||||||
@@ -1828,9 +1834,14 @@ export function getRoleForTrackFlow() {
|
|||||||
return {started_by_role: startedByRole};
|
return {started_by_role: startedByRole};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRoleFromTrackFlow() {
|
export function getSbr() {
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const sbr = params.get('sbr') ?? '';
|
const sbr = params.get('sbr') ?? '';
|
||||||
|
return sbr;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRoleFromTrackFlow() {
|
||||||
|
const sbr = getSbr();
|
||||||
const startedByRole = TrackFlowRoles[sbr] ?? '';
|
const startedByRole = TrackFlowRoles[sbr] ?? '';
|
||||||
|
|
||||||
return {started_by_role: startedByRole};
|
return {started_by_role: startedByRole};
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user