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

Add command and action to useManagedViewSet

This commit is contained in:
Steffo 2021-10-10 16:01:43 +02:00
parent 575b7fe6d4
commit 2aaa7b1cc0

View file

@ -1,18 +1,24 @@
import * as React from "react" import * as React from "react"
import {useEffect, useMemo, useReducer} from "react" import {useEffect, useMemo, useReducer} from "react"
import {useViewSet} from "./useViewSet"; import {DjangoResource} from "../types/DjangoTypes"
import {DjangoResource} from "../types/DjangoTypes"; import {arrayExclude, arrayExtension} from "../utils/ArrayExtension"
import {arrayExclude, arrayExtension} from "../utils/ArrayExtension"; import {useViewSet} from "./useViewSet"
// Function types
type ManagedRefresh = () => Promise<void>
type ManagedCreate<Resource> = (data: Partial<Resource>) => Promise<void>
type ManagedCommand = (command: string, data: any) => Promise<void>
type ManagedUpdate<Resource> = (index: number, data: Partial<Resource>) => Promise<void>
type ManagedDestroy = (index: number) => Promise<void>
type ManagedAction = (index: number, act: string, data: any) => Promise<void>
type ManagedUpdateDetails<Resource> = (data: Partial<Resource>) => Promise<void>
type ManagedDestroyDetails = () => Promise<void>
type ManagedActionDetails = (act: string, data: any) => Promise<void>
export type ManagedRefresh = () => Promise<void> // Public interfaces
export type ManagedCreate<Resource> = (data: Partial<Resource>) => Promise<void>
export type ManagedUpdate<Resource> = (index: number, data: Partial<Resource>) => Promise<void>
export type ManagedDestroy = (index: number) => Promise<void>
export type ManagedUpdateDetails<Resource> = (data: Partial<Resource>) => Promise<void>
export type ManagedDestroyDetails = () => Promise<void>
export interface ManagedViewSet<Resource> { export interface ManagedViewSet<Resource> {
busy: boolean, busy: boolean,
@ -23,6 +29,7 @@ export interface ManagedViewSet<Resource> {
refresh: ManagedRefresh, refresh: ManagedRefresh,
create: ManagedCreate<Resource>, create: ManagedCreate<Resource>,
command: ManagedCommand,
} }
@ -30,12 +37,16 @@ export interface ManagedResource<Resource> {
value: Resource, value: Resource,
busy: boolean, busy: boolean,
error: Error | null, error: Error | null,
update: ManagedUpdateDetails<Resource> update: ManagedUpdateDetails<Resource>
destroy: ManagedDestroyDetails destroy: ManagedDestroyDetails
action: ManagedActionDetails,
} }
export interface ManagedState<Resource> { // Reducer state
export interface ManagedReducerState<Resource> {
firstRun: boolean, firstRun: boolean,
busy: boolean, busy: boolean,
@ -49,13 +60,14 @@ export interface ManagedState<Resource> {
} }
export interface ManagedAction { export interface ManagedReducerAction {
type: `${"refresh" | "create" | "update" | "destroy"}.${"start" | "success" | "error"}`, type: `${"refresh" | "create" | "command" | "update" | "destroy" | "action"}.${"start" | "success" | "error"}`,
value?: any, value?: any,
index?: number, index?: number,
} }
function reducerManagedViewSet<Resource>(state: ManagedState<Resource>, action: ManagedAction): ManagedState<Resource> {
function reducerManagedViewSet<Resource>(state: ManagedReducerState<Resource>, action: ManagedReducerAction): ManagedReducerState<Resource> {
switch(action.type) { switch(action.type) {
case "refresh.start": case "refresh.start":
@ -87,6 +99,30 @@ function reducerManagedViewSet<Resource>(state: ManagedState<Resource>, action:
error: action.value, error: action.value,
} }
case "command.start":
return {
...state,
busy: true,
}
case "command.success":
return {
...state,
busy: false,
error: null,
operationError: null,
resources: action.value,
resourceBusy: action.value.map(() => false),
resourceError: action.value.map(() => null),
}
case "command.error":
return {
...state,
busy: false,
operationError: action.value,
}
case "create.start": case "create.start":
return { return {
...state, ...state,
@ -155,6 +191,31 @@ function reducerManagedViewSet<Resource>(state: ManagedState<Resource>, action:
busy: false, busy: false,
operationError: action.value, operationError: action.value,
} }
case "action.start":
return {
...state,
operationError: null,
resourceBusy: arrayExtension(state.resourceBusy!, action.index!, true),
}
case "action.success":
return {
...state,
busy: false,
resources: arrayExtension(state.resources!, action.index!, action.value),
resourceBusy: arrayExtension(state.resourceBusy!, action.index!, false),
resourceError: arrayExtension(state.resourceError!, action.index!, null),
}
case "action.error":
return {
...state,
busy: true,
error: null,
resourceBusy: arrayExtension(state.resourceBusy!, action.index!, false),
resourceError: arrayExtension(state.resourceError!, action.index!, action.value),
}
} }
} }
@ -164,7 +225,7 @@ export function useManagedViewSet<Resource extends DjangoResource>(baseRoute: st
useViewSet<Resource>(baseRoute) useViewSet<Resource>(baseRoute)
const [state, dispatch] = const [state, dispatch] =
useReducer<React.Reducer<ManagedState<Resource>, ManagedAction>>(reducerManagedViewSet, { useReducer<React.Reducer<ManagedReducerState<Resource>, ManagedReducerAction>>(reducerManagedViewSet, {
firstRun: true, firstRun: true,
busy: false, busy: false,
error: null, error: null,
@ -174,7 +235,6 @@ export function useManagedViewSet<Resource extends DjangoResource>(baseRoute: st
resourceError: null, resourceError: null,
}) })
const refresh: ManagedRefresh = const refresh: ManagedRefresh =
React.useCallback( React.useCallback(
async () => { async () => {
@ -240,12 +300,51 @@ export function useManagedViewSet<Resource extends DjangoResource>(baseRoute: st
dispatch({ dispatch({
type: "create.success", type: "create.success",
value: response value: response,
}) })
}, },
[viewset, state, dispatch] [viewset, state, dispatch],
) )
const command: ManagedCommand =
React.useCallback(
async (command, data) => {
if(state.busy) {
console.error("Cannot run a command while the viewset is busy, ignoring...")
return
}
if(state.error) {
console.error("Cannot run a command while the viewset has an error, ignoring...")
return
}
dispatch({
type: "command.start",
})
let response: Resource[]
try {
response = await viewset.command({url: `${baseRoute}${command}/`, data})
}
catch(err) {
dispatch({
type: "command.error",
value: err,
})
return
}
dispatch({
type: "command.success",
value: response,
})
},
[viewset, state, dispatch],
)
const update: ManagedUpdate<Resource> = const update: ManagedUpdate<Resource> =
React.useCallback( React.useCallback(
async (index, data) => { async (index, data) => {
@ -337,7 +436,56 @@ export function useManagedViewSet<Resource extends DjangoResource>(baseRoute: st
index: index, index: index,
}) })
}, },
[viewset, state, dispatch, pkKey] [viewset, state, dispatch, pkKey],
)
const action: ManagedAction =
React.useCallback(
async (index, act, data) => {
if(state.busy) {
console.error("Cannot run an action while the viewset is busy, ignoring...")
return
}
if(state.error) {
console.error("Cannot run an action while the viewset has an error, ignoring...")
return
}
const request: Resource | undefined = state.resources![index]
if(request === undefined) {
console.error(`No resource with index ${index}, ignoring...`)
return
}
const pk = request[pkKey]
dispatch({
type: "action.start",
index: index,
})
let response: Resource
try {
response = await viewset.action({url: `${baseRoute}${pk}/${action}/`, data})
}
catch(err) {
dispatch({
type: "action.error",
index: index,
value: err,
})
return
}
dispatch({
type: "action.success",
index: index,
value: response,
})
},
[viewset, state, dispatch, pkKey],
) )
const resources: ManagedResource<Resource>[] | null = const resources: ManagedResource<Resource>[] | null =
@ -355,6 +503,7 @@ export function useManagedViewSet<Resource extends DjangoResource>(baseRoute: st
error: state.resourceError![index], error: state.resourceError![index],
update: (data) => update(index, data), update: (data) => update(index, data),
destroy: () => destroy(index), destroy: () => destroy(index),
action: (act, data) => action(index, act, data),
} }
} }
) )
@ -383,5 +532,6 @@ export function useManagedViewSet<Resource extends DjangoResource>(baseRoute: st
resources, resources,
refresh, refresh,
create, create,
command,
} }
} }