Skip to content

Commit 7f387d2

Browse files
authored
Make clicking an external link in Firefox close the popup (#38)
* Update external links so that they close the popup in Firefox * Make PR deep links actual link elements that will close the popup
1 parent 19eb905 commit 7f387d2

File tree

6 files changed

+69
-43
lines changed

6 files changed

+69
-43
lines changed

src/deepLink.ts

-9
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,3 @@ export const getGitKrakenDeepLinkUrl = (provider: FocusViewSupportedProvider, ur
229229

230230
return getGKDotDevLinkUrl(redirectUrl);
231231
};
232-
233-
export const openGitKrakenDeepLink = (provider: FocusViewSupportedProvider, url: string | null) => {
234-
const deepLink = getGitKrakenDeepLinkUrl(provider, url);
235-
if (!deepLink) {
236-
return;
237-
}
238-
239-
window.open(deepLink, '_blank');
240-
};

src/popup/components/ConnectAProvider.tsx

+9-12
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,41 @@
11
import React from 'react';
22
import { GKDotDevUrl } from '../../shared';
3+
import { ExternalLink } from './ExternalLink';
34

45
export const ConnectAProvider = () => {
56
return (
67
<div className="connect-provider-container">
78
<div className="connect-provider-prompt text-center">
89
<div className="text-2xl bold">Connect an integration to see all of your pull requests</div>
910
<div className="provider-buttons">
10-
<a
11+
<ExternalLink
1112
className="provider-button text-sm text-secondary"
1213
href={`${GKDotDevUrl}/settings/integrations?connect=github`}
13-
target="_blank"
1414
>
1515
<img src="img/github-color.svg" height={24} />
1616
<div>GitHub</div>
17-
</a>
18-
<a
17+
</ExternalLink>
18+
<ExternalLink
1919
className="provider-button text-sm text-secondary"
2020
href={`${GKDotDevUrl}/settings/integrations?connect=gitlab`}
21-
target="_blank"
2221
>
2322
<img src="img/gitlab-color.svg" height={24} />
2423
<div>GitLab</div>
25-
</a>
26-
<a
24+
</ExternalLink>
25+
<ExternalLink
2726
className="provider-button text-sm text-secondary"
2827
href={`${GKDotDevUrl}/settings/integrations?connect=bitbucket`}
29-
target="_blank"
3028
>
3129
<img src="img/bitbucket-color.svg" height={24} />
3230
<div>Bitbucket</div>
33-
</a>
34-
<a
31+
</ExternalLink>
32+
<ExternalLink
3533
className="provider-button text-sm text-secondary"
3634
href={`${GKDotDevUrl}/settings/integrations?connect=azure`}
37-
target="_blank"
3835
>
3936
<img src="img/azuredevops-color.svg" height={24} />
4037
<div>Azure DevOps</div>
41-
</a>
38+
</ExternalLink>
4239
</div>
4340
<div className="text-sm text-secondary italic">
4441
*Only cloud-hosted providers are currently supported.

src/popup/components/ExternalLink.tsx

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react';
2+
import { tabs } from 'webextension-polyfill';
3+
4+
type ExternalLinkProps = {
5+
children: React.ReactNode;
6+
className?: string;
7+
href?: string;
8+
onClick?: () => any;
9+
title?: string;
10+
};
11+
12+
export const ExternalLink = ({ children, className, href, onClick, title }: ExternalLinkProps) => {
13+
return (
14+
<a
15+
className={className}
16+
href={href}
17+
rel="noreferrer noopener"
18+
target="_blank"
19+
title={title}
20+
onClick={async e => {
21+
// Opening external links in Firefox will not cause the popup to automatically close,
22+
// so we must manually close it.
23+
24+
// Closing the popup window before the link has finished opening will cause Firefox to open
25+
// the link in a new window instead of in a new tab. In order to prevent this, we manually
26+
// open the link in a new tab and await that, then close the popup window afterwards.
27+
e.preventDefault();
28+
29+
if (onClick) {
30+
await onClick();
31+
}
32+
if (href) {
33+
await tabs.create({ url: href });
34+
}
35+
36+
window.close();
37+
}}
38+
>
39+
{children}
40+
</a>
41+
);
42+
};

src/popup/components/FocusView.tsx

+10-16
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { GitProviderUtils } from '@gitkraken/provider-apis';
22
import { useQueryClient } from '@tanstack/react-query';
33
import React, { useEffect, useMemo, useState } from 'react';
44
import { storage } from 'webextension-polyfill';
5-
import { openGitKrakenDeepLink } from '../../deepLink';
5+
import { getGitKrakenDeepLinkUrl } from '../../deepLink';
66
import { ProviderMeta } from '../../providers';
77
import { GKDotDevUrl } from '../../shared';
88
import type {
@@ -12,6 +12,7 @@ import type {
1212
} from '../../types';
1313
import { useFocusViewConnectedProviders, useFocusViewDataQuery, usePullRequestDraftCountsQuery } from '../hooks';
1414
import { ConnectAProvider } from './ConnectAProvider';
15+
import { ExternalLink } from './ExternalLink';
1516

1617
type PullRequestRowProps = {
1718
userId: string;
@@ -22,17 +23,17 @@ type PullRequestRowProps = {
2223

2324
const PullRequestRow = ({ userId, pullRequest, provider, draftCount = 0 }: PullRequestRowProps) => {
2425
const queryClient = useQueryClient();
26+
const deepLinkUrl = getGitKrakenDeepLinkUrl(provider, pullRequest.url);
2527

2628
return (
2729
<>
2830
<div className="pull-request">
2931
<div className="pull-request-title truncate">{pullRequest.title}</div>
3032
<div className="repository-name text-secondary truncate">{pullRequest.repository.name}</div>
3133
<div className="pull-request-number">
32-
<a
34+
<ExternalLink
3335
className="text-link"
3436
href={pullRequest.url || undefined}
35-
target="_blank"
3637
onClick={() => {
3738
// Since there is a decent chance that the PR will be acted upon after the user clicks on it,
3839
// mark the focus view data as stale so that it will be refetched when the user returns.
@@ -41,32 +42,25 @@ const PullRequestRow = ({ userId, pullRequest, provider, draftCount = 0 }: PullR
4142
title={`View pull request on ${ProviderMeta[provider].name}`}
4243
>
4344
#{pullRequest.number}
44-
</a>
45+
</ExternalLink>
4546
</div>
46-
{pullRequest.url && (
47-
<a
48-
href="#"
49-
onClick={() => {
50-
openGitKrakenDeepLink(provider, pullRequest.url);
51-
}}
52-
title="Open with GitKraken"
53-
>
47+
{deepLinkUrl && (
48+
<ExternalLink href={deepLinkUrl} title="Open with GitKraken">
5449
<i className="fa-brands fa-gitkraken icon text-link text-lg" />
55-
</a>
50+
</ExternalLink>
5651
)}
5752
</div>
5853
{draftCount > 0 && (
59-
<a
54+
<ExternalLink
6055
className="pr-drafts-badge text-disabled"
6156
href={`${GKDotDevUrl}/drafts/suggested-change/${encodeURIComponent(
6257
btoa(pullRequest.uniqueId),
6358
)}?source=browserExtension`}
64-
target="_blank"
6559
title={`View code suggestion${draftCount === 1 ? '' : 's'} on gitkraken.dev`}
6660
>
6761
<i className="fa-regular fa-message-code icon" />
6862
Code Suggestion{draftCount === 1 ? '' : 's'}
69-
</a>
63+
</ExternalLink>
7064
)}
7165
</>
7266
);

src/popup/components/SignedIn.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { logoutUser } from '../../gkApi';
33
import type { PermissionsRequest } from '../../permissions-helper';
44
import { GKDotDevUrl } from '../../shared';
55
import type { User } from '../../types';
6+
import { ExternalLink } from './ExternalLink';
67
import { FocusView } from './FocusView';
78
import { RequestPermissionsBanner } from './RequestPermissionsBanner';
89

@@ -45,9 +46,9 @@ export const SignedIn = ({ permissionsRequest, user }: { permissionsRequest?: Pe
4546
<div>{user.name || user.username}</div>
4647
<div className="text-sm text-secondary">{user.email}</div>
4748
</div>
48-
<a href={GKDotDevUrl} target="_blank" title="Open gitkraken.dev">
49+
<ExternalLink href={GKDotDevUrl} title="Open gitkraken.dev">
4950
<i className="fa-regular fa-arrow-up-right-from-square icon text-lg" />
50-
</a>
51+
</ExternalLink>
5152
</div>
5253
<button className="icon-btn" onClick={onSignOutClick} title="Sign out">
5354
<i className="fa-regular fa-right-from-bracket icon text-lg" />

src/popup/components/SignedOut.tsx

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import type { PermissionsRequest } from '../../permissions-helper';
33
import { GKDotDevUrl } from '../../shared';
4+
import { ExternalLink } from './ExternalLink';
45
import { RequestPermissionsBanner } from './RequestPermissionsBanner';
56

67
export const SignedOut = ({ permissionsRequest }: { permissionsRequest?: PermissionsRequest }) => {
@@ -9,13 +10,13 @@ export const SignedOut = ({ permissionsRequest }: { permissionsRequest?: Permiss
910
{permissionsRequest && <RequestPermissionsBanner permissionsRequest={permissionsRequest} />}
1011
<div className="sign-in-prompt">
1112
<div className="text-2xl bold">Sign in to view your Pull Requests</div>
12-
<a className="sign-in-link text-sm text-secondary bg-02" href={`${GKDotDevUrl}/login`} target="_blank">
13+
<ExternalLink className="sign-in-link text-sm text-secondary bg-02" href={`${GKDotDevUrl}/login`}>
1314
<img src="img/gk-logo-36.svg" height={36} width={36} />
1415
Sign in with GitKraken
15-
</a>
16-
<a className="text-link" href={`${GKDotDevUrl}/register`} target="_blank">
16+
</ExternalLink>
17+
<ExternalLink className="text-link" href={`${GKDotDevUrl}/register`}>
1718
Create an account
18-
</a>
19+
</ExternalLink>
1920
</div>
2021
</div>
2122
);

0 commit comments

Comments
 (0)