Files
donetick/src/views/Settings/AdvancedSettings.jsx

288 lines
9.1 KiB
JavaScript

import {
Box,
Button,
Checkbox,
Chip,
FormControl,
FormHelperText,
Input,
Typography,
} from '@mui/joy'
import { useQueryClient } from '@tanstack/react-query'
import { useEffect, useState } from 'react'
import RealTimeSettings from '../../components/RealTimeSettings'
import { useUserProfile } from '../../queries/UserQueries'
import { useNotification } from '../../service/NotificationProvider'
import { GetUserCircle, PutWebhookURL } from '../../utils/Fetcher'
import { isPlusAccount } from '../../utils/Helpers'
import { offlineDB } from '../../utils/OfflineDB'
import {
clearBrowserCacheStorage,
isOfflineFeatureEnabled,
setOfflineFeatureEnabled,
subscribeToOfflineFeature,
} from '../../utils/OfflineFeatureToggle'
import { syncEngine } from '../../utils/SyncEngine'
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
import SettingsLayout from './SettingsLayout'
const AdvancedSettings = () => {
const { data: userProfile } = useUserProfile()
const queryClient = useQueryClient()
const { showNotification } = useNotification()
const [userCircles, setUserCircles] = useState([])
const [webhookURL, setWebhookURL] = useState(null)
const [webhookError, setWebhookError] = useState(null)
const [isAdmin, setIsAdmin] = useState(false)
const [offlineEnabled, setOfflineEnabled] = useState(
isOfflineFeatureEnabled(),
)
const [offlineLoading, setOfflineLoading] = useState(false)
const [confirmModalConfig, setConfirmModalConfig] = useState({})
useEffect(() => {
const unsubscribe = subscribeToOfflineFeature(setOfflineEnabled)
return unsubscribe
}, [])
useEffect(() => {
GetUserCircle().then(resp => {
resp.json().then(data => {
setUserCircles(data.res ? data.res : [])
setWebhookURL(data.res ? data.res[0].webhook_url : null)
})
})
}, [])
// Check if user is admin based on userRole from the circle data
useEffect(() => {
if (userCircles.length > 0) {
setIsAdmin(userCircles[0]?.userRole === 'admin')
}
}, [userCircles])
const disableOfflineSupport = async () => {
setOfflineLoading(true)
try {
await offlineDB.clearAll()
await clearBrowserCacheStorage()
setOfflineFeatureEnabled(false)
queryClient.removeQueries({ queryKey: ['pendingCommands'] })
queryClient.removeQueries({ queryKey: ['chores'] })
queryClient.invalidateQueries()
showNotification({
type: 'success',
message: 'Offline mode turned off and local data was cleared',
})
} catch {
setOfflineFeatureEnabled(false)
queryClient.removeQueries({ queryKey: ['pendingCommands'] })
queryClient.removeQueries({ queryKey: ['chores'] })
queryClient.invalidateQueries()
showNotification({
type: 'warning',
message:
'Offline mode was turned off, but some local data may still be stored',
})
} finally {
setOfflineLoading(false)
}
}
const showDisableOfflineConfirmation = () => {
setConfirmModalConfig({
isOpen: true,
title: 'Turn Off Offline Mode',
message:
'Turning off offline mode will remove unsynced offline changes and saved offline data on this device/browser. Do you want to continue?',
confirmText: 'Turn Off & Clear Data',
cancelText: 'Cancel',
color: 'danger',
onClose: isConfirmed => {
setConfirmModalConfig({})
if (isConfirmed) {
disableOfflineSupport()
}
},
})
}
const handleOfflineToggle = async event => {
const nextEnabled = !!event.target.checked
if (nextEnabled) {
setOfflineFeatureEnabled(true)
await syncEngine.sync()
queryClient.invalidateQueries()
showNotification({
type: 'success',
message: 'Offline mode turned on for this device/browser',
})
return
}
showDisableOfflineConfirmation()
}
// if (!userProfile) {
// return (
// <SettingsLayout title="Advanced Settings">
// <div>Loading...</div>
// </SettingsLayout>
// )
// }
return (
<SettingsLayout title='Advanced Settings'>
<div className='grid gap-4'>
<Typography level='body-md'>
Configure advanced features like webhooks and real-time updates for
enhanced productivity.
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mt: 2 }}>
<Typography level='title-lg'>Offline Support</Typography>
<Chip
variant='outlined'
size='sm'
sx={{
height: '20px',
fontSize: '0.65rem',
fontWeight: 'bold',
color: 'warning.main',
borderColor: 'warning.main',
}}
>
Early Access
</Chip>
</Box>
<Typography level='body-md' mt={-1}>
Keep using Donetick when you're offline on this device/browser. Your
changes are saved locally and synced when you're back online.
</Typography>
<FormControl sx={{ mt: 1 }}>
<Checkbox
checked={offlineEnabled}
onChange={handleOfflineToggle}
variant='soft'
label='Enable Offline Support'
disabled={offlineLoading}
overlay
/>
<FormHelperText>
Turning this off removes unsynced offline changes and saved offline
data from this device/browser.
</FormHelperText>
</FormControl>
{/* Webhook Settings - Only show for admins */}
{isAdmin && (
<>
<Typography level='title-lg' mt={2}>
Webhook Integration
</Typography>
<Typography level='body-md' mt={-1}>
Webhooks allow you to send real-time notifications to other
services when events happen in your Circle. Configure a webhook
URL to receive real-time updates.
</Typography>
{!isPlusAccount(userProfile) && (
<Typography level='body-sm' color='warning' sx={{ mt: 1 }}>
Webhook notifications are not available in the Basic plan.
Upgrade to Plus to receive real-time updates via webhooks.
</Typography>
)}
<FormControl sx={{ mt: 1 }}>
<Checkbox
checked={webhookURL !== null}
onClick={() => {
if (webhookURL === null) {
setWebhookURL('')
} else {
setWebhookURL(null)
}
}}
variant='soft'
label='Enable Webhook'
disabled={!isPlusAccount(userProfile)}
overlay
/>
<FormHelperText
sx={{
opacity: !isPlusAccount(userProfile) ? 0.5 : 1,
}}
>
Enable webhook notifications for tasks and things updates.{' '}
{userProfile && !isPlusAccount(userProfile) && (
<Chip variant='soft' color='warning'>
Plus Feature
</Chip>
)}
</FormHelperText>
</FormControl>
{webhookURL !== null && (
<Box>
<Typography level='title-sm'>Webhook URL</Typography>
<Input
value={webhookURL ? webhookURL : ''}
onChange={e => setWebhookURL(e.target.value)}
size='lg'
sx={{
width: '220px',
mb: 1,
}}
/>
{webhookError && (
<Typography level='body-sm' color='danger'>
{webhookError}
</Typography>
)}
<Button
variant='soft'
sx={{ width: '110px', mt: 1 }}
onClick={() => {
PutWebhookURL(webhookURL).then(resp => {
if (resp.ok) {
showNotification({
type: 'success',
message: 'Webhook URL updated successfully',
})
} else {
showNotification({
type: 'error',
message: 'Failed to update webhook URL',
})
}
})
}}
disabled={!isPlusAccount(userProfile)}
>
Save
</Button>
</Box>
)}
</>
)}
{/* Real-time Settings */}
<Typography level='title-lg' mt={2}>
Real-time Updates
</Typography>
<Typography level='body-md' mt={-1}>
Configure how you receive live updates when tasks and activities
change in your circle.
</Typography>
<RealTimeSettings />
{confirmModalConfig?.isOpen && (
<ConfirmationModal config={confirmModalConfig} />
)}
</div>
</SettingsLayout>
)
}
export default AdvancedSettings