From 511cd126c4a4ec48365ef864d2bc5a2edc9dc301 Mon Sep 17 00:00:00 2001 From: Curry Yang <163384738+CurryYangxx@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:37:34 +0800 Subject: [PATCH] refactor: account data fetching (#10463) --- packages/insomnia/src/common/organization.ts | 69 +++++++++++---- ...ganizationId.project.$projectId._index.tsx | 6 +- ...Id.workspace.$workspaceId.debug.runner.tsx | 6 +- ...zation.sync-organizations-and-projects.tsx | 7 -- .../insomnia/src/routes/organization.sync.tsx | 29 ------- packages/insomnia/src/routes/organization.tsx | 49 ++--------- .../editors/mock-response-extractor.tsx | 6 +- .../ui/components/header-plan-indicator.tsx | 7 +- .../import-modal/import-projects-modal.tsx | 6 +- .../modals/input-vault-key-modal.tsx | 4 +- .../modals/invite-modal/invite-form.tsx | 6 +- .../modals/invite-modal/invite-modal.tsx | 6 +- .../modals/workspace-duplicate-modal.tsx | 6 +- .../project/project-type-warning.tsx | 6 +- .../ui/components/settings/import-export.tsx | 7 +- .../app/insomnia-event-stream-context.tsx | 8 +- .../src/ui/hooks/use-account-server-data.ts | 86 +++++++++++++++++++ packages/insomnia/src/ui/hooks/use-plan.tsx | 18 ++-- .../insomnia/src/ui/hooks/use-user-service.ts | 5 +- packages/insomnia/src/ui/utils/router.ts | 3 +- 20 files changed, 197 insertions(+), 143 deletions(-) delete mode 100644 packages/insomnia/src/routes/organization.sync.tsx create mode 100644 packages/insomnia/src/ui/hooks/use-account-server-data.ts diff --git a/packages/insomnia/src/common/organization.ts b/packages/insomnia/src/common/organization.ts index 405a6be7d1..e6f89e14fb 100644 --- a/packages/insomnia/src/common/organization.ts +++ b/packages/insomnia/src/common/organization.ts @@ -1,26 +1,63 @@ -import { getCurrentPlan, getUserProfile } from 'insomnia-api'; +import { type CurrentPlan, getCurrentPlan, getUserProfile, type Organization, type User } from 'insomnia-api'; import { services } from 'insomnia-data'; import { invariant } from '~/common/utils/invariant'; +export interface OrganizationData { + organizations: Organization[]; + user?: User; + currentPlan?: CurrentPlan; +} + +// Fetches the organization triple (organizations / user / current plan) from the +// cloud API in parallel. Shared by both the TanStack query (the source of truth for +// React consumers) and `syncOrganizations` (the localStorage write path still used by +// the non-React readers). Throws if any of the three fail their invariant. +export async function fetchOrganizationData(sessionId: string): Promise { + const [organizations, user, currentPlan] = await Promise.all([ + services.organization.list(), + getUserProfile({ sessionId }), + getCurrentPlan({ sessionId }), + ]); + + invariant(organizations, 'Failed to load organizations'); + invariant(user && user.id, 'Failed to load user'); + invariant(currentPlan && currentPlan.planId, 'Failed to load current plan'); + + return { organizations, user, currentPlan }; +} + +// Write-through helper: persists the organization triple to localStorage so the +// non-React readers (`getInitialEntry`, various loaders, event-stream handlers, etc.) +// keep working while the migration to TanStack Query is incremental. +export function writeOrganizationDataToLocalStorage(accountId: string, data: OrganizationData) { + invariant(accountId, 'Account ID is not defined'); + localStorage.setItem(`${accountId}:spaces`, JSON.stringify(data.organizations)); + localStorage.setItem(`${accountId}:user`, JSON.stringify(data.user)); + localStorage.setItem(`${accountId}:currentPlan`, JSON.stringify(data.currentPlan)); +} + +// Reads the persisted triple back from localStorage. Used to seed the query's +// `initialData` so the first paint is instant (matching the old loader, which read +// localStorage synchronously). Defaults mirror the previous loader behaviour. +export function readOrganizationDataFromLocalStorage(accountId: string): OrganizationData { + const userRaw = localStorage.getItem(`${accountId}:user`); + const currentPlanRaw = localStorage.getItem(`${accountId}:currentPlan`); + return { + organizations: JSON.parse(localStorage.getItem(`${accountId}:spaces`) || '[]') as Organization[], + // Leave these undefined (not `{}`) when the key is absent, so consumers can rely on the + // declared optional shape — casting `{}` to User/CurrentPlan hides missing fields from the + // type checker and causes runtime NPEs (e.g. `currentPlan.type.includes`). + user: userRaw ? (JSON.parse(userRaw) as User) : undefined, + currentPlan: currentPlanRaw ? (JSON.parse(currentPlanRaw) as CurrentPlan) : undefined, + }; +} + // This is reusable action/loader implementations. export async function syncOrganizations(sessionId: string, accountId: string) { try { - const [organizations, user, currentPlan] = await Promise.all([ - services.organization.list(), - getUserProfile({ sessionId }), - getCurrentPlan({ sessionId }), - ]); - - invariant(organizations, 'Failed to load organizations'); - invariant(user && user.id, 'Failed to load user'); - invariant(currentPlan && currentPlan.planId, 'Failed to load current plan'); - - invariant(accountId, 'Account ID is not defined'); - - localStorage.setItem(`${accountId}:spaces`, JSON.stringify(organizations)); - localStorage.setItem(`${accountId}:user`, JSON.stringify(user)); - localStorage.setItem(`${accountId}:currentPlan`, JSON.stringify(currentPlan)); + const data = await fetchOrganizationData(sessionId); + writeOrganizationDataToLocalStorage(accountId, data); } catch (error) { console.log('[organization] Failed to load Organizations', error); } diff --git a/packages/insomnia/src/routes/organization.$organizationId.project.$projectId._index.tsx b/packages/insomnia/src/routes/organization.$organizationId.project.$projectId._index.tsx index f3adfc6339..c319d97bac 100644 --- a/packages/insomnia/src/routes/organization.$organizationId.project.$projectId._index.tsx +++ b/packages/insomnia/src/routes/organization.$organizationId.project.$projectId._index.tsx @@ -33,7 +33,6 @@ import { getAllLocalFiles } from '~/common/project'; import { sortMethodMap } from '~/common/sorting'; import { invariant } from '~/common/utils/invariant'; import { useRootLoaderData } from '~/root'; -import { useOrganizationLoaderData } from '~/routes/organization'; import { useInsomniaSyncPullRemoteFileActionFetcher } from '~/routes/organization.$organizationId.insomnia-sync.pull-remote-file'; import { useProjectLoaderData, useProjectRouteContext } from '~/routes/organization.$organizationId.project.$projectId'; import { useWorkspaceNewActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.new'; @@ -53,6 +52,7 @@ import { OrganizationTabList } from '~/ui/components/tabs/tab-list'; import { TimeFromNow } from '~/ui/components/time-from-now'; import { showResourceNotFoundToast } from '~/ui/components/toast-notification'; import { useInsomniaEventStreamContext } from '~/ui/context/app/insomnia-event-stream-context'; +import { useOrganizations } from '~/ui/hooks/use-account-server-data'; import { useGitFileIssues } from '~/ui/hooks/use-git-file-issues'; import { useTabNavigate } from '~/ui/hooks/use-insomnia-tab'; import { useOrganizationData } from '~/ui/hooks/use-organization-data'; @@ -97,7 +97,7 @@ const Component = ({ loaderData }: Route.ComponentProps) => { ) .map(f => f.formData?.get('backendProjectId')); - const organizationData = useOrganizationLoaderData(); + const organizations = useOrganizations(); const { presence } = useInsomniaEventStreamContext(); const { issuesByWorkspaceId } = useGitFileIssues(); const storageRules = useOrganizationStorageRule(organizationId); @@ -120,7 +120,7 @@ const Component = ({ loaderData }: Route.ComponentProps) => { const [importModalType, setImportModalType] = useState<'file' | 'clipboard' | 'uri' | null>(null); const [isNewProjectModalOpen, setIsNewProjectModalOpen] = useState(false); const [isUpdateProjectModalOpen, setIsUpdateProjectModalOpen] = useState(false); - const organization = organizationData?.organizations.find(o => o.id === organizationId); + const organization = organizations.find(o => o.id === organizationId); const isUserOwner = Boolean(organization?.is_owner); const collectionItems = useMemo( () => diff --git a/packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.runner.tsx b/packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.runner.tsx index bd6548f6a6..0730062ca9 100644 --- a/packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.runner.tsx +++ b/packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.runner.tsx @@ -33,7 +33,6 @@ import { buildRunnerItemKey, type RunnerItemStatus, type RunnerLiveItem } from ' import { invariant } from '~/common/utils/invariant'; import { defaultSendActionRuntime } from '~/network/network'; import { useRootLoaderData } from '~/root'; -import { useOrganizationLoaderData } from '~/routes/organization'; import type { CollectionRunnerContext } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.send'; import { sendActionImplementation } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.send'; import { AnalyticsEvent } from '~/ui/analytics'; @@ -55,6 +54,7 @@ import { Tooltip } from '~/ui/components/tooltip'; import { ResponseTimelineViewer } from '~/ui/components/viewers/response-timeline-viewer'; import { useInsomniaTabContext } from '~/ui/context/app/insomnia-tab-context'; import { useRunnerContext } from '~/ui/context/app/runner-context'; +import { useCurrentPlan } from '~/ui/hooks/use-account-server-data'; import { buildRunnerTabId } from '~/ui/hooks/use-insomnia-tab'; import { useRunnerRequestList } from '~/ui/hooks/use-runner-request-list'; import { @@ -155,7 +155,7 @@ export const Runner: FC = () => { const [searchParams] = useSearchParams(); const [errorMsg, setErrorMsg] = useState(null); - const organizationData = useOrganizationLoaderData(); + const currentPlan = useCurrentPlan(); const targetFolderId = searchParams.get('folder') || ''; const { organizationId, projectId, workspaceId } = useParams() as { @@ -290,7 +290,7 @@ export const Runner: FC = () => { window.main.trackAnalyticsEvent({ event: AnalyticsEvent.collectionRunExecute, - properties: { plan: organizationData?.currentPlan?.type || 'scratchpad', iterations: iterationCount }, + properties: { plan: currentPlan?.type || 'scratchpad', iterations: iterationCount }, }); updateTabById?.(buildRunnerTabId(workspaceId, targetFolderId), { temporary: false }); diff --git a/packages/insomnia/src/routes/organization.sync-organizations-and-projects.tsx b/packages/insomnia/src/routes/organization.sync-organizations-and-projects.tsx index 9aed14d40c..19ed7cf8cd 100644 --- a/packages/insomnia/src/routes/organization.sync-organizations-and-projects.tsx +++ b/packages/insomnia/src/routes/organization.sync-organizations-and-projects.tsx @@ -2,7 +2,6 @@ import type { Organization } from 'insomnia-api'; import { services } from 'insomnia-data'; import { href, redirect } from 'react-router'; -import { syncOrganizations } from '~/common/organization'; import { syncProjects } from '~/common/project'; import { invariant } from '~/common/utils/invariant'; import { findMigrationTargetSpaceId, migrateProjectsUnderOrganization } from '~/ui/organization-utils'; @@ -27,12 +26,6 @@ export async function clientAction({ request }: Route.ClientActionArgs) { const { id: sessionId, accountId } = await services.userSession.get(); const taskPromiseList = []; - if (asyncTaskList.includes(AsyncTask.SyncOrganization)) { - invariant(sessionId, 'sessionId is required'); - invariant(accountId, 'accountId is required'); - taskPromiseList.push(syncOrganizations(sessionId, accountId)); - } - if (asyncTaskList.includes(AsyncTask.MigrateProjects)) { const organizations = JSON.parse(localStorage.getItem(`${accountId}:spaces`) || '[]') as Organization[]; invariant(organizations.length, 'Failed to fetch organizations.'); diff --git a/packages/insomnia/src/routes/organization.sync.tsx b/packages/insomnia/src/routes/organization.sync.tsx deleted file mode 100644 index 4700a51a81..0000000000 --- a/packages/insomnia/src/routes/organization.sync.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { services } from 'insomnia-data'; - -import { syncOrganizations } from '~/common/organization'; -import { createFetcherSubmitHook } from '~/ui/utils/router'; - -import type { Route } from './+types/organization.sync'; - -export async function clientAction(_args: Route.ClientActionArgs) { - const { id: sessionId, accountId } = await services.userSession.get(); - - if (sessionId) { - await syncOrganizations(sessionId, accountId); - } - - return null; -} - -export const useOrganizationSyncActionFetcher = createFetcherSubmitHook( - submit => () => { - return submit( - {}, - { - method: 'POST', - action: '/organization/sync', - }, - ); - }, - clientAction, -); diff --git a/packages/insomnia/src/routes/organization.tsx b/packages/insomnia/src/routes/organization.tsx index 822ce9d579..61e782adec 100644 --- a/packages/insomnia/src/routes/organization.tsx +++ b/packages/insomnia/src/routes/organization.tsx @@ -1,9 +1,9 @@ -import { type Billing, type CurrentPlan, type FeatureList, type Organization, type User } from 'insomnia-api'; +import { type CurrentPlan, type User } from 'insomnia-api'; import type { Settings } from 'insomnia-data'; -import { models, services } from 'insomnia-data'; +import { models } from 'insomnia-data'; import React, { useCallback, useEffect, useState } from 'react'; import { Button, Link, ToggleButton, Tooltip, TooltipTrigger } from 'react-aria-components'; -import { href, NavLink, Outlet, useLocation, useNavigate, useParams, useRouteLoaderData } from 'react-router'; +import { href, NavLink, Outlet, useLocation, useNavigate, useParams } from 'react-router'; import * as reactUse from 'react-use'; import { useRootLoaderData } from '~/root'; @@ -30,45 +30,10 @@ import { InsomniaEventStreamProvider } from '~/ui/context/app/insomnia-event-str import { SidebarContext } from '~/ui/context/app/insomnia-sidebar-context'; import { InsomniaTabProvider } from '~/ui/context/app/insomnia-tab-context'; import { RunnerProvider } from '~/ui/context/app/runner-context'; +import { useCurrentPlan, useCurrentUser, useOrganizations } from '~/ui/hooks/use-account-server-data'; import { useCloseConnection } from '~/ui/hooks/use-close-connection'; import type { AsyncTask } from '~/ui/utils/router'; -import type { Route } from './+types/organization'; - -export interface OrganizationLoaderData { - organizations: Organization[]; - user?: User; - currentPlan?: CurrentPlan; -} - -export async function clientLoader(_args: Route.ClientLoaderArgs) { - const { id, accountId } = await services.userSession.get(); - if (id) { - const organizations = JSON.parse(localStorage.getItem(`${accountId}:spaces`) || '[]') as Organization[]; - const user = JSON.parse(localStorage.getItem(`${accountId}:user`) || '{}') as User; - const currentPlan = JSON.parse(localStorage.getItem(`${accountId}:currentPlan`) || '{}') as CurrentPlan; - return { - organizations, - user, - currentPlan, - }; - } - return { - organizations: [], - user: undefined, - currentPlan: undefined, - }; -} - -export interface OrganizationFeatureLoaderData { - featuresPromise: Promise; - billingPromise: Promise; -} - -export const useOrganizationLoaderData = () => { - return useRouteLoaderData('routes/organization'); -}; - interface IndicatorProps { asyncTaskStatus: 'error' | 'idle' | 'loading' | 'submitting'; settings: Settings; @@ -194,8 +159,10 @@ const LoginUserActions = ({ ); }; -const Component = ({ loaderData }: Route.ComponentProps) => { - const { organizations, user, currentPlan } = loaderData; +const Component = () => { + const organizations = useOrganizations(); + const user = useCurrentUser(); + const currentPlan = useCurrentPlan(); const { settings } = useRootLoaderData()!; const workspaceData = useWorkspaceLoaderData(); diff --git a/packages/insomnia/src/ui/components/editors/mock-response-extractor.tsx b/packages/insomnia/src/ui/components/editors/mock-response-extractor.tsx index 45b138ceb2..6ffc370988 100644 --- a/packages/insomnia/src/ui/components/editors/mock-response-extractor.tsx +++ b/packages/insomnia/src/ui/components/editors/mock-response-extractor.tsx @@ -3,12 +3,12 @@ import React, { useState } from 'react'; import { Button } from 'react-aria-components'; import { useNavigate, useParams } from 'react-router'; -import { useOrganizationLoaderData } from '~/routes/organization'; import { useRequestLoaderData } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId'; import { isInMockContentTypeList, useMockRoutePatcher, } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.mock-server.mock-route.$mockRouteId'; +import { useCurrentPlan } from '~/ui/hooks/use-account-server-data'; import { getContentTypeName, getMimeTypeFromContentType } from '../../../common/constants'; import { useWorkspaceLoaderData } from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId'; @@ -23,8 +23,8 @@ export const MockResponseExtractor = () => { const { activeProject, activeWorkspace } = useWorkspaceLoaderData()!; const isLocalProject = !activeProject?.remoteId; - const { currentPlan } = useOrganizationLoaderData()!; - const isEnterprise = currentPlan?.type.includes('enterprise'); + const currentPlan = useCurrentPlan(); + const isEnterprise = currentPlan?.type?.includes('enterprise'); // In a local project, users are not allowed to create a cloud mock server, only enterprise users can create a self-hosted mock server. // In a local project, users without enterprise plan can't create cloud mock server route from a request response diff --git a/packages/insomnia/src/ui/components/header-plan-indicator.tsx b/packages/insomnia/src/ui/components/header-plan-indicator.tsx index 2281adfcdf..a499100d6f 100644 --- a/packages/insomnia/src/ui/components/header-plan-indicator.tsx +++ b/packages/insomnia/src/ui/components/header-plan-indicator.tsx @@ -10,6 +10,7 @@ import { useTrialStartActionFetcher } from '~/routes/trial.start'; import { Icon } from '~/ui/components/icon'; import { TrialConfirmationModal } from '~/ui/components/modals/trial-confirmation-modal'; import { Tooltip } from '~/ui/components/tooltip'; +import { useInvalidateAccountData } from '~/ui/hooks/use-account-server-data'; import { usePlanData } from '~/ui/hooks/use-plan'; import { useUserService } from '~/ui/hooks/use-user-service'; import { formatNumber } from '~/ui/utils'; @@ -29,6 +30,7 @@ export const HeaderPlanIndicator = ({ isMinimal }: Props) => { const planName = `${planDisplayName} Plan`; const startFetcher = useTrialStartActionFetcher(); + const invalidateAccountData = useInvalidateAccountData(); const { load: usageLoad, state: usageState, data: usageData } = useResourceUsageFetcher(); function handleStartTrial() { @@ -66,8 +68,11 @@ export const HeaderPlanIndicator = ({ isMinimal }: Props) => { useEffect(() => { if (startFetcher.data?.success) { setCanTrial(false); + // trial.start wrote the new plan to localStorage; refresh the query so every + // plan consumer (this indicator included) reflects the upgraded plan. + invalidateAccountData(); } - }, [startFetcher.data?.success]); + }, [startFetcher.data?.success, invalidateAccountData]); const isUnlimited = isEnterpriseLike; diff --git a/packages/insomnia/src/ui/components/modals/import-modal/import-projects-modal.tsx b/packages/insomnia/src/ui/components/modals/import-modal/import-projects-modal.tsx index cdf4db716b..1b9c599e04 100644 --- a/packages/insomnia/src/ui/components/modals/import-modal/import-projects-modal.tsx +++ b/packages/insomnia/src/ui/components/modals/import-modal/import-projects-modal.tsx @@ -9,13 +9,13 @@ import * as reactUse from 'react-use'; import type { ScanResult } from '~/common/import'; import { importScannedResources } from '~/routes/import.resources'; import { scanImportResources } from '~/routes/import.scan'; -import { useOrganizationLoaderData } from '~/routes/organization'; import { createProject } from '~/routes/organization.$organizationId.project.new'; import { Checkbox } from '~/ui/components/base/checkbox'; import { Modal, type ModalHandle } from '~/ui/components/base/modal'; import { ModalHeader } from '~/ui/components/base/modal-header'; import { Icon } from '~/ui/components/icon'; import { Button } from '~/ui/components/themed-button'; +import { useOrganizations } from '~/ui/hooks/use-account-server-data'; import { selectFileOrFolder } from '~/ui/utils/select-file-or-folder'; import { showModal } from '..'; @@ -503,9 +503,9 @@ export const ImportProjectsModal = ({ organizationId, onHide }: { organizationId modalRef.current?.show(); }, []); - const organizationData = useOrganizationLoaderData(); + const organizations = useOrganizations(); const organizationName = - organizationData?.organizations.find(org => org.id === organizationId)?.name || 'Organization'; + organizations.find(org => org.id === organizationId)?.name || 'Organization'; const [projectItems, setProjectItems] = useState([]); const [processingUIStatus, setProcessingUiStatus] = useState<'loading' | 'importing' | 'error' | 'complete'>( diff --git a/packages/insomnia/src/ui/components/modals/input-vault-key-modal.tsx b/packages/insomnia/src/ui/components/modals/input-vault-key-modal.tsx index cf2ce58f51..8c1c4faaa1 100644 --- a/packages/insomnia/src/ui/components/modals/input-vault-key-modal.tsx +++ b/packages/insomnia/src/ui/components/modals/input-vault-key-modal.tsx @@ -5,10 +5,10 @@ import { Button, Dialog, Heading, Input, Modal, ModalOverlay } from 'react-aria- import { useRootLoaderData } from '~/root'; import { useResetVaultKeyFetcher } from '~/routes/auth.reset-vault-key'; import { useValidateVaultKeyActionFetcher } from '~/routes/auth.validate-vault-key'; -import { useOrganizationLoaderData } from '~/routes/organization'; import { PromptButton } from '~/ui/components/base/prompt-button'; import { Icon } from '~/ui/components/icon'; import { VaultKeyDisplayInput } from '~/ui/components/settings/vault-key-panel'; +import { useOrganizations } from '~/ui/hooks/use-account-server-data'; export interface InputVaultKeyModalProps { onClose: (vaultKey?: string) => void; @@ -23,7 +23,7 @@ export const InputVaultKeyModal = (props: InputVaultKeyModalProps) => { const [resetDone, setResetDone] = useState(false); const resetVaultKeyFetcher = useResetVaultKeyFetcher(); const validateVaultKeyFetcher = useValidateVaultKeyActionFetcher(); - const { organizations } = useOrganizationLoaderData()!; + const organizations = useOrganizations(); const isLoading = resetVaultKeyFetcher.state !== 'idle' || validateVaultKeyFetcher.state !== 'idle'; useEffect(() => { diff --git a/packages/insomnia/src/ui/components/modals/invite-modal/invite-form.tsx b/packages/insomnia/src/ui/components/modals/invite-modal/invite-form.tsx index 88a411e512..5fb4aeb113 100644 --- a/packages/insomnia/src/ui/components/modals/invite-modal/invite-form.tsx +++ b/packages/insomnia/src/ui/components/modals/invite-modal/invite-form.tsx @@ -18,11 +18,11 @@ import { getAppWebsiteBaseURL } from '~/common/constants'; import { docsPricingLearnMoreLink } from '~/common/documentation'; import { debounce } from '~/common/misc'; import { useRootLoaderData } from '~/root'; -import { useOrganizationLoaderData } from '~/routes/organization'; import { useCollaboratorsSearchLoaderFetcher } from '~/routes/organization.$organizationId.collaborators-search'; import { AnalyticsEvent } from '~/ui/analytics'; import { Icon } from '~/ui/components/icon'; import { useIsLightTheme } from '~/ui/hooks/theme'; +import { useOrganizations } from '~/ui/hooks/use-account-server-data'; import { startInvite } from './encryption'; import { OrganizationMemberRolesSelector, SELECTOR_TYPE } from './organization-member-roles-selector'; @@ -122,8 +122,8 @@ export const InviteForm = ({ const [searchParams, setSearchParams] = useSearchParams(); const { userSession } = useRootLoaderData()!; - const organizationData = useOrganizationLoaderData(); - const organization = organizationData?.organizations.find(o => o.id === organizationId); + const organizations = useOrganizations(); + const organization = organizations.find(o => o.id === organizationId); const isUserOwner = Boolean(organization?.is_owner); const sessionId = userSession.id; diff --git a/packages/insomnia/src/ui/components/modals/invite-modal/invite-modal.tsx b/packages/insomnia/src/ui/components/modals/invite-modal/invite-modal.tsx index c39e5fee25..1804f1088c 100644 --- a/packages/insomnia/src/ui/components/modals/invite-modal/invite-modal.tsx +++ b/packages/insomnia/src/ui/components/modals/invite-modal/invite-modal.tsx @@ -30,7 +30,6 @@ import { useParams, useSearchParams } from 'react-router'; import { getAppWebsiteBaseURL } from '~/common/constants'; import { debounce } from '~/common/misc'; import { invariant } from '~/common/utils/invariant'; -import { useOrganizationLoaderData } from '~/routes/organization'; import { useCollaboratorsFetcher } from '~/routes/organization.$organizationId.collaborators'; import { useInviteFetcher } from '~/routes/organization.$organizationId.collaborators.invites.$invitationId'; import { useReinviteFetcher } from '~/routes/organization.$organizationId.collaborators.invites.$invitationId.reinvite'; @@ -42,6 +41,7 @@ import { PromptButton } from '~/ui/components/base/prompt-button'; import { Icon } from '~/ui/components/icon'; import { AlertModal } from '~/ui/components/modals/alert-modal'; import { showModal } from '~/ui/components/modals/index'; +import { useOrganizations } from '~/ui/hooks/use-account-server-data'; import { InviteForm } from './invite-form'; import { OrganizationMemberRolesSelector, SELECTOR_TYPE } from './organization-member-roles-selector'; @@ -597,8 +597,8 @@ export const InviteModalContainer: FC<{ }> = ({ isOpen, setIsOpen }) => { const [loadingOrgInfo, setLoadingOrgInfo] = useState(true); const { organizationId } = useParams(); - const organizationData = useOrganizationLoaderData(); - const currentOrg = organizationData?.organizations.find(o => o.id === organizationId); + const organizations = useOrganizations(); + const currentOrg = organizations.find(o => o.id === organizationId); const [allRoles, setAllRoles] = useState([]); const [currentUserRoleInOrg, setCurrentUserRoleInOrg] = useState(null); const [orgFeatures, setOrgFeatures] = useState(null); diff --git a/packages/insomnia/src/ui/components/modals/workspace-duplicate-modal.tsx b/packages/insomnia/src/ui/components/modals/workspace-duplicate-modal.tsx index 1a00a3a06a..28f4d868ca 100644 --- a/packages/insomnia/src/ui/components/modals/workspace-duplicate-modal.tsx +++ b/packages/insomnia/src/ui/components/modals/workspace-duplicate-modal.tsx @@ -5,8 +5,8 @@ import React, { type FC, type MouseEventHandler, useEffect, useRef, useState } f import { OverlayContainer } from 'react-aria'; import { href, useNavigate, useParams } from 'react-router'; -import { useOrganizationLoaderData } from '~/routes/organization'; import { useWorkspaceMoveActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.move'; +import { useOrganizations } from '~/ui/hooks/use-account-server-data'; import { getWorkspaceLabel } from '../../../common/get-workspace-label'; import { scopeToBgColorMap, scopeToIconMap, scopeToTextColorMap } from '../../../common/get-workspace-label'; @@ -26,7 +26,7 @@ export const WorkspaceDuplicateModal: FC = ({ work organizationId: string; projectId: string; }; - const organizationData = useOrganizationLoaderData(); + const organizations = useOrganizations(); const [selectedOrgId, setSelectedOrgId] = useState(organizationId); const [projectOptions, setProjectOptions] = useState([]); const [selectedProjectId, setSelectedProjectId] = useState(''); @@ -115,7 +115,7 @@ export const WorkspaceDuplicateModal: FC = ({ work