@@ -9,6 +9,21 @@ import { inject, injectable } from "inversify";
9
9
import { TokenProvider } from "../../../src/user/token-provider" ;
10
10
import { UserDB } from "@gitpod/gitpod-db/lib" ;
11
11
import { Gitlab } from "@gitbeaker/node" ;
12
+ import { ProjectSchemaDefault , NamespaceInfoSchemaDefault } from "@gitbeaker/core/dist/types/services/Projects" ;
13
+
14
+ // Add missing fields to Gitbeaker's ProjectSchema type
15
+ type ProjectSchema = ProjectSchemaDefault & {
16
+ last_activity_at : string ;
17
+ namespace : NamespaceInfoSchemaDefault & {
18
+ avatar_url : string | null ;
19
+ parent_id : number | null ;
20
+ } ;
21
+ owner ?: {
22
+ id : number ;
23
+ name : string ;
24
+ avatar_url : string | null ;
25
+ } ;
26
+ } ;
12
27
13
28
@injectable ( )
14
29
export class GitLabAppSupport {
@@ -38,12 +53,12 @@ export class GitLabAppSupport {
38
53
//
39
54
const projectsWithAccess = await api . Projects . all ( { min_access_level : "40" , perPage : 100 } ) ;
40
55
for ( const project of projectsWithAccess ) {
41
- const anyProject = project as any ;
42
- const path = anyProject . path as string ;
43
- const fullPath = anyProject . path_with_namespace as string ;
44
- const cloneUrl = anyProject . http_url_to_repo as string ;
45
- const updatedAt = anyProject . last_activity_at as string ;
46
- const accountAvatarUrl = anyProject . owner ?. avatar_url as string ;
56
+ const aProject = project as ProjectSchema ;
57
+ const path = aProject . path as string ;
58
+ const fullPath = aProject . path_with_namespace as string ;
59
+ const cloneUrl = aProject . http_url_to_repo as string ;
60
+ const updatedAt = aProject . last_activity_at as string ;
61
+ const accountAvatarUrl = await this . getAccountAvatarUrl ( aProject , params . provider . host ) ;
47
62
const account = fullPath . split ( "/" ) [ 0 ] ;
48
63
49
64
( account === usersGitLabAccount ? ownersRepos : result ) . push ( {
@@ -61,4 +76,35 @@ export class GitLabAppSupport {
61
76
result . unshift ( ...ownersRepos ) ;
62
77
return result ;
63
78
}
79
+
80
+ protected async getAccountAvatarUrl ( project : ProjectSchema , providerHost : string ) : Promise < string > {
81
+ let owner = project . owner ;
82
+ if ( ! owner && project . namespace && ! project . namespace . parent_id ) {
83
+ // Fall back to "root namespace" / "top-level group"
84
+ owner = project . namespace ;
85
+ }
86
+ if ( ! owner ) {
87
+ // Could not determine account avatar
88
+ return "" ;
89
+ }
90
+ if ( owner . avatar_url ) {
91
+ const url = owner . avatar_url ;
92
+ // Sometimes GitLab avatar URLs are relative -- ensure we always use the correct host
93
+ return url [ 0 ] === "/" ? `https://${ providerHost } ${ url } ` : url ;
94
+ }
95
+ // If there is no avatar, generate the same default avatar that GitLab uses. Based on:
96
+ // - https://gitlab.com/gitlab-org/gitlab/-/blob/b2a22b6e85200ce55ab09b5c765043441b086c96/app/helpers/avatars_helper.rb#L151-161
97
+ // - https://gitlab.com/gitlab-org/gitlab/-/blob/861f52858a1db07bdb122fe947dec9b0a09ce807/app/assets/stylesheets/startup/startup-general.scss#L1611-1631
98
+ // - https://gitlab.com/gitlab-org/gitlab/-/blob/861f52858a1db07bdb122fe947dec9b0a09ce807/app/assets/stylesheets/startup/startup-general.scss#L420-422
99
+ const backgroundColors = [ "#fcf1ef" , "#f4f0ff" , "#f1f1ff" , "#e9f3fc" , "#ecf4ee" , "#fdf1dd" , "#f0f0f0" ] ;
100
+ const backgroundColor = backgroundColors [ owner . id % backgroundColors . length ] ;
101
+ // Uppercase first character of the name, support emojis, default to whitespace.
102
+ const text = String . fromCodePoint ( owner . name . codePointAt ( 0 ) || 32 /* space */ ) . toUpperCase ( ) ;
103
+ const svg = `<svg viewBox="0 0 32 32" height="32" width="32" style="background-color: ${ backgroundColor } " xmlns="http://www.w3.org/2000/svg">
104
+ <text x="50%" y="50%" dominant-baseline="central" text-anchor="middle" style='font-size: 0.875rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'>
105
+ ${ text }
106
+ </text>
107
+ </svg>` ;
108
+ return `data:image/svg+xml,${ encodeURIComponent ( svg . replace ( / \s + / g, " " ) ) } ` ;
109
+ }
64
110
}
0 commit comments