Fix skipping creating a user if we don't change the default name (#11013)

Closes: enso-org/cloud-v2#1473

This PR fixes a bug introduced in #10946
This commit is contained in:
Sergei Garin 2024-09-10 01:30:53 +03:00 committed by GitHub
parent 7b4b635fa8
commit 1fdaf37add
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 90 additions and 66 deletions

View File

@ -9,13 +9,14 @@ import * as portal from '#/components/Portal'
import * as suspense from '#/components/Suspense'
import * as mergeRefs from '#/utilities/mergeRefs'
import * as twv from '#/utilities/tailwindVariants'
import type { VariantProps } from '#/utilities/tailwindVariants'
import { tv } from '#/utilities/tailwindVariants'
import * as dialogProvider from './DialogProvider'
import * as dialogStackProvider from './DialogStackProvider'
import type * as types from './types'
import * as utlities from './utilities'
import * as variants from './variants'
import { DIALOG_BACKGROUND } from './variants'
// =================
// === Constants ===
@ -25,10 +26,10 @@ import * as variants from './variants'
*/
export interface DialogProps
extends types.DialogProps,
Omit<twv.VariantProps<typeof DIALOG_STYLES>, 'scrolledToTop'> {}
Omit<VariantProps<typeof DIALOG_STYLES>, 'scrolledToTop'> {}
const OVERLAY_STYLES = twv.tv({
base: 'fixed inset-0 isolate flex items-center justify-center bg-black/[25%]',
const OVERLAY_STYLES = tv({
base: 'fixed inset-0 isolate flex items-center justify-center bg-primary/20',
variants: {
isEntering: { true: 'animate-in fade-in duration-200 ease-out' },
isExiting: { true: 'animate-out fade-out duration-200 ease-in' },
@ -36,17 +37,23 @@ const OVERLAY_STYLES = twv.tv({
},
})
const MODAL_STYLES = twv.tv({
base: 'fixed inset-0 flex items-center justify-center text-center text-xs text-primary p-4',
const MODAL_STYLES = tv({
base: 'fixed inset-0 flex items-center justify-center text-xs text-primary',
variants: {
isEntering: { true: 'animate-in slide-in-from-top-1 ease-out duration-200' },
isExiting: { true: 'animate-out slide-out-to-top-1 ease-in duration-200' },
isEntering: { true: 'animate-in ease-out duration-200' },
isExiting: { true: 'animate-out ease-in duration-200' },
type: { modal: '', fullscreen: 'p-3.5' },
},
compoundVariants: [
{ type: 'modal', isEntering: true, class: 'slide-in-from-top-1' },
{ type: 'modal', isExiting: true, class: 'slide-out-to-top-1' },
{ type: 'fullscreen', isEntering: true, class: 'zoom-in-[1.015]' },
{ type: 'fullscreen', isExiting: true, class: 'zoom-out-[1.015]' },
],
})
const DIALOG_STYLES = twv.tv({
extend: variants.DIALOG_STYLES,
base: 'w-full overflow-y-auto',
const DIALOG_STYLES = tv({
base: DIALOG_BACKGROUND({ className: 'w-full flex flex-col text-left align-middle shadow-xl' }),
variants: {
type: {
modal: {
@ -74,6 +81,16 @@ const DIALOG_STYLES = twv.tv({
content: 'isolate',
},
},
rounded: {
none: { base: '' },
small: { base: 'rounded-sm' },
medium: { base: 'rounded-md' },
large: { base: 'rounded-lg' },
xlarge: { base: 'rounded-xl' },
xxlarge: { base: 'rounded-2xl', content: 'scroll-offset-edge-2xl' },
xxxlarge: { base: 'rounded-3xl', content: 'scroll-offset-edge-4xl' },
xxxxlarge: { base: 'rounded-4xl', content: 'scroll-offset-edge-6xl' },
},
/**
* The size of the dialog.
* Only applies to the `modal` type.
@ -120,6 +137,7 @@ const DIALOG_STYLES = twv.tv({
hideCloseButton: false,
size: 'medium',
padding: 'medium',
rounded: 'xxlarge',
},
})
@ -146,6 +164,7 @@ export function Dialog(props: DialogProps) {
rounded,
padding = type === 'modal' ? 'medium' : 'xlarge',
fitContent,
variants = DIALOG_STYLES,
...ariaDialogProps
} = props
@ -155,11 +174,13 @@ export function Dialog(props: DialogProps) {
* Handles the scroll event on the dialog content.
*/
const handleScroll = (scrollTop: number) => {
if (scrollTop > 0) {
setIsScrolledToTop(false)
} else {
setIsScrolledToTop(true)
}
React.startTransition(() => {
if (scrollTop > 0) {
setIsScrolledToTop(false)
} else {
setIsScrolledToTop(true)
}
})
}
const dialogId = aria.useId()
@ -169,7 +190,7 @@ export function Dialog(props: DialogProps) {
const overlayState = React.useRef<aria.OverlayTriggerState | null>(null)
const root = portal.useStrictPortalContext()
const styles = DIALOG_STYLES({
const styles = variants({
className,
type,
rounded,
@ -200,11 +221,7 @@ export function Dialog(props: DialogProps) {
return (
<aria.ModalOverlay
className={({ isEntering, isExiting }) =>
OVERLAY_STYLES({
isEntering,
isExiting,
blockInteractions: !isDismissable,
})
OVERLAY_STYLES({ isEntering, isExiting, blockInteractions: !isDismissable })
}
isDismissable={isDismissable}
isKeyboardDismissDisabled={isKeyboardDismissDisabled}
@ -218,7 +235,7 @@ export function Dialog(props: DialogProps) {
return (
<aria.Modal
className={MODAL_STYLES}
className={({ isEntering, isExiting }) => MODAL_STYLES({ type, isEntering, isExiting })}
isDismissable={isDismissable}
isKeyboardDismissDisabled={isKeyboardDismissDisabled}
UNSTABLE_portalContainer={root}
@ -252,25 +269,23 @@ export function Dialog(props: DialogProps) {
{(opts) => {
return (
<dialogProvider.DialogProvider value={{ close: opts.close, dialogId }}>
{((!hideCloseButton && closeButton !== 'floating') || title != null) && (
<aria.Header className={styles.header({ scrolledToTop: isScrolledToTop })}>
<ariaComponents.CloseButton
className={styles.closeButton()}
onPress={opts.close}
/>
<aria.Header className={styles.header({ scrolledToTop: isScrolledToTop })}>
<ariaComponents.CloseButton
className={styles.closeButton()}
onPress={opts.close}
/>
{title != null && (
<ariaComponents.Text.Heading
id={titleId}
level={2}
className={styles.heading()}
weight="semibold"
>
{title}
</ariaComponents.Text.Heading>
)}
</aria.Header>
)}
{title != null && (
<ariaComponents.Text.Heading
id={titleId}
level={2}
className={styles.heading()}
weight="semibold"
>
{title}
</ariaComponents.Text.Heading>
)}
</aria.Header>
<div
ref={(ref) => {

View File

@ -14,19 +14,4 @@ export const DIALOG_BACKGROUND = twv.tv({
export const DIALOG_STYLES = twv.tv({
extend: DIALOG_BACKGROUND,
base: 'flex flex-col text-left align-middle shadow-xl',
variants: {
rounded: {
none: '',
small: 'rounded-sm',
medium: 'rounded-md',
large: 'rounded-lg',
xlarge: 'rounded-xl',
xxlarge: 'rounded-2xl',
xxxlarge: 'rounded-3xl',
xxxxlarge: 'rounded-4xl',
},
},
defaultVariants: {
rounded: 'xxlarge',
},
})

View File

@ -7,6 +7,8 @@ import { useAuth } from '#/providers/AuthProvider'
import { useRemoteBackendStrict } from '#/providers/BackendProvider'
import { useText } from '#/providers/TextProvider'
import { Plan, PLANS } from '#/services/Backend'
import type { VariantProps } from '#/utilities/tailwindVariants'
import { tv } from '#/utilities/tailwindVariants'
import { Card } from './components'
import { getComponentPerPlan } from './getComponentForPlan'
@ -26,7 +28,7 @@ interface CreateCheckoutSessionMutation {
/**
* Props for {@link PlanSelector}
*/
export interface PlanSelectorProps {
export interface PlanSelectorProps extends VariantProps<typeof PLAN_SELECTOR_STYLES> {
readonly showFreePlan?: boolean
readonly hasTrial?: boolean
readonly userPlan?: Plan | undefined
@ -36,6 +38,26 @@ export interface PlanSelectorProps {
readonly onSubscribeError?: (error: Error) => void
}
const PLAN_SELECTOR_STYLES = tv({
base: DIALOG_BACKGROUND({
className: 'w-full snap-x overflow-auto rounded-4xl scroll-hidden',
}),
variants: {
showFreePlan: {
true: {
grid: 'grid-cols-1fr md:grid-cols-2 xl:grid-cols-4',
},
false: {
grid: 'grid-cols-1fr md:grid-cols-3 justify-center',
},
},
},
slots: {
grid: 'inline-grid min-w-full gap-6 p-6',
card: 'min-w-64 snap-center',
},
})
/**
* Plan selector component.
* Shows the available plans and allows the user to subscribe to one.
@ -49,6 +71,7 @@ export function PlanSelector(props: PlanSelectorProps) {
showFreePlan = true,
hasTrial = true,
isOrganizationAdmin = false,
variants = PLAN_SELECTOR_STYLES,
} = props
const { getText } = useText()
@ -77,13 +100,11 @@ export function PlanSelector(props: PlanSelectorProps) {
onError: (error) => onSubscribeError?.(error),
})
const classes = variants({ showFreePlan })
return (
<div
className={DIALOG_BACKGROUND({
className: 'w-full snap-x overflow-auto rounded-4xl scroll-hidden',
})}
>
<div className="inline-grid min-w-full grid-cols-1fr gap-6 p-6 md:grid-cols-2 xl:grid-cols-4">
<div className={classes.base()}>
<div className={classes.grid()}>
{PLANS.map((newPlan) => {
const paywallLevel = getPaywallLevel(newPlan)
const userPaywallLevel = getPaywallLevel(userPlan)
@ -96,7 +117,7 @@ export function PlanSelector(props: PlanSelectorProps) {
return (
<Card
key={newPlan}
className="min-w-64 snap-center"
className={classes.card()}
features={planProps.features}
subtitle={planProps.subtitle}
title={planProps.title}
@ -112,6 +133,7 @@ export function PlanSelector(props: PlanSelectorProps) {
})
const startEpochMs = Number(new Date())
while (true) {
const { data: session } = await refetchSession()
if (session && 'user' in session && session.user.plan === newPlan) {

View File

@ -68,6 +68,7 @@ const BASE_STEPS: Step[] = [
const userSession = useUserSession()
const { getText } = textProvider.useText()
const isUserCreated = userSession?.type === UserSessionType.full
const defaultName =
session && 'user' in session ? session.user.name : userSession?.email ?? ''
@ -85,7 +86,8 @@ const BASE_STEPS: Step[] = [
}
defaultValues={{ username: defaultName }}
onSubmit={({ username }) => {
if (username === defaultName) {
// If user is already created we shouldn't call `setUsername` if value wasn't changed
if (username === defaultName && isUserCreated) {
goToNextStep()
return
} else {