-
Notifications
You must be signed in to change notification settings - Fork 407
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
20b1746
8630434
b798873
c1289c5
26fc1e0
481f1cc
3204fb3
25f7660
5d05117
39f324e
a25e2f5
b92b717
bc05817
f017b29
2c47676
23cc048
1bef684
86699b8
261c2fa
bf650e9
26ad5b9
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, | ||
|
|
@@ -745,6 +747,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 | ||
|
|
@@ -816,14 +862,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, { | ||
|
|
@@ -846,14 +899,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 || {})); | ||
|
|
@@ -988,14 +1048,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') { | ||
|
|
@@ -1028,14 +1095,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', | ||
|
|
@@ -1059,14 +1133,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', | ||
|
|
@@ -1098,14 +1179,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', | ||
|
|
@@ -1294,12 +1382,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.