Username onboarding
This commit is contained in:
parent
5626cea9c3
commit
f9aaf30a32
17 changed files with 309 additions and 3 deletions
|
@ -71,6 +71,7 @@ export default {
|
|||
},
|
||||
replaceAvatar: { action: true },
|
||||
saveAvatarToDisk: { action: true },
|
||||
markCompletedUsernameOnboarding: { action: true },
|
||||
openUsernameReservationModal: { action: true },
|
||||
setUsernameEditState: { action: true },
|
||||
deleteUsername: { action: true },
|
||||
|
|
|
@ -34,6 +34,7 @@ import { assertDev } from '../util/assert';
|
|||
import { missingCaseError } from '../util/missingCaseError';
|
||||
import { ConfirmationDialog } from './ConfirmationDialog';
|
||||
import { ContextMenu } from './ContextMenu';
|
||||
import { UsernameOnboardingModalBody } from './UsernameOnboardingModalBody';
|
||||
import {
|
||||
ConversationDetailsIcon,
|
||||
IconType,
|
||||
|
@ -48,6 +49,7 @@ export enum EditState {
|
|||
ProfileName = 'ProfileName',
|
||||
Bio = 'Bio',
|
||||
Username = 'Username',
|
||||
UsernameOnboarding = 'UsernameOnboarding',
|
||||
}
|
||||
|
||||
type PropsExternalType = {
|
||||
|
@ -67,11 +69,13 @@ export type PropsDataType = {
|
|||
conversationId: string;
|
||||
familyName?: string;
|
||||
firstName: string;
|
||||
hasCompletedUsernameOnboarding: boolean;
|
||||
i18n: LocalizerType;
|
||||
isUsernameFlagEnabled: boolean;
|
||||
userAvatarData: ReadonlyArray<AvatarDataType>;
|
||||
username?: string;
|
||||
usernameEditState: UsernameEditState;
|
||||
markCompletedUsernameOnboarding: () => void;
|
||||
} & Pick<EmojiButtonProps, 'recentEmojis' | 'skinTone'>;
|
||||
|
||||
type PropsActionType = {
|
||||
|
@ -124,8 +128,10 @@ export function ProfileEditor({
|
|||
deleteUsername,
|
||||
familyName,
|
||||
firstName,
|
||||
hasCompletedUsernameOnboarding,
|
||||
i18n,
|
||||
isUsernameFlagEnabled,
|
||||
markCompletedUsernameOnboarding,
|
||||
onEditStateChanged,
|
||||
onProfileChanged,
|
||||
onSetSkinTone,
|
||||
|
@ -481,6 +487,16 @@ export function ProfileEditor({
|
|||
content = renderEditUsernameModalBody({
|
||||
onClose: () => setEditState(EditState.None),
|
||||
});
|
||||
} else if (editState === EditState.UsernameOnboarding) {
|
||||
content = (
|
||||
<UsernameOnboardingModalBody
|
||||
i18n={i18n}
|
||||
onNext={() => {
|
||||
markCompletedUsernameOnboarding();
|
||||
setEditState(EditState.Username);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else if (editState === EditState.None) {
|
||||
let maybeUsernameRow: JSX.Element | undefined;
|
||||
if (isUsernameFlagEnabled) {
|
||||
|
@ -560,7 +576,11 @@ export function ProfileEditor({
|
|||
info={username && generateUsernameLink(username, { short: true })}
|
||||
onClick={() => {
|
||||
openUsernameReservationModal();
|
||||
setEditState(EditState.Username);
|
||||
if (username || hasCompletedUsernameOnboarding) {
|
||||
setEditState(EditState.Username);
|
||||
} else {
|
||||
setEditState(EditState.UsernameOnboarding);
|
||||
}
|
||||
}}
|
||||
actions={actions}
|
||||
/>
|
||||
|
|
|
@ -32,11 +32,12 @@ export function ProfileEditorModal({
|
|||
toggleProfileEditorHasError,
|
||||
...restProps
|
||||
}: PropsType): JSX.Element {
|
||||
const MODAL_TITLES_BY_EDIT_STATE: Record<EditState, string> = {
|
||||
const MODAL_TITLES_BY_EDIT_STATE: Record<EditState, string | undefined> = {
|
||||
[EditState.BetterAvatar]: i18n('ProfileEditorModal--avatar'),
|
||||
[EditState.Bio]: i18n('ProfileEditorModal--about'),
|
||||
[EditState.None]: i18n('ProfileEditorModal--profile'),
|
||||
[EditState.ProfileName]: i18n('ProfileEditorModal--name'),
|
||||
[EditState.UsernameOnboarding]: undefined,
|
||||
[EditState.Username]: i18n('ProfileEditorModal--username'),
|
||||
};
|
||||
|
||||
|
|
37
ts/components/UsernameOnboardingModalBody.stories.tsx
Normal file
37
ts/components/UsernameOnboardingModalBody.stories.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { Meta, Story } from '@storybook/react';
|
||||
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
|
||||
import type { PropsType } from './UsernameOnboardingModalBody';
|
||||
import { UsernameOnboardingModalBody } from './UsernameOnboardingModalBody';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
export default {
|
||||
component: UsernameOnboardingModalBody,
|
||||
title: 'Components/UsernameOnboardingModalBody',
|
||||
argTypes: {
|
||||
i18n: {
|
||||
defaultValue: i18n,
|
||||
},
|
||||
onNext: { action: true },
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
type ArgsType = PropsType;
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
const Template: Story<ArgsType> = args => {
|
||||
return <UsernameOnboardingModalBody {...args} />;
|
||||
};
|
||||
|
||||
export const Normal = Template.bind({});
|
||||
Normal.args = {};
|
||||
Normal.story = {
|
||||
name: 'normal',
|
||||
};
|
68
ts/components/UsernameOnboardingModalBody.tsx
Normal file
68
ts/components/UsernameOnboardingModalBody.tsx
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Button } from './Button';
|
||||
|
||||
export type PropsType = Readonly<{
|
||||
i18n: LocalizerType;
|
||||
onNext: () => void;
|
||||
}>;
|
||||
|
||||
const CLASS = 'UsernameOnboardingModalBody';
|
||||
|
||||
const SUPPORT_URL = 'https://support.signal.org/hc/articles/5389476324250';
|
||||
|
||||
export function UsernameOnboardingModalBody({
|
||||
i18n,
|
||||
onNext,
|
||||
}: PropsType): JSX.Element {
|
||||
return (
|
||||
<div className={CLASS}>
|
||||
<div className={`${CLASS}__large-at`} />
|
||||
|
||||
<div className={`${CLASS}__title`}>{i18n(`icu:${CLASS}__title`)}</div>
|
||||
|
||||
<div className={`${CLASS}__row`}>
|
||||
<div className={`${CLASS}__row__icon ${CLASS}__row__icon--number`} />
|
||||
|
||||
<div className={`${CLASS}__row__body`}>
|
||||
{i18n(`icu:${CLASS}__row__number`)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`${CLASS}__row`}>
|
||||
<div className={`${CLASS}__row__icon ${CLASS}__row__icon--link`} />
|
||||
|
||||
<div className={`${CLASS}__row__body`}>
|
||||
{i18n(`icu:${CLASS}__row__link`)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`${CLASS}__row`}>
|
||||
<div className={`${CLASS}__row__icon ${CLASS}__row__icon--lock`} />
|
||||
|
||||
<div className={`${CLASS}__row__body`}>
|
||||
{i18n(`icu:${CLASS}__row__lock`)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`${CLASS}__row ${CLASS}__row--center`}>
|
||||
<a
|
||||
className={`${CLASS}__learn-more`}
|
||||
href={SUPPORT_URL}
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
{i18n(`icu:${CLASS}__learn-more`)}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<Button className={`${CLASS}__submit`} onClick={onNext}>
|
||||
{i18n(`icu:${CLASS}__continue`)}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue