-
Notifications
You must be signed in to change notification settings - Fork 406
feat(clerk-js): Introduce development modal to enable organizations #7159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8e87bd3
3a320a6
b46b37d
be67680
8036996
84de1f0
be32441
b99a632
56a47e3
d7da522
88582a3
308e220
bba1cbe
2ad4dc1
8635ede
1a4a887
7a03be4
9edbcab
5aebc26
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| '@clerk/clerk-js': minor | ||
| '@clerk/shared': minor | ||
| '@clerk/clerk-react': minor | ||
| '@clerk/vue': minor | ||
| --- | ||
|
|
||
| Introduce in-app development prompt to enable the Organizations feature | ||
|
|
||
| In development instances, when using organization components or hooks for the first time, developers will see a prompt to enable the Organizations feature directly in their app, eliminating the need to visit the Clerk Dashboard. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,9 @@ import { | |
| import type { | ||
| __experimental_CheckoutInstance, | ||
| __experimental_CheckoutOptions, | ||
| __internal_AttemptToEnableEnvironmentSettingParams, | ||
| __internal_CheckoutProps, | ||
| __internal_EnableOrganizationsPromptProps, | ||
| __internal_OAuthConsentProps, | ||
| __internal_PlanDetailsProps, | ||
| __internal_SubscriptionDetailsProps, | ||
|
|
@@ -35,9 +37,9 @@ import type { | |
| AuthenticateWithMetamaskParams, | ||
| AuthenticateWithOKXWalletParams, | ||
| BillingNamespace, | ||
| Clerk as ClerkInterface, | ||
| ClerkAPIError, | ||
| ClerkAuthenticateWithWeb3Params, | ||
| Clerk as ClerkInterface, | ||
| ClerkOptions, | ||
| ClientJSONSnapshot, | ||
| ClientResource, | ||
|
|
@@ -739,6 +741,50 @@ export class Clerk implements ClerkInterface { | |
| void this.#componentControls.ensureMounted().then(controls => controls.closeModal('userVerification')); | ||
| }; | ||
|
|
||
| public __internal_attemptToEnableEnvironmentSetting = ( | ||
| params: __internal_AttemptToEnableEnvironmentSettingParams, | ||
| ): { status: 'enabled' | 'prompt-shown' } => { | ||
| const { for: setting, caller } = params; | ||
|
|
||
| // If not in development instance, return enabled status in order to not open the prompt | ||
| if (this.#instanceType !== 'development') { | ||
| return { status: 'enabled' }; | ||
| } | ||
|
|
||
| switch (setting) { | ||
| case 'organizations': | ||
| if (!disabledOrganizationsFeature(this, this.environment)) { | ||
| return { status: 'enabled' }; | ||
| } | ||
|
|
||
| this.__internal_openEnableOrganizationsPrompt({ | ||
| caller, | ||
| // Reload current window to all invalidate all resources | ||
| // related to organizations, eg: roles | ||
| onSuccess: () => window.location.reload(), | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's still some delay in which the window is reloading and the screen kepts showing a blank state without having the component mounted due to the stale environment data. I'm thinking of applying an optimistic update, at least on the "enabled" status, to have the components mounted meanwhile, or not closing the modal and introduce a loading status until the window gets reloaded. |
||
| onClose: params.onClose, | ||
| } as __internal_EnableOrganizationsPromptProps); | ||
|
|
||
| return { status: 'prompt-shown' }; | ||
| default: | ||
| return { status: 'enabled' }; | ||
| } | ||
| }; | ||
|
|
||
| public __internal_openEnableOrganizationsPrompt = (props: __internal_EnableOrganizationsPromptProps): void => { | ||
| this.assertComponentsReady(this.#componentControls); | ||
| void this.#componentControls | ||
| .ensureMounted({ preloadHint: 'EnableOrganizationsPrompt' }) | ||
| .then(controls => controls.openModal('enableOrganizationsPrompt', props || {})); | ||
|
|
||
| this.telemetry?.record(eventPrebuiltComponentMounted('EnableOrganizationsPrompt', props)); | ||
| }; | ||
|
|
||
| public __internal_closeEnableOrganizationsPrompt = (): void => { | ||
| this.assertComponentsReady(this.#componentControls); | ||
| void this.#componentControls.ensureMounted().then(controls => controls.closeModal('enableOrganizationsPrompt')); | ||
| }; | ||
|
|
||
| public __internal_openBlankCaptchaModal = (): Promise<unknown> => { | ||
| this.assertComponentsReady(this.#componentControls); | ||
| return this.#componentControls | ||
|
|
@@ -810,14 +856,21 @@ export class Clerk implements ClerkInterface { | |
|
|
||
| public openOrganizationProfile = (props?: OrganizationProfileProps): void => { | ||
| this.assertComponentsReady(this.#componentControls); | ||
| if (disabledOrganizationsFeature(this, this.environment)) { | ||
| if (this.#instanceType === 'development') { | ||
|
|
||
| const { status } = this.__internal_attemptToEnableEnvironmentSetting({ | ||
| for: 'organizations', | ||
| caller: 'OrganizationProfile', | ||
| onClose: () => { | ||
| throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'), { | ||
| code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE, | ||
| }); | ||
| } | ||
| }, | ||
| }); | ||
|
|
||
| if (status === 'prompt-shown') { | ||
| return; | ||
| } | ||
|
|
||
| if (noOrganizationExists(this)) { | ||
| if (this.#instanceType === 'development') { | ||
| throw new ClerkRuntimeError(warnings.cannotRenderComponentWhenOrgDoesNotExist, { | ||
|
|
@@ -840,14 +893,21 @@ export class Clerk implements ClerkInterface { | |
|
|
||
| public openCreateOrganization = (props?: CreateOrganizationProps): void => { | ||
| this.assertComponentsReady(this.#componentControls); | ||
| if (disabledOrganizationsFeature(this, this.environment)) { | ||
| if (this.#instanceType === 'development') { | ||
|
|
||
| const { status } = this.__internal_attemptToEnableEnvironmentSetting({ | ||
| for: 'organizations', | ||
| caller: 'CreateOrganization', | ||
| onClose: () => { | ||
| throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'), { | ||
| code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE, | ||
| }); | ||
| } | ||
| }, | ||
| }); | ||
|
|
||
| if (status === 'prompt-shown') { | ||
| return; | ||
| } | ||
|
|
||
| void this.#componentControls | ||
| .ensureMounted({ preloadHint: 'CreateOrganization' }) | ||
| .then(controls => controls.openModal('createOrganization', props || {})); | ||
|
|
@@ -982,14 +1042,21 @@ export class Clerk implements ClerkInterface { | |
|
|
||
| public mountOrganizationProfile = (node: HTMLDivElement, props?: OrganizationProfileProps) => { | ||
| this.assertComponentsReady(this.#componentControls); | ||
| if (disabledOrganizationsFeature(this, this.environment)) { | ||
| if (this.#instanceType === 'development') { | ||
|
|
||
| const { status } = this.__internal_attemptToEnableEnvironmentSetting({ | ||
| for: 'organizations', | ||
| caller: 'OrganizationProfile', | ||
| onClose: () => { | ||
| throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'), { | ||
| code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE, | ||
| }); | ||
| } | ||
| }, | ||
| }); | ||
|
|
||
| if (status === 'prompt-shown') { | ||
| return; | ||
| } | ||
|
|
||
| const userExists = !noUserExists(this); | ||
| if (noOrganizationExists(this) && userExists) { | ||
| if (this.#instanceType === 'development') { | ||
|
|
@@ -1022,14 +1089,21 @@ export class Clerk implements ClerkInterface { | |
|
|
||
| public mountCreateOrganization = (node: HTMLDivElement, props?: CreateOrganizationProps) => { | ||
| this.assertComponentsReady(this.#componentControls); | ||
| if (disabledOrganizationsFeature(this, this.environment)) { | ||
| if (this.#instanceType === 'development') { | ||
|
|
||
| const { status } = this.__internal_attemptToEnableEnvironmentSetting({ | ||
| for: 'organizations', | ||
| caller: 'CreateOrganization', | ||
| onClose: () => { | ||
| throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'), { | ||
| code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE, | ||
| }); | ||
| } | ||
| }, | ||
| }); | ||
|
|
||
| if (status === 'prompt-shown') { | ||
| return; | ||
| } | ||
|
|
||
| void this.#componentControls?.ensureMounted({ preloadHint: 'CreateOrganization' }).then(controls => | ||
| controls.mountComponent({ | ||
| name: 'CreateOrganization', | ||
|
|
@@ -1053,14 +1127,21 @@ export class Clerk implements ClerkInterface { | |
|
|
||
| public mountOrganizationSwitcher = (node: HTMLDivElement, props?: OrganizationSwitcherProps) => { | ||
| this.assertComponentsReady(this.#componentControls); | ||
| if (disabledOrganizationsFeature(this, this.environment)) { | ||
| if (this.#instanceType === 'development') { | ||
|
|
||
| const { status } = this.__internal_attemptToEnableEnvironmentSetting({ | ||
| for: 'organizations', | ||
| caller: 'OrganizationSwitcher', | ||
| onClose: () => { | ||
| throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationSwitcher'), { | ||
| code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE, | ||
| }); | ||
| } | ||
| }, | ||
| }); | ||
|
|
||
| if (status === 'prompt-shown') { | ||
| return; | ||
| } | ||
|
|
||
| void this.#componentControls?.ensureMounted({ preloadHint: 'OrganizationSwitcher' }).then(controls => | ||
| controls.mountComponent({ | ||
| name: 'OrganizationSwitcher', | ||
|
|
@@ -1092,14 +1173,21 @@ export class Clerk implements ClerkInterface { | |
|
|
||
| public mountOrganizationList = (node: HTMLDivElement, props?: OrganizationListProps) => { | ||
| this.assertComponentsReady(this.#componentControls); | ||
| if (disabledOrganizationsFeature(this, this.environment)) { | ||
| if (this.#instanceType === 'development') { | ||
|
|
||
| const { status } = this.__internal_attemptToEnableEnvironmentSetting({ | ||
| for: 'organizations', | ||
| caller: 'OrganizationList', | ||
| onClose: () => { | ||
| throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationList'), { | ||
| code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE, | ||
| }); | ||
| } | ||
| }, | ||
| }); | ||
|
|
||
| if (status === 'prompt-shown') { | ||
| return; | ||
| } | ||
|
|
||
| void this.#componentControls?.ensureMounted({ preloadHint: 'OrganizationList' }).then(controls => | ||
| controls.mountComponent({ | ||
| name: 'OrganizationList', | ||
|
|
@@ -1279,12 +1367,17 @@ export class Clerk implements ClerkInterface { | |
| public mountTaskChooseOrganization = (node: HTMLDivElement, props?: TaskChooseOrganizationProps) => { | ||
| this.assertComponentsReady(this.#componentControls); | ||
|
|
||
| if (disabledOrganizationsFeature(this, this.environment)) { | ||
| if (this.#instanceType === 'development') { | ||
| const { status } = this.__internal_attemptToEnableEnvironmentSetting({ | ||
| for: 'organizations', | ||
| caller: 'TaskChooseOrganization', | ||
| onClose: () => { | ||
| throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('TaskChooseOrganization'), { | ||
| code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE, | ||
| }); | ||
| } | ||
| }, | ||
| }); | ||
|
|
||
| if (status === 'prompt-shown') { | ||
| return; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import type { ClerkResourceJSON, DevToolsResource, EnableEnvironmentSettingParams } from '@clerk/shared/types'; | ||
|
|
||
| import { BaseResource } from './Base'; | ||
|
|
||
| /** | ||
| * @internal | ||
| */ | ||
| export class DevTools extends BaseResource implements DevToolsResource { | ||
| pathRoot = '/dev_tools'; | ||
|
|
||
| protected fromJSON(_data: ClerkResourceJSON | null): this { | ||
| return this; | ||
| } | ||
|
|
||
| async __internal_enableEnvironmentSetting(params: EnableEnvironmentSettingParams) { | ||
| await this._basePatch({ | ||
| path: `${this.pathRoot}/enable_environment_setting`, | ||
| body: params, | ||
| }); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.