Skip to content
This repository was archived by the owner on Apr 5, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@

eslint --max-warnings 0 src --ext .ts --ext .tsx --cache
npm test -- --watchAll=false
git add -A src
31,057 changes: 19,616 additions & 11,441 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"react-bootstrap": "2.1.1",
"react-dom": "17.0.2",
"react-dropzone": "11.4.2",
"react-password-strength-bar": "^0.4.0",
"react-redux": "7.2.6",
"react-router-dom": "6.2.1",
"react-scripts": "4.0.3",
Expand Down
82 changes: 17 additions & 65 deletions src/__tests__/__snapshots__/storybook.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -991,74 +991,10 @@ exports[`Storyshots Registration default 1`] = `
className="form-control"
id="formBasicPassword"
onChange={[Function]}
placeholder="Must contain one number, uppercase & lowercase letter each"
placeholder="Enter your super secret strong password password."
type="password"
value=""
/>
<div>
<img
alt="status icon password length"
src="info-24px.svg"
/>
<span
className="sr-only"
>
Missing:
</span>
<span
className="text-muted"
>
Passwords must be between 8 and 20 characters.
</span>
</div>
<div>
<img
alt="status icon password contains uppercase character"
src="info-24px.svg"
/>
<span
className="sr-only"
>
Missing:
</span>
<span
className="text-muted"
>
Passwords must be at least contain 1 uppercase character.
</span>
</div>
<div>
<img
alt="status icon password contains lowercase character"
src="info-24px.svg"
/>
<span
className="sr-only"
>
Missing:
</span>
<span
className="text-muted"
>
Passwords must be at least contain 1 lowercase character.
</span>
</div>
<div>
<img
alt="status icon password contains number"
src="info-24px.svg"
/>
<span
className="sr-only"
>
Missing:
</span>
<span
className="text-muted"
>
Passwords must be at least contain 1 number.
</span>
</div>
</div>
<div>
<label
Expand All @@ -1074,6 +1010,22 @@ exports[`Storyshots Registration default 1`] = `
type="password"
value=""
/>
<div>
<img
alt="status icon password length"
src="info-24px.svg"
/>
<span
className="sr-only"
>
Missing:
</span>
<span
className="text-muted"
>
Passwords must be at least strong.
</span>
</div>
<div>
<img
alt="status icon passwords match"
Expand Down
8 changes: 8 additions & 0 deletions src/background/api/sharedApiTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Interface describing the standart return value of the backend
*/
// FIXME implement it if needed.
export interface ApiStatusResponse {
responseCode: number
responseStatus: { statusMessage: string; message: string }
}
12 changes: 10 additions & 2 deletions src/background/api/userInformation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { hostname, userPath } from "./api"
import store from "../redux/store"
import { updateUser } from "../redux/actions/user"
import { UserState } from "../redux/actions/userTypes"
import { ApiStatusResponse } from "../api/sharedApiTypes"

export interface UserInformation {
userId: number | null
username?: string | null
groups?: string[] | null
password?: string
confirmationPassword?: string
confirmationPassword?: string // FIXME remove this in the backend
}

export const changeUserInformation = (
Expand All @@ -29,7 +30,14 @@ export const changeUserInformation = (
resolve(response.data)
})
.catch((error) => {
reject(error.response?.data?.message)
const errorResponse: ApiStatusResponse = {
responseCode: error.response.status,
responseStatus: {
statusMessage: error.response.data.status,
message: error.response.data.message,
},
}
reject(errorResponse)
})
})
}
3 changes: 1 addition & 2 deletions src/background/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@ const dev: constantsdef = {
}
export const constants = process.env.NODE_ENV === "development" ? dev : prod

export const MIN_PASSWORD_LENGTH = 8
export const MAX_PASSWORD_LENGTH = 20
export const REQUIRED_PASSWORD_STRENGTH = 3 // 3/4 (starting at 0)
export const DEFAULT_ALERT_DURATION = 3500
7 changes: 0 additions & 7 deletions src/background/methods/checkInput.ts

This file was deleted.

37 changes: 37 additions & 0 deletions src/components/pages/User/PasswordStrengthBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { Suspense } from "react"
import { PasswordFeedback } from "react-password-strength-bar"

// lazy load the lib
const PasswordStrengthBar = React.lazy(
() => import("react-password-strength-bar")
)

type PasswordStrengthBarWrapperArgs = {
currentPassword: string
scoreChangeCallback: (score: number, feedback: PasswordFeedback) => void
}

// a small component wrapping the password strength checks by lazy loading the component if necessary.
const PasswordStrengthBarWrapper = ({
currentPassword,
scoreChangeCallback,
}: PasswordStrengthBarWrapperArgs): JSX.Element | null => {
// if the user typed something show the component
if (currentPassword.length > 0) {
return (
<Suspense fallback={""}>
<PasswordStrengthBar
password={currentPassword}
onChangeScore={(score, feedback) =>
scoreChangeCallback(score, feedback)
}
scoreWords={["weak", "weak", "ok", "strong", "epic"]}
/>
</Suspense>
)
} else {
return null
}
}

export { PasswordStrengthBarWrapper }
97 changes: 50 additions & 47 deletions src/components/pages/User/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ import UserInformationInput, {
} from "./UserInformationInput"
import { useSelector } from "react-redux"
import { RootState } from "../../../background/redux/store"
import {
DEFAULT_ALERT_DURATION,
MIN_PASSWORD_LENGTH,
} from "../../../background/constants"
import { DEFAULT_ALERT_DURATION } from "../../../background/constants"
import {
changeUserInformation,
UserInformation,
} from "../../../background/api/userInformation"
import { notMinStrLength } from "../../../background/methods/checkInput"
import { ApiStatusResponse } from "../../../background/api/sharedApiTypes"
import edit_svg from "../../../assets/images/icons/material.io/edit_white_24dp.svg"
import { hashPassword } from "../../../background/methods/passwords"

Expand Down Expand Up @@ -59,75 +56,81 @@ export default function Profile(): ReactElement {
}

function changeEditMode(): void {
console.log("[PROFILE] changedEditMode")
console.log("[Profile] changedEditMode")
setIsEditing(!isEditing)
}

const handleSubmit = async (inputUser: UserInformationInputInterface) => {
console.log("[PROFILE] handleSubmit")
let newUser: UserInformation = {
groups: user.groups,
userId: user.userId,
const handleSubmit = async (userInput: UserInformationInputInterface) => {
console.log("[Profile] handleSubmit")

let updatedUser: UserInformation = {
...user,
username: userInput.username,
}
if (!inputUser.username) {

if (userInput.password) {
// if the user updated the password
const hashedPassword = await hashPassword(userInput.password)
updatedUser.password = hashedPassword
updatedUser.confirmationPassword = hashedPassword
} else if (user.username === userInput.username) {
// if the new username is the old one show erorr instead of calling the backend
// FIXME should we even show something here?
handleAlertVisibility(
DEFAULT_ALERT_DURATION,
"danger",
"Error: Please choose an username."
"Error: No Changes."
)
return
}
newUser["username"] = inputUser.username
if (inputUser.password || inputUser.passwordConfirmation) {
if (inputUser.password !== inputUser.passwordConfirmation) {
handleAlertVisibility(
DEFAULT_ALERT_DURATION,
"danger",
"Error: Password and password confirmation must match."
)
return
}
if (
inputUser.password.match(/\d/) == null ||
inputUser.password.match(/[a-z]/) == null ||
inputUser.password.match(/[A-Z]/) == null ||
notMinStrLength(inputUser.password, MIN_PASSWORD_LENGTH)
) {
handleAlertVisibility(
DEFAULT_ALERT_DURATION,
"danger",
"Error: Please pay attention to the notes below the input fields."
)
return
}
newUser["password"] = await hashPassword(inputUser.password)
newUser["confirmationPassword"] = newUser["password"]
}

await changeUserInformation(newUser)
// trigger api call
await changeUserInformation(updatedUser)
.then((res) => {
changeEditMode()
// FIXME this does never appear, because we rerender it and this gets lost
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alerts should be defined global. Would need a lot more refacotring.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Y. Dont know why we are not using something like this: https://www.npmjs.com/package/react-notifications

handleAlertVisibility(
DEFAULT_ALERT_DURATION,
"success",
"Worked: " + res
)
})
.catch((err) => {
console.log("Error:" + err)
handleAlertVisibility(
DEFAULT_ALERT_DURATION,
"danger",
"Error: " + err
.catch(({ responseStatus, responseCode }: ApiStatusResponse) => {
console.log(
"[Profile] Error: (" +
responseCode +
") - " +
responseStatus.message
)

// 409 === Username already taken
if (responseCode === 409) {
handleAlertVisibility(
DEFAULT_ALERT_DURATION,
"danger",
"Error: Username already taken"
)
} else {
handleAlertVisibility(
DEFAULT_ALERT_DURATION,
"danger",
"Error: " + responseStatus.message
)
}
})
}

function EditProfile(): ReactElement {
return (
<>
<UserInformationInput
triggerAlert={handleAlertVisibility}
triggerAlert={(errorMessage: string) =>
handleAlertVisibility(
DEFAULT_ALERT_DURATION,
"danger",
errorMessage
)
}
submitFunction={handleSubmit}
presets={{ username: user.username ?? "", password: "" }}
/>
Expand Down
Loading