From 4cb2136fcd961cc62fdee4f46b0df8f210061ffc Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Wed, 28 Aug 2024 08:16:00 -0400 Subject: [PATCH 01/15] feat: add bitbucket cloud support Signed-off-by: Adam Setch --- src/app.tsx | 7 +- .../settings/SettingsFooter.test.tsx | 2 +- src/components/settings/SettingsFooter.tsx | 2 +- src/context/App.tsx | 19 +++ src/routes/Accounts.test.tsx | 2 +- src/routes/Accounts.tsx | 21 ++- src/routes/Login.tsx | 15 +- src/routes/LoginWithBitbucketCloud.tsx | 137 ++++++++++++++++++ src/types.ts | 4 + src/utils/auth/types.ts | 19 ++- src/utils/auth/utils.ts | 17 ++- src/utils/constants.ts | 7 + src/utils/helpers.ts | 4 + src/utils/notifications.ts | 8 +- 14 files changed, 250 insertions(+), 14 deletions(-) create mode 100644 src/routes/LoginWithBitbucketCloud.tsx diff --git a/src/app.tsx b/src/app.tsx index ba4c4e721..c22af7d42 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -12,6 +12,7 @@ import { AppContext, AppProvider } from './context/App'; import { AccountsRoute } from './routes/Accounts'; import { FiltersRoute } from './routes/Filters'; import { LoginRoute } from './routes/Login'; +import { LoginWithBitbucketCloud } from './routes/LoginWithBitbucketCloud'; import { LoginWithOAuthApp } from './routes/LoginWithOAuthApp'; import { LoginWithPersonalAccessToken } from './routes/LoginWithPersonalAccessToken'; import { NotificationsRoute } from './routes/Notifications'; @@ -61,7 +62,7 @@ export const App = () => { } /> @@ -74,6 +75,10 @@ export const App = () => { element={} /> } /> + } + /> diff --git a/src/components/settings/SettingsFooter.test.tsx b/src/components/settings/SettingsFooter.test.tsx index 86b624f01..094a7232e 100644 --- a/src/components/settings/SettingsFooter.test.tsx +++ b/src/components/settings/SettingsFooter.test.tsx @@ -125,7 +125,7 @@ describe('routes/components/settings/SettingsFooter.tsx', () => { }); fireEvent.click(screen.getByTitle('Accounts')); - expect(mockNavigate).toHaveBeenCalledWith('/accounts'); + expect(mockNavigate).toHaveBeenCalledWith('/settings/accounts'); }); it('should quit the app', async () => { diff --git a/src/components/settings/SettingsFooter.tsx b/src/components/settings/SettingsFooter.tsx index a5fd609a4..58d4454e5 100644 --- a/src/components/settings/SettingsFooter.tsx +++ b/src/components/settings/SettingsFooter.tsx @@ -39,7 +39,7 @@ export const SettingsFooter: FC = () => { className={BUTTON_CLASS_NAME} title="Accounts" onClick={() => { - navigate('/accounts'); + navigate('/settings/accounts'); }} > diff --git a/src/context/App.tsx b/src/context/App.tsx index eb93a8e91..a56bd3478 100644 --- a/src/context/App.tsx +++ b/src/context/App.tsx @@ -15,6 +15,7 @@ import { type AuthState, type GitifyError, GroupBy, + type Hostname, OpenPreference, type SettingsState, type SettingsValue, @@ -25,6 +26,7 @@ import type { Notification } from '../typesGitHub'; import { headNotifications } from '../utils/api/client'; import { migrateAuthenticatedAccounts } from '../utils/auth/migration'; import type { + LoginBitbucketCloudOptions, LoginOAuthAppOptions, LoginPersonalAccessTokenOptions, } from '../utils/auth/types'; @@ -97,6 +99,7 @@ interface AppContextState { loginWithGitHubApp: () => void; loginWithOAuthApp: (data: LoginOAuthAppOptions) => void; loginWithPersonalAccessToken: (data: LoginPersonalAccessTokenOptions) => void; + loginWithBitbucketCloud: (data: LoginBitbucketCloudOptions) => void; logoutFromAccount: (account: Account) => void; notifications: AccountNotifications[]; @@ -242,6 +245,21 @@ export const AppProvider = ({ children }: { children: ReactNode }) => { [auth, settings], ); + const loginWithBitbucketCloud = useCallback( + async ({ token, workspace, username }: LoginBitbucketCloudOptions) => { + const updatedAuth = await addAccount( + auth, + 'App Password', + token, + `https://api.bitbucket.org/internal/workspaces/${workspace}` as Hostname, + username, + ); + setAuth(updatedAuth); + saveState({ auth: updatedAuth, settings }); + }, + [auth, settings], + ); + const logoutFromAccount = useCallback( async (account: Account) => { // Remove notifications for account @@ -320,6 +338,7 @@ export const AppProvider = ({ children }: { children: ReactNode }) => { loginWithGitHubApp, loginWithOAuthApp, loginWithPersonalAccessToken, + loginWithBitbucketCloud, logoutFromAccount, notifications, diff --git a/src/routes/Accounts.test.tsx b/src/routes/Accounts.test.tsx index 768c7f25f..b03726d17 100644 --- a/src/routes/Accounts.test.tsx +++ b/src/routes/Accounts.test.tsx @@ -196,7 +196,7 @@ describe('routes/Accounts.tsx', () => { 'token-123-456', ); await waitFor(() => - expect(mockNavigate).toHaveBeenNthCalledWith(1, '/accounts', { + expect(mockNavigate).toHaveBeenNthCalledWith(1, '/settings/accounts', { replace: true, }), ); diff --git a/src/routes/Accounts.tsx b/src/routes/Accounts.tsx index ae8ffc8b8..891c2806c 100644 --- a/src/routes/Accounts.tsx +++ b/src/routes/Accounts.tsx @@ -1,4 +1,5 @@ import { + BeakerIcon, FeedPersonIcon, KeyIcon, MarkGithubIcon, @@ -48,7 +49,7 @@ export const AccountsRoute: FC = () => { const setAsPrimaryAccount = useCallback((account: Account) => { auth.accounts = [account, ...auth.accounts.filter((a) => a !== account)]; saveState({ auth, settings }); - navigate('/accounts', { replace: true }); + navigate('/settings/accounts', { replace: true }); }, []); const loginWithGitHub = useCallback(async () => { @@ -67,6 +68,10 @@ export const AccountsRoute: FC = () => { return navigate('/login-oauth-app', { replace: true }); }, []); + const loginWithBitbucketCloud = useCallback(() => { + return navigate('/login-bitbucket-cloud', { replace: true }); + }, []); + return (
Accounts
@@ -155,7 +160,7 @@ export const AccountsRoute: FC = () => { title={`Refresh ${account.user.login}`} onClick={async () => { await refreshAccount(account); - navigate('/accounts', { replace: true }); + navigate('/settings/accounts', { replace: true }); }} > { +
diff --git a/src/routes/Login.tsx b/src/routes/Login.tsx index 2a661eb67..8e2a41767 100644 --- a/src/routes/Login.tsx +++ b/src/routes/Login.tsx @@ -1,4 +1,9 @@ -import { KeyIcon, MarkGithubIcon, PersonIcon } from '@primer/octicons-react'; +import { + BeakerIcon, + KeyIcon, + MarkGithubIcon, + PersonIcon, +} from '@primer/octicons-react'; import log from 'electron-log'; import { type FC, useCallback, useContext, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -63,6 +68,14 @@ export const LoginRoute: FC = () => { > OAuth App + ); }; diff --git a/src/routes/LoginWithBitbucketCloud.tsx b/src/routes/LoginWithBitbucketCloud.tsx new file mode 100644 index 000000000..e8675c150 --- /dev/null +++ b/src/routes/LoginWithBitbucketCloud.tsx @@ -0,0 +1,137 @@ +import { BookIcon, KeyIcon, SignInIcon } from '@primer/octicons-react'; +import log from 'electron-log'; +import { type FC, useCallback, useContext, useState } from 'react'; +import { Form, type FormRenderProps } from 'react-final-form'; +import { useNavigate } from 'react-router-dom'; +import { Header } from '../components/Header'; +import { Button } from '../components/buttons/Button'; +import { FieldInput } from '../components/fields/FieldInput'; +import { AppContext } from '../context/App'; +import { Size, type Token, type Username, type Workspace } from '../types'; +import type { LoginBitbucketCloudOptions } from '../utils/auth/types'; +import { isValidAppPassword } from '../utils/auth/utils'; +import { Constants } from '../utils/constants'; + +interface IValues { + username: Username; + token?: Token; + workspace?: Workspace; +} + +interface IFormErrors { + username?: string; + token?: string; + workspace?: string; +} + +export const validate = (values: IValues): IFormErrors => { + const errors: IFormErrors = {}; + + if (!values.username) { + errors.token = 'Required'; + } + + if (!values.token) { + errors.token = 'Required'; + } else if (!isValidAppPassword(values.token)) { + errors.token = 'Invalid app password.'; + } + + if (!values.workspace) { + errors.workspace = 'Required'; + } + + return errors; +}; + +export const LoginWithBitbucketCloud: FC = () => { + const { loginWithBitbucketCloud } = useContext(AppContext); + const navigate = useNavigate(); + const [isValidToken, setIsValidToken] = useState(true); + + const renderForm = (formProps: FormRenderProps) => { + const { handleSubmit, submitting, pristine, values } = formProps; + + return ( +
+ + + + + + + {!isValidToken && ( +
+ This token could not be validated with {values.workspace}. +
+ )} + +
+ + +
+ + ); + }; + + const login = useCallback( + async (data: IValues) => { + setIsValidToken(true); + try { + await loginWithBitbucketCloud(data as LoginBitbucketCloudOptions); + navigate(-1); + } catch (err) { + log.error('Auth: failed to login with personal access token', err); + setIsValidToken(false); + } + }, + [loginWithBitbucketCloud], + ); + + return ( + <> +
Login with Bitbucket Cloud
+ +
+
+ {renderForm} +
+
+ + ); +}; diff --git a/src/types.ts b/src/types.ts index d21ffb95f..c17a6b83f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -39,6 +39,10 @@ export type ClientSecret = Branded; export type Hostname = Branded; +export type Username = Branded; + +export type Workspace = Branded; + export type Link = Branded; export type Status = 'loading' | 'success' | 'error'; diff --git a/src/utils/auth/types.ts b/src/utils/auth/types.ts index d66bbbce4..730840d2a 100644 --- a/src/utils/auth/types.ts +++ b/src/utils/auth/types.ts @@ -4,11 +4,20 @@ import type { ClientSecret, Hostname, Token, + Username, + Workspace, } from '../../types'; -export type AuthMethod = 'GitHub App' | 'Personal Access Token' | 'OAuth App'; +export type AuthMethod = + | 'GitHub App' + | 'Personal Access Token' + | 'OAuth App' + | 'App Password'; -export type PlatformType = 'GitHub Cloud' | 'GitHub Enterprise Server'; +export type PlatformType = + | 'GitHub Cloud' + | 'GitHub Enterprise Server' + | 'Bitbucket Cloud'; export interface LoginOAuthAppOptions { hostname: Hostname; @@ -21,6 +30,12 @@ export interface LoginPersonalAccessTokenOptions { token: Token; } +export interface LoginBitbucketCloudOptions { + username: Username; + token: Token; + workspace: Workspace; +} + export interface AuthResponse { authCode: AuthCode; authOptions: LoginOAuthAppOptions; diff --git a/src/utils/auth/utils.ts b/src/utils/auth/utils.ts index d79fcbce0..8be9d7f18 100644 --- a/src/utils/auth/utils.ts +++ b/src/utils/auth/utils.ts @@ -10,6 +10,7 @@ import type { Hostname, Link, Token, + Username, } from '../../types'; import type { UserDetails } from '../../typesGitHub'; import { getAuthenticatedUser } from '../api/client'; @@ -128,6 +129,7 @@ export async function addAccount( method: AuthMethod, token: Token, hostname: Hostname, + username?: Username, ): Promise { let newAccount = { hostname: hostname, @@ -136,7 +138,16 @@ export async function addAccount( token: token, } as Account; - newAccount = await refreshAccount(newAccount); + if (newAccount.platform === 'Bitbucket Cloud') { + newAccount.user = { + id: 0, + login: username, + name: username, + avatar: null, + }; + } else { + newAccount = await refreshAccount(newAccount); + } return { accounts: [...auth.accounts, newAccount], @@ -238,6 +249,10 @@ export function isValidToken(token: Token) { return /^[A-Z0-9_]{40}$/i.test(token); } +export function isValidAppPassword(token: Token) { + return /^[A-Z0-9_]{36}$/i.test(token); +} + export function getAccountUUID(account: Account): string { return btoa(`${account.hostname}-${account.user.id}-${account.method}`); } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index eabacbaeb..ead888d29 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -11,6 +11,8 @@ export const Constants = { clientSecret: process.env.OAUTH_CLIENT_SECRET as ClientSecret, }, + BITBUCKET_API_BASE_URL: 'https://api.bitbucket.org/', + GITHUB_API_BASE_URL: 'https://api.github.com/', GITHUB_API_GRAPHQL_URL: 'https://api.github.com/graphql', @@ -35,6 +37,11 @@ export const Constants = { PARTICIPATING_URL: 'https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#about-participating-and-watching-notifications' as Link, }, + + ATLASSIAN_DOCS: { + BITBUCKET_APP_PASSWORD_URL: + 'https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/' as Link, + }, }; export const Errors: Record = { diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index eb688437d..755467da6 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -12,6 +12,10 @@ import { } from './subject'; export function getPlatformFromHostname(hostname: string): PlatformType { + if (hostname.startsWith(Constants.BITBUCKET_API_BASE_URL)) { + return 'Bitbucket Cloud'; + } + return hostname.endsWith(Constants.DEFAULT_AUTH_OPTIONS.hostname) ? 'GitHub Cloud' : 'GitHub Enterprise Server'; diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 0a2dc618f..9ea253bc6 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -114,10 +114,10 @@ function getNotifications(state: GitifyState) { return state.auth.accounts.map((account) => { return { account, - notifications: listNotificationsForAuthenticatedUser( - account, - state.settings, - ), + notifications: + account.platform === 'Bitbucket Cloud' + ? null + : listNotificationsForAuthenticatedUser(account, state.settings), }; }); } From 3fcc1f38403c2365c28ba7d1b261c237f7e0075f Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Wed, 28 Aug 2024 08:29:11 -0400 Subject: [PATCH 02/15] feat: add bitbucket cloud support Signed-off-by: Adam Setch --- src/utils/api/client.ts | 20 ++++++++++++++++- src/utils/api/request.ts | 14 ++++++++++++ src/utils/notifications.ts | 45 +++++++++++++++++++++++--------------- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/utils/api/client.ts b/src/utils/api/client.ts index a2f133e89..05ce8626f 100644 --- a/src/utils/api/client.ts +++ b/src/utils/api/client.ts @@ -24,7 +24,7 @@ import type { } from '../../typesGitHub'; import { QUERY_SEARCH_DISCUSSIONS } from './graphql/discussions'; import { formatAsGitHubSearchSyntax } from './graphql/utils'; -import { apiRequestAuth } from './request'; +import { apiRequestAuth, apiRequestBitbucket } from './request'; import { getGitHubAPIBaseUrl, getGitHubGraphQLUrl } from './utils'; /** @@ -69,6 +69,24 @@ export function listNotificationsForAuthenticatedUser( return apiRequestAuth(url.toString() as Link, 'GET', account.token); } +/** + * List all notifications for the current user, sorted by most recently updated. + * + * Endpoint documentation: https://docs.github.com/en/rest/activity/notifications#list-notifications-for-the-authenticated-user + */ +export function listBitbucketWork( + account: Account, +): AxiosPromise { + const url = `${account.hostname}/overview-view-state?fields=pullRequestFilters.*,pullRequests.authored.links.html,-pullRequests.authored.extra.*`; + + return apiRequestBitbucket( + url.toString() as Link, + 'GET', + account.user.login, + account.token, + ); +} + /** * Marks a thread as "read." Marking a thread as "read" is equivalent to * clicking a notification in your notification inbox on GitHub. diff --git a/src/utils/api/request.ts b/src/utils/api/request.ts index 03a71b175..90f955c8b 100644 --- a/src/utils/api/request.ts +++ b/src/utils/api/request.ts @@ -24,3 +24,17 @@ export function apiRequestAuth( axios.defaults.headers.common['Content-Type'] = 'application/json'; return axios({ method, url, data }); } + +export function apiRequestBitbucket( + url: Link, + method: Method, + username: string, + token: Token, + data = {}, +): AxiosPromise | null { + axios.defaults.headers.common.Accept = 'application/json'; + axios.defaults.headers.common.Authorization = `Basic ${btoa(`${username}:${token}`)}`; + axios.defaults.headers.common['Cache-Control'] = 'no-cache'; + axios.defaults.headers.common['Content-Type'] = 'application/json'; + return axios({ method, url, data }); +} diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 9ea253bc6..a50a20384 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -5,7 +5,10 @@ import type { SettingsState, } from '../types'; import { Notification } from '../typesGitHub'; -import { listNotificationsForAuthenticatedUser } from './api/client'; +import { + listBitbucketWork, + listNotificationsForAuthenticatedUser, +} from './api/client'; import { determineFailureType } from './api/errors'; import { getAccountUUID } from './auth/utils'; import { hideWindow, showWindow, updateTrayIcon } from './comms'; @@ -116,7 +119,7 @@ function getNotifications(state: GitifyState) { account, notifications: account.platform === 'Bitbucket Cloud' - ? null + ? listBitbucketWork(account) : listNotificationsForAuthenticatedUser(account, state.settings), }; }); @@ -132,22 +135,28 @@ export async function getAllNotifications( .filter((response) => !!response) .map(async (accountNotifications) => { try { - let notifications = ( - await accountNotifications.notifications - ).data.map((notification: Notification) => ({ - ...notification, - account: accountNotifications.account, - })); - - notifications = await enrichNotifications(notifications, state); - - notifications = filterNotifications(notifications, state.settings); - - return { - account: accountNotifications.account, - notifications: notifications, - error: null, - }; + if (accountNotifications.account.platform === 'Bitbucket Cloud') { + console.log( + JSON.stringify(await accountNotifications.notifications), + ); + } else { + let notifications = ( + await accountNotifications.notifications + ).data.map((notification: Notification) => ({ + ...notification, + account: accountNotifications.account, + })); + + notifications = await enrichNotifications(notifications, state); + + notifications = filterNotifications(notifications, state.settings); + + return { + account: accountNotifications.account, + notifications: notifications, + error: null, + }; + } } catch (error) { log.error( 'Error occurred while fetching account notifications', From 3734ce17da47645771caba2c09fa7a147928c17b Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Wed, 28 Aug 2024 08:40:57 -0400 Subject: [PATCH 03/15] feat: add bitbucket cloud support Signed-off-by: Adam Setch --- src/utils/notifications.ts | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index a50a20384..2c36022e5 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -135,10 +135,37 @@ export async function getAllNotifications( .filter((response) => !!response) .map(async (accountNotifications) => { try { + // TODO - this needs to be correctly implemented if (accountNotifications.account.platform === 'Bitbucket Cloud') { - console.log( - JSON.stringify(await accountNotifications.notifications), - ); + const pulls = + // biome-ignore lint/suspicious/noExplicitAny: + ((await accountNotifications.notifications).data as any) + .pullRequests.authored; + + console.log(JSON.stringify(pulls, null, 2)); + + const notifications = pulls.map((pull: any) => ({ + id: 1, + reason: 'pull_request', + repository: { + full_name: pull.links.html.href, + owner: { + avatar_url: + 'https://avatars.githubusercontent.com/u/987654321?v=4', + }, + }, + subject: { + title: pull.extra.commit_statuses[0].key, + url: pull.links.html.href, + }, + account: accountNotifications.account, + })); + + return { + account: accountNotifications.account, + notifications: notifications, + error: null, + }; } else { let notifications = ( await accountNotifications.notifications From b6535ba56385b7c2649518543e6e26c4474cff20 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Sun, 1 Sep 2024 11:49:57 -0400 Subject: [PATCH 04/15] feat: bitbucket cloud support Signed-off-by: Adam Setch --- src/utils/api/client.ts | 3 ++- src/utils/notifications.ts | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/utils/api/client.ts b/src/utils/api/client.ts index 05ce8626f..0e5af3e75 100644 --- a/src/utils/api/client.ts +++ b/src/utils/api/client.ts @@ -77,8 +77,9 @@ export function listNotificationsForAuthenticatedUser( export function listBitbucketWork( account: Account, ): AxiosPromise { - const url = `${account.hostname}/overview-view-state?fields=pullRequestFilters.*,pullRequests.authored.links.html,-pullRequests.authored.extra.*`; + const url = `${account.hostname}/overview-view-state?fields=pullRequests.reviewing.id,pullRequests.reviewing.title,pullRequest.reviewing.state,pullRequests.reviewing.author,pullRequests.reviewing.created_on,pullRequests.reviewing.updated_on,pullRequests.reviewing.links,pullRequests.reviewing.task_count,pullRequests.reviewing.comment_count,pullRequests.reviewing.destination.repository.*`; + console.log(url); return apiRequestBitbucket( url.toString() as Link, 'GET', diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 2c36022e5..2756997f2 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -140,23 +140,30 @@ export async function getAllNotifications( const pulls = // biome-ignore lint/suspicious/noExplicitAny: ((await accountNotifications.notifications).data as any) - .pullRequests.authored; + .pullRequests?.reviewing; - console.log(JSON.stringify(pulls, null, 2)); - - const notifications = pulls.map((pull: any) => ({ + const notifications = pulls?.map((pull: any) => ({ id: 1, - reason: 'pull_request', + reason: 'review_requested', + updated_at: pull.updated_on, + url: pull.links.html.href, repository: { - full_name: pull.links.html.href, + full_name: pull.destination.repository.links.html.href, owner: { - avatar_url: - 'https://avatars.githubusercontent.com/u/987654321?v=4', + avatar_url: pull.destination.repository.links.avatar.href, }, }, subject: { - title: pull.extra.commit_statuses[0].key, + number: pull.id, + title: pull.title, url: pull.links.html.href, + type: 'PullRequest', + user: { + login: pull.author.display_name, + html_url: pull.author.links.html.href, + avatar_url: pull.author.links.avatar.href, + type: 'User', + }, }, account: accountNotifications.account, })); From 812fcfc5ff182ede30a90fc41126dd33992247c4 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Mon, 2 Sep 2024 10:33:38 -0400 Subject: [PATCH 05/15] feat: bitbucket cloud support Signed-off-by: Adam Setch --- assets/icons/bitbucket.svg | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 assets/icons/bitbucket.svg diff --git a/assets/icons/bitbucket.svg b/assets/icons/bitbucket.svg new file mode 100644 index 000000000..894ed83bf --- /dev/null +++ b/assets/icons/bitbucket.svg @@ -0,0 +1,7 @@ + + \ No newline at end of file From e9405113c6259f9e573d3f3fb47107c46218801e Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Mon, 2 Sep 2024 10:45:19 -0400 Subject: [PATCH 06/15] feat: bitbucket cloud support Signed-off-by: Adam Setch --- src/utils/links.ts | 8 +++++++- src/utils/notifications.ts | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/utils/links.ts b/src/utils/links.ts index e19aaadde..46a899e55 100644 --- a/src/utils/links.ts +++ b/src/utils/links.ts @@ -57,7 +57,13 @@ export function openRepository(repository: Repository) { } export async function openNotification(notification: Notification) { - const url = await generateGitHubWebUrl(notification); + let url = '' as Link; + if (notification.account.platform === 'Bitbucket Cloud') { + url = notification.url; + } else { + url = await generateGitHubWebUrl(notification); + } + openExternalLink(url); } diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 2756997f2..be36ac8ea 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -142,22 +142,25 @@ export async function getAllNotifications( ((await accountNotifications.notifications).data as any) .pullRequests?.reviewing; + // console.log(pulls); const notifications = pulls?.map((pull: any) => ({ - id: 1, + id: `${pull.destination.repository.full_name}-${pull.id}`, reason: 'review_requested', updated_at: pull.updated_on, url: pull.links.html.href, repository: { - full_name: pull.destination.repository.links.html.href, + full_name: pull.destination.repository.full_name, owner: { avatar_url: pull.destination.repository.links.avatar.href, }, + html_url: pull.destination.repository.links.html.href, }, subject: { number: pull.id, title: pull.title, url: pull.links.html.href, type: 'PullRequest', + state: 'open', user: { login: pull.author.display_name, html_url: pull.author.links.html.href, From c26e9e8f96d21dcb3d96a4dc2100ea656a42aa3a Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Mon, 2 Sep 2024 10:49:04 -0400 Subject: [PATCH 07/15] feat: bitbucket cloud support Signed-off-by: Adam Setch --- src/components/NotificationRow.tsx | 7 ++- src/components/RepositoryNotifications.tsx | 57 +++++++++++----------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx index 1f0752f22..c1819b8cc 100644 --- a/src/components/NotificationRow.tsx +++ b/src/components/NotificationRow.tsx @@ -45,6 +45,11 @@ export const NotificationRow: FC = ({ const [showAsRead, setShowAsRead] = useState(false); const handleNotification = useCallback(() => { + if (notification.account.platform === 'Bitbucket Cloud') { + openNotification(notification); + return; + } + setAnimateExit(!settings.delayNotificationState); setShowAsRead(settings.delayNotificationState); @@ -127,7 +132,7 @@ export const NotificationRow: FC = ({ - {!animateExit && ( + {!animateExit && notification.account.platform !== 'Bitbucket Cloud' && ( {isMarkAsDoneFeatureSupported(notification.account) && ( = ({ - {!animateExit && ( - - {isMarkAsDoneFeatureSupported(repoNotifications[0].account) && ( + {!animateExit && + repoNotifications[0].account.platform !== 'Bitbucket Cloud' && ( + + {isMarkAsDoneFeatureSupported(repoNotifications[0].account) && ( + ) => { + // Don't trigger onClick of parent element. + event.stopPropagation(); + setAnimateExit(!settings.delayNotificationState); + setShowAsRead(settings.delayNotificationState); + markRepoNotificationsDone(repoNotifications[0]); + }} + /> + )} ) => { // Don't trigger onClick of parent element. event.stopPropagation(); setAnimateExit(!settings.delayNotificationState); setShowAsRead(settings.delayNotificationState); - markRepoNotificationsDone(repoNotifications[0]); + markRepoNotificationsRead(repoNotifications[0]); }} /> - )} - ) => { - // Don't trigger onClick of parent element. - event.stopPropagation(); - setAnimateExit(!settings.delayNotificationState); - setShowAsRead(settings.delayNotificationState); - markRepoNotificationsRead(repoNotifications[0]); - }} - /> - - - )} + + + )} {showRepositoryNotifications && From 535b37f2cadb38311283fec2b3d71cd177511602 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Mon, 2 Sep 2024 10:51:59 -0400 Subject: [PATCH 08/15] feat: bitbucket cloud support Signed-off-by: Adam Setch --- src/components/icons/PlatformIcon.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/icons/PlatformIcon.tsx b/src/components/icons/PlatformIcon.tsx index 7fb50501c..824ef5c46 100644 --- a/src/components/icons/PlatformIcon.tsx +++ b/src/components/icons/PlatformIcon.tsx @@ -1,4 +1,4 @@ -import { MarkGithubIcon, ServerIcon } from '@primer/octicons-react'; +import { BeakerIcon, MarkGithubIcon, ServerIcon } from '@primer/octicons-react'; import type { FC } from 'react'; import type { Size } from '../../types'; import type { PlatformType } from '../../utils/auth/types'; @@ -15,6 +15,7 @@ export const PlatformIcon: FC = (props: IPlatformIcon) => { {props.type === 'GitHub Enterprise Server' && ( )} + {props.type === 'Bitbucket Cloud' && } ); }; From 9471f6295ff660e207bc4c681b26184c86b69a35 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Mon, 2 Sep 2024 10:54:18 -0400 Subject: [PATCH 09/15] feat: bitbucket cloud support Signed-off-by: Adam Setch --- src/utils/links.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/utils/links.ts b/src/utils/links.ts index 46a899e55..94a9e2faf 100644 --- a/src/utils/links.ts +++ b/src/utils/links.ts @@ -34,9 +34,13 @@ export function openGitHubPulls(hostname: Hostname) { } export function openAccountProfile(account: Account) { - const url = new URL(`https://${account.hostname}`); - url.pathname = account.user.login; - openExternalLink(url.toString() as Link); + if (account.platform === 'Bitbucket Cloud') { + openExternalLink('https://bitbucket.org/account/settings/' as Link); + } else { + const url = new URL(`https://${account.hostname}`); + url.pathname = account.user.login; + openExternalLink(url.toString() as Link); + } } export function openUserProfile(user: SubjectUser) { From b03d53e7adbb29745741f7f46b8dd3b8cfa2a27c Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Mon, 2 Sep 2024 11:01:52 -0400 Subject: [PATCH 10/15] feat: bitbucket cloud support Signed-off-by: Adam Setch --- src/components/AccountNotifications.tsx | 24 +++++++++++++++++------- src/routes/LoginWithBitbucketCloud.tsx | 1 + src/utils/links.ts | 7 +++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/components/AccountNotifications.tsx b/src/components/AccountNotifications.tsx index 6ed8afa8d..759aeecdc 100644 --- a/src/components/AccountNotifications.tsx +++ b/src/components/AccountNotifications.tsx @@ -13,6 +13,7 @@ import type { Notification } from '../typesGitHub'; import { cn } from '../utils/cn'; import { openAccountProfile, + openBitbucketPulls, openGitHubIssues, openGitHubPulls, } from '../utils/links'; @@ -115,17 +116,26 @@ export const AccountNotifications: FC = ( - openGitHubIssues(account.hostname)} - /> + {account.platform !== 'Bitbucket Cloud' && ( + openGitHubIssues(account.hostname)} + /> + )} openGitHubPulls(account.hostname)} + onClick={() => { + event.stopPropagation(); + if (account.platform === 'Bitbucket Cloud') { + openBitbucketPulls(account); + } else { + openGitHubPulls(account.hostname); + } + }} /> { const renderForm = (formProps: FormRenderProps) => { const { handleSubmit, submitting, pristine, values } = formProps; + // TODO - Correctly set account.id and account.hostname return (
Date: Mon, 2 Sep 2024 11:04:33 -0400 Subject: [PATCH 11/15] feat: bitbucket cloud support Signed-off-by: Adam Setch --- src/components/Sidebar.tsx | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 6391d205f..b6edf5ebc 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -15,6 +15,7 @@ import { quitApp } from '../utils/comms'; import Constants from '../utils/constants'; import { getFilterCount } from '../utils/helpers'; import { + openBitbucketPulls, openGitHubIssues, openGitHubNotifications, openGitHubPulls, @@ -38,8 +39,10 @@ export const Sidebar: FC = () => { } = useContext(AppContext); // We naively assume that the first account is the primary account for the purposes of our sidebar quick links + const primaryAccount = auth.accounts[0]; + const primaryAccountHostname = - auth.accounts[0]?.hostname ?? Constants.DEFAULT_AUTH_OPTIONS.hostname; + primaryAccount?.hostname ?? Constants.DEFAULT_AUTH_OPTIONS.hostname; const toggleFilters = () => { if (location.pathname.startsWith('/filters')) { @@ -91,16 +94,24 @@ export const Sidebar: FC = () => { onClick={() => openGitHubNotifications(primaryAccountHostname)} /> - openGitHubIssues(primaryAccountHostname)} - /> + {primaryAccount?.platform !== 'Bitbucket Cloud' && ( + openGitHubIssues(primaryAccountHostname)} + /> + )} openGitHubPulls(primaryAccountHostname)} + onClick={() => { + if (primaryAccount?.platform === 'Bitbucket Cloud') { + openBitbucketPulls(primaryAccount); + } else { + openGitHubPulls(primaryAccountHostname); + } + }} /> From 127c328f23481e8844734caa604cc5c84b78f82f Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Tue, 3 Sep 2024 07:36:39 -0400 Subject: [PATCH 12/15] feat: bitbucket cloud support add bitbucket icon, implement account refresh Signed-off-by: Adam Setch --- src/components/icons/AuthMethodIcon.tsx | 1 + src/components/icons/BitbucketIcon.tsx | 50 +++++++++++++++++++++++++ src/components/icons/PlatformIcon.tsx | 5 ++- src/components/notification/Pills.tsx | 13 +++++++ src/context/App.tsx | 2 +- src/routes/Accounts.tsx | 23 ++++++++---- src/typesGitHub.ts | 1 + src/utils/api/bitbucket.ts | 38 +++++++++++++++++++ src/utils/api/client.ts | 21 +---------- src/utils/auth/utils.ts | 44 +++++++++++++++++----- src/utils/notifications.ts | 10 ++--- 11 files changed, 162 insertions(+), 46 deletions(-) create mode 100644 src/components/icons/BitbucketIcon.tsx create mode 100644 src/utils/api/bitbucket.ts diff --git a/src/components/icons/AuthMethodIcon.tsx b/src/components/icons/AuthMethodIcon.tsx index 90251bd79..aff914c5e 100644 --- a/src/components/icons/AuthMethodIcon.tsx +++ b/src/components/icons/AuthMethodIcon.tsx @@ -14,6 +14,7 @@ export const AuthMethodIcon: FC = (props: IAuthMethodIcon) => { {props.type === 'GitHub App' && } {props.type === 'Personal Access Token' && } {props.type === 'OAuth App' && } + {props.type === 'App Password' && } ); }; diff --git a/src/components/icons/BitbucketIcon.tsx b/src/components/icons/BitbucketIcon.tsx new file mode 100644 index 000000000..dadc1f763 --- /dev/null +++ b/src/components/icons/BitbucketIcon.tsx @@ -0,0 +1,50 @@ +import type { FC } from 'react'; +import { Size } from '../../types'; +import { cn } from '../../utils/cn'; + +interface IBitbucketIcon { + onClick?: () => void; + size: Size; +} + +export const BitbucketIcon: FC = ({ + onClick, + size = Size.MEDIUM, + ...props +}: IBitbucketIcon) => ( + onClick?.()} + xmlns="https://www.w3.org/2000/svg" + xmlnsXlink="https://www.w3.org/1999/xlink" + viewBox="0 0 512 512" + role="img" + aria-label="Bitbucket Cloud" + {...props} + > + + + + + + + + +); diff --git a/src/components/icons/PlatformIcon.tsx b/src/components/icons/PlatformIcon.tsx index 824ef5c46..d33b71d06 100644 --- a/src/components/icons/PlatformIcon.tsx +++ b/src/components/icons/PlatformIcon.tsx @@ -1,7 +1,8 @@ -import { BeakerIcon, MarkGithubIcon, ServerIcon } from '@primer/octicons-react'; +import { MarkGithubIcon, ServerIcon } from '@primer/octicons-react'; import type { FC } from 'react'; import type { Size } from '../../types'; import type { PlatformType } from '../../utils/auth/types'; +import { BitbucketIcon } from './BitbucketIcon'; export interface IPlatformIcon { type: PlatformType; @@ -15,7 +16,7 @@ export const PlatformIcon: FC = (props: IPlatformIcon) => { {props.type === 'GitHub Enterprise Server' && ( )} - {props.type === 'Bitbucket Cloud' && } + {props.type === 'Bitbucket Cloud' && } ); }; diff --git a/src/components/notification/Pills.tsx b/src/components/notification/Pills.tsx index 5b6f877da..0519ce9cb 100644 --- a/src/components/notification/Pills.tsx +++ b/src/components/notification/Pills.tsx @@ -1,4 +1,5 @@ import { + CheckboxIcon, CommentIcon, IssueClosedIcon, MilestoneIcon, @@ -24,6 +25,10 @@ export const Pills: FC = ({ notification }: IPills) => { notification.subject.comments > 1 ? 'comments' : 'comment' }`; + const tasksPillDescription = `${notification.subject.tasks} ${ + notification.subject.tasks > 1 ? 'tasks' : 'task' + }`; + const labelsPillDescription = notification.subject.labels ?.map((label) => `🏷️ ${label}`) .join('\n'); @@ -87,6 +92,14 @@ export const Pills: FC = ({ notification }: IPills) => { } /> )} + {notification.subject?.tasks > 0 && ( + + )} ) ); diff --git a/src/context/App.tsx b/src/context/App.tsx index a56bd3478..bb32ee388 100644 --- a/src/context/App.tsx +++ b/src/context/App.tsx @@ -251,7 +251,7 @@ export const AppProvider = ({ children }: { children: ReactNode }) => { auth, 'App Password', token, - `https://api.bitbucket.org/internal/workspaces/${workspace}` as Hostname, + `https://api.bitbucket.org/internal/workspaces/${workspace}` as Hostname, // TODO - ideally we don't set it like this username, ); setAuth(updatedAuth); diff --git a/src/routes/Accounts.tsx b/src/routes/Accounts.tsx index 891c2806c..48f8eeb84 100644 --- a/src/routes/Accounts.tsx +++ b/src/routes/Accounts.tsx @@ -1,5 +1,4 @@ import { - BeakerIcon, FeedPersonIcon, KeyIcon, MarkGithubIcon, @@ -17,6 +16,7 @@ import { useNavigate } from 'react-router-dom'; import { Header } from '../components/Header'; import { AuthMethodIcon } from '../components/icons/AuthMethodIcon'; import { AvatarIcon } from '../components/icons/AvatarIcon'; +import { BitbucketIcon } from '../components/icons/BitbucketIcon'; import { PlatformIcon } from '../components/icons/PlatformIcon'; import { AppContext } from '../context/App'; import { BUTTON_CLASS_NAME } from '../styles/gitify'; @@ -112,8 +112,13 @@ export const AccountsRoute: FC = () => { title="Open Host" onClick={() => openHost(account.hostname)} > - - {account.hostname} +
+ + {account.hostname.split('/').pop()} +
@@ -227,11 +232,13 @@ export const AccountsRoute: FC = () => { title="Login with Bitbucket Cloud" onClick={loginWithBitbucketCloud} > - - +
+ + +
diff --git a/src/typesGitHub.ts b/src/typesGitHub.ts index a9ebf20fc..1d67e6715 100644 --- a/src/typesGitHub.ts +++ b/src/typesGitHub.ts @@ -268,6 +268,7 @@ export interface GitifySubject { reviews?: GitifyPullRequestReview[]; linkedIssues?: string[]; comments?: number; + tasks?: number; labels?: string[]; milestone?: Milestone; } diff --git a/src/utils/api/bitbucket.ts b/src/utils/api/bitbucket.ts new file mode 100644 index 000000000..17d54a1dd --- /dev/null +++ b/src/utils/api/bitbucket.ts @@ -0,0 +1,38 @@ +import type { AxiosPromise } from 'axios'; +import type { Account, Link } from '../../types'; +import type { Notification, UserDetails } from '../../typesGitHub'; +import { apiRequestBitbucket } from './request'; +/** + * List all notifications for the current user, sorted by most recently updated. + * + * Endpoint documentation: https://docs.github.com/en/rest/activity/notifications#list-notifications-for-the-authenticated-user + */ +// TODO - Correct types +export function listBitbucketWork( + account: Account, +): AxiosPromise { + const url = `${account.hostname}/overview-view-state?fields=pullRequests.reviewing.id,pullRequests.reviewing.title,pullRequest.reviewing.state,pullRequests.reviewing.author,pullRequests.reviewing.created_on,pullRequests.reviewing.updated_on,pullRequests.reviewing.links,pullRequests.reviewing.task_count,pullRequests.reviewing.comment_count,pullRequests.reviewing.destination.repository.*`; + + return apiRequestBitbucket( + url.toString() as Link, + 'GET', + account.user.login, + account.token, + ); +} + +/** + * Get the authenticated user + * + * Endpoint documentation: https://docs.github.com/en/rest/users/users#get-the-authenticated-user + */ +export function getBitbucketUser(account: Account): AxiosPromise { + const url = 'https://api.bitbucket.org/2.0/user'; + + return apiRequestBitbucket( + url.toString() as Link, + 'GET', + account.user.login, + account.token, + ); +} diff --git a/src/utils/api/client.ts b/src/utils/api/client.ts index 0e5af3e75..a2f133e89 100644 --- a/src/utils/api/client.ts +++ b/src/utils/api/client.ts @@ -24,7 +24,7 @@ import type { } from '../../typesGitHub'; import { QUERY_SEARCH_DISCUSSIONS } from './graphql/discussions'; import { formatAsGitHubSearchSyntax } from './graphql/utils'; -import { apiRequestAuth, apiRequestBitbucket } from './request'; +import { apiRequestAuth } from './request'; import { getGitHubAPIBaseUrl, getGitHubGraphQLUrl } from './utils'; /** @@ -69,25 +69,6 @@ export function listNotificationsForAuthenticatedUser( return apiRequestAuth(url.toString() as Link, 'GET', account.token); } -/** - * List all notifications for the current user, sorted by most recently updated. - * - * Endpoint documentation: https://docs.github.com/en/rest/activity/notifications#list-notifications-for-the-authenticated-user - */ -export function listBitbucketWork( - account: Account, -): AxiosPromise { - const url = `${account.hostname}/overview-view-state?fields=pullRequests.reviewing.id,pullRequests.reviewing.title,pullRequest.reviewing.state,pullRequests.reviewing.author,pullRequests.reviewing.created_on,pullRequests.reviewing.updated_on,pullRequests.reviewing.links,pullRequests.reviewing.task_count,pullRequests.reviewing.comment_count,pullRequests.reviewing.destination.repository.*`; - - console.log(url); - return apiRequestBitbucket( - url.toString() as Link, - 'GET', - account.user.login, - account.token, - ); -} - /** * Marks a thread as "read." Marking a thread as "read" is equivalent to * clicking a notification in your notification inbox on GitHub. diff --git a/src/utils/auth/utils.ts b/src/utils/auth/utils.ts index 8be9d7f18..4e0fad3c2 100644 --- a/src/utils/auth/utils.ts +++ b/src/utils/auth/utils.ts @@ -13,6 +13,7 @@ import type { Username, } from '../../types'; import type { UserDetails } from '../../typesGitHub'; +import { getBitbucketUser } from '../api/bitbucket'; import { getAuthenticatedUser } from '../api/client'; import { apiRequest } from '../api/request'; import { Constants } from '../constants'; @@ -138,16 +139,7 @@ export async function addAccount( token: token, } as Account; - if (newAccount.platform === 'Bitbucket Cloud') { - newAccount.user = { - id: 0, - login: username, - name: username, - avatar: null, - }; - } else { - newAccount = await refreshAccount(newAccount); - } + newAccount = await refreshAccount(newAccount); return { accounts: [...auth.accounts, newAccount], @@ -165,6 +157,38 @@ export function removeAccount(auth: AuthState, account: Account): AuthState { } export async function refreshAccount(account: Account): Promise { + if (account.platform === 'Bitbucket Cloud') { + return refreshBitbucketAccount(account); + } + + return refreshGitHubAccount(account); +} + +export async function refreshBitbucketAccount( + account: Account, +): Promise { + try { + // TODO correctly type + // biome-ignore lint/suspicious/noExplicitAny: + const res: any = await getBitbucketUser(account); + + console.log(JSON.stringify(res)); + + // Refresh user data + account.user = { + id: res.data.account_id, + login: res.data.username, + name: res.data.display_name, + avatar: res.data.links.avatar.href, + }; + } catch (error) { + log.error('Failed to refresh account', error); + } + + return account; +} + +export async function refreshGitHubAccount(account: Account): Promise { try { const res = await getAuthenticatedUser(account.hostname, account.token); diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index be36ac8ea..5350d7d10 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -5,10 +5,8 @@ import type { SettingsState, } from '../types'; import { Notification } from '../typesGitHub'; -import { - listBitbucketWork, - listNotificationsForAuthenticatedUser, -} from './api/client'; +import { listBitbucketWork } from './api/bitbucket'; +import { listNotificationsForAuthenticatedUser } from './api/client'; import { determineFailureType } from './api/errors'; import { getAccountUUID } from './auth/utils'; import { hideWindow, showWindow, updateTrayIcon } from './comms'; @@ -142,7 +140,7 @@ export async function getAllNotifications( ((await accountNotifications.notifications).data as any) .pullRequests?.reviewing; - // console.log(pulls); + console.log(pulls); const notifications = pulls?.map((pull: any) => ({ id: `${pull.destination.repository.full_name}-${pull.id}`, reason: 'review_requested', @@ -167,6 +165,8 @@ export async function getAllNotifications( avatar_url: pull.author.links.avatar.href, type: 'User', }, + comments: pull.comment_count, + tasks: pull.task_count, }, account: accountNotifications.account, })); From e9d505aa18a26d20869a113d52ff65b31743305f Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Tue, 3 Sep 2024 07:57:23 -0400 Subject: [PATCH 13/15] feat: bitbucket cloud support Signed-off-by: Adam Setch --- src/components/AccountNotifications.tsx | 9 +++++++-- src/utils/auth/utils.ts | 2 -- src/utils/notifications.ts | 13 ++++++++----- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/AccountNotifications.tsx b/src/components/AccountNotifications.tsx index 759aeecdc..c0f30a0a3 100644 --- a/src/components/AccountNotifications.tsx +++ b/src/components/AccountNotifications.tsx @@ -121,14 +121,19 @@ export const AccountNotifications: FC = ( title="My Issues" icon={IssueOpenedIcon} size={Size.SMALL} - onClick={() => openGitHubIssues(account.hostname)} + onClick={(event: MouseEvent) => { + // Don't trigger onClick of parent element. + event.stopPropagation(); + openGitHubIssues(account.hostname); + }} /> )} { + onClick={(event: MouseEvent) => { + // Don't trigger onClick of parent element. event.stopPropagation(); if (account.platform === 'Bitbucket Cloud') { openBitbucketPulls(account); diff --git a/src/utils/auth/utils.ts b/src/utils/auth/utils.ts index 4e0fad3c2..bc7cbf4d0 100644 --- a/src/utils/auth/utils.ts +++ b/src/utils/auth/utils.ts @@ -172,8 +172,6 @@ export async function refreshBitbucketAccount( // biome-ignore lint/suspicious/noExplicitAny: const res: any = await getBitbucketUser(account); - console.log(JSON.stringify(res)); - // Refresh user data account.user = { id: res.data.account_id, diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 5350d7d10..da5ef0847 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -135,12 +135,15 @@ export async function getAllNotifications( try { // TODO - this needs to be correctly implemented if (accountNotifications.account.platform === 'Bitbucket Cloud') { - const pulls = - // biome-ignore lint/suspicious/noExplicitAny: - ((await accountNotifications.notifications).data as any) - .pullRequests?.reviewing; + // biome-ignore lint/suspicious/noExplicitAny: + const res = (await accountNotifications.notifications).data as any; - console.log(pulls); + // TODO - when using IP allowlists, Bitbucket doesn't return any response indicator + + const pulls = res.pullRequests?.reviewing; + + // console.log(JSON.stringify(pulls)); + // biome-ignore lint/suspicious/noExplicitAny: const notifications = pulls?.map((pull: any) => ({ id: `${pull.destination.repository.full_name}-${pull.id}`, reason: 'review_requested', From 64df440edd4dce46eadf019a9f1d7a09e170a2ff Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Tue, 3 Sep 2024 12:12:30 -0400 Subject: [PATCH 14/15] feat: :zap: ts study group demo time Signed-off-by: Adam Setch --- src/routes/Login.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/routes/Login.tsx b/src/routes/Login.tsx index 8e2a41767..208fd44a8 100644 --- a/src/routes/Login.tsx +++ b/src/routes/Login.tsx @@ -1,13 +1,9 @@ -import { - BeakerIcon, - KeyIcon, - MarkGithubIcon, - PersonIcon, -} from '@primer/octicons-react'; +import { KeyIcon, MarkGithubIcon, PersonIcon } from '@primer/octicons-react'; import log from 'electron-log'; import { type FC, useCallback, useContext, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { Button } from '../components/buttons/Button'; +import { BitbucketIcon } from '../components/icons/BitbucketIcon'; import { LogoIcon } from '../components/icons/LogoIcon'; import { AppContext } from '../context/App'; import { Size } from '../types'; @@ -69,12 +65,11 @@ export const LoginRoute: FC = () => { OAuth App ); From a61df49590c91e34e3e0e9cb141348162cc56e50 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Wed, 4 Sep 2024 09:53:20 -0400 Subject: [PATCH 15/15] feat: bitbucket cloud support Signed-off-by: Adam Setch --- .../__snapshots__/Accounts.test.tsx.snap | 184 +++++++++++++----- src/routes/__snapshots__/Login.test.tsx.snap | 92 +++++++++ src/utils/auth/utils.ts | 10 + 3 files changed, 232 insertions(+), 54 deletions(-) diff --git a/src/routes/__snapshots__/Accounts.test.tsx.snap b/src/routes/__snapshots__/Accounts.test.tsx.snap index 4da1a9b83..6539c5bd2 100644 --- a/src/routes/__snapshots__/Accounts.test.tsx.snap +++ b/src/routes/__snapshots__/Accounts.test.tsx.snap @@ -94,26 +94,30 @@ exports[`routes/Accounts.tsx General should render itself & its children 1`] = ` title="Open Host" type="button" > - - - - github.com + + + github.com +
@@ -267,26 +271,30 @@ exports[`routes/Accounts.tsx General should render itself & its children 1`] = ` title="Open Host" type="button" > - - - - github.gitify.io + + + github.gitify.io +
@@ -440,26 +448,30 @@ exports[`routes/Accounts.tsx General should render itself & its children 1`] = ` title="Open Host" type="button" > - - - - github.com + + + github.com +
@@ -695,6 +707,70 @@ exports[`routes/Accounts.tsx General should render itself & its children 1`] = ` /> +
diff --git a/src/routes/__snapshots__/Login.test.tsx.snap b/src/routes/__snapshots__/Login.test.tsx.snap index e3c8e0754..d661efa35 100644 --- a/src/routes/__snapshots__/Login.test.tsx.snap +++ b/src/routes/__snapshots__/Login.test.tsx.snap @@ -128,6 +128,52 @@ exports[`routes/Login.tsx should render itself & its children 1`] = ` OAuth App + , @@ -255,6 +301,52 @@ exports[`routes/Login.tsx should render itself & its children 1`] = ` OAuth App + , "debug": [Function], diff --git a/src/utils/auth/utils.ts b/src/utils/auth/utils.ts index bc7cbf4d0..8a64d78d6 100644 --- a/src/utils/auth/utils.ts +++ b/src/utils/auth/utils.ts @@ -139,6 +139,16 @@ export async function addAccount( token: token, } as Account; + // TODO - find a better way to pass the username through + if (username) { + newAccount.user = { + id: 0, + login: username, + name: username, + avatar: '' as Link, + }; + } + newAccount = await refreshAccount(newAccount); return {