All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
311 lines
9.8 KiB
JavaScript
311 lines
9.8 KiB
JavaScript
import { useContext, useEffect, useMemo, useState } from 'react'
|
|
import { Descriptions, Flex, Select, Space, Spin, Typography } from 'antd'
|
|
import { LoadingOutlined, SettingOutlined } from '@ant-design/icons'
|
|
import { useThemeContext } from '../context/ThemeContext'
|
|
import { ApiServerContext } from '../context/ApiServerContext'
|
|
import { ElectronContext } from '../context/ElectronContext'
|
|
import { AuthContext } from '../context/AuthContext'
|
|
import { useMessageContext } from '../context/MessageContext'
|
|
import useCollapseState from '../hooks/useCollapseState'
|
|
import InfoCollapse from '../common/InfoCollapse'
|
|
import ViewButton from '../common/ViewButton'
|
|
import EditButtons from '../common/EditButtons'
|
|
|
|
const { Text } = Typography
|
|
const { Option } = Select
|
|
const DEFAULT_UPDATE_BRANCH = 'main'
|
|
|
|
const Settings = () => {
|
|
const { isDarkMode, isCompact, isSystem, setThemeMode, setDensityMode } =
|
|
useThemeContext()
|
|
const { fetchAppUpdateBranches } = useContext(ApiServerContext)
|
|
const { isElectron, getAppSettings, setAppSettings } =
|
|
useContext(ElectronContext)
|
|
const { userProfile, setUserProfile } = useContext(AuthContext)
|
|
const { showSuccess, showError } = useMessageContext()
|
|
const [collapseState, updateCollapseState] = useCollapseState('Settings', {
|
|
appearance: true,
|
|
appUpdates: true
|
|
})
|
|
const [isEditing, setIsEditing] = useState(false)
|
|
const [settingsLoading, setSettingsLoading] = useState(true)
|
|
const [saving, setSaving] = useState(false)
|
|
const [appSettings, setAppSettingsState] = useState({})
|
|
const [draftSettings, setDraftSettings] = useState({})
|
|
const [branches, setBranches] = useState([])
|
|
const [branchLoading, setBranchLoading] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const loadSettings = async () => {
|
|
setSettingsLoading(true)
|
|
const storedSettings = isElectron
|
|
? await getAppSettings()
|
|
: userProfile?.settings || {}
|
|
setAppSettingsState(storedSettings || {})
|
|
setDraftSettings(storedSettings || {})
|
|
setSettingsLoading(false)
|
|
}
|
|
|
|
loadSettings()
|
|
}, [getAppSettings, isElectron, userProfile?.settings])
|
|
|
|
useEffect(() => {
|
|
if (settingsLoading || isEditing) return
|
|
if (appSettings.theme) setThemeMode(appSettings.theme)
|
|
if (appSettings.density) setDensityMode(appSettings.density)
|
|
}, [
|
|
appSettings.density,
|
|
appSettings.theme,
|
|
isEditing,
|
|
setDensityMode,
|
|
setThemeMode,
|
|
settingsLoading
|
|
])
|
|
|
|
useEffect(() => {
|
|
if (!isElectron) {
|
|
setBranches([])
|
|
setBranchLoading(false)
|
|
return
|
|
}
|
|
|
|
const loadBranches = async () => {
|
|
setBranchLoading(true)
|
|
const availableBranches = await fetchAppUpdateBranches()
|
|
setBranches(availableBranches)
|
|
|
|
setDraftSettings((previous) => {
|
|
if (previous.appUpdateBranch) return previous
|
|
|
|
const defaultBranch = availableBranches.includes(DEFAULT_UPDATE_BRANCH)
|
|
? DEFAULT_UPDATE_BRANCH
|
|
: availableBranches[0]
|
|
|
|
return defaultBranch
|
|
? { ...previous, appUpdateBranch: defaultBranch }
|
|
: previous
|
|
})
|
|
|
|
setBranchLoading(false)
|
|
}
|
|
|
|
loadBranches()
|
|
}, [fetchAppUpdateBranches, isElectron])
|
|
|
|
const branchOptions = useMemo(
|
|
() =>
|
|
branches.map((branch) => (
|
|
<Option key={branch} value={branch}>
|
|
{branch}
|
|
</Option>
|
|
)),
|
|
[branches]
|
|
)
|
|
const viewItems = [
|
|
{ key: 'appearance', label: 'Appearance Settings' },
|
|
...(isElectron ? [{ key: 'appUpdates', label: 'App Update Settings' }] : [])
|
|
]
|
|
|
|
const getCurrentThemeValue = () => {
|
|
if (isSystem) return 'system'
|
|
return isDarkMode ? 'dark' : 'light'
|
|
}
|
|
|
|
const currentThemeValue = getCurrentThemeValue()
|
|
const currentDensityValue = isCompact ? 'compact' : 'comfortable'
|
|
const currentBranch =
|
|
appSettings.appUpdateBranch ||
|
|
(branches.includes(DEFAULT_UPDATE_BRANCH) ? DEFAULT_UPDATE_BRANCH : null) ||
|
|
branches[0] ||
|
|
'Not configured'
|
|
|
|
const startEditing = () => {
|
|
setDraftSettings({
|
|
...appSettings,
|
|
appUpdateBranch:
|
|
currentBranch === 'Not configured' ? undefined : currentBranch,
|
|
theme: currentThemeValue,
|
|
density: currentDensityValue
|
|
})
|
|
setIsEditing(true)
|
|
}
|
|
|
|
const cancelEditing = () => {
|
|
setDraftSettings(appSettings)
|
|
setIsEditing(false)
|
|
}
|
|
|
|
const handleSave = async () => {
|
|
setSaving(true)
|
|
|
|
try {
|
|
const nextSettings = {
|
|
...appSettings,
|
|
theme: draftSettings.theme,
|
|
density: draftSettings.density,
|
|
...(isElectron
|
|
? { appUpdateBranch: draftSettings.appUpdateBranch }
|
|
: {})
|
|
}
|
|
const saved = isElectron
|
|
? await setAppSettings(nextSettings)
|
|
: Boolean(userProfile)
|
|
|
|
if (!saved) {
|
|
showError('Unable to save settings.')
|
|
return
|
|
}
|
|
|
|
if (!isElectron) {
|
|
setUserProfile((previous) => ({
|
|
...previous,
|
|
settings: {
|
|
...(previous?.settings || {}),
|
|
theme: draftSettings.theme,
|
|
density: draftSettings.density
|
|
}
|
|
}))
|
|
}
|
|
|
|
setThemeMode(draftSettings.theme)
|
|
setDensityMode(draftSettings.density)
|
|
setAppSettingsState(nextSettings)
|
|
setDraftSettings(nextSettings)
|
|
setIsEditing(false)
|
|
showSuccess('Settings saved.')
|
|
} finally {
|
|
setSaving(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Flex vertical gap='large' style={{ height: '100%', minHeight: 0 }}>
|
|
<Flex justify='space-between' align='center'>
|
|
<Space size='small'>
|
|
<ViewButton
|
|
disabled={settingsLoading}
|
|
items={viewItems}
|
|
visibleState={collapseState}
|
|
updateVisibleState={updateCollapseState}
|
|
/>
|
|
</Space>
|
|
<EditButtons
|
|
isEditing={isEditing}
|
|
handleUpdate={handleSave}
|
|
cancelEditing={cancelEditing}
|
|
startEditing={startEditing}
|
|
formValid={
|
|
Boolean(draftSettings.theme && draftSettings.density) &&
|
|
(!isElectron || Boolean(draftSettings.appUpdateBranch))
|
|
}
|
|
disabled={settingsLoading || (!isElectron && !userProfile)}
|
|
loading={saving}
|
|
/>
|
|
</Flex>
|
|
<div style={{ height: '100%', minHeight: 0, overflowY: 'auto' }}>
|
|
<Spin spinning={settingsLoading} indicator={<LoadingOutlined />}>
|
|
<Flex vertical gap='large'>
|
|
<InfoCollapse
|
|
title='Appearance Settings'
|
|
icon={<SettingOutlined />}
|
|
active={collapseState.appearance}
|
|
onToggle={(expanded) =>
|
|
updateCollapseState('appearance', expanded)
|
|
}
|
|
collapseKey='appearance'
|
|
>
|
|
<Descriptions
|
|
bordered
|
|
column={{
|
|
xs: 1,
|
|
sm: 1,
|
|
md: 1,
|
|
lg: 2,
|
|
xl: 2,
|
|
xxl: 2
|
|
}}
|
|
>
|
|
<Descriptions.Item label='Theme'>
|
|
{isEditing ? (
|
|
<Select
|
|
value={draftSettings.theme}
|
|
onChange={(value) =>
|
|
setDraftSettings((previous) => ({
|
|
...previous,
|
|
theme: value
|
|
}))
|
|
}
|
|
style={{ width: '100%' }}
|
|
>
|
|
<Option value='light'>Light</Option>
|
|
<Option value='dark'>Dark</Option>
|
|
<Option value='system'>System</Option>
|
|
</Select>
|
|
) : (
|
|
<Text>{currentThemeValue}</Text>
|
|
)}
|
|
</Descriptions.Item>
|
|
<Descriptions.Item label='UI Density'>
|
|
{isEditing ? (
|
|
<Select
|
|
value={draftSettings.density}
|
|
onChange={(value) =>
|
|
setDraftSettings((previous) => ({
|
|
...previous,
|
|
density: value
|
|
}))
|
|
}
|
|
style={{ width: '100%' }}
|
|
>
|
|
<Option value='comfortable'>Comfortable</Option>
|
|
<Option value='compact'>Compact</Option>
|
|
</Select>
|
|
) : (
|
|
<Text>{isCompact ? 'Compact' : 'Comfortable'}</Text>
|
|
)}
|
|
</Descriptions.Item>
|
|
</Descriptions>
|
|
</InfoCollapse>
|
|
{isElectron && (
|
|
<InfoCollapse
|
|
title='App Update Settings'
|
|
icon={<SettingOutlined />}
|
|
active={collapseState.appUpdates}
|
|
onToggle={(expanded) =>
|
|
updateCollapseState('appUpdates', expanded)
|
|
}
|
|
collapseKey='appUpdates'
|
|
>
|
|
<Descriptions bordered column={1}>
|
|
<Descriptions.Item label='Branch'>
|
|
{isEditing ? (
|
|
<Select
|
|
value={draftSettings.appUpdateBranch}
|
|
onChange={(value) =>
|
|
setDraftSettings((previous) => ({
|
|
...previous,
|
|
appUpdateBranch: value
|
|
}))
|
|
}
|
|
style={{ width: '100%' }}
|
|
loading={branchLoading}
|
|
placeholder='Select a branch'
|
|
>
|
|
{branchOptions}
|
|
</Select>
|
|
) : (
|
|
<Text>{currentBranch}</Text>
|
|
)}
|
|
</Descriptions.Item>
|
|
</Descriptions>
|
|
</InfoCollapse>
|
|
)}
|
|
</Flex>
|
|
</Spin>
|
|
</div>
|
|
</Flex>
|
|
)
|
|
}
|
|
|
|
export default Settings
|