mirror of
https://github.com/usatiuk/writer.git
synced 2025-10-28 16:07:49 +01:00
basic password change
This commit is contained in:
4668
frontend/package-lock.json
generated
4668
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,38 +6,38 @@
|
||||
"test": "jest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/autoprefixer": "^9.7.1",
|
||||
"@types/autoprefixer": "^9.7.2",
|
||||
"@types/enzyme": "^3.10.5",
|
||||
"@types/enzyme-adapter-react-16": "^1.0.6",
|
||||
"@types/highlight.js": "^9.12.3",
|
||||
"@types/jest": "^25.1.4",
|
||||
"@types/highlight.js": "^9.12.4",
|
||||
"@types/jest": "^25.2.3",
|
||||
"@types/parcel-bundler": "^1.12.1",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
"@types/react-redux": "^7.1.7",
|
||||
"@types/react-router": "^5.1.4",
|
||||
"@types/react-router-dom": "^5.1.3",
|
||||
"@types/react": "^16.9.35",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
"@types/react-router": "^5.1.7",
|
||||
"@types/react-router-dom": "^5.1.5",
|
||||
"@types/sass": "^1.16.0",
|
||||
"autoprefixer": "^9.7.4",
|
||||
"autoprefixer": "^9.8.0",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-react-16": "^1.15.2",
|
||||
"jest": "^25.1.0",
|
||||
"jest": "^26.0.1",
|
||||
"parcel-bundler": "^1.12.4",
|
||||
"postcss-modules": "^1.5.0",
|
||||
"postcss-modules": "^2.0.0",
|
||||
"redux-devtools-extension": "^2.13.8",
|
||||
"sass": "^1.26.3",
|
||||
"ts-jest": "^25.2.1"
|
||||
"sass": "^1.26.8",
|
||||
"ts-jest": "^26.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blueprintjs/core": "^3.24.0",
|
||||
"@blueprintjs/icons": "^3.14.0",
|
||||
"highlight.js": "^9.18.1",
|
||||
"react": "^16.13.0",
|
||||
"react-dom": "^16.13.0",
|
||||
"@blueprintjs/core": "^3.28.1",
|
||||
"@blueprintjs/icons": "^3.18.0",
|
||||
"highlight.js": "^10.0.3",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-router": "^5.1.2",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-spring": "^8.0.27",
|
||||
"redux": "^4.0.5",
|
||||
"redux-persist": "^6.0.0",
|
||||
|
||||
@@ -1,7 +1,76 @@
|
||||
import * as React from "react";
|
||||
|
||||
export function AccountComponent() {
|
||||
return <div>Hello</div>;
|
||||
}
|
||||
|
||||
export { AccountComponent as Account };
|
||||
import { Button, Card, FormGroup, H2, InputGroup } from "@blueprintjs/core";
|
||||
import * as React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Dispatch } from "redux";
|
||||
import { IAppState } from "~redux/reducers";
|
||||
import { userPassChange } from "~redux/user/actions";
|
||||
|
||||
export interface IAccountComponentProps {
|
||||
username: string;
|
||||
changePass: (password: string) => void;
|
||||
}
|
||||
|
||||
export function AccountComponent(props: IAccountComponentProps) {
|
||||
const [pass, setPass] = React.useState("");
|
||||
|
||||
return (
|
||||
<Card className="AuthForm" elevation={2}>
|
||||
<form
|
||||
onSubmit={(e: React.FormEvent<any>) => {
|
||||
e.preventDefault();
|
||||
if (pass.trim()) {
|
||||
props.changePass(pass);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="header">
|
||||
<H2>Account</H2>
|
||||
</div>
|
||||
<FormGroup label="Username">
|
||||
<InputGroup
|
||||
name="username"
|
||||
leftIcon="person"
|
||||
disabled={true}
|
||||
value={props.username}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup label="Password">
|
||||
<InputGroup
|
||||
name="password"
|
||||
type="password"
|
||||
leftIcon="key"
|
||||
value={pass}
|
||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||
setPass(e.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
<div className="footer">
|
||||
<Button
|
||||
className="submit"
|
||||
intent="primary"
|
||||
icon="floppy-disk"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function mapStateToProps(state: IAppState) {
|
||||
return { username: state.user.user.username };
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch: Dispatch) {
|
||||
return {
|
||||
changePass: (password: string) => dispatch(userPassChange(password)),
|
||||
};
|
||||
}
|
||||
|
||||
export const Account = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(AccountComponent);
|
||||
|
||||
@@ -24,3 +24,19 @@ export function showSharedToast() {
|
||||
timeout: 2000,
|
||||
});
|
||||
}
|
||||
|
||||
export function showPasswordSavedToast() {
|
||||
AppToaster.show({
|
||||
message: "Password saved!",
|
||||
intent: "success",
|
||||
timeout: 2000,
|
||||
});
|
||||
}
|
||||
|
||||
export function showPasswordNotSavedToast(error: string) {
|
||||
AppToaster.show({
|
||||
message: "Password not saved! " + error,
|
||||
intent: "danger",
|
||||
timeout: 2000,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,3 +3,7 @@ import { fetchJSON, fetchJSONAuth } from "../utils";
|
||||
export async function fetchUser() {
|
||||
return fetchJSONAuth("/users/user", "GET");
|
||||
}
|
||||
|
||||
export async function changeUserPassword(newPassword: string) {
|
||||
return fetchJSONAuth("/users/edit", "POST", { password: newPassword });
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { Action } from "redux";
|
||||
import { IUserAuthJSON, IUserJSON } from "~../../src/entity/User";
|
||||
import { showPasswordNotSavedToast, showPasswordSavedToast } from "~AppToaster";
|
||||
|
||||
export enum UserTypes {
|
||||
USER_GET = "USER_GET",
|
||||
USER_GET_SUCCESS = "USER_GET_SUCCESS",
|
||||
USER_GET_FAIL = "USER_GET_FAIL",
|
||||
USER_LOGOUT = "USER_LOGOUT",
|
||||
USER_PASS_CHANGE = "USER_PASS_CHANGE",
|
||||
USER_PASS_CHANGE_SUCCESS = "USER_PASS_CHANGE_SUCCESS",
|
||||
USER_PASS_CHANGE_FAIL = "USER_PASS_CHANGE_FAIL",
|
||||
}
|
||||
|
||||
export interface IUserGetAction extends Action {
|
||||
@@ -29,6 +33,24 @@ export interface IUserGetFailAction extends Action {
|
||||
};
|
||||
}
|
||||
|
||||
export interface IUserPassChangeAction extends Action {
|
||||
type: UserTypes.USER_PASS_CHANGE;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface IUserPassChangeSuccessAction extends Action {
|
||||
type: UserTypes.USER_PASS_CHANGE_SUCCESS;
|
||||
payload: IUserAuthJSON;
|
||||
}
|
||||
|
||||
export interface IUserPassChangeFailAction extends Action {
|
||||
type: UserTypes.USER_PASS_CHANGE_FAIL;
|
||||
payload: {
|
||||
error: string;
|
||||
logout: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export function getUser(): IUserGetAction {
|
||||
return { type: UserTypes.USER_GET };
|
||||
}
|
||||
@@ -48,8 +70,33 @@ export function getUserFail(
|
||||
return { type: UserTypes.USER_GET_FAIL, payload: { error, logout } };
|
||||
}
|
||||
|
||||
export function userPassChange(password: string): IUserPassChangeAction {
|
||||
return { type: UserTypes.USER_PASS_CHANGE, password };
|
||||
}
|
||||
|
||||
export function userPassChangeSuccess(
|
||||
user: IUserAuthJSON,
|
||||
): IUserPassChangeSuccessAction {
|
||||
showPasswordSavedToast();
|
||||
return { type: UserTypes.USER_PASS_CHANGE_SUCCESS, payload: user };
|
||||
}
|
||||
|
||||
export function userPassChangeFail(
|
||||
error: string,
|
||||
logout: boolean,
|
||||
): IUserPassChangeFailAction {
|
||||
showPasswordNotSavedToast(error);
|
||||
return {
|
||||
type: UserTypes.USER_PASS_CHANGE_FAIL,
|
||||
payload: { error, logout },
|
||||
};
|
||||
}
|
||||
|
||||
export type UserAction =
|
||||
| IUserGetAction
|
||||
| IUserGetSuccessAction
|
||||
| IUserGetFailAction
|
||||
| IUserLogoutAction;
|
||||
| IUserLogoutAction
|
||||
| IUserPassChangeAction
|
||||
| IUserPassChangeFailAction
|
||||
| IUserPassChangeSuccessAction;
|
||||
|
||||
@@ -18,12 +18,14 @@ export const userReducer: Reducer<IUserState, AuthAction> = (
|
||||
switch (action.type) {
|
||||
case AuthTypes.AUTH_SUCCESS:
|
||||
case UserTypes.USER_GET_SUCCESS:
|
||||
case UserTypes.USER_PASS_CHANGE_SUCCESS:
|
||||
return {
|
||||
...defaultUserState,
|
||||
user: action.payload,
|
||||
};
|
||||
break;
|
||||
case UserTypes.USER_GET_FAIL:
|
||||
case UserTypes.USER_PASS_CHANGE_FAIL:
|
||||
return defaultUserState;
|
||||
break;
|
||||
case UserTypes.USER_LOGOUT:
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { all, call, delay, put, race, takeLatest } from "redux-saga/effects";
|
||||
import { fetchUser } from "~redux/api/user";
|
||||
import { changeUserPassword, fetchUser } from "~redux/api/user";
|
||||
|
||||
import { getUserFail, getUserSuccess, UserTypes } from "./actions";
|
||||
import {
|
||||
getUserFail,
|
||||
getUserSuccess,
|
||||
IUserPassChangeAction,
|
||||
userPassChangeFail,
|
||||
userPassChangeSuccess,
|
||||
UserTypes,
|
||||
} from "./actions";
|
||||
|
||||
function* getUser() {
|
||||
try {
|
||||
@@ -25,6 +32,31 @@ function* getUser() {
|
||||
}
|
||||
}
|
||||
|
||||
export function* userSaga() {
|
||||
yield all([takeLatest(UserTypes.USER_GET, getUser)]);
|
||||
function* userPassChange(action: IUserPassChangeAction) {
|
||||
try {
|
||||
const { response, timeout } = yield race({
|
||||
response: call(changeUserPassword, action.password),
|
||||
timeout: delay(10000),
|
||||
});
|
||||
|
||||
if (timeout) {
|
||||
yield put(userPassChangeFail("Timeout", false));
|
||||
return;
|
||||
}
|
||||
if (response.data) {
|
||||
const user = response.data;
|
||||
yield put(userPassChangeSuccess(user));
|
||||
} else {
|
||||
yield put(userPassChangeFail(response.error, true));
|
||||
}
|
||||
} catch (e) {
|
||||
yield put(userPassChangeFail("Internal error", false));
|
||||
}
|
||||
}
|
||||
|
||||
export function* userSaga() {
|
||||
yield all([
|
||||
takeLatest(UserTypes.USER_GET, getUser),
|
||||
takeLatest(UserTypes.USER_PASS_CHANGE, userPassChange),
|
||||
]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user