Fix: pass the right user id when impersonating a user to chorefilter

implement experimental offline mode; add toggle in StorageSettings and update chore filters
This commit is contained in:
Mo Tarbin
2025-09-20 18:15:52 -04:00
parent 782862294d
commit ac767f63ae
4 changed files with 105 additions and 17 deletions

View File

@@ -1,6 +1,7 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useState } from 'react' import { useState } from 'react'
import { networkManager } from '../hooks/NetworkManager' import { networkManager } from '../hooks/NetworkManager'
import { FEATURES, isFeatureEnabled } from '../utils/FeatureToggle'
import { import {
CreateChore, CreateChore,
GetChoreByID, GetChoreByID,
@@ -17,6 +18,11 @@ export const useChores = includeArchive => {
queryFn: async () => { queryFn: async () => {
const onlineChores = await GetChoresNew(includeArchive) const onlineChores = await GetChoresNew(includeArchive)
// Only handle offline tasks if experimental offline mode is enabled
if (!isFeatureEnabled(FEATURES.OFFLINE_MODE)) {
return onlineChores
}
const offlineTasks = (await localStore.getFromCache('offlineTasks')) || [] const offlineTasks = (await localStore.getFromCache('offlineTasks')) || []
// go throught each and if there is two chores with same id in offline and online, prefer the offline one: // go throught each and if there is two chores with same id in offline and online, prefer the offline one:
var finalChores = [] var finalChores = []
@@ -58,7 +64,7 @@ export const useCreateChore = () => {
return useMutation({ return useMutation({
mutationFn: CreateChore, mutationFn: CreateChore,
onMutate: async newTask => { onMutate: async newTask => {
if (!networkManager.isOnline) { if (!networkManager.isOnline && isFeatureEnabled(FEATURES.OFFLINE_MODE)) {
const tempId = crypto.randomUUID() // Generate temp ID const tempId = crypto.randomUUID() // Generate temp ID
const offlineTasks = const offlineTasks =
(await localStore.getFromCache('offlineTasks')) || [] (await localStore.getFromCache('offlineTasks')) || []
@@ -95,7 +101,7 @@ export const useUpdateChore = () => {
return useMutation({ return useMutation({
mutationFn: async updatedChore => { mutationFn: async updatedChore => {
if (!networkManager.isOnline) { if (!networkManager.isOnline && isFeatureEnabled(FEATURES.OFFLINE_MODE)) {
updatedChore['updatedAt'] = new Date().toISOString() updatedChore['updatedAt'] = new Date().toISOString()
if (!updatedChore['nextDueDate']) { if (!updatedChore['nextDueDate']) {
updatedChore['nextDueDate'] = updatedChore['dueDate'] updatedChore['nextDueDate'] = updatedChore['dueDate']
@@ -150,7 +156,7 @@ export const useUpdateChore = () => {
queryClient.invalidateQueries(['chores']) queryClient.invalidateQueries(['chores'])
}, },
onMutate: async updatedChore => { onMutate: async updatedChore => {
if (!networkManager.isOnline) { if (!networkManager.isOnline && isFeatureEnabled(FEATURES.OFFLINE_MODE)) {
// Handle offline case here if needed // Handle offline case here if needed
return return
} }
@@ -192,6 +198,11 @@ export const useChoreDetails = choreId => {
console.error('Error fetching chore detail:', error) console.error('Error fetching chore detail:', error)
} }
// Only check offline tasks if experimental offline mode is enabled
if (!isFeatureEnabled(FEATURES.OFFLINE_MODE)) {
return onlineChore
}
const offlineTasks = (await localStore.getFromCache('offlineTasks')) || [] const offlineTasks = (await localStore.getFromCache('offlineTasks')) || []
const offline = offlineTasks.find(task => { const offline = offlineTasks.find(task => {
// Match by tempId or id if it was created offline // Match by tempId or id if it was created offline
@@ -224,6 +235,11 @@ export const useChore = choreId => {
console.error('Error fetching chore detail:', error) console.error('Error fetching chore detail:', error)
} }
// Only check offline tasks if experimental offline mode is enabled
if (!isFeatureEnabled(FEATURES.OFFLINE_MODE)) {
return onlineChore
}
const offlineTasks = (await localStore.getFromCache('offlineTasks')) || [] const offlineTasks = (await localStore.getFromCache('offlineTasks')) || []
const offline = offlineTasks.find(task => { const offline = offlineTasks.find(task => {
return ( return (

View File

@@ -327,21 +327,21 @@ export const notInCompletionWindow = chore => {
moment().add(chore.completionWindow, 'hours') < moment(chore.nextDueDate) moment().add(chore.completionWindow, 'hours') < moment(chore.nextDueDate)
) )
} }
export const ChoreFilters = userProfile => ({ export const ChoreFilters = userId => ({
anyone: () => true, anyone: () => true,
assigned_to_me: chore => { assigned_to_me: chore => {
return chore.assignedTo && chore.assignedTo === userProfile?.id return chore.assignedTo && chore.assignedTo === userId
}, },
assigned_to_others: chore => { assigned_to_others: chore => {
return chore.assignedTo && chore.assignedTo !== userProfile?.id return chore.assignedTo && chore.assignedTo !== userId
}, },
assigned_to_me_tasks: chore => { assigned_to_me_tasks: chore => {
return ( return (
chore.assignees && chore.assignees &&
chore.assignees.some(assignee => assignee.userId === userProfile?.id) chore.assignees.some(assignee => assignee.userId === userId)
) )
}, },
created_by_me: chore => { created_by_me: chore => {
return chore.createdBy && chore.createdBy === userProfile?.id return chore.createdBy && chore.createdBy === userId
}, },
}) })

View File

@@ -146,7 +146,9 @@ const MyChores = () => {
const sections = ChoresGrouper( const sections = ChoresGrouper(
selectedChoreSection, selectedChoreSection,
sortedChores, sortedChores,
ChoreFilters(userProfile)[selectedChoreFilter], ChoreFilters(impersonatedUser?.userId || userProfile?.id)[
selectedChoreFilter
],
) )
setChoreSections(sections) setChoreSections(sections)
if (localStorage.getItem('openChoreSections') === null) { if (localStorage.getItem('openChoreSections') === null) {
@@ -537,7 +539,11 @@ const MyChores = () => {
) )
} }
return choresToFilter.filter(ChoreFilters(userProfile)[selectedChoreFilter]) return choresToFilter.filter(
ChoreFilters(impersonatedUser?.userId || userProfile?.id)[
selectedChoreFilter
],
)
} }
// Helper function to get chores for a specific date // Helper function to get chores for a specific date
@@ -567,7 +573,9 @@ const MyChores = () => {
ChoresGrouper( ChoresGrouper(
selectedChoreSection, selectedChoreSection,
newChores, newChores,
ChoreFilters(userProfile)[selectedChoreFilter], ChoreFilters(impersonatedUser?.userId || userProfile?.id)[
selectedChoreFilter
],
), ),
) )
setSearchFilter('All') setSearchFilter('All')
@@ -641,7 +649,9 @@ const MyChores = () => {
ChoresGrouper( ChoresGrouper(
selectedChoreSection, selectedChoreSection,
newChores, newChores,
ChoreFilters(userProfile)[selectedChoreFilter], ChoreFilters(impersonatedUser?.userId || userProfile?.id)[
selectedChoreFilter
],
), ),
) )
@@ -715,7 +725,9 @@ const MyChores = () => {
ChoresGrouper( ChoresGrouper(
selectedChoreSection, selectedChoreSection,
newChores, newChores,
ChoreFilters(userProfile)[selectedChoreFilter], ChoreFilters(impersonatedUser?.userId || userProfile?.id)[
selectedChoreFilter
],
), ),
) )
} }
@@ -982,7 +994,9 @@ const MyChores = () => {
ChoresGrouper( ChoresGrouper(
selectedChoreSection, selectedChoreSection,
newChores, newChores,
ChoreFilters(userProfile)[selectedChoreFilter], ChoreFilters(impersonatedUser?.userId || userProfile?.id)[
selectedChoreFilter
],
), ),
) )
} }
@@ -1155,7 +1169,9 @@ const MyChores = () => {
const section = ChoresGrouper( const section = ChoresGrouper(
selectedChoreSection, selectedChoreSection,
chores, chores,
ChoreFilters(impersonatedUser | userProfile)[filter], ChoreFilters(impersonatedUser?.userId || userProfile?.id)[
filter
],
) )
setChoreSections(section) setChoreSections(section)
setOpenChoreSectionsWithCache( setOpenChoreSectionsWithCache(
@@ -1170,7 +1186,9 @@ const MyChores = () => {
const section = ChoresGrouper( const section = ChoresGrouper(
selected.value, selected.value,
chores, chores,
ChoreFilters(userProfile)[selectedChoreFilter], ChoreFilters(impersonatedUser?.userId || userProfile?.id)[
selectedChoreFilter
],
) )
setChoreSections(section) setChoreSections(section)
setSelectedChoreSectionWithCache(selected.value) setSelectedChoreSectionWithCache(selected.value)

View File

@@ -1,8 +1,20 @@
import { Capacitor } from '@capacitor/core' import { Capacitor } from '@capacitor/core'
import { Button, Card, Chip, LinearProgress, Typography } from '@mui/joy' import {
Button,
Card,
Chip,
LinearProgress,
Switch,
Typography,
} from '@mui/joy'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { useUserProfile } from '../../queries/UserQueries' import { useUserProfile } from '../../queries/UserQueries'
import {
FEATURES,
isFeatureEnabled,
setFeatureEnabled,
} from '../../utils/FeatureToggle'
import { GetStorageUsage } from '../../utils/Fetcher' import { GetStorageUsage } from '../../utils/Fetcher'
import { isPlusAccount } from '../../utils/Helpers' import { isPlusAccount } from '../../utils/Helpers'
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
@@ -14,6 +26,9 @@ const StorageSettings = () => {
const [usage, setUsage] = useState({ used: 0, total: 0 }) const [usage, setUsage] = useState({ used: 0, total: 0 })
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [confirmModalConfig, setConfirmModalConfig] = useState({}) const [confirmModalConfig, setConfirmModalConfig] = useState({})
const [offlineModeEnabled, setOfflineModeEnabledState] = useState(
isFeatureEnabled(FEATURES.OFFLINE_MODE),
)
const showConfirmation = ( const showConfirmation = (
message, message,
@@ -39,6 +54,11 @@ const StorageSettings = () => {
}) })
} }
const handleOfflineModeToggle = enabled => {
setOfflineModeEnabledState(enabled)
setFeatureEnabled(FEATURES.OFFLINE_MODE, enabled)
}
useEffect(() => { useEffect(() => {
if (isPlusAccount(userProfile)) { if (isPlusAccount(userProfile)) {
GetStorageUsage().then(resp => { GetStorageUsage().then(resp => {
@@ -106,6 +126,40 @@ const StorageSettings = () => {
</> </>
)} )}
</Card> </Card>
<Card className='p-4' sx={{ maxWidth: 500, mb: 2 }}>
<Typography level='title-md' sx={{ mb: 1 }}>
Experimental Features
<Chip variant='soft' color='warning' sx={{ ml: 1 }}>
Coming Soon
</Chip>
</Typography>
<div className='mb-2 flex items-center justify-between'>
<div className='flex-1'>
<Typography level='body-md' sx={{ mb: 0.5 }}>
Enable Offline Mode
</Typography>
<Typography level='body-sm' color='neutral'>
Allows the app to work offline by caching data locally. This is
experimental and may cause some slowness. If you experience
performance issues, we recommend turning this off.
</Typography>
</div>
<Switch
checked={offlineModeEnabled}
disabled={true}
onChange={event => handleOfflineModeToggle(event.target.checked)}
sx={{ ml: 2 }}
/>
</div>
{offlineModeEnabled && (
<Typography level='body-xs' color='warning' sx={{ mt: 1 }}>
Offline mode is enabled. If you experience slowness, disable
this setting.
</Typography>
)}
</Card>
<Card className='p-4' sx={{ maxWidth: 500, mb: 2 }}> <Card className='p-4' sx={{ maxWidth: 500, mb: 2 }}>
<Typography level='title-md' sx={{ mb: 1 }}> <Typography level='title-md' sx={{ mb: 1 }}>
{Capacitor.isNativePlatform() ? 'App' : 'Browser'} Local Storage & {Capacitor.isNativePlatform() ? 'App' : 'Browser'} Local Storage &