@@ -9,6 +9,20 @@ 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 ;
19
+ } ;
20
+ owner ?: {
21
+ id : number ;
22
+ name : string ;
23
+ avatar_url : string ;
24
+ } ;
25
+ } ;
12
26
13
27
@injectable ( )
14
28
export class GitLabAppSupport {
@@ -38,12 +52,12 @@ export class GitLabAppSupport {
38
52
//
39
53
const projectsWithAccess = await api . Projects . all ( { min_access_level : "40" , perPage : 100 } ) ;
40
54
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 ;
55
+ const aProject = project as ProjectSchema ;
56
+ const path = aProject . path as string ;
57
+ const fullPath = aProject . path_with_namespace as string ;
58
+ const cloneUrl = aProject . http_url_to_repo as string ;
59
+ const updatedAt = aProject . last_activity_at as string ;
60
+ const accountAvatarUrl = await this . getAccountAvatarUrl ( aProject , params . provider . host ) ;
47
61
const account = fullPath . split ( "/" ) [ 0 ] ;
48
62
49
63
( account === usersGitLabAccount ? ownersRepos : result ) . push ( {
@@ -61,4 +75,27 @@ export class GitLabAppSupport {
61
75
result . unshift ( ...ownersRepos ) ;
62
76
return result ;
63
77
}
78
+
79
+ protected async getAccountAvatarUrl ( project : ProjectSchema , providerHost : string ) : Promise < string > {
80
+ const owner = project . owner || project . namespace ;
81
+ if ( owner . avatar_url ) {
82
+ const url = owner . avatar_url ;
83
+ // Sometimes GitLab avatar URLs are relative -- ensure we always use the correct host
84
+ return url [ 0 ] === "/" ? `https://${ providerHost } ${ url } ` : url ;
85
+ }
86
+ // If there is no avatar, generate the same default avatar that GitLab uses. Based on:
87
+ // - https://gitlab.com/gitlab-org/gitlab/-/blob/b2a22b6e85200ce55ab09b5c765043441b086c96/app/helpers/avatars_helper.rb#L151-161
88
+ // - https://gitlab.com/gitlab-org/gitlab-foss/-/blob/84b4743475246e91dc78c3f25f9b335c40be84cd/app/assets/stylesheets/startup/startup-general.scss#L1611-1631
89
+ // - https://gitlab.com/gitlab-org/gitlab-foss/-/blob/84b4743475246e91dc78c3f25f9b335c40be84cd/app/assets/stylesheets/startup/startup-general.scss#L420-422
90
+ const text = owner . name [ 0 ] . toUpperCase ( ) ;
91
+ const backgroundColor = [ "#fcf1ef" , "#f4f0ff" , "#f1f1ff" , "#e9f3fc" , "#ecf4ee" , "#fdf1dd" , "#f0f0f0" ] [
92
+ owner . id % 7
93
+ ] ;
94
+ const svg = `<svg viewBox="0 0 32 32" height="32" width="32" style="background-color: ${ backgroundColor } " xmlns="http://www.w3.org/2000/svg">
95
+ <text x="50%" y="50%" dominant-baseline="middle" 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"'>
96
+ ${ text }
97
+ </text>
98
+ </svg>` ;
99
+ return `data:image/svg+xml,${ encodeURIComponent ( svg . replace ( / \s + / g, " " ) ) } ` ;
100
+ }
64
101
}
0 commit comments