diff --git a/plugins/circleci/src/api/index.ts b/plugins/circleci/src/api/index.ts
index d98f511f35..f683f79ff9 100644
--- a/plugins/circleci/src/api/index.ts
+++ b/plugins/circleci/src/api/index.ts
@@ -48,8 +48,15 @@ export class CircleCIApi {
});
}
- async getBuilds(options: CircleCIOptions) {
+ async getBuilds(
+ { limit = 10, offset = 0 }: { limit: number; offset: number },
+ options: CircleCIOptions,
+ ) {
return getBuildSummaries(options.token, {
+ options: {
+ limit,
+ offset,
+ },
vcs: {},
circleHost: this.apiUrl,
...options,
diff --git a/plugins/circleci/src/components/App.tsx b/plugins/circleci/src/components/App.tsx
index 3324753b88..46b70cb500 100644
--- a/plugins/circleci/src/components/App.tsx
+++ b/plugins/circleci/src/components/App.tsx
@@ -16,20 +16,24 @@
import React from 'react';
import { Switch, Route } from 'react-router';
import { BuildsPage } from '../pages/BuildsPage';
-import { SettingsPage } from '../pages/SettingsPage';
import { DetailedViewPage } from '../pages/BuildWithStepsPage';
import { AppStateProvider } from '../state';
+import { Settings } from './Settings';
-export const App = () => (
-
-
-
-
-
-
-
-);
+export const App = () => {
+ return (
+
+ <>
+
+
+
+
+
+ >
+
+ );
+};
diff --git a/plugins/circleci/src/components/Layout/Layout.tsx b/plugins/circleci/src/components/Layout/Layout.tsx
index eab8ab0e88..2028e1533d 100644
--- a/plugins/circleci/src/components/Layout/Layout.tsx
+++ b/plugins/circleci/src/components/Layout/Layout.tsx
@@ -19,20 +19,22 @@ import { Header, Page, pageTheme, HeaderLabel } from '@backstage/core';
import logo from '../../assets/circle-logo-badge-white-15.png';
import { Box } from '@material-ui/core';
-export const Layout: React.FC = ({ children }) => (
-
-
-
- Circle CI
-
- }
- >
-
-
-
- {children}
-
-);
+export const Layout: React.FC = ({ children }) => {
+ return (
+
+
+
+ Circle CI
+
+ }
+ >
+
+
+
+ {children}
+
+ );
+};
diff --git a/plugins/circleci/src/components/PluginHeader/PluginHeader.tsx b/plugins/circleci/src/components/PluginHeader/PluginHeader.tsx
index 7bd35d0805..4bab4b5921 100644
--- a/plugins/circleci/src/components/PluginHeader/PluginHeader.tsx
+++ b/plugins/circleci/src/components/PluginHeader/PluginHeader.tsx
@@ -19,9 +19,11 @@ import { ContentHeader, SupportButton } from '@backstage/core';
import { Button, IconButton, Box, Typography } from '@material-ui/core';
import ArrowBack from '@material-ui/icons/ArrowBack';
import SettingsIcon from '@material-ui/icons/Settings';
+import { useSettings } from '../../state';
export type Props = { title?: string };
export const PluginHeader: FC = ({ title = 'Circle CI' }) => {
+ const [, { showSettings }] = useSettings();
const location = useLocation();
const notRoot = !location.pathname.match(/\/circleci\/?$/);
const isSettingsPage = location.pathname.match(/\/circleci\/settings\/?/);
@@ -40,11 +42,7 @@ export const PluginHeader: FC = ({ title = 'Circle CI' }) => {
)}
>
{!isSettingsPage && (
- }
- >
+ }>
Settings
)}
diff --git a/plugins/circleci/src/components/Settings/Settings.tsx b/plugins/circleci/src/components/Settings/Settings.tsx
new file mode 100644
index 0000000000..b33bf59dd8
--- /dev/null
+++ b/plugins/circleci/src/components/Settings/Settings.tsx
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React, { useState } from 'react';
+import {
+ Button,
+ TextField,
+ List,
+ ListItem,
+ Snackbar,
+ Box,
+ Dialog,
+ DialogTitle,
+} from '@material-ui/core';
+import { Alert } from '@material-ui/lab';
+import { useSettings } from '../../state';
+
+const Settings = () => {
+ const [
+ {
+ repo: repoFromStore,
+ owner: ownerFromStore,
+ token: tokenFromStore,
+ showSettings,
+ },
+ { saveSettings, hideSettings },
+ ] = useSettings();
+
+ const [token, setToken] = React.useState(() => tokenFromStore);
+ const [owner, setOwner] = React.useState(() => ownerFromStore);
+ const [repo, setRepo] = React.useState(() => repoFromStore);
+
+ React.useEffect(() => {
+ if (tokenFromStore !== token) {
+ setToken(tokenFromStore);
+ }
+ if (ownerFromStore !== owner) {
+ setOwner(ownerFromStore);
+ }
+ if (repoFromStore !== repo) {
+ setRepo(repoFromStore);
+ }
+ }, [ownerFromStore, repoFromStore, tokenFromStore]);
+
+ const [saved, setSaved] = useState(false);
+
+ return (
+ <>
+ setSaved(false)}
+ >
+ Credentials saved.
+
+
+ >
+ );
+};
+
+export default Settings;
diff --git a/plugins/circleci/src/pages/SettingsPage/index.ts b/plugins/circleci/src/components/Settings/index.ts
similarity index 91%
rename from plugins/circleci/src/pages/SettingsPage/index.ts
rename to plugins/circleci/src/components/Settings/index.ts
index e0ba8157b2..c04ded6dea 100644
--- a/plugins/circleci/src/pages/SettingsPage/index.ts
+++ b/plugins/circleci/src/components/Settings/index.ts
@@ -13,4 +13,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-export { default as SettingsPage } from './SettingsPage';
+export { default as Settings } from './Settings';
diff --git a/plugins/circleci/src/pages/BuildWithStepsPage/BuildWithStepsPage.tsx b/plugins/circleci/src/pages/BuildWithStepsPage/BuildWithStepsPage.tsx
index 7dbe5ef393..0a4dfd4050 100644
--- a/plugins/circleci/src/pages/BuildWithStepsPage/BuildWithStepsPage.tsx
+++ b/plugins/circleci/src/pages/BuildWithStepsPage/BuildWithStepsPage.tsx
@@ -15,7 +15,7 @@
*/
import React, { FC, useEffect } from 'react';
import { useParams } from 'react-router-dom';
-import { Content, InfoCard } from '@backstage/core';
+import { Content, InfoCard, Progress } from '@backstage/core';
import { BuildWithSteps, BuildStepAction } from '../../api';
import { Grid, Box, Link, IconButton } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
@@ -26,7 +26,7 @@ import { useBuildWithSteps, useSettings } from '../../state';
import LaunchIcon from '@material-ui/icons/Launch';
const IconLink = IconButton as typeof Link;
-const BuildName: FC<{ build: BuildWithSteps | null }> = ({ build }) => (
+const BuildName: FC<{ build?: BuildWithSteps }> = ({ build }) => (
#{build?.build_num} - {build?.subject}
@@ -95,7 +95,7 @@ const BuildWithStepsPage: FC<{}> = () => {
const { buildId = '' } = useParams();
const classes = useStyles();
const [settings] = useSettings();
- const [build, { startPolling, stopPolling }] = useBuildWithSteps(
+ const [{ loading, value }, { startPolling, stopPolling }] = useBuildWithSteps(
parseInt(buildId, 10),
);
@@ -112,11 +112,11 @@ const BuildWithStepsPage: FC<{}> = () => {
}
+ className={pickClassName(classes, value)}
+ title={}
cardClassName={classes.cardContent}
>
-
+ {loading ? : }
@@ -125,7 +125,7 @@ const BuildWithStepsPage: FC<{}> = () => {
);
};
-const BuildsList: FC<{ build: BuildWithSteps | null }> = ({ build }) => (
+const BuildsList: FC<{ build?: BuildWithSteps }> = ({ build }) => (
{build &&
build.steps &&
diff --git a/plugins/circleci/src/pages/BuildsPage/lib/Builds/Builds.tsx b/plugins/circleci/src/pages/BuildsPage/lib/Builds/Builds.tsx
index 41001c91dd..bed96bb24c 100644
--- a/plugins/circleci/src/pages/BuildsPage/lib/Builds/Builds.tsx
+++ b/plugins/circleci/src/pages/BuildsPage/lib/Builds/Builds.tsx
@@ -13,73 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React, { FC, useEffect } from 'react';
-import { CITableBuildInfo, CITable } from '../CITable';
-import { BuildSummary } from '../../../../api';
-import { useSettings, useBuilds } from '../../../../state';
-
-const makeReadableStatus = (status: string | undefined) => {
- if (!status) return '';
- return ({
- retried: 'Retried',
- canceled: 'Canceled',
- infrastructure_fail: 'Infra fail',
- timedout: 'Timedout',
- not_run: 'Not run',
- running: 'Running',
- failed: 'Failed',
- queued: 'Queued',
- scheduled: 'Scheduled',
- not_running: 'Not running',
- no_tests: 'No tests',
- fixed: 'Fixed',
- success: 'Success',
- } as Record)[status];
-};
-
-const transform = (
- buildsData: BuildSummary[],
- restartBuild: { (buildId: number): Promise },
-): CITableBuildInfo[] => {
- return buildsData.map((buildData) => {
- const tableBuildInfo: CITableBuildInfo = {
- id: String(buildData.build_num),
- buildName: buildData.subject
- ? buildData.subject +
- (buildData.retry_of ? ` (retry of #${buildData.retry_of})` : '')
- : '',
- onRestartClick: () =>
- typeof buildData.build_num !== 'undefined' &&
- restartBuild(buildData.build_num),
- source: {
- branchName: String(buildData.branch),
- commit: {
- hash: String(buildData.vcs_revision),
- url: 'todo',
- },
- },
- status: makeReadableStatus(buildData.status),
- buildUrl: buildData.build_url,
- };
- return tableBuildInfo;
- });
-};
+import React, { FC } from 'react';
+import { CITable } from '../CITable';
+import { useBuilds } from '../../../../state';
export const Builds: FC<{}> = () => {
const [
- builds,
- { restartBuild: handleRestartBuild, startPolling, stopPolling },
+ { total, loading, value, projectName, page, pageSize },
+ { setPage, retry, setPageSize },
] = useBuilds();
- const [{ repo, owner }] = useSettings();
-
- useEffect(() => {
- startPolling();
- return () => stopPolling();
- }, [repo, owner]);
-
- const transformedBuilds = transform(builds, handleRestartBuild);
-
return (
-
+
);
};
diff --git a/plugins/circleci/src/pages/BuildsPage/lib/CITable/CITable.tsx b/plugins/circleci/src/pages/BuildsPage/lib/CITable/CITable.tsx
index f8e0026ff5..b349dcfcbe 100644
--- a/plugins/circleci/src/pages/BuildsPage/lib/CITable/CITable.tsx
+++ b/plugins/circleci/src/pages/BuildsPage/lib/CITable/CITable.tsx
@@ -114,14 +114,46 @@ const generatedColumns: TableColumn[] = [
width: '10%',
},
];
-export const CITable: FC<{
+
+type Props = {
+ loading: boolean;
+ retry: () => void;
builds: CITableBuildInfo[];
projectName: string;
-}> = React.memo(({ builds = [], projectName }) => {
+ page: number;
+ onChangePage: (page: number) => void;
+ total: number;
+ pageSize: number;
+ onChangePageSize: (pageSize: number) => void;
+};
+export const CITable: FC = ({
+ projectName,
+ loading,
+ pageSize,
+ page,
+ retry,
+ builds,
+ onChangePage,
+ onChangePageSize,
+ total,
+}) => {
return (
,
+ tooltip: 'Refresh Data',
+ isFreeAction: true,
+ onClick: () => retry(),
+ },
+ ]}
data={builds}
+ onChangePage={onChangePage}
+ onChangeRowsPerPage={onChangePageSize}
title={
@@ -132,4 +164,4 @@ export const CITable: FC<{
columns={generatedColumns}
/>
);
-});
+};
diff --git a/plugins/circleci/src/pages/SettingsPage/SettingsPage.tsx b/plugins/circleci/src/pages/SettingsPage/SettingsPage.tsx
deleted file mode 100644
index 62b874d70e..0000000000
--- a/plugins/circleci/src/pages/SettingsPage/SettingsPage.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2020 Spotify AB
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import React, { useState } from 'react';
-import {
- Button,
- TextField,
- List,
- Grid,
- ListItem,
- Snackbar,
- Box,
-} from '@material-ui/core';
-import { Alert } from '@material-ui/lab';
-import { InfoCard, Content } from '@backstage/core';
-import { Layout } from '../../components/Layout';
-import { PluginHeader } from '../../components/PluginHeader';
-import { useSettings } from '../../state';
-
-const SettingsPage = () => {
- const [
- { repo: repoFromStore, owner: ownerFromStore, token: tokenFromStore },
- { saveSettings },
- ] = useSettings();
-
- const [token, setToken] = React.useState(() => tokenFromStore);
- const [owner, setOwner] = React.useState(() => ownerFromStore);
- const [repo, setRepo] = React.useState(() => repoFromStore);
-
- React.useEffect(() => {
- if (tokenFromStore !== token) {
- setToken(tokenFromStore);
- }
- if (ownerFromStore !== owner) {
- setOwner(ownerFromStore);
- }
- if (repoFromStore !== repo) {
- setRepo(repoFromStore);
- }
- }, [ownerFromStore, repoFromStore, tokenFromStore]);
-
- const [saved, setSaved] = useState(false);
-
- return (
-
-
-
-
-
-
-
- Project Credentials
- {/* {authed ? : } */}
- setSaved(false)}
- >
- Credentials saved.
-
- >
- }
- >
-
-
- setToken(e.target.value)}
- />
-
-
- setOwner(e.target.value)}
- />
-
-
- setRepo(e.target.value)}
- />
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default SettingsPage;
diff --git a/plugins/circleci/src/state/AppState.tsx b/plugins/circleci/src/state/AppState.tsx
index ecea9e5221..2e98f86661 100644
--- a/plugins/circleci/src/state/AppState.tsx
+++ b/plugins/circleci/src/state/AppState.tsx
@@ -17,7 +17,6 @@ import React, { FC, useReducer, Dispatch, Reducer } from 'react';
import { circleCIApiRef } from '../api';
import { State, Action, SettingsState } from './types';
export { SettingsState };
-import equal from 'fast-deep-equal';
export const AppContext = React.createContext<[State, Dispatch]>(
[] as any,
@@ -25,13 +24,10 @@ export const AppContext = React.createContext<[State, Dispatch]>(
export const STORAGE_KEY = `${circleCIApiRef.id}.settings`;
const initialState: State = {
- settings: {
- owner: '',
- repo: '',
- token: '',
- },
- builds: [],
- buildsWithSteps: {},
+ owner: '',
+ repo: '',
+ token: '',
+ showSettings: false,
};
const reducer: Reducer = (state, action) => {
@@ -39,23 +35,12 @@ const reducer: Reducer = (state, action) => {
case 'setCredentials':
return {
...state,
- settings: { ...state.settings, ...action.payload },
+ ...action.payload,
};
- case 'setBuilds':
- if (equal(action.payload, state.builds)) return state;
- return {
- ...state,
- builds: action.payload,
- };
- case 'setBuildWithSteps': {
- return {
- ...state,
- buildsWithSteps: {
- ...state.buildsWithSteps,
- [action.payload.build_num!]: action.payload,
- },
- };
- }
+ case 'showSettings':
+ return { ...state, showSettings: true };
+ case 'hideSettings':
+ return { ...state, showSettings: false };
default:
return state;
}
diff --git a/plugins/circleci/src/state/types.ts b/plugins/circleci/src/state/types.ts
index de801ad213..41b3577082 100644
--- a/plugins/circleci/src/state/types.ts
+++ b/plugins/circleci/src/state/types.ts
@@ -13,41 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { BuildSummary, BuildWithSteps } from '../api';
-export type SettingsState = {
- owner: string;
- repo: string;
- token: string;
+export type Settings = { owner: string; repo: string; token: string };
+export type SettingsState = Settings & {
+ showSettings: boolean;
};
-export type BuildsState = BuildSummary[];
+export type State = SettingsState;
-export type State = {
- settings: SettingsState;
- builds: BuildsState;
- buildsWithSteps: BuildsWithStepsState;
-};
+type SettingsAction =
+ | {
+ type: 'setCredentials';
+ payload: {
+ repo: string;
+ owner: string;
+ token: string;
+ };
+ }
+ | { type: 'showSettings' }
+ | { type: 'hideSettings' };
-type SettingsAction = {
- type: 'setCredentials';
- payload: {
- repo: string;
- owner: string;
- token: string;
- };
-};
-
-type BuildsAction = {
- type: 'setBuilds';
- payload: BuildSummary[];
-};
-
-type BuildsWithStepsAction = {
- type: 'setBuildWithSteps';
- payload: BuildWithSteps;
-};
-
-export type BuildsWithStepsState = Record;
-
-export type Action = SettingsAction | BuildsAction | BuildsWithStepsAction;
+export type Action = SettingsAction;
diff --git a/plugins/circleci/src/state/useBuildWithSteps.ts b/plugins/circleci/src/state/useBuildWithSteps.ts
index 96d3a3f159..d77203cce0 100644
--- a/plugins/circleci/src/state/useBuildWithSteps.ts
+++ b/plugins/circleci/src/state/useBuildWithSteps.ts
@@ -14,50 +14,43 @@
* limitations under the License.
*/
import { errorApiRef, useApi } from '@backstage/core';
-import { useContext } from 'react';
+import { useCallback } from 'react';
import { circleCIApiRef, GitType } from '../api/index';
-import { AppContext } from '.';
import { useSettings } from './useSettings';
-
import { useAsyncPolling } from './useAsyncPolling';
+import { useAsyncRetry } from 'react-use';
-const INTERVAL_AMOUNT = 3000;
-
+const INTERVAL_AMOUNT = 1500;
export function useBuildWithSteps(buildId: number) {
- const [settings] = useSettings();
- const [{ buildsWithSteps }, dispatch] = useContext(AppContext);
+ const [{ token, repo, owner }] = useSettings();
const api = useApi(circleCIApiRef);
const errorApi = useApi(errorApiRef);
- const { isPolling, startPolling, stopPolling } = useAsyncPolling(
- () => getBuildWithSteps(),
- INTERVAL_AMOUNT,
- );
-
- const getBuildWithSteps = async () => {
+ const getBuildWithSteps = useCallback(async () => {
try {
const options = {
- token: settings.token,
+ token: token,
vcs: {
- owner: settings.owner,
- repo: settings.repo,
+ owner: owner,
+ repo: repo,
type: GitType.GITHUB,
},
};
- const build = await api.getBuild(buildId, options);
- if (isPolling) dispatch({ type: 'setBuildWithSteps', payload: build });
+ const b = await api.getBuild(buildId, options);
+ return Promise.resolve(b);
} catch (e) {
errorApi.post(e);
+ return Promise.reject(e);
}
- };
+ }, [token, owner, repo, buildId]);
const restartBuild = async () => {
try {
await api.retry(buildId, {
- token: settings.token,
+ token: token,
vcs: {
- owner: settings.owner,
- repo: settings.repo,
+ owner: owner,
+ repo: repo,
type: GitType.GITHUB,
},
});
@@ -66,15 +59,22 @@ export function useBuildWithSteps(buildId: number) {
}
};
- const build = buildsWithSteps[buildId];
+ const { loading, value, retry } = useAsyncRetry(() => getBuildWithSteps(), [
+ getBuildWithSteps,
+ ]);
+
+ const { startPolling, stopPolling } = useAsyncPolling(
+ getBuildWithSteps,
+ INTERVAL_AMOUNT,
+ );
return [
- build,
+ { loading, value, retry },
{
restartBuild,
+ getBuildWithSteps,
startPolling,
stopPolling,
- getBuildWithSteps,
},
] as const;
}
diff --git a/plugins/circleci/src/state/useBuilds.tsx b/plugins/circleci/src/state/useBuilds.tsx
index d46ac642a5..4435c2b8bd 100644
--- a/plugins/circleci/src/state/useBuilds.tsx
+++ b/plugins/circleci/src/state/useBuilds.tsx
@@ -14,52 +14,102 @@
* limitations under the License.
*/
import { errorApiRef, useApi } from '@backstage/core';
-import { GitType } from 'circleci-api';
-import { useContext } from 'react';
+import { GitType, BuildSummary } from 'circleci-api';
+import { useState, useEffect, useCallback } from 'react';
import { circleCIApiRef } from '../api/index';
-import { AppContext } from '.';
-import { useAsyncPolling } from './useAsyncPolling';
+import { useAsyncRetry } from 'react-use';
+import { CITableBuildInfo } from '../pages/BuildsPage/lib/CITable';
+import { useSettings } from './useSettings';
-const INTERVAL_AMOUNT = 3000;
+const makeReadableStatus = (status: string | undefined) => {
+ if (!status) return '';
+ return ({
+ retried: 'Retried',
+ canceled: 'Canceled',
+ infrastructure_fail: 'Infra fail',
+ timedout: 'Timedout',
+ not_run: 'Not run',
+ running: 'Running',
+ failed: 'Failed',
+ queued: 'Queued',
+ scheduled: 'Scheduled',
+ not_running: 'Not running',
+ no_tests: 'No tests',
+ fixed: 'Fixed',
+ success: 'Success',
+ } as Record)[status];
+};
+
+export const transform = (
+ buildsData: BuildSummary[],
+ restartBuild: { (buildId: number): Promise },
+): CITableBuildInfo[] => {
+ return buildsData.map((buildData) => {
+ const tableBuildInfo: CITableBuildInfo = {
+ id: String(buildData.build_num),
+ buildName: buildData.subject
+ ? buildData.subject +
+ (buildData.retry_of ? ` (retry of #${buildData.retry_of})` : '')
+ : '',
+ onRestartClick: () =>
+ typeof buildData.build_num !== 'undefined' &&
+ restartBuild(buildData.build_num),
+ source: {
+ branchName: String(buildData.branch),
+ commit: {
+ hash: String(buildData.vcs_revision),
+ url: 'todo',
+ },
+ },
+ status: makeReadableStatus(buildData.status),
+ buildUrl: buildData.build_url,
+ };
+ return tableBuildInfo;
+ });
+};
export function useBuilds() {
- const [{ builds, settings }, dispatch] = useContext(AppContext);
+ const [{ repo, owner, token }] = useSettings();
const api = useApi(circleCIApiRef);
const errorApi = useApi(errorApiRef);
- const { isPolling, startPolling, stopPolling } = useAsyncPolling(
- () => getBuilds(),
- INTERVAL_AMOUNT,
- );
- const getBuilds = async () => {
- if (settings.owner === '' || settings.repo === '') return;
- try {
- const newBuilds = await api.getBuilds({
- token: settings.token,
- vcs: {
- owner: settings.owner,
- repo: settings.repo,
- type: GitType.GITHUB,
- },
- });
- if (isPolling)
- dispatch({
- type: 'setBuilds',
- payload: newBuilds,
- });
- } catch (e) {
- errorApi.post(e);
- }
- };
+ const [total, setTotal] = useState(0);
+ const [page, setPage] = useState(0);
+ const [pageSize, setPageSize] = useState(5);
+
+ const getBuilds = useCallback(
+ async ({ limit, offset }: { limit: number; offset: number }) => {
+ if (owner === '' || repo === '') {
+ return;
+ }
+ try {
+ return await api.getBuilds(
+ { limit, offset },
+ {
+ token: token,
+ vcs: {
+ owner: owner,
+ repo: repo,
+ type: GitType.GITHUB,
+ },
+ },
+ );
+ } catch (e) {
+ errorApi.post(e);
+ return Promise.reject(e);
+ }
+ },
+ [repo, token, owner],
+ );
const restartBuild = async (buildId: number) => {
try {
await api.retry(buildId, {
- token: settings.token,
+ token: token,
vcs: {
- owner: settings.owner,
- repo: settings.repo,
+ owner: owner,
+ repo: repo,
type: GitType.GITHUB,
},
});
@@ -68,12 +118,35 @@ export function useBuilds() {
}
};
+ useEffect(() => {
+ getBuilds({ limit: 1, offset: 0 }).then((b) => setTotal(b?.[0].build_num!));
+ }, [repo]);
+
+ const { loading, value, retry } = useAsyncRetry(
+ () =>
+ getBuilds({
+ offset: page * pageSize,
+ limit: pageSize,
+ }).then((builds) => transform(builds ?? [], restartBuild)),
+ [page, pageSize, getBuilds],
+ );
+
+ const projectName = `${owner}/${repo}`;
return [
- builds,
{
+ page,
+ pageSize,
+ loading,
+ value,
+ projectName,
+ total,
+ },
+ {
+ getBuilds,
+ setPage,
+ setPageSize,
restartBuild,
- startPolling,
- stopPolling,
+ retry,
},
] as const;
}
diff --git a/plugins/circleci/src/state/useSettings.ts b/plugins/circleci/src/state/useSettings.ts
index 34b2f5e402..04ce83a0a9 100644
--- a/plugins/circleci/src/state/useSettings.ts
+++ b/plugins/circleci/src/state/useSettings.ts
@@ -14,12 +14,11 @@
* limitations under the License.
*/
import { useContext, useEffect } from 'react';
-import { AppContext, STORAGE_KEY, SettingsState } from '.';
+import { AppContext, STORAGE_KEY } from '.';
import { useApi, errorApiRef } from '@backstage/core';
-
+import { Settings } from './types';
export function useSettings() {
- const [{ settings }, dispatch] = useContext(AppContext);
-
+ const [settings, dispatch] = useContext(AppContext);
const errorApi = useApi(errorApiRef);
@@ -45,20 +44,22 @@ export function useSettings() {
rehydrate();
}, []);
- const persist = (state: SettingsState) => {
+ const persist = (state: Settings) => {
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(state));
};
return [
settings,
{
- saveSettings: (state: SettingsState) => {
+ saveSettings: (state: Settings) => {
persist(state);
dispatch({
type: 'setCredentials',
payload: state,
});
},
+ showSettings: () => dispatch({ type: 'showSettings' }),
+ hideSettings: () => dispatch({ type: 'hideSettings' }),
},
] as const;
}