Skip to content

Commit

Permalink
Show code suggestions on PRs in Focus View (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
jdgarcia authored Apr 24, 2024
1 parent 5d587c2 commit 679e04e
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 37 deletions.
20 changes: 19 additions & 1 deletion src/gkApi.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { cookies, storage } from 'webextension-polyfill';
import { checkOrigins } from './permissions-helper';
import { DefaultCacheTimeMinutes, sessionCachedFetch, updateExtensionIcon } from './shared';
import type { Provider, ProviderConnection, ProviderToken, User } from './types';
import type { Provider, ProviderConnection, ProviderToken, PullRequestDraftCounts, User } from './types';

declare const MODE: 'production' | 'development' | 'none';

Expand Down Expand Up @@ -140,3 +140,21 @@ export const fetchProviderToken = async (provider: Provider) => {
const payload = await res.json();
return payload.data as ProviderToken;
};

export const fetchDraftCounts = async (prUniqueIds: string[]) => {
const token = await getAccessToken();
if (!token) {
return null;
}

const res = await fetch(`${gkApiUrl}/v1/drafts/counts?type=suggested_pr_change`, {
headers: {
Authorization: `Bearer ${token}`,
},
method: 'POST',
body: JSON.stringify({ prEntityIds: prUniqueIds }),
});

const payload = await res.json();
return payload.data as { counts: PullRequestDraftCounts };
};
107 changes: 80 additions & 27 deletions src/popup/components/FocusView.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,78 @@
import type { GitPullRequest, PullRequestBucket } from '@gitkraken/provider-apis';
import type { PullRequestBucket } from '@gitkraken/provider-apis';
import { GitProviderUtils } from '@gitkraken/provider-apis';
import React, { useEffect, useState } from 'react';
import { storage } from 'webextension-polyfill';
import { fetchProviderConnections } from '../../gkApi';
import { fetchDraftCounts, fetchProviderConnections } from '../../gkApi';
import { fetchFocusViewData, ProviderMeta } from '../../providers';
import { DefaultCacheTimeMinutes, sessionCachedFetch } from '../../shared';
import type { FocusViewData, FocusViewSupportedProvider } from '../../types';
import { DefaultCacheTimeMinutes, GKDotDevUrl, sessionCachedFetch } from '../../shared';
import type {
FocusViewData,
FocusViewSupportedProvider,
GitPullRequestWithUniqueID,
PullRequestBucketWithUniqueIDs,
} from '../../types';
import { ConnectAProvider } from './ConnectAProvider';

const PullRequestRow = ({ pullRequest }: { pullRequest: GitPullRequest }) => {
type PullRequestRowProps = {
pullRequest: GitPullRequestWithUniqueID;
draftCount?: number;
};

const PullRequestRow = ({ pullRequest, draftCount = 0 }: PullRequestRowProps) => {
return (
<div className="pull-request">
<div className="pull-request-title truncate">{pullRequest.title}</div>
<div className="repository-name text-secondary truncate">{pullRequest.repository.name}</div>
<a
className="pull-request-number text-link"
href={pullRequest.url || undefined}
target="_blank"
onClick={() => {
// Since there is a decent chance that the PR will be acted upon after the user clicks on it,
// invalidate the cache so that the PR shows up in the appropriate bucket (or not at all) the
// next time the popup is opened.
void storage.session.remove('focusViewData');
}}
>
#{pullRequest.number}
</a>
{/* <a>
<>
<div className="pull-request">
<div className="pull-request-title truncate">{pullRequest.title}</div>
<div className="repository-name text-secondary truncate">{pullRequest.repository.name}</div>
<a
className="pull-request-number text-link"
href={pullRequest.url || undefined}
target="_blank"
onClick={() => {
// Since there is a decent chance that the PR will be acted upon after the user clicks on it,
// invalidate the cache so that the PR shows up in the appropriate bucket (or not at all) the
// next time the popup is opened.
void storage.session.remove('focusViewData');
}}
>
#{pullRequest.number}
</a>
{/* <a>
<i className="fa-brands fa-gitkraken icon text-link" />
</a> */}
</div>
</div>
{draftCount > 0 && (
<a
className="pr-drafts-badge text-disabled"
href={`${GKDotDevUrl}/drafts/suggested-change/${encodeURIComponent(pullRequest.uniqueId)}`}
target="_blank"
>
<i className="fa-regular fa-message-code icon" />
Code Suggestion{draftCount === 1 ? '' : 's'}
</a>
)}
</>
);
};

const Bucket = ({ bucket }: { bucket: PullRequestBucket }) => {
type BucketProps = {
bucket: PullRequestBucketWithUniqueIDs;
prDraftCountsByEntityID: Record<string, { count: number } | undefined>;
};

const Bucket = ({ bucket, prDraftCountsByEntityID }: BucketProps) => {
return (
<div className="pull-request-bucket">
<div className="pull-request-bucket-header text-sm text-secondary bold">
<i className={`fa-regular fa-${bucket.faIconName} icon text-lg`} />
{bucket.name}
</div>
{bucket.pullRequests.map(pullRequest => (
<PullRequestRow key={pullRequest.id} pullRequest={pullRequest} />
<PullRequestRow
key={pullRequest.id}
pullRequest={pullRequest}
draftCount={prDraftCountsByEntityID[pullRequest.uniqueId]?.count}
/>
))}
</div>
);
Expand All @@ -50,6 +81,9 @@ const Bucket = ({ bucket }: { bucket: PullRequestBucket }) => {
export const FocusView = () => {
const [connectedProviders, setConnectedProviders] = useState<FocusViewSupportedProvider[]>([]);
const [selectedProvider, setSelectedProvider] = useState<FocusViewSupportedProvider>();
const [prDraftCountsByEntityID, setPRDraftCountsByEntityID] = useState<
Record<string, { count: number } | undefined>
>({});
const [pullRequestBuckets, setPullRequestBuckets] = useState<PullRequestBucket[]>();
const [isFirstLoad, setIsFirstLoad] = useState(true);
const [isLoadingPullRequests, setIsLoadingPullRequests] = useState(true);
Expand Down Expand Up @@ -129,6 +163,19 @@ export const FocusView = () => {
setPullRequestBuckets(buckets);
setIsLoadingPullRequests(false);
setIsFirstLoad(false);

if (selectedProvider === 'github' && focusViewData.pullRequests.length) {
const draftCounts = await sessionCachedFetch('focusViewDraftCounts', DefaultCacheTimeMinutes, () => {
if (!focusViewData) {
return null;
}
const prUniqueIds = focusViewData.pullRequests.map(pr => pr.uniqueId);
return fetchDraftCounts(prUniqueIds);
});
if (draftCounts) {
setPRDraftCountsByEntityID(draftCounts.counts);
}
}
};

void loadData();
Expand All @@ -147,7 +194,7 @@ export const FocusView = () => {
: pullRequestBuckets;

const onProviderChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
void storage.session.remove('focusViewData');
void storage.session.remove(['focusViewData', 'focusViewDraftCounts']);
void storage.local.set({ focusViewSelectedProvider: e.target.value });
setSelectedProvider(e.target.value as FocusViewSupportedProvider);
setFilterString('');
Expand Down Expand Up @@ -204,7 +251,13 @@ export const FocusView = () => {
</div>
) : (
<div className="pull-request-buckets">
{filteredBuckets?.map(bucket => <Bucket key={bucket.id} bucket={bucket} />)}
{filteredBuckets?.map(bucket => (
<Bucket
key={bucket.id}
bucket={bucket as PullRequestBucketWithUniqueIDs}
prDraftCountsByEntityID={prDraftCountsByEntityID}
/>
))}
</div>
)}
</div>
Expand Down
16 changes: 11 additions & 5 deletions src/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const ProviderMeta: Record<FocusViewSupportedProvider, { name: string; ic
github: { name: 'GitHub', iconSrc: 'img/github-color.svg' },
gitlab: { name: 'GitLab', iconSrc: 'img/gitlab-color.svg' },
bitbucket: { name: 'Bitbucket', iconSrc: 'img/bitbucket-color.svg' },
azure: { name: 'Azure DevOps', iconSrc: 'img/azure-color.svg' },
azure: { name: 'Azure DevOps', iconSrc: 'img/azuredevops-color.svg' },
};

const fetchGitHubFocusViewData = async (token: string) => {
Expand All @@ -21,7 +21,13 @@ const fetchGitHubFocusViewData = async (token: string) => {
username: providerUser.username,
});

return { providerUser: providerUser, pullRequests: pullRequests };
return {
providerUser: providerUser,
pullRequests: pullRequests.map(pr => ({
...pr,
uniqueId: JSON.stringify(['github', 'pr', '1', '', pr.graphQLId || pr.id]),
})),
};
};

const fetchGitLabFocusViewData = async (token: string) => {
Expand All @@ -36,7 +42,7 @@ const fetchGitLabFocusViewData = async (token: string) => {
username: providerUser.username,
});

return { providerUser: providerUser, pullRequests: pullRequests };
return { providerUser: providerUser, pullRequests: pullRequests.map(pr => ({ ...pr, uniqueId: '' })) };
};

const fetchBitbucketFocusViewData = async (token: string) => {
Expand All @@ -48,7 +54,7 @@ const fetchBitbucketFocusViewData = async (token: string) => {
userId: providerUser.id,
});

return { providerUser: providerUser, pullRequests: pullRequests };
return { providerUser: providerUser, pullRequests: pullRequests.map(pr => ({ ...pr, uniqueId: '' })) };
};

const fetchAzureFocusViewData = async (token: string) => {
Expand All @@ -68,7 +74,7 @@ const fetchAzureFocusViewData = async (token: string) => {
projects: projects.map(project => ({ ...project, project: project.name })),
});

return { providerUser: providerUser, pullRequests: pullRequests };
return { providerUser: providerUser, pullRequests: pullRequests.map(pr => ({ ...pr, uniqueId: '' })) };
};

export const fetchFocusViewData = async (provider: FocusViewSupportedProvider): Promise<FocusViewData | null> => {
Expand Down
2 changes: 1 addition & 1 deletion src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const DefaultCacheTimeMinutes = 30;
export const sessionCachedFetch = async <T>(
key: SessionCacheKey,
cacheTimeMinutes: number,
fetchFn: () => Promise<T>,
fetchFn: () => Promise<T> | T,
) => {
const sessionStorage = await storage.session.get(key);
const data = sessionStorage[key] as CachedFetchResponse<T> | undefined;
Expand Down
14 changes: 11 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Account, GitPullRequest } from '@gitkraken/provider-apis';
import type { Account, GitPullRequest, PullRequestBucket } from '@gitkraken/provider-apis';

export interface User {
email: string;
Expand All @@ -24,9 +24,17 @@ export type Provider =

export type FocusViewSupportedProvider = 'github' | 'gitlab' | 'bitbucket' | 'azure';

export type GitPullRequestWithUniqueID = GitPullRequest & { uniqueId: string };

export type PullRequestBucketWithUniqueIDs = Omit<PullRequestBucket, 'pullRequests'> & {
pullRequests: GitPullRequestWithUniqueID[];
};

export type PullRequestDraftCounts = Record<string, { count: number } | undefined>;

export type FocusViewData = {
providerUser: Account;
pullRequests: GitPullRequest[];
pullRequests: GitPullRequestWithUniqueID[];
};

export interface ProviderConnection {
Expand All @@ -50,7 +58,7 @@ export interface CacheContext {
enterpriseConnectionsCache?: EnterpriseProviderConnection[];
}

export type SessionCacheKey = 'user' | 'providerConnections' | 'focusViewData';
export type SessionCacheKey = 'user' | 'providerConnections' | 'focusViewData' | 'focusViewDraftCounts';

export type CachedFetchResponse<T> = {
data: T;
Expand Down
15 changes: 15 additions & 0 deletions static/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

--color-text-primary: #0d1017;
--color-text-secondary: #63717c;
--color-text-disabled: #63717c;
--color-link: #009e78;

--color-bg-primary: #ebebeb;
Expand Down Expand Up @@ -252,6 +253,17 @@ html {
.focus-view .pull-request-bucket .pull-request .pull-request-number {
width: 64px;
}
.focus-view .pull-request-bucket .pr-drafts-badge {
border: 1px solid var(--color-border-input);
border-radius: 12px;
padding: 4px 8px;
display: flex;
align-items: center;
width: fit-content;
}
.focus-view .pull-request-bucket .pr-drafts-badge .icon {
margin-right: 4px;
}

/* Generic components */
button.icon-btn {
Expand Down Expand Up @@ -281,6 +293,9 @@ button.icon-btn {
.text-secondary {
color: var(--color-text-secondary);
}
.text-disabled {
color: var(--color-text-disabled);
}
.text-link {
color: var(--color-link);
}
Expand Down

0 comments on commit 679e04e

Please sign in to comment.