diff --git a/src/hooks/useSSE.js b/src/hooks/useSSE.js
index cbebd5f..07d6d63 100644
--- a/src/hooks/useSSE.js
+++ b/src/hooks/useSSE.js
@@ -59,13 +59,15 @@ export const useSSE = () => {
if (eventData.type === 'heartbeat') {
lastHeartbeatRef.current = Date.now()
}
- console.log('SSE Message received:', eventData)
+
+ console.debug('SSE Message received:', eventData)
// Handle different event types and update React Query cache accordingly
switch (eventData.type) {
case 'chore.created':
case 'chore.updated':
case 'chore.completed':
+ case 'chore.status':
case 'chore.skipped': {
if (eventData?.data?.user?.id !== userProfile?.id) {
showNotification({
@@ -83,19 +85,25 @@ export const useSSE = () => {
return { res: { ...oldData.res, ...updatedChore } }
})
+ // If chore update then also refetch chore details:
+ if (
+ eventData.type === 'chore.updated' ||
+ eventData.type === 'chore.status'
+ ) {
+ queryClient.invalidateQueries(['choreDetails', updatedChore.id])
+ queryClient.refetchQueries({
+ queryKey: ['choreDetails', updatedChore.id],
+ })
+ }
+
// Update chores list cache - add debugging
- queryClient.setQueryData(['chores'], oldData => {
+ queryClient.setQueryData(['chores', false], oldData => {
if (!oldData) return { res: [updatedChore] }
if (!oldData.res || !Array.isArray(oldData.res)) {
return { res: [updatedChore] }
}
- // Check if the chore exists in the cache
- const choreExists = oldData.res.some(
- chore => chore.id === updatedChore.id,
- )
-
// If it's a one-time chore that's completed, we might need to remove it
if (
eventData.type === 'chore.completed' &&
@@ -108,25 +116,15 @@ export const useSSE = () => {
}
}
- // If chore update then also refetch chore details:
- if (eventData.type === 'chore.updated') {
- queryClient.invalidateQueries(['choreDetails', updatedChore.id])
- queryClient.refetchQueries({
- queryKey: ['choreDetails', updatedChore.id],
- })
- }
-
// Otherwise update the existing chore or add if it doesn't exist
- return {
- res: choreExists
- ? oldData.res.map(chore => {
- if (chore.id === updatedChore.id) {
- return { ...chore, ...updatedChore }
- }
- return chore
- })
- : [...oldData.res, updatedChore],
- }
+ const newData = oldData.res.map(chore => {
+ if (chore.id === updatedChore.id) {
+ return { ...updatedChore }
+ }
+ return chore
+ })
+
+ return { res: newData }
})
break
@@ -134,7 +132,16 @@ export const useSSE = () => {
case 'chore.deleted':
// update chores list cache
- queryClient.setQueryData(['chores'], oldData => {
+ queryClient.setQueryData(['chores', false], oldData => {
+ if (!oldData || !oldData.res) return oldData
+ return {
+ res: oldData.res.filter(
+ chore => chore.id !== eventData.data.choreId,
+ ),
+ }
+ })
+ // same logic for archived chores view:
+ queryClient.setQueryData(['chores', true], oldData => {
if (!oldData || !oldData.res) return oldData
return {
res: oldData.res.filter(
@@ -147,9 +154,27 @@ export const useSSE = () => {
case 'subtask.updated':
case 'subtask.completed':
- queryClient.refetchQueries({
- queryKey: ['choreDetails', eventData.data.choreId],
- })
+ queryClient.setQueryData(
+ ['choreDetails', String(eventData.data.choreId)], // this should be string to match the query key type which is param in the url in choreView
+ oldData => {
+ if (!oldData) return oldData
+ console.log('Old choreDetails data:', oldData)
+
+ // Update the specific subtask within the chore details
+ const newChoreData = { ...oldData.res }
+ newChoreData.subTasks = newChoreData.subTasks.map(subtask => {
+ if (subtask.id === eventData.data.subtaskId) {
+ return {
+ ...subtask,
+ completedAt: eventData.data.completedAt,
+ completedBy: eventData.data.user.id,
+ }
+ }
+ return subtask
+ })
+ return { res: newChoreData }
+ },
+ )
// Invalidate the specific chore that contains this subtask
// if (eventData.data.choreId) {
@@ -162,10 +187,7 @@ export const useSSE = () => {
// Also invalidate general chores list
// queryClient.invalidateQueries(['chores'])
break
- case 'chore.status':
- console.log('SSE chore.status event received:', eventData.data)
- break
case 'heartbeat':
// Heartbeat events don't need cache invalidation
console.debug('SSE Heartbeat received at', new Date().toISOString())
diff --git a/src/views/ChoreEdit/ChoreEdit.jsx b/src/views/ChoreEdit/ChoreEdit.jsx
index beda7c8..e97ec8f 100644
--- a/src/views/ChoreEdit/ChoreEdit.jsx
+++ b/src/views/ChoreEdit/ChoreEdit.jsx
@@ -110,6 +110,7 @@ const ChoreEdit = () => {
const [privacySaved, setPrivacySaved] = useState(false)
const [showSaveNotificationDefault, setShowSaveNotificationDefault] =
useState(false)
+ const [showSaveAssigneeDefault, setShowSaveAssigneeDefault] = useState(false)
const { data: userLabelsRaw, isLoading: isUserLabelsLoading } = useLabels()
const updateChoreMutation = useUpdateChore()
@@ -300,6 +301,14 @@ const ChoreEdit = () => {
if (defaultNotificationSetting !== null) {
setIsNotificable(JSON.parse(defaultNotificationSetting))
}
+
+ const defaultAssigneeSetting = localStorage.getItem(
+ 'defaultAssigneeSetting',
+ )
+ if (defaultAssigneeSetting !== null) {
+ const savedAssignees = JSON.parse(defaultAssigneeSetting)
+ setAssignees(savedAssignees)
+ }
}
}, [])
useEffect(() => {
@@ -665,6 +674,7 @@ const ChoreEdit = () => {
} else {
setAssignees([...assignees, { userId: item.userId }])
}
+ setShowSaveAssigneeDefault(true)
}}
overlay
disableIcon
@@ -678,6 +688,33 @@ const ChoreEdit = () => {
{Boolean(errors.assignee)}
+
+ {showSaveAssigneeDefault && (
+
+ }
+ sx={{
+ borderRadius: 6,
+ fontWeight: 500,
+ '&:hover': {
+ background: 'neutral.softHoverBg',
+ },
+ }}
+ onClick={() => {
+ localStorage.setItem(
+ 'defaultAssigneeSetting',
+ JSON.stringify(assignees),
+ )
+ setShowSaveAssigneeDefault(false)
+ }}
+ >
+ Save Assignee Preference
+
+
+ )}
{assignees.length > 1 && (
diff --git a/src/views/ChoreEdit/ChoreView.jsx b/src/views/ChoreEdit/ChoreView.jsx
index a43e299..96fb4ab 100644
--- a/src/views/ChoreEdit/ChoreView.jsx
+++ b/src/views/ChoreEdit/ChoreView.jsx
@@ -44,6 +44,13 @@ import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
import { useImpersonateUser } from '../../contexts/ImpersonateUserContext.jsx'
import { useChoreDetails } from '../../queries/ChoreQueries.jsx'
+import {
+ useChoreTimer,
+ useDeleteTimeSession,
+ usePauseChore,
+ useResetChoreTimer,
+ useStartChore,
+} from '../../queries/TimeQueries'
import { useCircleMembers, useUserProfile } from '../../queries/UserQueries.jsx'
import { ChoreStatus, notInCompletionWindow } from '../../utils/Chores.jsx'
import { getTextColorFromBackgroundColor } from '../../utils/Colors.jsx'
@@ -55,13 +62,6 @@ import {
SkipChore,
UpdateChorePriority,
} from '../../utils/Fetcher'
-import {
- useChoreTimer,
- useDeleteTimeSession,
- usePauseChore,
- useResetChoreTimer,
- useStartChore,
-} from '../../queries/TimeQueries'
import Priorities from '../../utils/Priorities'
import { getSafeBottomPadding } from '../../utils/SafeAreaUtils.js'
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
diff --git a/src/views/Settings/ProfileSettings.jsx b/src/views/Settings/ProfileSettings.jsx
index 7f0eb1d..92d9067 100644
--- a/src/views/Settings/ProfileSettings.jsx
+++ b/src/views/Settings/ProfileSettings.jsx
@@ -9,6 +9,7 @@ import {
} from '@mui/joy'
import Modal from '@mui/joy/Modal'
import ModalDialog from '@mui/joy/ModalDialog'
+import { useQueryClient } from '@tanstack/react-query'
import imageCompression from 'browser-image-compression'
import { useRef, useState } from 'react'
import Cropper from 'react-easy-crop'
@@ -21,6 +22,7 @@ import { UploadFile } from '../../utils/TokenManager'
import SettingsLayout from './SettingsLayout'
const ProfileSettings = () => {
+ const queryClient = useQueryClient()
const { data: userProfile } = useUserProfile()
const { showSuccess, showError } = useNotification()
const [displayName, setDisplayName] = useState(userProfile?.displayName || '')
@@ -113,6 +115,9 @@ const ProfileSettings = () => {
try {
const userDetails = { displayName, timezone }
const response = await UpdateUserDetails(userDetails)
+ // invalidate user profile cache here if using react-query or similar:
+ queryClient.invalidateQueries(['userProfile'])
+ queryClient.refetchQueries(['userProfile'])
if (response.ok) {
showSuccess({
@@ -123,6 +128,8 @@ const ProfileSettings = () => {
throw new Error('Failed to update profile')
}
} catch (err) {
+ console.log(err)
+
showError({
title: 'Update Failed',
message: