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

View File

@ -14,19 +14,4 @@ export const DIALOG_BACKGROUND = twv.tv({
export const DIALOG_STYLES = twv.tv({ export const DIALOG_STYLES = twv.tv({
extend: DIALOG_BACKGROUND, extend: DIALOG_BACKGROUND,
base: 'flex flex-col text-left align-middle shadow-xl', 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 { useRemoteBackendStrict } from '#/providers/BackendProvider'
import { useText } from '#/providers/TextProvider' import { useText } from '#/providers/TextProvider'
import { Plan, PLANS } from '#/services/Backend' import { Plan, PLANS } from '#/services/Backend'
import type { VariantProps } from '#/utilities/tailwindVariants'
import { tv } from '#/utilities/tailwindVariants'
import { Card } from './components' import { Card } from './components'
import { getComponentPerPlan } from './getComponentForPlan' import { getComponentPerPlan } from './getComponentForPlan'
@ -26,7 +28,7 @@ interface CreateCheckoutSessionMutation {
/** /**
* Props for {@link PlanSelector} * Props for {@link PlanSelector}
*/ */
export interface PlanSelectorProps { export interface PlanSelectorProps extends VariantProps<typeof PLAN_SELECTOR_STYLES> {
readonly showFreePlan?: boolean readonly showFreePlan?: boolean
readonly hasTrial?: boolean readonly hasTrial?: boolean
readonly userPlan?: Plan | undefined readonly userPlan?: Plan | undefined
@ -36,6 +38,26 @@ export interface PlanSelectorProps {
readonly onSubscribeError?: (error: Error) => void 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. * Plan selector component.
* Shows the available plans and allows the user to subscribe to one. * Shows the available plans and allows the user to subscribe to one.
@ -49,6 +71,7 @@ export function PlanSelector(props: PlanSelectorProps) {
showFreePlan = true, showFreePlan = true,
hasTrial = true, hasTrial = true,
isOrganizationAdmin = false, isOrganizationAdmin = false,
variants = PLAN_SELECTOR_STYLES,
} = props } = props
const { getText } = useText() const { getText } = useText()
@ -77,13 +100,11 @@ export function PlanSelector(props: PlanSelectorProps) {
onError: (error) => onSubscribeError?.(error), onError: (error) => onSubscribeError?.(error),
}) })
const classes = variants({ showFreePlan })
return ( return (
<div <div className={classes.base()}>
className={DIALOG_BACKGROUND({ <div className={classes.grid()}>
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">
{PLANS.map((newPlan) => { {PLANS.map((newPlan) => {
const paywallLevel = getPaywallLevel(newPlan) const paywallLevel = getPaywallLevel(newPlan)
const userPaywallLevel = getPaywallLevel(userPlan) const userPaywallLevel = getPaywallLevel(userPlan)
@ -96,7 +117,7 @@ export function PlanSelector(props: PlanSelectorProps) {
return ( return (
<Card <Card
key={newPlan} key={newPlan}
className="min-w-64 snap-center" className={classes.card()}
features={planProps.features} features={planProps.features}
subtitle={planProps.subtitle} subtitle={planProps.subtitle}
title={planProps.title} title={planProps.title}
@ -112,6 +133,7 @@ export function PlanSelector(props: PlanSelectorProps) {
}) })
const startEpochMs = Number(new Date()) const startEpochMs = Number(new Date())
while (true) { while (true) {
const { data: session } = await refetchSession() const { data: session } = await refetchSession()
if (session && 'user' in session && session.user.plan === newPlan) { if (session && 'user' in session && session.user.plan === newPlan) {

View File

@ -68,6 +68,7 @@ const BASE_STEPS: Step[] = [
const userSession = useUserSession() const userSession = useUserSession()
const { getText } = textProvider.useText() const { getText } = textProvider.useText()
const isUserCreated = userSession?.type === UserSessionType.full
const defaultName = const defaultName =
session && 'user' in session ? session.user.name : userSession?.email ?? '' session && 'user' in session ? session.user.name : userSession?.email ?? ''
@ -85,7 +86,8 @@ const BASE_STEPS: Step[] = [
} }
defaultValues={{ username: defaultName }} defaultValues={{ username: defaultName }}
onSubmit={({ username }) => { 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() goToNextStep()
return return
} else { } else {