deleting documents

This commit is contained in:
2019-02-10 15:50:48 +03:00
parent d5709b1887
commit 67813da2e7
7 changed files with 149 additions and 18 deletions

View File

@@ -1,3 +1,4 @@
import { Position, Toaster } from "@blueprintjs/core";
import * as React from "react";
import { connect } from "react-redux";
import { Route, RouteComponentProps, Switch, withRouter } from "react-router";
@@ -6,6 +7,11 @@ import { Home } from "~Home/Home";
import { Landing } from "~Landing/Landing";
import { IAppState } from "~redux/reducers";
export const AppToaster = Toaster.create({
className: "recipe-toaster",
position: Position.TOP,
});
interface IAppComponentProps extends RouteComponentProps {
loggedIn: boolean;
}

View File

@@ -53,12 +53,15 @@
h1 {
margin-left: 0;
margin-right: auto;
flex-shrink: 0;
}
button {
.buttons {
width: 1rem;
height: 1rem;
margin-left: auto;
margin-right: 0;
display: flex;
justify-content: end;
}
}
textarea {

View File

@@ -6,8 +6,13 @@ import { connect } from "react-redux";
import { RouteComponentProps, withRouter } from "react-router";
import { Dispatch } from "redux";
import { IDocumentJSON } from "~../../src/entity/Document";
import { AppToaster } from "~App";
import { LoadingStub } from "~LoadingStub";
import { fetchDocsStart } from "~redux/docs/actions";
import {
deleteDocCancel,
deleteDocStart,
fetchDocsStart,
} from "~redux/docs/actions";
import { IAppState } from "~redux/reducers";
export interface IDocumentEditComponentProps extends RouteComponentProps {
@@ -15,7 +20,10 @@ export interface IDocumentEditComponentProps extends RouteComponentProps {
fetching: boolean;
spinner: boolean;
fetchDocs: () => void;
deleteDoc: (id: number) => void;
cancelDelete: () => void;
}
export class DocumentEditComponent extends React.PureComponent<
@@ -30,13 +38,33 @@ export class DocumentEditComponent extends React.PureComponent<
<div className="document">
<div className="documentHeader">
<H1 contentEditable={true}>{doc.name}</H1>
<Button
icon="tick"
minimal={true}
onClick={() =>
this.props.history.push(`/docs/${id}`)
}
/>
<div className="buttons">
<Button
icon="trash"
minimal={true}
onClick={() => {
this.props.history.push(`/`);
this.props.deleteDoc(id);
AppToaster.show({
message: "Document deleted!",
intent: "danger",
timeout: 1900,
action: {
text: "Undo",
onClick: () =>
this.props.cancelDelete(),
},
});
}}
/>
<Button
icon="tick"
minimal={true}
onClick={() =>
this.props.history.push(`/docs/${id}`)
}
/>
</div>
</div>
<TextArea>{doc.content}</TextArea>
</div>
@@ -59,6 +87,8 @@ function mapStateToProps(state: IAppState) {
function mapDispatchToProps(dispatch: Dispatch) {
return {
fetchDocs: () => dispatch(fetchDocsStart()),
cancelDelete: () => dispatch(deleteDocCancel()),
deleteDoc: (id: number) => dispatch(deleteDocStart(id)),
};
}

View File

@@ -30,13 +30,15 @@ export class DocumentViewComponent extends React.PureComponent<
<div className="document">
<div className="documentHeader">
<H1>{doc.name}</H1>
<Button
icon="edit"
minimal={true}
onClick={() =>
this.props.history.push(`/docs/${id}/edit`)
}
/>
<div className="buttons">
<Button
icon="edit"
minimal={true}
onClick={() =>
this.props.history.push(`/docs/${id}/edit`)
}
/>
</div>
</div>
<Text>{doc.content}</Text>
</div>

View File

@@ -11,6 +11,11 @@ export enum DocsTypes {
DOC_NEW_SUCCESS = "DOC_NEW_SUCCESS",
DOC_NEW_RESET = "DOC_NEW_RESET",
DOC_DELETE_START = "DOC_DELETE_START",
DOC_DELETE_FAIL = "DOC_DELETE_FAIL",
DOC_DELETE_SUCCESS = "DOC_DELETE_SUCCESS",
DOC_DELETE_CANCEL = "DOC_DELETE_CANCEL",
DOCS_SHOW_SPINNER = "DOCS_SHOW_SPINNER",
}
@@ -92,6 +97,45 @@ export function newDocReset(): IDocNewResetAction {
return { type: DocsTypes.DOC_NEW_RESET };
}
export interface IDocDeleteStartAction extends Action {
type: DocsTypes.DOC_DELETE_START;
id: number;
}
export interface IDocDeleteFailAction extends Action {
type: DocsTypes.DOC_DELETE_FAIL;
payload: {
error: string;
};
}
export interface IDocDeleteSuccessAction extends Action {
type: DocsTypes.DOC_DELETE_SUCCESS;
payload: {
id: number;
};
}
export interface IDocDeleteCancelAction extends Action {
type: DocsTypes.DOC_DELETE_CANCEL;
}
export function deleteDocStart(id: number): IDocDeleteStartAction {
return { type: DocsTypes.DOC_DELETE_START, id };
}
export function deleteDocFail(error: string): IDocDeleteFailAction {
return { type: DocsTypes.DOC_DELETE_FAIL, payload: { error } };
}
export function deleteDocSuccess(id: number): IDocDeleteSuccessAction {
return { type: DocsTypes.DOC_DELETE_SUCCESS, payload: { id } };
}
export function deleteDocCancel(): IDocDeleteCancelAction {
return { type: DocsTypes.DOC_DELETE_CANCEL };
}
export type DocsAction =
| IDocsFetchStartAction
| IDocsFetchFailAction
@@ -99,4 +143,9 @@ export type DocsAction =
| IDocsShowSpinnerAction
| IDocNewFailAction
| IDocNewStartAction
| IDocNewSuccessAction | IDocNewResetAction;
| IDocNewSuccessAction
| IDocNewResetAction
| IDocDeleteFailAction
| IDocDeleteStartAction
| IDocDeleteSuccessAction
| IDocDeleteCancelAction;

View File

@@ -45,6 +45,11 @@ export const docsReducer: Reducer<IDocsState, DocsAction> = (
case DocsTypes.DOC_NEW_RESET: {
return { ...state, newDocumentID: null };
}
case DocsTypes.DOC_DELETE_SUCCESS: {
const all = { ...state.all };
delete all[action.payload.id];
return { ...state, all };
}
case DocsTypes.DOCS_FETCH_FAIL:
return { ...defaultDocsState, ...action.payload };
default:

View File

@@ -6,14 +6,18 @@ import {
fork,
put,
race,
take,
takeLatest,
} from "redux-saga/effects";
import { createNewDoc, fetchAllDocs } from "~redux/api/docs";
import { createNewDoc, deleteDoc, fetchAllDocs } from "~redux/api/docs";
import {
deleteDocFail,
deleteDocSuccess,
DocsTypes,
fetchDocsFail,
fetchDocsSuccess,
IDocDeleteStartAction,
IDocNewStartAction,
IDocsFetchStartAction,
newDocFail,
@@ -84,9 +88,41 @@ function* docNewStart(action: IDocNewStartAction) {
}
}
function* docDeleteStart(action: IDocDeleteStartAction) {
try {
const { cancelled } = yield race({
timeout: delay(2000),
cancelled: take(DocsTypes.DOC_DELETE_CANCEL),
});
if (!cancelled) {
const { response, timeout } = yield race({
response: call(deleteDoc, action.id),
timeout: delay(10000),
});
if (timeout) {
yield put(deleteDocFail("Timeout"));
return;
}
if (response) {
if (response.data == null) {
yield put(deleteDocFail(response.error));
} else {
yield put(deleteDocSuccess(action.id));
}
}
}
} catch (e) {
yield put(deleteDocFail("Internal error"));
}
}
export function* docsSaga() {
yield all([
takeLatest(DocsTypes.DOCS_FETCH_START, docsFetchStart),
takeLatest(DocsTypes.DOC_NEW_START, docNewStart),
takeLatest(DocsTypes.DOC_DELETE_START, docDeleteStart),
]);
}