1- import type { CancellationToken } from 'vscode' ;
1+ import type { AuthenticationSession , CancellationToken } from 'vscode' ;
2+ import type { AutolinkReference , DynamicAutolinkReference } from '../../../autolinks/models/autolinks' ;
23import { IssuesCloudHostIntegrationId } from '../../../constants.integrations' ;
34import type { Account } from '../../../git/models/author' ;
45import type { Issue , IssueShape } from '../../../git/models/issue' ;
@@ -17,12 +18,118 @@ const authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes })
1718const maxPagesPerRequest = 10 ;
1819
1920export interface LinearTeamDescriptor extends IssueResourceDescriptor {
21+ avatarUrl : string | undefined ;
22+ }
23+
24+ export interface LinearOrganizationDescriptor extends IssueResourceDescriptor {
2025 url : string ;
2126}
2227
2328export interface LinearProjectDescriptor extends IssueResourceDescriptor { }
2429
2530export class LinearIntegration extends IssuesIntegration < IssuesCloudHostIntegrationId . Linear > {
31+ private _autolinks : Map < string , ( AutolinkReference | DynamicAutolinkReference ) [ ] > | undefined ;
32+ override async autolinks ( ) : Promise < ( AutolinkReference | DynamicAutolinkReference ) [ ] > {
33+ const connected = this . maybeConnected ?? ( await this . isConnected ( ) ) ;
34+ if ( ! connected || this . _session == null ) {
35+ return [ ] ;
36+ }
37+ const cachedAutolinks = this . _autolinks ?. get ( this . _session . accessToken ) ;
38+ if ( cachedAutolinks != null ) return cachedAutolinks ;
39+
40+ const organization = await this . getOrganization ( this . _session ) ;
41+ if ( organization == null ) return [ ] ;
42+
43+ const autolinks : ( AutolinkReference | DynamicAutolinkReference ) [ ] = [ ] ;
44+
45+ const teams = await this . getTeams ( this . _session ) ;
46+ for ( const team of teams ?? [ ] ) {
47+ const dashedPrefix = `${ team . key } -` ;
48+ const underscoredPrefix = `${ team . key } _` ;
49+
50+ autolinks . push ( {
51+ prefix : dashedPrefix ,
52+ url : `${ organization . url } /issue/${ dashedPrefix } <num>` ,
53+ alphanumeric : false ,
54+ ignoreCase : false ,
55+ title : `Open Issue ${ dashedPrefix } <num> on ${ organization . name } ` ,
56+
57+ type : 'issue' ,
58+ description : `${ organization . name } Issue ${ dashedPrefix } <num>` ,
59+ descriptor : { ...organization } ,
60+ } ) ;
61+ autolinks . push ( {
62+ prefix : underscoredPrefix ,
63+ url : `${ organization . url } /issue/${ dashedPrefix } <num>` ,
64+ alphanumeric : false ,
65+ ignoreCase : false ,
66+ referenceType : 'branch' ,
67+ title : `Open Issue ${ dashedPrefix } <num> on ${ organization . name } ` ,
68+
69+ type : 'issue' ,
70+ description : `${ organization . name } Issue ${ dashedPrefix } <num>` ,
71+ descriptor : { ...organization } ,
72+ } ) ;
73+ }
74+
75+ this . _autolinks ??= new Map < string , ( AutolinkReference | DynamicAutolinkReference ) [ ] > ( ) ;
76+ this . _autolinks . set ( this . _session . accessToken , autolinks ) ;
77+
78+ return autolinks ;
79+ }
80+
81+ private _organizations : Map < string , LinearOrganizationDescriptor | undefined > | undefined ;
82+ private async getOrganization (
83+ { accessToken } : AuthenticationSession ,
84+ force : boolean = false ,
85+ ) : Promise < LinearOrganizationDescriptor | undefined > {
86+ this . _organizations ??= new Map < string , LinearOrganizationDescriptor | undefined > ( ) ;
87+
88+ const cachedResources = this . _organizations . get ( accessToken ) ;
89+
90+ if ( cachedResources == null || force ) {
91+ const api = await this . getProvidersApi ( ) ;
92+ const organization = await api . getLinearOrganization ( { accessToken : accessToken } ) ;
93+ const descriptor : LinearOrganizationDescriptor | undefined = organization && {
94+ id : organization . id ,
95+ key : organization . key ,
96+ name : organization . name ,
97+ url : organization . url ,
98+ } ;
99+ if ( descriptor ) {
100+ this . _organizations . set ( accessToken , descriptor ) ;
101+ }
102+ }
103+
104+ return this . _organizations . get ( accessToken ) ;
105+ }
106+
107+ private _teams : Map < string , LinearTeamDescriptor [ ] | undefined > | undefined ;
108+ private async getTeams (
109+ { accessToken } : AuthenticationSession ,
110+ force : boolean = false ,
111+ ) : Promise < LinearTeamDescriptor [ ] | undefined > {
112+ this . _teams ??= new Map < string , LinearTeamDescriptor [ ] | undefined > ( ) ;
113+
114+ const cachedResources = this . _teams . get ( accessToken ) ;
115+
116+ if ( cachedResources == null || force ) {
117+ const api = await this . getProvidersApi ( ) ;
118+ const teams = await api . getLinearTeamsForCurrentUser ( { accessToken : accessToken } ) ;
119+ const descriptors : LinearTeamDescriptor [ ] | undefined = teams ?. map ( t => ( {
120+ id : t . id ,
121+ key : t . key ,
122+ name : t . name ,
123+ avatarUrl : t . iconUrl ,
124+ } ) ) ;
125+ if ( descriptors ) {
126+ this . _teams . set ( accessToken , descriptors ) ;
127+ }
128+ }
129+
130+ return this . _teams . get ( accessToken ) ;
131+ }
132+
26133 protected override getProviderResourcesForUser (
27134 _session : ProviderAuthenticationSession ,
28135 ) : Promise < ResourceDescriptor [ ] | undefined > {
0 commit comments