feat(projects): 添加检出功能。
This commit is contained in:
parent
a137c0efb8
commit
61dad695fb
@ -887,6 +887,39 @@
|
|||||||
},
|
},
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "checkout",
|
||||||
|
"description": null,
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "checkoutInput",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "INPUT_OBJECT",
|
||||||
|
"name": "CheckoutInput",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Boolean",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"inputFields": null,
|
"inputFields": null,
|
||||||
@ -1060,6 +1093,57 @@
|
|||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "INPUT_OBJECT",
|
||||||
|
"name": "CheckoutInput",
|
||||||
|
"description": null,
|
||||||
|
"fields": null,
|
||||||
|
"inputFields": [
|
||||||
|
{
|
||||||
|
"name": "projectId",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "branch",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "commitNumber",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interfaces": null,
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "__Schema",
|
"name": "__Schema",
|
||||||
|
10
package-lock.json
generated
10
package-lock.json
generated
@ -2949,6 +2949,11 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/classnames": {
|
||||||
|
"version": "2.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz",
|
||||||
|
"integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw=="
|
||||||
|
},
|
||||||
"@types/enzyme": {
|
"@types/enzyme": {
|
||||||
"version": "3.10.8",
|
"version": "3.10.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.8.tgz",
|
||||||
@ -5181,6 +5186,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"classnames": {
|
||||||
|
"version": "2.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
|
||||||
|
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
|
||||||
|
},
|
||||||
"clean-css": {
|
"clean-css": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
|
||||||
|
@ -30,7 +30,9 @@
|
|||||||
"@fortawesome/free-solid-svg-icons": "^5.15.2",
|
"@fortawesome/free-solid-svg-icons": "^5.15.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||||
"@tailwindcss/postcss7-compat": "^2.0.3",
|
"@tailwindcss/postcss7-compat": "^2.0.3",
|
||||||
|
"@types/classnames": "^2.2.11",
|
||||||
"autoprefixer": "^9.8.6",
|
"autoprefixer": "^9.8.6",
|
||||||
|
"classnames": "^2.2.6",
|
||||||
"formik": "^2.2.6",
|
"formik": "^2.2.6",
|
||||||
"graphql": "^15.5.0",
|
"graphql": "^15.5.0",
|
||||||
"mobx": "^6.1.7",
|
"mobx": "^6.1.7",
|
||||||
|
12
src/components/commit-actions/action-button.scss
Normal file
12
src/components/commit-actions/action-button.scss
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.btn {
|
||||||
|
@apply p-2 w-4 h-4 box-content inline-flex items-center justify-center;
|
||||||
|
@apply rounded-full;
|
||||||
|
@apply text-red-500;
|
||||||
|
@apply hover:bg-red-100;
|
||||||
|
&[disabled] {
|
||||||
|
@apply text-red-300;
|
||||||
|
}
|
||||||
|
&:global(.waiting) {
|
||||||
|
@apply text-red-500;
|
||||||
|
}
|
||||||
|
}
|
10
src/components/commit-actions/action-button.scss.d.ts
vendored
Normal file
10
src/components/commit-actions/action-button.scss.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// This file is automatically generated from your CSS. Any edits will be overwritten.
|
||||||
|
declare namespace ActionButtonScssNamespace {
|
||||||
|
export interface IActionButtonScss {
|
||||||
|
btn: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const ActionButtonScssModule: ActionButtonScssNamespace.IActionButtonScss;
|
||||||
|
|
||||||
|
export = ActionButtonScssModule;
|
62
src/components/commit-actions/action-button.tsx
Normal file
62
src/components/commit-actions/action-button.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { RenderableProps } from 'preact';
|
||||||
|
import { FC } from 'preact/compat';
|
||||||
|
import { h } from 'preact';
|
||||||
|
import styles from './action-button.scss';
|
||||||
|
import { makeAutoObservable, makeObservable } from 'mobx';
|
||||||
|
import { observer, useLocalObservable } from 'mobx-react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { useCommitActionsStore } from './commit-actions.store';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onClick?: () => Promise<void>;
|
||||||
|
waiting?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Store {
|
||||||
|
constructor(public waiting: boolean = false) {
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
setWaiting(val: boolean) {
|
||||||
|
this.waiting = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ActionButton: FC<Props> = observer(
|
||||||
|
({ onClick, waiting, children }: RenderableProps<Props>) => {
|
||||||
|
const commitActionsStore = useCommitActionsStore();
|
||||||
|
const store = useLocalObservable(() => new Store(waiting));
|
||||||
|
|
||||||
|
const doAction = () => {
|
||||||
|
if (!onClick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
store.setWaiting(true);
|
||||||
|
commitActionsStore?.setCurrentWork?.('checkout');
|
||||||
|
onClick().finally(() => {
|
||||||
|
store.setWaiting(false);
|
||||||
|
commitActionsStore?.clearCurrentWork?.('checkout');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={classNames([
|
||||||
|
styles.btn,
|
||||||
|
{
|
||||||
|
'waiting animate-pulse': store.waiting
|
||||||
|
}
|
||||||
|
])}
|
||||||
|
disabled={!!commitActionsStore.currentWork}
|
||||||
|
onClick={doAction}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ActionButton.defaultProps = {
|
||||||
|
waiting: false,
|
||||||
|
disabled: false
|
||||||
|
};
|
3
src/components/commit-actions/commit-actions.scss
Normal file
3
src/components/commit-actions/commit-actions.scss
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.component {
|
||||||
|
@apply inline;
|
||||||
|
}
|
10
src/components/commit-actions/commit-actions.scss.d.ts
vendored
Normal file
10
src/components/commit-actions/commit-actions.scss.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// This file is automatically generated from your CSS. Any edits will be overwritten.
|
||||||
|
declare namespace CommitActionsScssNamespace {
|
||||||
|
export interface ICommitActionsScss {
|
||||||
|
component: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const CommitActionsScssModule: CommitActionsScssNamespace.ICommitActionsScss;
|
||||||
|
|
||||||
|
export = CommitActionsScssModule;
|
30
src/components/commit-actions/commit-actions.store.tsx
Normal file
30
src/components/commit-actions/commit-actions.store.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { makeAutoObservable } from 'mobx';
|
||||||
|
import { useContext } from 'preact/hooks';
|
||||||
|
import { createContext } from 'preact';
|
||||||
|
|
||||||
|
type Work = string;
|
||||||
|
|
||||||
|
export class CommitActionsStore {
|
||||||
|
clearCurrentWork(work: Work) {
|
||||||
|
if (work === this.currentWork) {
|
||||||
|
this.currentWork = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constructor() {
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
currentWork?: Work;
|
||||||
|
setCurrentWork(work: Work) {
|
||||||
|
this.currentWork = work;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const commitActionsContext = createContext<CommitActionsStore>(
|
||||||
|
new CommitActionsStore()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const CommitActionsStoreProvider = commitActionsContext.Provider;
|
||||||
|
|
||||||
|
export const useCommitActionsStore = () => {
|
||||||
|
return useContext(commitActionsContext);
|
||||||
|
};
|
69
src/components/commit-actions/commit-actions.tsx
Normal file
69
src/components/commit-actions/commit-actions.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { h } from 'preact';
|
||||||
|
import styles from './commit-actions.scss';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { autorun, makeAutoObservable } from 'mobx';
|
||||||
|
import { ActionButton } from './action-button';
|
||||||
|
import { gql, useMutation } from '@apollo/client';
|
||||||
|
import { CheckoutInput, Project } from '../../generated/graphql';
|
||||||
|
import {
|
||||||
|
CommitActionsStoreProvider,
|
||||||
|
CommitActionsStore
|
||||||
|
} from './commit-actions.store';
|
||||||
|
import {
|
||||||
|
faCloudDownloadAlt,
|
||||||
|
faDownload,
|
||||||
|
faGlobeAsia,
|
||||||
|
faVial
|
||||||
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { useLocalObservable } from 'mobx-react';
|
||||||
|
|
||||||
|
const CHECKOUT = gql`
|
||||||
|
mutation Checkout($input: CheckoutInput!) {
|
||||||
|
checkout(checkoutInput: $input)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
project: Project;
|
||||||
|
commitNumber: string;
|
||||||
|
branch?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Store {
|
||||||
|
constructor() {
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
isTasksWorking = [false, false, false, false];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CommitActions = ({ project, commitNumber }: Props) => {
|
||||||
|
const [checkout] = useMutation<CheckoutInput>(CHECKOUT);
|
||||||
|
|
||||||
|
const onCheckoutBtnClick = async () => {
|
||||||
|
await checkout({
|
||||||
|
variables: {
|
||||||
|
input: { projectId: project.id, commitNumber } as CheckoutInput
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const commitActionsStore = useLocalObservable(() => new CommitActionsStore());
|
||||||
|
return (
|
||||||
|
<section className={[styles.component, 'commit-actions'].join(' ')}>
|
||||||
|
<CommitActionsStoreProvider value={commitActionsStore}>
|
||||||
|
<ActionButton onClick={onCheckoutBtnClick}>
|
||||||
|
<FontAwesomeIcon icon={faDownload}></FontAwesomeIcon>
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton>
|
||||||
|
<FontAwesomeIcon icon={faCloudDownloadAlt}></FontAwesomeIcon>
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton>
|
||||||
|
<FontAwesomeIcon icon={faVial}></FontAwesomeIcon>
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton>
|
||||||
|
<FontAwesomeIcon icon={faGlobeAsia}></FontAwesomeIcon>
|
||||||
|
</ActionButton>
|
||||||
|
</CommitActionsStoreProvider>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
@ -5,11 +5,16 @@
|
|||||||
@apply bg-gray-100 overflow-auto max-h-full;
|
@apply bg-gray-100 overflow-auto max-h-full;
|
||||||
}
|
}
|
||||||
.item {
|
.item {
|
||||||
|
@apply grid grid-cols-2 grid-rows-2;
|
||||||
@apply bg-white text-gray-700;
|
@apply bg-white text-gray-700;
|
||||||
@apply py-2 px-4 my-px;
|
@apply py-2 px-4 my-px;
|
||||||
@apply hover:bg-gray-50;
|
@apply hover:bg-gray-50;
|
||||||
|
|
||||||
time {
|
time {
|
||||||
|
@apply col-start-1;
|
||||||
@apply text-sm text-gray-400;
|
@apply text-sm text-gray-400;
|
||||||
}
|
}
|
||||||
|
:global(.commit-actions) {
|
||||||
|
@apply col-start-2 row-start-1 row-span-2 justify-self-end self-center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { LogList, Project, ListLogsArgs } from '../../generated/graphql';
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import { gql, useQuery } from '@apollo/client';
|
import { gql, useQuery } from '@apollo/client';
|
||||||
import styles from './commit-log-list.scss';
|
import styles from './commit-log-list.scss';
|
||||||
|
import { CommitActions } from '../commit-actions/commit-actions';
|
||||||
|
|
||||||
const LIST_LOGS = gql`
|
const LIST_LOGS = gql`
|
||||||
query ListLogs($args: ListLogsArgs!) {
|
query ListLogs($args: ListLogsArgs!) {
|
||||||
@ -36,6 +37,7 @@ export const CommitLogList = ({ project, branch }: Props) => {
|
|||||||
<li className={styles.item} key={log.hash}>
|
<li className={styles.item} key={log.hash}>
|
||||||
<h4>{log.message}</h4>
|
<h4>{log.message}</h4>
|
||||||
<time dateTime={log.date}>{log.date}</time>
|
<time dateTime={log.date}>{log.date}</time>
|
||||||
|
<CommitActions project={project} commitNumber={log.hash} />
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
4
src/components/commons/button/button.scss
Normal file
4
src/components/commons/button/button.scss
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.component {
|
||||||
|
@apply py-2 px-4;
|
||||||
|
@apply bg-transparent border-gray-400 border rounded-md;
|
||||||
|
}
|
24
src/components/commons/button/button.tsx
Normal file
24
src/components/commons/button/button.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { FC } from 'preact/compat';
|
||||||
|
import { h } from 'preact';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
loading?: boolean;
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Button: FC<Props> = ({
|
||||||
|
loading,
|
||||||
|
title,
|
||||||
|
children
|
||||||
|
}: RenderableProps<Props>) => {
|
||||||
|
const disabled = loading;
|
||||||
|
return (
|
||||||
|
<button disabled={disabled} title={title}>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Button.defaultProps = {
|
||||||
|
loading: false
|
||||||
|
};
|
@ -103,6 +103,7 @@ export type Mutation = {
|
|||||||
createProject: Project;
|
createProject: Project;
|
||||||
modifyProject: Project;
|
modifyProject: Project;
|
||||||
deleteProject: Scalars['Float'];
|
deleteProject: Scalars['Float'];
|
||||||
|
checkout: Scalars['Boolean'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -121,6 +122,11 @@ export type MutationDeleteProjectArgs = {
|
|||||||
id: Scalars['String'];
|
id: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationCheckoutArgs = {
|
||||||
|
checkoutInput: CheckoutInput;
|
||||||
|
};
|
||||||
|
|
||||||
export type CreateProjectInput = {
|
export type CreateProjectInput = {
|
||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
comment: Scalars['String'];
|
comment: Scalars['String'];
|
||||||
@ -136,3 +142,9 @@ export type UpdateProjectInput = {
|
|||||||
webUrl?: Maybe<Scalars['String']>;
|
webUrl?: Maybe<Scalars['String']>;
|
||||||
webHookSecret?: Maybe<Scalars['String']>;
|
webHookSecret?: Maybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CheckoutInput = {
|
||||||
|
projectId: Scalars['String'];
|
||||||
|
branch?: Maybe<Scalars['String']>;
|
||||||
|
commitNumber?: Maybe<Scalars['String']>;
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user