feat(projects): 添加检出功能。
This commit is contained in:
parent
a137c0efb8
commit
61dad695fb
@ -887,6 +887,39 @@
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"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,
|
||||
@ -1060,6 +1093,57 @@
|
||||
"enumValues": 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",
|
||||
"name": "__Schema",
|
||||
|
10
package-lock.json
generated
10
package-lock.json
generated
@ -2949,6 +2949,11 @@
|
||||
"@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": {
|
||||
"version": "3.10.8",
|
||||
"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": {
|
||||
"version": "4.2.3",
|
||||
"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/react-fontawesome": "^0.1.14",
|
||||
"@tailwindcss/postcss7-compat": "^2.0.3",
|
||||
"@types/classnames": "^2.2.11",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"classnames": "^2.2.6",
|
||||
"formik": "^2.2.6",
|
||||
"graphql": "^15.5.0",
|
||||
"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;
|
||||
}
|
||||
.item {
|
||||
@apply grid grid-cols-2 grid-rows-2;
|
||||
@apply bg-white text-gray-700;
|
||||
@apply py-2 px-4 my-px;
|
||||
@apply hover:bg-gray-50;
|
||||
|
||||
time {
|
||||
@apply col-start-1;
|
||||
@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 { gql, useQuery } from '@apollo/client';
|
||||
import styles from './commit-log-list.scss';
|
||||
import { CommitActions } from '../commit-actions/commit-actions';
|
||||
|
||||
const LIST_LOGS = gql`
|
||||
query ListLogs($args: ListLogsArgs!) {
|
||||
@ -36,6 +37,7 @@ export const CommitLogList = ({ project, branch }: Props) => {
|
||||
<li className={styles.item} key={log.hash}>
|
||||
<h4>{log.message}</h4>
|
||||
<time dateTime={log.date}>{log.date}</time>
|
||||
<CommitActions project={project} commitNumber={log.hash} />
|
||||
</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;
|
||||
modifyProject: Project;
|
||||
deleteProject: Scalars['Float'];
|
||||
checkout: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
|
||||
@ -121,6 +122,11 @@ export type MutationDeleteProjectArgs = {
|
||||
id: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationCheckoutArgs = {
|
||||
checkoutInput: CheckoutInput;
|
||||
};
|
||||
|
||||
export type CreateProjectInput = {
|
||||
name: Scalars['String'];
|
||||
comment: Scalars['String'];
|
||||
@ -136,3 +142,9 @@ export type UpdateProjectInput = {
|
||||
webUrl?: 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