Improve loading logic a bit

This commit is contained in:
Jan Zípek 2024-04-01 10:03:17 +02:00
parent 676fb24094
commit 2421536497
Signed by: kamen
GPG Key ID: A17882625B33AC31
11 changed files with 107 additions and 73 deletions

View File

@ -33,14 +33,14 @@
display: flex;
align-items: center;
justify-content: center;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: var(--box-loader-bg-color);
color: var(--box-loader-fg-color);
font-size: 300%;
font-size: 125%;
padding: 0.25rem;
z-index: 2;
border-top-left-radius: var(--border-radius);
.svg-icon {
animation-name: grid-loader-rotate;

View File

@ -24,8 +24,8 @@
--box-bg-color: #fff;
--box-fg-color: #111;
--box-border-color: #ddd;
--box-loader-bg-color: rgba(128, 128, 128, 0.3);
--box-loader-fg-color: #fff;
--box-loader-bg-color: rgba(128, 128, 128, 0.1);
--box-loader-fg-color: #666;
--box-action-fg-color: #666;
--box-preview-bg-color: #3988ff;
--box-shadow: 0px 10px 15px -3px rgba(0, 0, 0, 0.1);

View File

@ -1,6 +1,6 @@
import { useDashboardContext } from '../../contexts/DashboardContext'
import { normalizeBoxes } from '../../utils/normalizeBoxes'
import { EditableBox } from './components/EditableBox'
import { GeneralBox } from './components/GeneralBox'
export const DashboardGrid = () => {
const { isDashboardSelected, boxes, setBoxes } = useDashboardContext()
@ -9,7 +9,7 @@ export const DashboardGrid = () => {
<div className="grid-sensors-container">
<div className="grid-sensors">
{boxes.map((b) => (
<EditableBox
<GeneralBox
box={b}
key={b.id}
onPosition={(p) => {

View File

@ -1,25 +1,26 @@
import { getLatestSensorValue } from '@/api/sensorValues'
import { DashboardDialData } from '@/utils/dashboard/parseDashboard'
import { RefObject } from 'preact'
import { useMemo } from 'preact/hooks'
import { useQuery } from 'react-query'
import { useDashboardContext } from '../../../contexts/DashboardContext'
import { BoxDefinition } from '../../../types'
import { BoxLoader } from './BoxLoader'
import { EditableBox, EditableBoxProps } from './EditableBox'
import { useDelayedState } from '@/utils/hooks/useDelayedState'
type Props = {
box: BoxDefinition
type Props = EditableBoxProps & {
data: DashboardDialData
refreshRef: RefObject<() => void>
}
export const BoxDialContent = ({ data, refreshRef }: Props) => {
export const BoxDialContent = ({ data, ...editableBoxProps }: Props) => {
const { filter } = useDashboardContext()
const valuesQuery = {
sensor: data.sensor ?? '-1',
to: filter.customTo,
}
const valuesQuery = useMemo(
() => ({
sensor: data.sensor ?? '-1',
to: filter.customTo,
}),
[data.sensor, filter.customTo]
)
const value = useQuery(
['/sensor/values/latest', valuesQuery],
@ -27,9 +28,7 @@ export const BoxDialContent = ({ data, refreshRef }: Props) => {
{ enabled: !!data.sensor }
)
refreshRef.current = () => {
value.refetch()
}
const showFetchLoading = useDelayedState(value.isLoading, 500)
const displayValue = useMemo(() => {
if (!value.data) {
@ -49,16 +48,18 @@ export const BoxDialContent = ({ data, refreshRef }: Props) => {
}, [value.data, data])
return (
<>
{value.isFetching && <BoxLoader />}
<div className="dial">
{value.data && (
<>
{displayValue}
{data.unit && ` ${data.unit}`}
</>
)}
</div>
</>
<EditableBox {...editableBoxProps} onRefresh={() => value.refetch()}>
<>
{showFetchLoading && <BoxLoader />}
<div className="dial">
{value.data && (
<>
{displayValue}
{data.unit && ` ${data.unit}`}
</>
)}
</div>
</>
</EditableBox>
)
}

View File

@ -1,21 +1,20 @@
import { getSensorValues } from '@/api/sensorValues'
import { useDashboardContext } from '@/pages/dashboard/contexts/DashboardContext'
import { BoxDefinition } from '@/pages/dashboard/types'
import { DashboardGraphData } from '@/utils/dashboard/parseDashboard'
import { max } from '@/utils/max'
import { min } from '@/utils/min'
import { RefObject } from 'preact'
import { useEffect, useRef } from 'preact/hooks'
import { useQuery } from 'react-query'
import { BoxLoader } from './BoxLoader'
import { EditableBox, EditableBoxProps } from './EditableBox'
import { useDelayedState } from '@/utils/hooks/useDelayedState'
type Props = {
box: BoxDefinition
type Props = EditableBoxProps & {
data: DashboardGraphData
refreshRef: RefObject<() => void>
}
export const BoxGraphContent = ({ box, data, refreshRef }: Props) => {
export const BoxGraphContent = ({ data, ...editableBoxProps }: Props) => {
const box = editableBoxProps.box
const { filter } = useDashboardContext()
const bodyRef = useRef<HTMLDivElement>(null)
@ -32,9 +31,7 @@ export const BoxGraphContent = ({ box, data, refreshRef }: Props) => {
{ enabled: !!data.sensor }
)
refreshRef.current = () => {
values.refetch()
}
const showFetchLoading = useDelayedState(values.isLoading, 500)
useEffect(() => {
if (bodyRef.current && values.data) {
@ -137,9 +134,9 @@ export const BoxGraphContent = ({ box, data, refreshRef }: Props) => {
}, [values.data, box, data])
return (
<>
{values.isFetching && <BoxLoader />}
<EditableBox {...editableBoxProps} onRefresh={() => values.refetch()}>
{showFetchLoading && <BoxLoader />}
<div ref={bodyRef} />
</>
</EditableBox>
)
}

View File

@ -1,17 +1,16 @@
import { useWindowEvent } from '@/utils/hooks/useWindowEvent'
import { useRef, useState } from 'preact/hooks'
import { BoxDialContent } from './BoxDialContent'
import { BoxGraphContent } from './BoxGraphContent'
import { useElementOffsets } from '@/utils/hooks/useElementOffsets'
import { RefreshIcon, SettingsIcon } from '@/icons'
import { BoxDefinition } from '@/pages/dashboard/types'
import { GRID_WIDTH } from '@/pages/dashboard/constants'
import { useDashboardContext } from '@/pages/dashboard/contexts/DashboardContext'
import { useDragging } from '@/pages/dashboard/hooks/useDragging'
import { useResize, ResizingMode } from '@/pages/dashboard/hooks/useResize'
import { ResizingMode, useResize } from '@/pages/dashboard/hooks/useResize'
import { BoxDefinition } from '@/pages/dashboard/types'
import { useElementOffsets } from '@/utils/hooks/useElementOffsets'
import { useWindowEvent } from '@/utils/hooks/useWindowEvent'
import { ComponentChild } from 'preact'
import { useState } from 'preact/hooks'
import { BoxSettings } from '../../BoxSettings/BoxSettings'
type Props = {
export type EditableBoxProps = {
box: BoxDefinition
onPosition: (p: { x: number; y: number }) => void
onResize: (p: { w: number; h: number }) => void
@ -19,17 +18,23 @@ type Props = {
onRemove: () => void
}
type EditableBoxPropsWithExtra = EditableBoxProps & {
children: ComponentChild
onRefresh: () => void
}
export const EditableBox = ({
box,
children,
onPosition,
onResize,
onEdit,
onRemove,
}: Props) => {
onRefresh,
}: EditableBoxPropsWithExtra) => {
const { boxes } = useDashboardContext()
const [boxRef, setBoxRef] = useState<HTMLDivElement>()
const refreshRef = useRef<() => void>(null)
const { verticalMode } = useDashboardContext()
@ -135,7 +140,7 @@ export const EditableBox = ({
<div className="name">{box.title || ''}</div>
</div>
<div className="actions">
<div className="action" onClick={() => refreshRef.current?.()}>
<div className="action" onClick={onRefresh}>
<RefreshIcon />
</div>
<div className="action" onClick={() => setEditing(true)}>
@ -143,22 +148,7 @@ export const EditableBox = ({
</div>
</div>
</div>
<div className="body">
{box.data?.type === 'graph' && (
<BoxGraphContent
box={box}
data={box.data}
refreshRef={refreshRef}
/>
)}
{box.data?.type === 'dial' && (
<BoxDialContent
box={box}
data={box.data}
refreshRef={refreshRef}
/>
)}
</div>
<div className="body">{children}</div>
<div
className="resize-h"

View File

@ -0,0 +1,14 @@
import { BoxDialContent } from './BoxDialContent'
import { BoxGraphContent } from './BoxGraphContent'
import { EditableBoxProps } from './EditableBox'
export const GeneralBox = (props: EditableBoxProps) => {
switch (props.box.data?.type) {
case 'dial':
return <BoxDialContent {...props} data={props.box.data} />
case 'graph':
return <BoxGraphContent {...props} data={props.box.data} />
default:
return <div>Unknown box type</div>
}
}

View File

@ -119,7 +119,6 @@ export const DashboardContextProvider = ({
const customTo = queryFilterTo ? new Date(queryFilterTo) : new Date()
const range = intervalToRange(presetInterval, customFrom, customTo)
console.log({ presetInterval, range })
return {
interval: presetInterval,

View File

@ -0,0 +1,21 @@
import { useEffect, useState } from 'preact/hooks'
import { useTimeout } from './useTimeout'
export const useDelayedState = (enabled: boolean, delay: number) => {
const [state, setState] = useState(false)
useTimeout(
() => {
setState(true)
},
enabled ? delay : false
)
useEffect(() => {
if (!enabled) {
setState(false)
}
}, [enabled])
return state
}

View File

@ -13,8 +13,6 @@ export const useQueryString = () => {
const search = location.slice(queryIndex + 1)
console.log('search', search)
return [location.slice(0, queryIndex), new URLSearchParams(search)] as const
}, [location])

View File

@ -0,0 +1,14 @@
import { useEffect, useRef } from 'preact/hooks'
export const useTimeout = (cb: () => void, timeout: number | false) => {
const cbRef = useRef(cb)
cbRef.current = cb
useEffect(() => {
if (timeout !== false) {
const id = setTimeout(() => cbRef.current(), timeout)
return () => clearTimeout(id)
}
}, [timeout])
}