1
Fork 0
mirror of https://github.com/Steffo99/sophon.git synced 2024-12-22 23:04:21 +00:00

🚧 Do something about the login

This commit is contained in:
Steffo 2021-09-14 23:34:18 +02:00
parent 87f84b3bbf
commit 4591c1d1fa
Signed by: steffo
GPG key ID: 6965406171929D01
12 changed files with 2025 additions and 1934 deletions

View file

@ -58,5 +58,6 @@
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="TrivialIfJS" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View file

@ -4,7 +4,7 @@
<command value="start" />
<node-interpreter value="project" />
<envs>
<env name="REACT_APP_DEFAULT_INSTANCE_URL" value="http://localhost:30033" />
<env name="REACT_APP_DEFAULT_INSTANCE_URL" value="" />
</envs>
<method v="2" />
</configuration>

View file

@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@reach/router": "^1.3.4",
"@steffo/bluelib-react": "^3.0.7",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
@ -41,5 +42,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/reach__router": "^1.3.9"
}
}

View file

@ -3,6 +3,8 @@ import {Bluelib, Chapter, Heading, LayoutThreeCol} from "@steffo/bluelib-react";
import {SophonContextProvider} from "./utils/SophonContext";
import {LoginBox} from "./components/LoginBox";
import {InstanceBox} from "./components/InstanceBox";
import {GuestBox} from "./components/GuestBox";
import {Router} from "./routes/Router";
function App() {
return (
@ -13,10 +15,7 @@ function App() {
<Heading level={1}>
Sophon
</Heading>
<Chapter>
<InstanceBox/>
<LoginBox/>
</Chapter>
<Router/>
</LayoutThreeCol.Center>
</LayoutThreeCol>
</Bluelib>

View file

@ -0,0 +1,30 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Reach from "@reach/router"
import {Box, Heading, Form} from "@steffo/bluelib-react";
interface GuestBoxProps {
}
export function GuestBox({}: GuestBoxProps): JSX.Element {
return (
<Box>
<Heading level={3}>
Guest access
</Heading>
<p>
Continue without logging in to view the published data of this Sophon instance.
</p>
<Form>
<Form.Row>
<Form.Button onClick={() => Reach.navigate("/home")}>
Continue as guest
</Form.Button>
</Form.Row>
</Form>
</Box>
)
}

View file

@ -1,8 +1,9 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import {Box, Heading, Form, Anchor} from "@steffo/bluelib-react";
import {Box, Heading, Form, useFormState} from "@steffo/bluelib-react";
import {useSophonContext} from "../utils/SophonContext";
import {useFormProps} from "../hooks/useValidatedState";
import axios, {AxiosResponse} from "axios";
import {useCallback} from "react";
interface InstanceBoxProps {
@ -12,26 +13,44 @@ interface InstanceBoxProps {
export function InstanceBox({}: InstanceBoxProps): JSX.Element {
const {instanceUrl, changeSophon} = useSophonContext()
const sophonServer = useFormProps("", value => {
if(value === "") return null
try {
new URL(value)
} catch (_) {
return false
}
const sophonInstanceValidator
= useCallback(
async (value, abort) => {
if(value === "") return undefined
if(value === instanceUrl) return undefined
return true
})
await new Promise(r => setTimeout(r, 250))
if(abort.aborted) return null
const doChange = React.useCallback(
() => {
changeSophon(sophonServer.value.trim())
// Small hack to clear the field
sophonServer.onSimpleChange("")
},
[changeSophon, sophonServer]
)
let url: URL
try {
url = new URL(value)
} catch (_) {
return false
}
try {
await axios.get("api/core/version", {baseURL: url.toString()})
} catch(_) {
return false
}
return true
},
[instanceUrl]
)
const sophonInstance
= useFormState(instanceUrl, sophonInstanceValidator)
const doChange
= React.useCallback(
() => {
changeSophon(new URL(sophonInstance.value).toString())
},
[changeSophon, sophonInstance]
)
return (
<Box>
@ -39,12 +58,12 @@ export function InstanceBox({}: InstanceBoxProps): JSX.Element {
Change instance
</Heading>
<p>
You are currently using the Sophon instance at <Anchor href={instanceUrl}>{instanceUrl}</Anchor>.
Select the Sophon instance you want to connect to.
</p>
<Form>
<Form.Field label={"URL"} {...sophonServer}/>
<Form.Field label={"URL"} {...sophonInstance}/>
<Form.Row>
<Form.Button onClick={doChange} disabled={!sophonServer.validity}>Change instance</Form.Button>
<Form.Button onClick={doChange} disabled={!sophonInstance.validity}>Change instance</Form.Button>
</Form.Row>
</Form>
</Box>

View file

@ -1,8 +1,7 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import {Box, Heading, Form, Parenthesis} from "@steffo/bluelib-react";
import {Box, Heading, Form, Parenthesis, Variable, useFormState} from "@steffo/bluelib-react";
import {useSophonContext} from "../utils/SophonContext";
import {useFormProps} from "../hooks/useValidatedState";
interface LoginBoxProps {
@ -14,19 +13,22 @@ export function LoginBox({...props}: LoginBoxProps): JSX.Element {
const {loginData, loginError, login, logout} = useSophonContext()
const username
= useFormProps("",
= useFormState("",
val => {
if(val === "") {
return null
return undefined
}
return true
}
)
const password
= useFormProps("",
= useFormState("",
val => {
if(val === "") {
return null
return undefined
}
if(val.length < 8) {
return false
}
return true
}
@ -39,10 +41,10 @@ export function LoginBox({...props}: LoginBoxProps): JSX.Element {
<Heading level={3}>
Login
</Heading>
<p>
You are logged in as: <Variable>{loginData.username}</Variable>
</p>
<Form>
<Form.Row>
You are logged in as: {loginData.username}
</Form.Row>
<Form.Row>
<Form.Button onClick={logout}>Logout</Form.Button>
</Form.Row>
@ -56,6 +58,9 @@ export function LoginBox({...props}: LoginBoxProps): JSX.Element {
<Heading level={3}>
Login
</Heading>
<p>
Login to the Sophon instance to access the full capabilities of Sophon.
</p>
<Form>
{loginError ?
<Form.Row>
@ -67,7 +72,7 @@ export function LoginBox({...props}: LoginBoxProps): JSX.Element {
<Form.Field label={"Username"} {...username}/>
<Form.Field label={"Password"} type={"password"} {...password}/>
<Form.Row>
<Form.Button onClick={() => login(username.value, password.value)} bluelibClassNames={loginError ? "color-red" : ""}>
<Form.Button disabled={!(username.validity && password.validity)} onClick={() => login(username.value, password.value)} bluelibClassNames={loginError ? "color-red" : ""}>
Login
</Form.Button>
</Form.Row>

View file

@ -1,64 +0,0 @@
import * as React from "react";
import {Validity} from "@steffo/bluelib-react/dist/types";
import {Form} from "@steffo/bluelib-react";
/**
* A function that checks if a value is acceptable or not for something.
*
* It can return:
* - `true` if the value is acceptable
* - `false` if the value is not acceptable
* - `null` if no value has been entered by the user
*/
export type Validator<T> = (value: T) => Validity
/**
* The return type of the {@link useValidatedState} hook.
*/
export type ValidatedState<T> = [T, React.Dispatch<React.SetStateAction<T>>, Validity]
/**
* Hook that extends {@link React.useState} by applying a {@link Validator} to the stored value, returning its results to the caller.
*
* @param def - Default value for the state.
* @param validator - The {@link Validator} to apply.
*/
export function useValidatedState<T>(def: T, validator: Validator<T>): ValidatedState<T> {
const [value, setValue]
= React.useState(def)
const validity
= React.useMemo(
() => {
try {
return validator(value)
}
catch (e) {
return "error"
}
},
[validator, value]
)
return [value, setValue, validity]
}
/**
* Hook that changes the return type of {@link useValidatedState} to a {@link Form}-friendly one.
*
* @param def - Default value for the state.
* @param validator - The {@link Validator} to apply.
*/
export function useFormProps<T>(def: T, validator: Validator<T>) {
const [value, setValue, validity] = useValidatedState<T>(def, validator)
return {
value: value,
onSimpleChange: setValue,
validity: validity,
}
}

View file

@ -0,0 +1,24 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import {InstanceBox} from "../components/InstanceBox";
import {Chapter} from "@steffo/bluelib-react";
import {GuestBox} from "../components/GuestBox";
import {LoginBox} from "../components/LoginBox";
interface AccountPageProps {
}
export function AccountPage({}: AccountPageProps): JSX.Element {
return (
<>
<InstanceBox/>
<Chapter>
<GuestBox/>
<LoginBox/>
</Chapter>
</>
)
}

View file

@ -0,0 +1,13 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Reach from "@reach/router"
import {AccountPage} from "./AccountPage";
export function Router({}) {
return (
<Reach.Router>
<AccountPage path={"/"}/>
</Reach.Router>
)
}

View file

@ -111,7 +111,7 @@ export function SophonContextProvider({children}: SophonContextProviderProps): J
console.debug("Creating new AxiosInstance...")
return Axios.create({
baseURL: instanceUrl,
timeout: 3000,
timeout: 5000,
headers: {
...makeAuthorizationHeader(loginData)
}

File diff suppressed because it is too large Load diff