Skip to content

Commit

Permalink
feat: verify email addresses on signup (#13632)
Browse files Browse the repository at this point in the history
  • Loading branch information
raquelmsmith authored Feb 24, 2023
1 parent e04f721 commit 1d5181f
Show file tree
Hide file tree
Showing 58 changed files with 905 additions and 91 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ storybook-static
dist/
node_modules/
pnpm-lock.yaml
posthog/templates/email/*
2 changes: 0 additions & 2 deletions cypress/e2e/invites.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ describe('Invite Signup', () => {
cy.get('.Popover button:first-child').click()
cy.get('[data-attr=signup-role-at-organization]').contains('Engineering')
cy.get('[data-attr=password-signup]').click()
cy.get('.Toastify__toast-body').should('contain', 'You have joined')
cy.location('pathname').should('include', urls.projectHomepage())
})

Expand Down Expand Up @@ -84,7 +83,6 @@ describe('Invite Signup', () => {
cy.get('.Popover button:first-child').click()
cy.get('[data-attr=signup-role-at-organization]').contains('Engineering')
cy.get('[data-attr=password-signup]').click()
cy.get('.Toastify__toast-body').should('contain', 'You have joined')
cy.location('pathname').should('include', urls.projectHomepage())

// Log out, log in as main
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
"posthog_user"."current_organization_id",
"posthog_user"."current_team_id",
"posthog_user"."email",
"posthog_user"."pending_email",
"posthog_user"."temporary_token",
"posthog_user"."distinct_id",
"posthog_user"."email_opt_in",
"posthog_user"."partial_notification_settings",
"posthog_user"."anonymize_data",
"posthog_user"."toolbar_mode",
"posthog_user"."is_email_verified",
"posthog_user"."events_column_config"
FROM "posthog_user"
WHERE "posthog_user"."id" = 2
Expand Down Expand Up @@ -170,12 +172,14 @@
"posthog_user"."current_organization_id",
"posthog_user"."current_team_id",
"posthog_user"."email",
"posthog_user"."pending_email",
"posthog_user"."temporary_token",
"posthog_user"."distinct_id",
"posthog_user"."email_opt_in",
"posthog_user"."partial_notification_settings",
"posthog_user"."anonymize_data",
"posthog_user"."toolbar_mode",
"posthog_user"."is_email_verified",
"posthog_user"."events_column_config"
FROM "posthog_user"
WHERE "posthog_user"."id" = 2
Expand Down
3 changes: 2 additions & 1 deletion ee/api/test/__snapshots__/test_time_to_see_data.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"user": {
"distinct_id": "",
"first_name": "",
"email": ""
"email": "",
"is_email_verified": false
}
},
"children": [
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/hedgehog/mail-hog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
120 changes: 66 additions & 54 deletions frontend/src/layout/navigation/ProjectNotice.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { useActions, useValues } from 'kea'
import { Link } from 'lib/lemon-ui/Link'
import { navigationLogic } from './navigationLogic'
import { navigationLogic, ProjectNoticeVariant } from './navigationLogic'
import { inviteLogic } from 'scenes/organization/Settings/inviteLogic'
import { AlertMessage } from 'lib/lemon-ui/AlertMessage'
import { IconPlus, IconSettings } from 'lib/lemon-ui/icons'
import { AlertMessageAction } from 'lib/lemon-ui/AlertMessage/AlertMessage'
import { userLogic } from 'scenes/userLogic'
import { organizationLogic } from 'scenes/organizationLogic'
import { urls } from 'scenes/urls'
import { verifyEmailLogic } from 'scenes/authentication/signup/verify-email/verifyEmailLogic'

interface ProjectNoticeBlueprint {
message: JSX.Element | string
action?: AlertMessageAction
type?: 'info' | 'warning' | 'success' | 'error'
}

export function ProjectNotice(): JSX.Element | null {
const { projectNoticeVariantWithClosability } = useValues(navigationLogic)
const { currentOrganization } = useValues(organizationLogic)
const { updateCurrentTeam } = useActions(userLogic)
const { user } = useValues(userLogic)
const { closeProjectNotice } = useActions(navigationLogic)
const { showInviteModal } = useActions(inviteLogic)
const { requestVerificationLink } = useActions(verifyEmailLogic)

if (!projectNoticeVariantWithClosability) {
return null
Expand All @@ -29,67 +33,75 @@ export function ProjectNotice(): JSX.Element | null {

const altTeamForIngestion = currentOrganization?.teams?.find((team) => !team.is_demo && !team.ingested_event)

const NOTICES: Record<'demo_project' | 'real_project_with_no_events' | 'invite_teammates', ProjectNoticeBlueprint> =
{
demo_project: {
message: (
<>
This is a demo project with dummy data.
{altTeamForIngestion && (
<>
{' '}
When you're ready, head on over to the{' '}
<Link
onClick={() => {
updateCurrentTeam(altTeamForIngestion?.id, urls.ingestion())
}}
data-attr="demo-project-alt-team-ingestion_link"
>
ingestion wizard
</Link>{' '}
to get started with your own data.
</>
)}
</>
),
const NOTICES: Record<ProjectNoticeVariant, ProjectNoticeBlueprint> = {
demo_project: {
message: (
<>
This is a demo project with dummy data.
{altTeamForIngestion && (
<>
{' '}
When you're ready, head on over to the{' '}
<Link
onClick={() => {
updateCurrentTeam(altTeamForIngestion?.id, urls.ingestion())
}}
data-attr="demo-project-alt-team-ingestion_link"
>
ingestion wizard
</Link>{' '}
to get started with your own data.
</>
)}
</>
),
},
real_project_with_no_events: {
message: (
<>
This project has no events yet. Go to the{' '}
<Link to="/ingestion" data-attr="real_project_with_no_events-ingestion_link">
ingestion wizard
</Link>{' '}
or grab your project API key/HTML snippet from{' '}
<Link to="/project/settings" data-attr="real_project_with_no_events-settings">
Project Settings
</Link>{' '}
to get things moving
</>
),
action: {
to: '/ingestion',
'data-attr': 'demo-warning-cta',
icon: <IconSettings />,
children: 'Go to wizard',
},
real_project_with_no_events: {
message: (
<>
This project has no events yet. Go to the{' '}
<Link to="/ingestion" data-attr="real_project_with_no_events-ingestion_link">
ingestion wizard
</Link>{' '}
or grab your project API key/HTML snippet from{' '}
<Link to="/project/settings" data-attr="real_project_with_no_events-settings">
Project Settings
</Link>{' '}
to get things moving
</>
),
action: {
to: '/ingestion',
'data-attr': 'demo-warning-cta',
icon: <IconSettings />,
children: 'Go to wizard',
},
},
invite_teammates: {
message: 'Get more out of PostHog by inviting your team for free',
action: {
'data-attr': 'invite-warning-cta',
onClick: showInviteModal,
icon: <IconPlus />,
children: 'Invite team members',
},
invite_teammates: {
message: 'Get more out of PostHog by inviting your team for free',
action: {
'data-attr': 'invite-warning-cta',
onClick: showInviteModal,
icon: <IconPlus />,
children: 'Invite team members',
},
},
unverified_email: {
message: 'Please verify your email address.',
action: {
'data-attr': 'unverified-email-cta',
onClick: () => user && requestVerificationLink(user.uuid),
children: 'Send verification email',
},
}
type: 'warning',
},
}

const relevantNotice = NOTICES[projectNoticeVariant]

return (
<AlertMessage
type="info"
type={relevantNotice.type || 'info'}
className="my-6"
action={relevantNotice.action}
onClose={isClosable ? () => closeProjectNotice(projectNoticeVariant) : undefined}
Expand Down
24 changes: 21 additions & 3 deletions frontend/src/layout/navigation/navigationLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,25 @@ import { VersionType } from '~/types'
import type { navigationLogicType } from './navigationLogicType'
import { membersLogic } from 'scenes/organization/Settings/membersLogic'
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'

export type ProjectNoticeVariant = 'demo_project' | 'real_project_with_no_events' | 'invite_teammates'
export type ProjectNoticeVariant =
| 'demo_project'
| 'real_project_with_no_events'
| 'invite_teammates'
| 'unverified_email'

export const navigationLogic = kea<navigationLogicType>({
path: ['layout', 'navigation', 'navigationLogic'],
connect: {
values: [sceneLogic, ['sceneConfig'], membersLogic, ['members', 'membersLoading']],
values: [
sceneLogic,
['sceneConfig'],
membersLogic,
['members', 'membersLoading'],
featureFlagLogic,
['featureFlags'],
],
actions: [eventUsageLogic, ['reportProjectNoticeDismissed']],
},
actions: {
Expand Down Expand Up @@ -182,17 +194,21 @@ export const navigationLogic = kea<navigationLogicType>({
organizationLogic.selectors.currentOrganization,
teamLogic.selectors.currentTeam,
preflightLogic.selectors.preflight,
userLogic.selectors.user,
s.members,
s.membersLoading,
s.projectNoticesAcknowledged,
s.featureFlags,
],
(
organization,
currentTeam,
preflight,
user,
members,
membersLoading,
projectNoticesAcknowledged
projectNoticesAcknowledged,
featureFlags
): [ProjectNoticeVariant, boolean] | null => {
if (!organization) {
return null
Expand All @@ -203,6 +219,8 @@ export const navigationLogic = kea<navigationLogicType>({
// Don't show this project-level warning in the PostHog demo environemnt though,
// as then Announcement is shown instance-wide
return ['demo_project', false]
} else if (!user?.is_email_verified && featureFlags['require-email-verification'] === true) {
return ['unverified_email', false]
} else if (
!projectNoticesAcknowledged['real_project_with_no_events'] &&
currentTeam &&
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/api.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export const MOCK_DEFAULT_USER: UserType = {
has_password: true,
is_staff: true,
is_impersonated: false,
is_email_verified: true,
is_2fa_enabled: false,
team: MOCK_DEFAULT_TEAM,
organization: MOCK_DEFAULT_ORGANIZATION,
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/lib/components/hedgehogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import supportHeroHog from 'public/hedgehog/support-hero-hog.png'
import xRayHog2 from 'public/hedgehog/x-ray-hogs-02.png'
import laptopHog3 from 'public/hedgehog/laptop-hog-03.png'
import detectiveHog from 'public/hedgehog/detective-hog.png'
import mailHog from 'public/hedgehog/mail-hog.png'

type HedgehogProps = Omit<ImgHTMLAttributes<HTMLImageElement>, 'src'>

Expand Down Expand Up @@ -100,3 +101,6 @@ export const SupportHeroHog = (props: HedgehogProps): JSX.Element => {
export const DetectiveHog = (props: HedgehogProps): JSX.Element => {
return <SquaredHedgehog src={detectiveHog} {...props} />
}
export const MailHog = (props: HedgehogProps): JSX.Element => {
return <SquaredHedgehog src={mailHog} {...props} />
}
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export const FEATURE_FLAGS = {
RECORDING_DEBUGGING: 'recording-debugging', // owner #team-session-recordings
FF_JSON_PAYLOADS: 'ff-json-payloads', // owner @EDsCODE
PERSON_GROUPS_PROPERTY_DEFINITIONS: 'person-groups-property-definitions', // owner: @macobo
REQUIRE_EMAIL_VERIFICATION: 'require-email-verification', // owner: @raquelmsmith
DATA_EXPLORATION_QUERIES_ON_DASHBOARDS: 'data-exploration-queries-on-dashboards', // owner: @pauldambra
NEW_REFRESH_UX: 'new-refresh-ux', // owner: @yakkomajuri
SAMPLING: 'sampling', // owner: @yakkomajuri
Expand Down
1 change: 1 addition & 0 deletions frontend/src/scenes/appScenes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@ export const appScenes: Record<Scene, () => any> = {
[Scene.IntegrationsRedirect]: () => import('./IntegrationsRedirect/IntegrationsRedirect'),
[Scene.IngestionWarnings]: () => import('./data-management/ingestion-warnings/IngestionWarningsView'),
[Scene.Query]: () => import('./query/QueryScene'),
[Scene.VerifyEmail]: () => import('./authentication/signup/verify-email/VerifyEmail'),
}
9 changes: 2 additions & 7 deletions frontend/src/scenes/authentication/inviteSignupLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { loaders } from 'kea-loaders'
import { urlToAction } from 'kea-router'
import { forms } from 'kea-forms'
import api from 'lib/api'
import { lemonToast } from 'lib/lemon-ui/lemonToast'
import { PrevalidatedInvite } from '~/types'
import type { inviteSignupLogicType } from './inviteSignupLogicType'

Expand Down Expand Up @@ -94,12 +93,8 @@ export const inviteSignupLogic = kea<inviteSignupLogicType>([
}

try {
await api.create(`api/signup/${values.invite.id}/`, payload)
lemonToast.success(
`You have joined ${values.invite?.organization_name}! Taking you to PostHog now…`
)
await breakpoint(2000) // timeout for the user to read the toast
window.location.href = '/' // hard refresh because the current_organization changed
const res = await api.create(`api/signup/${values.invite.id}/`, payload)
location.href = res.redirect_url || '/' // hard refresh because the current_organization changed
} catch (e) {
actions.setSignupManualErrors({
generic: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Meta } from '@storybook/react'
import { useEffect } from 'react'

import { VerifyEmail } from './VerifyEmail'
import { verifyEmailLogic } from './verifyEmailLogic'

export default {
title: 'Scenes-Other/Verify Email',
parameters: {
layout: 'fullscreen',
options: { showPanel: false },
viewMode: 'story',
},
} as Meta

export const VerifyEmailPending = (): JSX.Element => {
useEffect(() => {
verifyEmailLogic.actions.setView('pending')
verifyEmailLogic.actions.setUuid('12345678')
}, [])
return <VerifyEmail />
}
export const VerifyingEmail = (): JSX.Element => {
useEffect(() => {
verifyEmailLogic.actions.setView('verify')
}, [])
return <VerifyEmail />
}
export const VerifyEmailSuccess = (): JSX.Element => {
useEffect(() => {
verifyEmailLogic.actions.setView('success')
}, [])
return <VerifyEmail />
}
export const VerifyEmailInvalid = (): JSX.Element => {
useEffect(() => {
verifyEmailLogic.actions.setView('invalid')
verifyEmailLogic.actions.setUuid('12345678')
}, [])
return <VerifyEmail />
}
Loading

0 comments on commit 1d5181f

Please sign in to comment.