Skip to content

Commit 32686a4

Browse files
committed
Adds connect integrations into Inspect autolinks
1 parent 5f1c176 commit 32686a4

File tree

7 files changed

+155
-65
lines changed

7 files changed

+155
-65
lines changed

src/webviews/apps/commitDetails/components/commit-details-app.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
CreatePatchFromWipCommand,
1313
DidChangeDraftStateNotification,
1414
DidChangeHasAccountNotification,
15+
DidChangeIntegrationsNotification,
1516
DidChangeNotification,
1617
DidChangeWipStateNotification,
1718
ExecuteCommitActionCommand,
@@ -337,6 +338,10 @@ export class GlCommitDetailsApp extends LitElement {
337338
this.state = { ...this.state!, hasAccount: msg.params.hasAccount };
338339
this.dispatchEvent(new CustomEvent('state-changed', { detail: this.state }));
339340
break;
341+
case DidChangeIntegrationsNotification.is(msg):
342+
this.state = { ...this.state!, hasIntegrationsConnected: msg.params.hasIntegrationsConnected };
343+
this.dispatchEvent(new CustomEvent('state-changed', { detail: this.state }));
344+
break;
340345
}
341346
}
342347

src/webviews/apps/commitDetails/components/gl-commit-details.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,23 +257,43 @@ export class GlCommitDetails extends GlDetailsBase {
257257
};
258258
}
259259

260+
private renderLearnAboutAutolinks(compact = false) {
261+
const chipLabel = compact ? nothing : html`<span class="mq-hide-sm">Learn about autolinks</span>`;
262+
263+
const hasIntegrationsConnected = this.state?.hasIntegrationsConnected ?? false;
264+
let label = 'Learn about autolinks';
265+
if (!hasIntegrationsConnected) {
266+
label = `<a href="command:gitlens.showSettingsPage!autolinks">${label}</a>\n\n<a href="command:gitlens.plus.cloudIntegrations.connect">Connect an Integration</a> &mdash;`;
267+
268+
if (!this.state?.hasAccount) {
269+
label += ' sign up and';
270+
}
271+
272+
label += ' to get access to automatic rich autolinks for services like Jira, GitHub, and more.';
273+
}
274+
275+
return html`<gl-action-chip
276+
href="command:gitlens.showSettingsPage!autolinks"
277+
data-action="autolink-settings"
278+
icon="info"
279+
.label=${label}
280+
overlay=${hasIntegrationsConnected ? 'tooltip' : 'popover'}
281+
>${chipLabel}</gl-action-chip
282+
>`;
283+
}
284+
260285
private renderAutoLinksChips() {
261286
const autolinkState = this.autolinkState;
262287
if (autolinkState == null) return html`<span></span>`;
263288

264289
const { autolinks, issues, prs, size } = autolinkState;
265290

266291
if (size === 0) {
267-
return html`<gl-action-chip
268-
href="command:gitlens.showSettingsPage!autolinks"
269-
data-action="autolink-settings"
270-
icon="info"
271-
label="Learn about autolinks"
272-
><span class="mq-hide-sm">Learn about autolinks</span></gl-action-chip
273-
>`;
292+
return this.renderLearnAboutAutolinks();
274293
}
275294

276295
return html`<div class="message-block-group">
296+
${this.renderLearnAboutAutolinks(true)}
277297
${when(autolinks.length, () =>
278298
autolinks.map(autolink => {
279299
let name = autolink.description ?? autolink.title;

src/webviews/apps/shared/components/chips/action-chip.ts

Lines changed: 75 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { css, html, LitElement, nothing } from 'lit';
22
import { customElement, property, query } from 'lit/decorators.js';
3+
import { linkStyles } from '../../../plus/shared/components/vscode.css';
4+
import { handleUnsafeOverlayContent } from '../overlays/overlays.utils';
35
import { focusOutline } from '../styles/lit/a11y.css';
6+
import '../overlays/popover';
47
import '../overlays/tooltip';
58
import '../code-icon';
69

@@ -11,65 +14,76 @@ export class ActionChip extends LitElement {
1114
delegatesFocus: true,
1215
};
1316

14-
static override styles = css`
15-
:host {
16-
box-sizing: border-box;
17-
display: inline-flex;
18-
justify-content: center;
19-
align-items: center;
20-
min-width: 2rem;
21-
height: 2rem;
22-
border-radius: 0.5rem;
23-
color: inherit;
24-
padding: 0.2rem;
25-
vertical-align: text-bottom;
26-
text-decoration: none;
27-
cursor: pointer;
28-
}
29-
30-
:host(:focus-within) {
31-
${focusOutline}
32-
}
33-
34-
:host(:hover) {
35-
background-color: var(--vscode-toolbar-hoverBackground);
36-
}
37-
38-
:host(:active) {
39-
background-color: var(--vscode-toolbar-activeBackground);
40-
}
41-
42-
:host([disabled]) {
43-
pointer-events: none;
44-
opacity: 0.5;
45-
}
46-
47-
a {
48-
display: inline-flex;
49-
justify-content: center;
50-
align-items: center;
51-
gap: 0.2rem;
52-
vertical-align: middle;
53-
color: inherit;
54-
text-decoration: none;
55-
}
56-
a:focus {
57-
outline: none;
58-
}
59-
60-
::slotted(*) {
61-
padding-inline-end: 0.2rem;
62-
vertical-align: middle;
63-
text-transform: capitalize;
64-
}
65-
`;
17+
static override styles = [
18+
linkStyles,
19+
css`
20+
:host {
21+
display: inline-flex;
22+
justify-content: center;
23+
align-items: center;
24+
vertical-align: text-bottom;
25+
border-radius: 0.5rem;
26+
}
27+
28+
* {
29+
box-sizing: border-box;
30+
}
31+
32+
:host(:focus-within) {
33+
${focusOutline}
34+
}
35+
36+
:host(:hover) {
37+
background-color: var(--vscode-toolbar-hoverBackground);
38+
}
39+
40+
:host(:active) {
41+
background-color: var(--vscode-toolbar-activeBackground);
42+
}
43+
44+
:host([disabled]) {
45+
pointer-events: none;
46+
opacity: 0.5;
47+
}
48+
49+
.chip {
50+
display: inline-flex;
51+
justify-content: center;
52+
align-items: center;
53+
gap: 0.2rem;
54+
vertical-align: middle;
55+
color: inherit;
56+
min-width: 2rem;
57+
height: 2rem;
58+
color: inherit;
59+
padding: 0.2rem;
60+
text-decoration: none;
61+
cursor: pointer;
62+
}
63+
.chip:hover {
64+
text-decoration: none;
65+
}
66+
.chip:focus {
67+
outline: none;
68+
}
69+
70+
::slotted(*) {
71+
padding-inline-end: 0.2rem;
72+
vertical-align: middle;
73+
text-transform: capitalize;
74+
}
75+
`,
76+
];
6677

6778
@property()
6879
href?: string;
6980

7081
@property()
7182
label?: string;
7283

84+
@property()
85+
overlay: 'tooltip' | 'popover' = 'tooltip';
86+
7387
@property()
7488
icon = '';
7589

@@ -84,17 +98,26 @@ export class ActionChip extends LitElement {
8498
return this.renderContent();
8599
}
86100

101+
if (this.overlay === 'popover') {
102+
return html`<gl-popover hoist
103+
>${this.renderContent()}
104+
<div slot="content">${handleUnsafeOverlayContent(this.label)}</div></gl-popover
105+
>`;
106+
}
107+
87108
return html`<gl-tooltip hoist content="${this.label}">${this.renderContent()}</gl-tooltip>`;
88109
}
89110

90111
private renderContent() {
91112
return html`
92113
<a
114+
class="chip"
93115
part="base"
94116
role="${!this.href ? 'button' : nothing}"
95117
type="${!this.href ? 'button' : nothing}"
96118
?disabled=${this.disabled}
97119
href=${this.href ?? nothing}
120+
slot=${this.overlay === 'popover' ? 'anchor' : nothing}
98121
>
99122
<code-icon part="icon" icon="${this.icon}"></code-icon><slot></slot>
100123
</a>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
2+
3+
export function handleUnsafeOverlayContent(content?: string) {
4+
if (content?.includes('\n')) {
5+
return unsafeHTML(content.replace(/\n\n/g, '<hr>').replace(/\n/g, '<br>'));
6+
}
7+
8+
return content;
9+
}

src/webviews/apps/shared/components/overlays/tooltip.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type SlTooltip from '@shoelace-style/shoelace/dist/components/tooltip/too
22
import { setDefaultAnimation } from '@shoelace-style/shoelace/dist/utilities/animation-registry.js';
33
import { css, html, LitElement, nothing } from 'lit';
44
import { customElement, property } from 'lit/decorators.js';
5+
import { handleUnsafeOverlayContent } from './overlays.utils';
56
import '@shoelace-style/shoelace/dist/components/tooltip/tooltip.js';
6-
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
77

88
setDefaultAnimation('tooltip.show', null);
99
setDefaultAnimation('tooltip.hide', null);
@@ -112,11 +112,7 @@ export class GlTooltip extends LitElement {
112112
>
113113
<slot></slot>
114114
<div slot="content">
115-
<slot name="content"
116-
>${this.content?.includes('\n')
117-
? unsafeHTML(this.content?.replace(/\n\n/g, '<hr>').replace(/\n/g, '<br>'))
118-
: this.content}</slot
119-
>
115+
<slot name="content">${handleUnsafeOverlayContent(this.content)}</slot>
120116
</div>
121117
</sl-tooltip>`;
122118
}

src/webviews/commitDetails/commitDetailsWebview.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { confirmDraftStorage } from '../../plus/drafts/utils/-webview/drafts.uti
4646
import type { Subscription } from '../../plus/gk/models/subscription';
4747
import type { SubscriptionChangeEvent } from '../../plus/gk/subscriptionService';
4848
import { ensureAccount } from '../../plus/gk/utils/-webview/acount.utils';
49+
import type { ConnectionStateChangeEvent } from '../../plus/integrations/integrationService';
4950
import { supportsCodeSuggest } from '../../plus/integrations/providers/models';
5051
import { getEntityIdentifierInput } from '../../plus/integrations/providers/utils';
5152
import {
@@ -97,6 +98,7 @@ import {
9798
CreatePatchFromWipCommand,
9899
DidChangeDraftStateNotification,
99100
DidChangeHasAccountNotification,
101+
DidChangeIntegrationsNotification,
100102
DidChangeNotification,
101103
DidChangeWipStateNotification,
102104
ExecuteCommitActionCommand,
@@ -163,6 +165,7 @@ interface Context {
163165
orgSettings: State['orgSettings'];
164166
source?: Sources;
165167
hasAccount: boolean | undefined;
168+
hasIntegrationsConnected: boolean;
166169
}
167170

168171
export class CommitDetailsWebviewProvider
@@ -202,12 +205,14 @@ export class CommitDetailsWebviewProvider
202205
wip: undefined,
203206
orgSettings: this.getOrgSettings(),
204207
hasAccount: undefined,
208+
hasIntegrationsConnected: false,
205209
};
206210

207211
this._disposable = Disposable.from(
208212
configuration.onDidChangeAny(this.onAnyConfigurationChanged, this),
209213
onDidChangeContext(this.onContextChanged, this),
210214
this.container.subscription.onDidChange(this.onSubscriptionChanged, this),
215+
container.integrations.onDidChangeConnectionState(this.onIntegrationConnectionStateChanged, this),
211216
);
212217
}
213218

@@ -907,6 +912,24 @@ export class CommitDetailsWebviewProvider
907912
return this._context.hasAccount;
908913
}
909914

915+
private async onIntegrationConnectionStateChanged(e: ConnectionStateChangeEvent) {
916+
const isConnected = e.reason === 'connected';
917+
if (this._context.hasIntegrationsConnected === isConnected) return;
918+
919+
void this.host.notify(DidChangeIntegrationsNotification, {
920+
hasIntegrationsConnected: await this.getHasIntegrationsConnected(true),
921+
});
922+
}
923+
924+
async getHasIntegrationsConnected(force = false): Promise<boolean> {
925+
if (this._context.hasIntegrationsConnected != null && !force) return this._context.hasIntegrationsConnected;
926+
927+
const hasAny = (await this.container.integrations.getConfigured()).length > 0;
928+
this._context.hasIntegrationsConnected = hasAny;
929+
930+
return this._context.hasIntegrationsConnected;
931+
}
932+
910933
private getPreferences(): Preferences {
911934
return {
912935
pullRequestExpanded: this.container.storage.getWorkspace('views:commitDetails:pullRequestExpanded') ?? true,
@@ -1201,6 +1224,10 @@ export class CommitDetailsWebviewProvider
12011224
current.hasAccount = await this.getHasAccount();
12021225
}
12031226

1227+
if (current.hasIntegrationsConnected == null) {
1228+
current.hasIntegrationsConnected = await this.getHasIntegrationsConnected();
1229+
}
1230+
12041231
const state = serialize<State>({
12051232
...this.host.baseWebviewState,
12061233
mode: current.mode,
@@ -1216,6 +1243,7 @@ export class CommitDetailsWebviewProvider
12161243
orgSettings: current.orgSettings,
12171244
inReview: current.inReview,
12181245
hasAccount: current.hasAccount,
1246+
hasIntegrationsConnected: current.hasIntegrationsConnected,
12191247
});
12201248
return state;
12211249
}

src/webviews/commitDetails/protocol.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export interface State extends WebviewState {
108108
wip?: Wip;
109109
inReview?: boolean;
110110
hasAccount: boolean;
111+
hasIntegrationsConnected: boolean;
111112
}
112113

113114
export type ShowCommitDetailsViewCommandArgs = string[];
@@ -246,3 +247,11 @@ export interface DidChangeDraftStateParams {
246247
inReview: boolean;
247248
}
248249
export const DidChangeDraftStateNotification = new IpcNotification<DidChangeDraftStateParams>(scope, 'didChange/patch');
250+
251+
export interface DidChangeIntegrationsParams {
252+
hasIntegrationsConnected: boolean;
253+
}
254+
export const DidChangeIntegrationsNotification = new IpcNotification<DidChangeIntegrationsParams>(
255+
scope,
256+
'didChange/integrations',
257+
);

0 commit comments

Comments
 (0)