This commit is contained in:
Stephen Zhou 2026-04-29 17:43:34 +08:00
parent 96bc73e47d
commit 1ea16409d4
No known key found for this signature in database
6 changed files with 87 additions and 31 deletions

View File

@ -19,13 +19,13 @@ const DeployDrawer: FC = () => {
const open = drawer.open
const { environmentOptions } = useSourceApps({ enabled: open })
const appDataQuery = useQuery({
useQuery({
...deploymentAppDataQueryOptions(drawerAppId ?? ''),
queryFn: () => useDeploymentsStore.getState().fetchAppData(drawerAppId!),
enabled: open && Boolean(drawerAppId) && !storedAppData,
})
const appData = storedAppData ?? (appDataQuery.data?.appId === drawerAppId ? appDataQuery.data : undefined)
const appData = storedAppData
const environments = environmentOptions
const releases = appData?.releaseHistory.data?.map(row => row.release ?? row).filter(release => release.id) ?? []
const defaultReleaseId = releases[0]?.id

View File

@ -1,6 +1,5 @@
'use client'
import type { AppInfo } from '../types'
import type { AppDeploymentSummary, EnvironmentOption } from '@/contract/console/deployments'
import { useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { consoleQuery } from '@/service/client'
@ -20,7 +19,6 @@ export function useSourceApps(options: UseSourceAppsOptions = {}) {
const { enabled = true, environmentId, keyword, notDeployed } = options
const instancesById = useDeploymentsStore(deploymentsSelectors.instancesById)
const listRefreshToken = useDeploymentsStore(deploymentsSelectors.listRefreshToken)
const query = useMemo(() => ({
pageNumber: 1,
resultsPerPage: MAX_SOURCE_APPS,
@ -28,6 +26,7 @@ export function useSourceApps(options: UseSourceAppsOptions = {}) {
...(notDeployed ? { notDeployed: true } : {}),
...(keyword?.trim() ? { query: keyword.trim() } : {}),
}), [environmentId, keyword, notDeployed])
const sourceAppsList = useDeploymentsStore(deploymentsSelectors.sourceAppsList(query))
const listQueryOptions = consoleQuery.deployments.list.queryOptions({
input: { query },
@ -44,44 +43,21 @@ export function useSourceApps(options: UseSourceAppsOptions = {}) {
queryFn: () => useDeploymentsStore.getState().fetchSourceApps(query),
})
const appIds = useMemo(() => {
return (listQuery.data?.data ?? [])
.map(summary => summary.id)
.filter((id): id is string => Boolean(id))
}, [listQuery.data?.data])
const apps = useMemo<AppInfo[]>(() => {
return appIds
return sourceAppsList.appIds
.map(id => instancesById[id])
.filter((app): app is AppInfo => Boolean(app))
}, [appIds, instancesById])
}, [sourceAppsList.appIds, instancesById])
const appMap = useMemo<Map<string, AppInfo>>(() => {
return new Map(apps.map(a => [a.id, a]))
}, [apps])
const summaries = useMemo<Record<string, AppDeploymentSummary>>(() => {
return Object.fromEntries(
(listQuery.data?.data ?? [])
.filter(summary => summary.id)
.map(summary => [summary.id!, summary]),
)
}, [listQuery.data?.data])
const environmentOptions = useMemo<EnvironmentOption[]>(() => {
return listQuery.data?.filters
?.filter(filter => filter.kind === 'environment' && filter.id)
.map(filter => ({
id: filter.id,
name: filter.name,
})) ?? []
}, [listQuery.data?.filters])
return {
apps,
appMap,
summaries,
environmentOptions,
summaries: sourceAppsList.summaries,
environmentOptions: sourceAppsList.environmentOptions,
isLoading: listQuery.isLoading,
isFetching: listQuery.isFetching,
isError: listQuery.isError,

View File

@ -27,6 +27,10 @@ import {
updateEnvironmentAccessPolicy,
waitForAppInstanceInDeploymentList,
} from '../data'
import {
sourceAppsListKey,
toSourceAppsList,
} from './source-apps'
export type StartDeployParams = {
appId: string
@ -170,12 +174,22 @@ class DeploymentsActionImpl implements DeploymentsAction {
}))
}
#applySourceAppsList = (query: ListAppDeploymentsQuery, response: ListAppDeploymentsReply) => {
this.#set(state => ({
sourceAppLists: {
...state.sourceAppLists,
[sourceAppsListKey(query)]: toSourceAppsList(response),
},
}))
}
fetchSourceApps = async (query: ListAppDeploymentsQuery) => {
const response = await listAppDeployments(query)
const apps = response.data
?.map(toAppInfoFromSummary)
.filter((app): app is AppInfo => Boolean(app)) ?? []
this.upsertInstances(apps)
this.#applySourceAppsList(query, response)
return response
}
@ -211,10 +225,16 @@ class DeploymentsActionImpl implements DeploymentsAction {
this.upsertInstances([app])
}),
waitForAppInstanceInDeploymentList(response.appInstanceId).then((list) => {
const query = {
pageNumber: 1,
resultsPerPage: 100,
}
const apps = list?.data
?.map(toAppInfoFromSummary)
.filter((app): app is AppInfo => Boolean(app)) ?? []
this.upsertInstances(apps)
if (list)
this.#applySourceAppsList(query, list)
}),
])
this.bumpDeploymentListRefresh()

View File

@ -1,5 +1,6 @@
import type { DeploymentAppData } from '../data'
import type { AppInfo } from '../types'
import type { SourceAppsList } from './source-apps'
import type { APIToken } from '@/contract/console/deployments'
export type CreatedApiToken = Pick<APIToken, 'id' | 'environmentId' | 'maskedPrefix' | 'name'> & {
@ -10,6 +11,7 @@ export type CreatedApiToken = Pick<APIToken, 'id' | 'environmentId' | 'maskedPre
export type DeploymentsState = {
instancesById: Record<string, AppInfo>
appData: Record<string, DeploymentAppData>
sourceAppLists: Record<string, SourceAppsList>
listRefreshToken: number
createdApiToken?: CreatedApiToken
@ -32,6 +34,7 @@ export type DeploymentsState = {
export const initialDeploymentsState: DeploymentsState = {
instancesById: {},
appData: {},
sourceAppLists: {},
listRefreshToken: 0,
createdApiToken: undefined,

View File

@ -1,4 +1,6 @@
import type { ListAppDeploymentsQuery } from '../data'
import type { DeploymentsStore } from './actions'
import { emptySourceAppsList, sourceAppsListKey } from './source-apps'
export const deploymentsSelectors = {
appData: (appId?: string) => (state: DeploymentsStore) =>
@ -8,4 +10,6 @@ export const deploymentsSelectors = {
appId ? state.instancesById[appId] : undefined,
instancesById: (state: DeploymentsStore) => state.instancesById,
listRefreshToken: (state: DeploymentsStore) => state.listRefreshToken,
sourceAppsList: (query: ListAppDeploymentsQuery) => (state: DeploymentsStore) =>
state.sourceAppLists[sourceAppsListKey(query)] ?? emptySourceAppsList,
}

View File

@ -0,0 +1,53 @@
import type { ListAppDeploymentsQuery } from '../data'
import type {
AppDeploymentSummary,
EnvironmentOption,
ListAppDeploymentsReply,
} from '@/contract/console/deployments'
export type SourceAppsList = {
appIds: string[]
summaries: Record<string, AppDeploymentSummary>
environmentOptions: EnvironmentOption[]
}
export const emptySourceAppsList: SourceAppsList = {
appIds: [],
summaries: {},
environmentOptions: [],
}
export const sourceAppsListKey = ({
environmentId = '',
notDeployed = false,
pageNumber = 1,
query = '',
resultsPerPage = 100,
}: ListAppDeploymentsQuery) => {
return [
environmentId,
notDeployed ? 'not-deployed' : 'all',
pageNumber,
resultsPerPage,
query.trim(),
].join('|')
}
export const toSourceAppsList = (response: ListAppDeploymentsReply): SourceAppsList => {
const summaries = Object.fromEntries(
(response.data ?? [])
.filter(summary => summary.id)
.map(summary => [summary.id!, summary]),
)
return {
appIds: Object.keys(summaries),
summaries,
environmentOptions: response.filters
?.filter(filter => filter.kind === 'environment' && filter.id)
.map(filter => ({
id: filter.id,
name: filter.name,
})) ?? [],
}
}