4
4
* See License.AGPL.txt in the project root for license information.
5
5
*/
6
6
7
- import { CommitContext , ContextURL , GitpodServer , WorkspaceInfo } from "@gitpod/gitpod-protocol" ;
7
+ import { CommitContext , GitpodServer , WithReferrerContext } from "@gitpod/gitpod-protocol" ;
8
8
import { SelectAccountPayload } from "@gitpod/gitpod-protocol/lib/auth" ;
9
9
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error" ;
10
10
import { Deferred } from "@gitpod/gitpod-protocol/lib/util/deferred" ;
11
11
import { FunctionComponent , useCallback , useEffect , useMemo , useState } from "react" ;
12
12
import { useHistory , useLocation } from "react-router" ;
13
13
import { Button } from "../components/Button" ;
14
- import Modal , { ModalBody , ModalFooter , ModalHeader } from "../components/Modal" ;
15
14
import RepositoryFinder from "../components/RepositoryFinder" ;
16
15
import SelectIDEComponent from "../components/SelectIDEComponent" ;
17
16
import SelectWorkspaceClassComponent from "../components/SelectWorkspaceClassComponent" ;
@@ -21,6 +20,7 @@ import { useFeatureFlags } from "../contexts/FeatureFlagContext";
21
20
import { useCurrentOrg } from "../data/organizations/orgs-query" ;
22
21
import { useListProjectsQuery } from "../data/projects/list-projects-query" ;
23
22
import { useCreateWorkspaceMutation } from "../data/workspaces/create-workspace-mutation" ;
23
+ import { useListWorkspacesQuery } from "../data/workspaces/list-workspaces-query" ;
24
24
import { useWorkspaceContext } from "../data/workspaces/resolve-context-query" ;
25
25
import { openAuthorizeWindow } from "../provider-utils" ;
26
26
import { gitpodHostUrl } from "../service/service" ;
@@ -29,6 +29,7 @@ import { StartWorkspaceOptions } from "../start/start-workspace-options";
29
29
import { StartWorkspaceError } from "../start/StartPage" ;
30
30
import { useCurrentUser } from "../user-context" ;
31
31
import { SelectAccountModal } from "../user-settings/SelectAccountModal" ;
32
+ import { WorkspaceEntry } from "./WorkspaceEntry" ;
32
33
33
34
export const useNewCreateWorkspacePage = ( ) => {
34
35
const { startWithOptions } = useFeatureFlags ( ) ;
@@ -40,6 +41,7 @@ export function CreateWorkspacePage() {
40
41
const user = useCurrentUser ( ) ;
41
42
const currentOrg = useCurrentOrg ( ) . data ;
42
43
const projects = useListProjectsQuery ( ) ;
44
+ const workspaces = useListWorkspacesQuery ( { limit : 50 , orgId : currentOrg ?. id } ) ;
43
45
const location = useLocation ( ) ;
44
46
const history = useHistory ( ) ;
45
47
const props = StartWorkspaceOptions . parseSearchParams ( location . search ) ;
@@ -101,14 +103,29 @@ export function CreateWorkspacePage() {
101
103
) ;
102
104
const [ errorIde , setErrorIde ] = useState < string | undefined > ( undefined ) ;
103
105
104
- const [ existingWorkspaces , setExistingWorkspaces ] = useState < WorkspaceInfo [ ] > ( [ ] ) ;
106
+ const existingWorkspaces = useMemo ( ( ) => {
107
+ if ( ! workspaces . data || ! CommitContext . is ( workspaceContext . data ) ) {
108
+ return [ ] ;
109
+ }
110
+ return workspaces . data . filter (
111
+ ( ws ) =>
112
+ ws . latestInstance ?. status ?. phase === "running" &&
113
+ CommitContext . is ( ws . workspace . context ) &&
114
+ CommitContext . is ( workspaceContext . data ) &&
115
+ ws . workspace . context . repository . cloneUrl === workspaceContext . data . repository . cloneUrl &&
116
+ ws . workspace . context . revision === workspaceContext . data . revision ,
117
+ ) ;
118
+ } , [ workspaces . data , workspaceContext . data ] ) ;
105
119
const [ selectAccountError , setSelectAccountError ] = useState < SelectAccountPayload | undefined > ( undefined ) ;
106
120
107
121
const createWorkspace = useCallback (
108
122
async ( options ?: Omit < GitpodServer . CreateWorkspaceOptions , "contextUrl" > ) => {
109
123
// add options from search params
110
124
const opts = options || { } ;
111
125
126
+ // we already have shown running workspaces to the user
127
+ opts . ignoreRunningWorkspaceOnSameCommit = true ;
128
+
112
129
if ( ! opts . workspaceClass ) {
113
130
opts . workspaceClass = selectedWsClass ;
114
131
}
@@ -123,14 +140,17 @@ export function CreateWorkspacePage() {
123
140
}
124
141
125
142
const organizationId = currentOrg ?. id ;
126
- console . log ( "organizationId: " + JSON . stringify ( organizationId ) ) ;
127
143
if ( ! organizationId && ! ! user ?. additionalData ?. isMigratedToTeamOnlyAttribution ) {
128
144
// We need an organizationId for this group of users
129
145
console . warn ( "Skipping createWorkspace" ) ;
130
146
return ;
131
147
}
132
148
133
149
try {
150
+ if ( createWorkspaceMutation . isLoading || createWorkspaceMutation . isSuccess ) {
151
+ console . log ( "Skipping duplicate createWorkspace call." ) ;
152
+ return ;
153
+ }
134
154
const result = await createWorkspaceMutation . mutateAsync ( {
135
155
contextUrl : contextURL ,
136
156
organizationId,
@@ -140,19 +160,42 @@ export function CreateWorkspacePage() {
140
160
window . location . href = result . workspaceURL ;
141
161
} else if ( result . createdWorkspaceId ) {
142
162
history . push ( `/start/#${ result . createdWorkspaceId } ` ) ;
143
- } else if ( result . existingWorkspaces && result . existingWorkspaces . length > 0 ) {
144
- setExistingWorkspaces ( result . existingWorkspaces ) ;
145
163
}
146
164
} catch ( error ) {
147
165
console . log ( error ) ;
148
166
}
149
167
} ,
150
- [ createWorkspaceMutation , history , contextURL , selectedIde , selectedWsClass , currentOrg ?. id , user ?. additionalData ?. isMigratedToTeamOnlyAttribution , useLatestIde ] ,
168
+ [
169
+ createWorkspaceMutation ,
170
+ history ,
171
+ contextURL ,
172
+ selectedIde ,
173
+ selectedWsClass ,
174
+ currentOrg ?. id ,
175
+ user ?. additionalData ?. isMigratedToTeamOnlyAttribution ,
176
+ useLatestIde ,
177
+ ] ,
151
178
) ;
152
179
153
180
// Need a wrapper here so we call createWorkspace w/o any arguments
154
181
const onClickCreate = useCallback ( ( ) => createWorkspace ( ) , [ createWorkspace ] ) ;
155
182
183
+ // if the context URL has a referrer prefix, we set the referrerIde as the selected IDE and immediately start a workspace.
184
+ useEffect ( ( ) => {
185
+ if ( workspaceContext . data && WithReferrerContext . is ( workspaceContext . data ) ) {
186
+ let options : Omit < GitpodServer . CreateWorkspaceOptions , "contextUrl" > | undefined ;
187
+ if ( workspaceContext . data . referrerIde ) {
188
+ setSelectedIde ( workspaceContext . data . referrerIde ) ;
189
+ options = {
190
+ ideSettings : {
191
+ defaultIde : workspaceContext . data . referrerIde ,
192
+ } ,
193
+ } ;
194
+ }
195
+ createWorkspace ( options ) ;
196
+ }
197
+ } , [ workspaceContext . data , createWorkspace ] ) ;
198
+
156
199
if ( SelectAccountPayload . is ( selectAccountError ) ) {
157
200
return (
158
201
< SelectAccountModal
@@ -214,6 +257,24 @@ export function CreateWorkspacePage() {
214
257
{ isLoading ? "Loading ..." : isStarting ? "Creating Workspace ..." : "New Workspace" }
215
258
</ Button >
216
259
</ div >
260
+ { existingWorkspaces . length > 0 && (
261
+ < div className = "w-full flex flex-col justify-end px-6" >
262
+ < p className = "mt-6 text-center text-base" > Running workspaces on this revision</ p >
263
+ < >
264
+ { existingWorkspaces . map ( ( w ) => {
265
+ return (
266
+ < a
267
+ key = { w . workspace . id }
268
+ href = { w . latestInstance ?. ideUrl || `/start/${ w . workspace . id } }` }
269
+ className = "rounded-xl group hover:bg-gray-100 dark:hover:bg-gray-800 flex"
270
+ >
271
+ < WorkspaceEntry info = { w } shortVersion = { true } />
272
+ </ a >
273
+ ) ;
274
+ } ) }
275
+ </ >
276
+ </ div >
277
+ ) }
217
278
< div >
218
279
< StatusMessage
219
280
error = { createWorkspaceMutation . error as StartWorkspaceError }
@@ -225,14 +286,6 @@ export function CreateWorkspacePage() {
225
286
/>
226
287
</ div >
227
288
</ div >
228
- { existingWorkspaces . length > 0 && (
229
- < ExistingWorkspaceModal
230
- existingWorkspaces = { existingWorkspaces }
231
- createWorkspace = { createWorkspace }
232
- isStarting = { isStarting }
233
- onClose = { ( ) => setExistingWorkspaces ( [ ] ) }
234
- />
235
- ) }
236
289
</ div >
237
290
) ;
238
291
}
@@ -351,62 +404,3 @@ const StatusMessage: FunctionComponent<StatusMessageProps> = ({
351
404
return < p className = "text-base text-gitpod-red w-96" > Unknown Error: { JSON . stringify ( error , null , 2 ) } </ p > ;
352
405
}
353
406
} ;
354
-
355
- interface ExistingWorkspaceModalProps {
356
- existingWorkspaces : WorkspaceInfo [ ] ;
357
- onClose : ( ) => void ;
358
- isStarting : boolean ;
359
- createWorkspace : ( opts : Omit < GitpodServer . CreateWorkspaceOptions , "contextUrl" > ) => void ;
360
- }
361
-
362
- const ExistingWorkspaceModal : FunctionComponent < ExistingWorkspaceModalProps > = ( {
363
- existingWorkspaces,
364
- onClose,
365
- isStarting,
366
- createWorkspace,
367
- } ) => {
368
- return (
369
- < Modal visible = { true } closeable = { true } onClose = { onClose } >
370
- < ModalHeader > Running Workspaces</ ModalHeader >
371
- < ModalBody >
372
- < p className = "mt-1 mb-2 text-base" >
373
- You already have running workspaces with the same context. You can open an existing one or open a
374
- new workspace.
375
- </ p >
376
- < >
377
- { existingWorkspaces . map ( ( w ) => {
378
- const normalizedContextUrl =
379
- ContextURL . getNormalizedURL ( w . workspace ) ?. toString ( ) || "undefined" ;
380
- return (
381
- < a
382
- key = { w . workspace . id }
383
- href = { w . latestInstance ?. ideUrl || `/start/${ w . workspace . id } }` }
384
- className = "rounded-xl group hover:bg-gray-100 dark:hover:bg-gray-800 flex p-3 my-1"
385
- >
386
- < div className = "w-full" >
387
- < p className = "text-base text-black dark:text-gray-100 font-bold" >
388
- { w . workspace . id }
389
- </ p >
390
- < p className = "truncate" title = { normalizedContextUrl } >
391
- { normalizedContextUrl }
392
- </ p >
393
- </ div >
394
- </ a >
395
- ) ;
396
- } ) }
397
- </ >
398
- </ ModalBody >
399
- < ModalFooter >
400
- < button className = "secondary" onClick = { onClose } >
401
- Cancel
402
- </ button >
403
- < Button
404
- loading = { isStarting }
405
- onClick = { ( ) => createWorkspace ( { ignoreRunningWorkspaceOnSameCommit : true } ) }
406
- >
407
- { isStarting ? "Creating Workspace ..." : "New Workspace" }
408
- </ Button >
409
- </ ModalFooter >
410
- </ Modal >
411
- ) ;
412
- } ;
0 commit comments