5
5
*/
6
6
7
7
import React from "react" ;
8
- import { WhitelistedRepository , WorkspaceInfo } from "@gitpod/gitpod-protocol" ;
8
+ import { WhitelistedRepository , Workspace , WorkspaceInfo } from "@gitpod/gitpod-protocol" ;
9
9
import Header from "../components/Header" ;
10
10
import DropDown from "../components/DropDown"
11
11
import { WorkspaceModel } from "./workspace-model" ;
12
12
import { WorkspaceEntry } from "./WorkspaceEntry" ;
13
- import Modal from "../components/Modal" ;
14
13
import { getGitpodService , gitpodHostUrl } from "../service/service" ;
14
+ import { StartWorkspaceModal , WsStartEntry } from "./StartWorkspaceModal" ;
15
15
16
16
export interface WorkspacesProps {
17
17
}
@@ -46,7 +46,7 @@ export class Workspaces extends React.Component<WorkspacesProps, WorkspacesState
46
46
47
47
render ( ) {
48
48
const wsModel = this . workspaceModel ;
49
- const toggleTemplateModal = ( ) => this . setState ( {
49
+ const toggleStartWSModal = ( ) => this . setState ( {
50
50
isTemplateModelOpen : ! this . state ?. isTemplateModelOpen
51
51
} ) ;
52
52
const onActive = ( ) => wsModel ! . active = true ;
@@ -58,7 +58,7 @@ export class Workspaces extends React.Component<WorkspacesProps, WorkspacesState
58
58
< div className = "flex" >
59
59
< div className = "py-4" >
60
60
< svg width = "16" height = "16" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
61
- < path fill-rule = "evenodd" clip-rule = "evenodd" d = "M6 2a4 4 0 100 8 4 4 0 000-8zM0 6a6 6 0 1110.89 3.477l4.817 4.816a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 010 6z" fill = "#A8A29E" />
61
+ < path fillRule = "evenodd" clipRule = "evenodd" d = "M6 2a4 4 0 100 8 4 4 0 000-8zM0 6a6 6 0 1110.89 3.477l4.817 4.816a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 010 6z" fill = "#A8A29E" />
62
62
</ svg >
63
63
</ div >
64
64
< input className = "border-0" type = "text" placeholder = "Search Workspaces" onChange = { ( v ) => { if ( wsModel ) wsModel . setSearch ( v . target . value ) } } />
@@ -85,6 +85,10 @@ export class Workspaces extends React.Component<WorkspacesProps, WorkspacesState
85
85
onClick : ( ) => { if ( wsModel ) wsModel . limit = 200 ; }
86
86
} ] } />
87
87
</ div >
88
+ { wsModel && this . state ?. workspaces . length > 0 ?
89
+ < button onClick = { toggleStartWSModal } className = "ml-2 font-medium" > New Workspace</ button >
90
+ : null
91
+ }
88
92
</ div >
89
93
{ wsModel && (
90
94
this . state ?. workspaces . length > 0 || wsModel . searchTerm ?
@@ -131,30 +135,56 @@ export class Workspaces extends React.Component<WorkspacesProps, WorkspacesState
131
135
< div className = "px-6 py-3 flex justify-between space-x-2 text-sm text-gray-400 border-t border-gray-200 h-96" >
132
136
< div className = "flex flex-col items-center w-96 m-auto" >
133
137
< h3 className = "text-center pb-3" > No Active Workspaces</ h3 >
134
- < div className = "text-center pb-6 text-gray-500" > Prefix a git repository URL with gitpod.io/# or open a workspace template . < a className = "text-gray-400 underline underline-thickness-thin underline-offset-small hover:text-gray-600" href = "https://www.gitpod.io/docs/getting-started/" > Learn how to get started </ a > </ div >
135
- < button onClick = { toggleTemplateModal } className = "font-medium" > Select Template </ button >
138
+ < div className = "text-center pb-6 text-gray-500" > Prefix any git repository URL with gitpod.io/# or start a new workspace for a recently used project . < a className = "text-gray-400 underline underline-thickness-thin underline-offset-small hover:text-gray-600" href = "https://www.gitpod.io/docs/getting-started/" > Learn more </ a > </ div >
139
+ < button onClick = { toggleStartWSModal } className = "font-medium" > New Workspace </ button >
136
140
</ div >
137
141
</ div >
138
142
</ div >
139
143
) }
140
- < Modal onClose = { toggleTemplateModal } visible = { ! ! this . state ?. isTemplateModelOpen } >
141
- < h3 className = "pb-2" > Select Template</ h3 >
142
- { /* separator */ }
143
- < div className = "border-t mt-2 -mx-6 px-6 py-2" >
144
- < p className = "mt-1 mb-2 text-base" > Select a template to open a workspace.</ p >
145
- < div className = "space-y-2 mt-4 overflow-y-scroll h-80 pr-2" >
146
- { this . state ?. repos && this . state . repos . map ( r => {
147
- const url = gitpodHostUrl . withContext ( r . url ) . toString ( ) ;
148
- return < a key = { r . name } href = { url } className = "rounded-xl group hover:bg-gray-100 flex p-4 my-1" >
149
- < div className = "w-full" >
150
- < p className = "text-base text-gray-800 font-semibold" > { r . name } </ p >
151
- < p > { r . url } </ p >
152
- </ div >
153
- </ a > ;
154
- } ) }
155
- </ div >
156
- </ div >
157
- </ Modal >
144
+ < StartWorkspaceModal
145
+ onClose = { toggleStartWSModal }
146
+ visible = { ! ! this . state ?. isTemplateModelOpen }
147
+ examples = { this . state ?. repos && this . state . repos . map ( r => ( {
148
+ title : r . name ,
149
+ description : r . description || r . url ,
150
+ startUrl : gitpodHostUrl . withContext ( r . url ) . toString ( )
151
+ } ) ) }
152
+ recent = { wsModel && this . state ?. workspaces ?
153
+ this . getRecentSuggestions ( )
154
+ : [ ] } />
158
155
</ > ;
159
156
}
157
+
158
+ protected getRecentSuggestions ( ) : WsStartEntry [ ] {
159
+ if ( this . workspaceModel ) {
160
+ const all = this . workspaceModel . getAllFetchedWorkspaces ( ) ;
161
+ if ( all && all . size > 0 ) {
162
+ const index = new Map < string , WsStartEntry & { lastUse : string } > ( ) ;
163
+ for ( const ws of Array . from ( all . values ( ) ) ) {
164
+ const repoUrl = Workspace . getFullRepositoryUrl ( ws . workspace ) ;
165
+ if ( repoUrl ) {
166
+ const lastUse = WorkspaceInfo . lastActiveISODate ( ws ) ;
167
+ let entry = index . get ( repoUrl ) ;
168
+ if ( ! entry ) {
169
+ entry = {
170
+ title : Workspace . getFullRepositoryName ( ws . workspace ) || repoUrl ,
171
+ description : repoUrl ,
172
+ startUrl : gitpodHostUrl . withContext ( repoUrl ) . toString ( ) ,
173
+ lastUse,
174
+ } ;
175
+ index . set ( repoUrl , entry ) ;
176
+ } else {
177
+ if ( entry . lastUse . localeCompare ( lastUse ) < 0 ) {
178
+ entry . lastUse = lastUse ;
179
+ }
180
+ }
181
+ }
182
+ }
183
+ const list = Array . from ( index . values ( ) ) ;
184
+ list . sort ( ( a , b ) => b . lastUse . localeCompare ( a . lastUse ) ) ;
185
+ return list ;
186
+ }
187
+ }
188
+ return [ ] ;
189
+ }
160
190
}
0 commit comments