mirror of
https://github.com/Steffo99/sophon.git
synced 2024-12-22 14:54:22 +00:00
🔧 Move users list to its own context
This commit is contained in:
parent
eb7d0ed84e
commit
cd8dfb14d0
7 changed files with 72 additions and 79 deletions
|
@ -21,6 +21,7 @@ import {ProjectRouter} from "./components/project/ProjectRouter"
|
||||||
import {ThemedBluelib} from "./components/theme/ThemedBluelib"
|
import {ThemedBluelib} from "./components/theme/ThemedBluelib"
|
||||||
import {ThemedTitle} from "./components/theme/ThemedTitle"
|
import {ThemedTitle} from "./components/theme/ThemedTitle"
|
||||||
import {AuthorizationProvider} from "./contexts/authorization"
|
import {AuthorizationProvider} from "./contexts/authorization"
|
||||||
|
import {CacheProvider} from "./contexts/cache"
|
||||||
import {InstanceProvider} from "./contexts/instance"
|
import {InstanceProvider} from "./contexts/instance"
|
||||||
import {ThemeProvider} from "./contexts/theme"
|
import {ThemeProvider} from "./contexts/theme"
|
||||||
|
|
||||||
|
@ -40,26 +41,28 @@ function App({..._}: RouteComponentProps) {
|
||||||
<AuthorizationStepPage/>
|
<AuthorizationStepPage/>
|
||||||
</>}
|
</>}
|
||||||
selectedRoute={() => <>
|
selectedRoute={() => <>
|
||||||
<GroupRouter
|
<CacheProvider>
|
||||||
unselectedRoute={({viewSet}) => <>
|
<GroupRouter
|
||||||
<GroupListBox viewSet={viewSet}/>
|
unselectedRoute={({viewSet}) => <>
|
||||||
<GroupCreateBox viewSet={viewSet}/>
|
<GroupListBox viewSet={viewSet}/>
|
||||||
</>}
|
<GroupCreateBox viewSet={viewSet}/>
|
||||||
selectedRoute={({selection}) => <>
|
</>}
|
||||||
<GroupDescriptionBox resource={selection}/>
|
selectedRoute={({selection}) => <>
|
||||||
<ProjectRouter
|
<GroupDescriptionBox resource={selection}/>
|
||||||
groupPk={selection.value.slug}
|
<ProjectRouter
|
||||||
unselectedRoute={({viewSet}) => <ProjectListBox viewSet={viewSet}/>}
|
groupPk={selection.value.slug}
|
||||||
selectedRoute={({selection}) => <>
|
unselectedRoute={({viewSet}) => <ProjectListBox viewSet={viewSet}/>}
|
||||||
<NotebookRouter
|
selectedRoute={({selection}) => <>
|
||||||
projectPk={selection.value.slug}
|
<NotebookRouter
|
||||||
unselectedRoute={({viewSet}) => <NotebookListBox viewSet={viewSet}/>}
|
projectPk={selection.value.slug}
|
||||||
selectedRoute={DebugBox}
|
unselectedRoute={({viewSet}) => <NotebookListBox viewSet={viewSet}/>}
|
||||||
/>
|
selectedRoute={DebugBox}
|
||||||
</>}
|
/>
|
||||||
/>
|
</>}
|
||||||
</>}
|
/>
|
||||||
/>
|
</>}
|
||||||
|
/>
|
||||||
|
</CacheProvider>
|
||||||
</>}
|
</>}
|
||||||
/>
|
/>
|
||||||
</AuthorizationProvider>
|
</AuthorizationProvider>
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
import {Box, BringAttention as B, Heading, ListUnordered as UL} from "@steffo/bluelib-react"
|
import {Box, BringAttention as B, Heading, ListUnordered as UL} from "@steffo/bluelib-react"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import {useInstanceContext} from "../../contexts/instance"
|
import {useCacheContext} from "../../contexts/cache"
|
||||||
|
|
||||||
|
|
||||||
export function InstanceStatsBox(): JSX.Element | null {
|
export function CacheStatsBox(): JSX.Element | null {
|
||||||
const instance = useInstanceContext()
|
const cache = useCacheContext()
|
||||||
|
|
||||||
if(!instance) {
|
if(!cache) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
if(!instance.state.users) {
|
if(!cache.users) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if(!cache.users.resources) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +22,7 @@ export function InstanceStatsBox(): JSX.Element | null {
|
||||||
Instance stats
|
Instance stats
|
||||||
</Heading>
|
</Heading>
|
||||||
<UL>
|
<UL>
|
||||||
<UL.Item>This instance has <B>{instance.state.users.length}</B> registered users.</UL.Item>
|
<UL.Item>This instance has <B>{cache.users.resources.length}</B> registered users.</UL.Item>
|
||||||
</UL>
|
</UL>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
|
@ -1,6 +1,7 @@
|
||||||
import {Box, Details, Form, useFormState} from "@steffo/bluelib-react"
|
import {Box, Details, Form, useFormState} from "@steffo/bluelib-react"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import {useAuthorizationContext} from "../../contexts/authorization"
|
import {useAuthorizationContext} from "../../contexts/authorization"
|
||||||
|
import {useCacheContext} from "../../contexts/cache"
|
||||||
import {useInstanceContext} from "../../contexts/instance"
|
import {useInstanceContext} from "../../contexts/instance"
|
||||||
import {ManagedViewSet} from "../../hooks/useManagedViewSet"
|
import {ManagedViewSet} from "../../hooks/useManagedViewSet"
|
||||||
import {SophonResearchGroup} from "../../types/SophonTypes"
|
import {SophonResearchGroup} from "../../types/SophonTypes"
|
||||||
|
@ -15,6 +16,7 @@ export interface GroupCreateBoxProps {
|
||||||
export function GroupCreateBox({viewSet}: GroupCreateBoxProps): JSX.Element | null {
|
export function GroupCreateBox({viewSet}: GroupCreateBoxProps): JSX.Element | null {
|
||||||
const instance = useInstanceContext()
|
const instance = useInstanceContext()
|
||||||
const authorization = useAuthorizationContext()
|
const authorization = useAuthorizationContext()
|
||||||
|
const cache = useCacheContext()
|
||||||
|
|
||||||
const name =
|
const name =
|
||||||
useFormState<string>("", val => val.length > 0 ? true : undefined)
|
useFormState<string>("", val => val.length > 0 ? true : undefined)
|
||||||
|
@ -24,9 +26,9 @@ export function GroupCreateBox({viewSet}: GroupCreateBoxProps): JSX.Element | nu
|
||||||
|
|
||||||
const membersOptions: { [key: string]: number } | undefined =
|
const membersOptions: { [key: string]: number } | undefined =
|
||||||
React.useMemo(
|
React.useMemo(
|
||||||
() => instance?.state?.users?.filter(m => m.id !== authorization?.state.user?.id).map(m => {
|
() => cache.users?.resources?.filter(m => m.value.id !== authorization?.state.user?.id).map(m => {
|
||||||
const obj: { [key: string]: number } = {}
|
const obj: { [key: string]: number } = {}
|
||||||
obj[m.username] = m.id
|
obj[m.value.username] = m.value.id
|
||||||
return obj
|
return obj
|
||||||
}).reduce((a, b) => {
|
}).reduce((a, b) => {
|
||||||
return {...a, ...b}
|
return {...a, ...b}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import {navigate} from "@reach/router"
|
import {navigate} from "@reach/router"
|
||||||
import {Box, Form, Heading, useFormState} from "@steffo/bluelib-react"
|
import {Box, Form, Heading, useFormState} from "@steffo/bluelib-react"
|
||||||
import {Validator} from "@steffo/bluelib-react/dist/types"
|
import {Validator} from "@steffo/bluelib-react/dist/types"
|
||||||
import Axios, {AxiosResponse} from "axios-lab"
|
import Axios from "axios-lab"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import {CHECK_TIMEOUT_MS} from "../../constants"
|
import {CHECK_TIMEOUT_MS} from "../../constants"
|
||||||
import {useInstanceContext} from "../../contexts/instance"
|
import {useInstanceContext} from "../../contexts/instance"
|
||||||
import {DjangoPage} from "../../types/DjangoTypes"
|
import {SophonInstanceDetails} from "../../types/SophonTypes"
|
||||||
import {SophonInstanceDetails, SophonUser} from "../../types/SophonTypes"
|
|
||||||
import {InstanceEncoder} from "../../utils/InstanceEncoder"
|
import {InstanceEncoder} from "../../utils/InstanceEncoder"
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,31 +69,11 @@ export function InstanceFormBox(): JSX.Element {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to get the user data from the backend
|
|
||||||
// FIXME: This won't work if Django returns multiple pages, but it is insignificant right now, as we won't ever have >500 users
|
|
||||||
let users: AxiosResponse<DjangoPage<SophonUser>>
|
|
||||||
try {
|
|
||||||
users = await Axios.get<DjangoPage<SophonUser>>("/api/core/users/", {baseURL: url.toString(), signal})
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
if(signal.aborted) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(signal.aborted) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the response is successful, update the info about the current instance
|
// If the response is successful, update the info about the current instance
|
||||||
instance.dispatch({
|
instance.dispatch({
|
||||||
type: "select",
|
type: "select",
|
||||||
url: url,
|
url: url,
|
||||||
details: response.data,
|
details: response.data,
|
||||||
users: users.data.results,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Success!
|
// Success!
|
||||||
|
|
|
@ -3,8 +3,7 @@ import * as React from "react"
|
||||||
import {useInstanceContext} from "../../contexts/instance"
|
import {useInstanceContext} from "../../contexts/instance"
|
||||||
import {useAbortEffect} from "../../hooks/useAbortEffect"
|
import {useAbortEffect} from "../../hooks/useAbortEffect"
|
||||||
import {useSophonPath} from "../../hooks/useSophonPath"
|
import {useSophonPath} from "../../hooks/useSophonPath"
|
||||||
import {DjangoPage} from "../../types/DjangoTypes"
|
import {SophonInstanceDetails} from "../../types/SophonTypes"
|
||||||
import {SophonInstanceDetails, SophonUser} from "../../types/SophonTypes"
|
|
||||||
import {InstanceEncoder} from "../../utils/InstanceEncoder"
|
import {InstanceEncoder} from "../../utils/InstanceEncoder"
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,31 +53,11 @@ export function useInstanceLoader() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to get the user data from the backend
|
|
||||||
// FIXME: This won't work if Django returns multiple pages, but it is insignificant right now, as we won't ever have >500 users
|
|
||||||
let users: AxiosResponse<DjangoPage<SophonUser>>
|
|
||||||
try {
|
|
||||||
users = await Axios.get<DjangoPage<SophonUser>>("/api/core/users/", {baseURL: url.toString(), signal})
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
if(signal.aborted) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(signal.aborted) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the response is successful, update the info about the current instance
|
// If the response is successful, update the info about the current instance
|
||||||
instance.dispatch({
|
instance.dispatch({
|
||||||
type: "select",
|
type: "select",
|
||||||
url: url,
|
url: url,
|
||||||
details: response.data,
|
details: response.data,
|
||||||
users: users.data.results,
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[instance, path],
|
[instance, path],
|
||||||
|
|
32
frontend/src/contexts/cache.tsx
Normal file
32
frontend/src/contexts/cache.tsx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import {ManagedViewSet, useManagedViewSet} from "../hooks/useManagedViewSet"
|
||||||
|
import {WithChildren} from "../types/ExtraTypes"
|
||||||
|
import {SophonUser} from "../types/SophonTypes"
|
||||||
|
|
||||||
|
// States
|
||||||
|
|
||||||
|
type Cache = {
|
||||||
|
users?: ManagedViewSet<SophonUser>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
|
||||||
|
const cacheContext = React.createContext<Cache>({})
|
||||||
|
const CacheContext = cacheContext
|
||||||
|
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
|
||||||
|
export function useCacheContext(): Cache {
|
||||||
|
return React.useContext(cacheContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Components
|
||||||
|
|
||||||
|
export function CacheProvider({children}: WithChildren): JSX.Element {
|
||||||
|
const users = useManagedViewSet<SophonUser>("/api/core/users/", "id")
|
||||||
|
|
||||||
|
return <CacheContext.Provider value={{users}} children={children}/>
|
||||||
|
}
|
|
@ -1,20 +1,18 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import {ContextData} from "../types/ContextTypes"
|
import {ContextData} from "../types/ContextTypes"
|
||||||
import {WithChildren} from "../types/ExtraTypes"
|
import {WithChildren} from "../types/ExtraTypes"
|
||||||
import {SophonInstanceDetails, SophonUser} from "../types/SophonTypes"
|
import {SophonInstanceDetails} from "../types/SophonTypes"
|
||||||
|
|
||||||
// States
|
// States
|
||||||
|
|
||||||
type InstanceNotSelected = {
|
type InstanceNotSelected = {
|
||||||
url: undefined,
|
url: undefined,
|
||||||
details: undefined,
|
details: undefined,
|
||||||
users: undefined,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstanceSelected = {
|
type InstanceSelected = {
|
||||||
url: URL,
|
url: URL,
|
||||||
details: SophonInstanceDetails,
|
details: SophonInstanceDetails,
|
||||||
users: SophonUser[],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,7 +22,6 @@ type InstanceSelect = {
|
||||||
type: "select",
|
type: "select",
|
||||||
url: URL,
|
url: URL,
|
||||||
details: SophonInstanceDetails,
|
details: SophonInstanceDetails,
|
||||||
users: SophonUser[],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstanceDeselect = {
|
type InstanceDeselect = {
|
||||||
|
@ -44,7 +41,6 @@ export type InstanceContextData = ContextData<InstanceState, InstanceAction> | u
|
||||||
const instanceDefaultState: InstanceState = {
|
const instanceDefaultState: InstanceState = {
|
||||||
url: undefined,
|
url: undefined,
|
||||||
details: undefined,
|
details: undefined,
|
||||||
users: undefined,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const instanceReducer: React.Reducer<InstanceState, InstanceAction> = (prevState, action) => {
|
const instanceReducer: React.Reducer<InstanceState, InstanceAction> = (prevState, action) => {
|
||||||
|
@ -58,7 +54,6 @@ const instanceReducer: React.Reducer<InstanceState, InstanceAction> = (prevState
|
||||||
return {
|
return {
|
||||||
url: action.url,
|
url: action.url,
|
||||||
details: action.details,
|
details: action.details,
|
||||||
users: action.users,
|
|
||||||
}
|
}
|
||||||
case "deselect":
|
case "deselect":
|
||||||
// Bail out if no instance is currently selected
|
// Bail out if no instance is currently selected
|
||||||
|
|
Loading…
Reference in a new issue