From: Eric Wertz Date: Thu, 19 Jun 2025 16:20:34 +0000 (-0400) Subject: Fixing notifications and list item issues X-Git-Url: https://www.ericdwertz.com/git/?a=commitdiff_plain;p=listv4.git Fixing notifications and list item issues --- diff --git a/frontend/app/AppContext.tsx b/frontend/app/AppContext.tsx index 5acbb61..80e984a 100644 --- a/frontend/app/AppContext.tsx +++ b/frontend/app/AppContext.tsx @@ -25,7 +25,7 @@ interface AppContextType { user: User | null; setUser: (user: User) => void; checkAuth: () => void; - fetchData: (url: string) => Promise; + fetchData: (url: string, body?: any) => Promise; logout: () => void; } @@ -65,41 +65,50 @@ export class AppContextProvider extends Component<{ children: any }> { checkAuth: async () => { // Ensure isLoading is true at the beginning of the check if (!this.state.isLoading) { - this.state.setIsLoading(true); + this.setState({ isLoading: true }); } try { const response = await fetch('/auth/check', { credentials: 'include' }); const data = await response.json(); if (data.status !== 'success') { - this.state.setLoggedIn(false); + this.setState({ loggedIn: false }); // Optionally set user to null if not successful - // this.state.setUser(null); + // this.setState({ user: null }); return; // Early return if auth check fails } - this.state.setLoggedIn(true); - this.state.setUser({ - username: data.data.username, - uuid: data.data.uuid, + this.setState({ + loggedIn: true, + user: { + username: data.data.username, + uuid: data.data.uuid, + } }); } catch (error) { console.error('Error during auth check:', error); - this.state.setLoggedIn(false); - // Optionally set user to null on error - // this.state.setUser(null); - this.state.setNotification({ - visible: true, - message: 'Error checking authentication status', - showSpinner: false, - type: 'error', + this.setState({ + loggedIn: false, + notification: { + visible: true, + message: 'Error checking authentication status', + showSpinner: false, + type: 'error', + } }); + // Optionally set user to null on error + // this.setState({ user: null }); } finally { // This will always run, ensuring isLoading is set to false - this.state.setIsLoading(false); + this.setState({ isLoading: false }); } }, - fetchData: async (url: string) => { - return fetch(url, { credentials: 'include' }) + fetchData: async (url: string, body?: any) => { + return fetch(url, { + credentials: 'include', + method: body ? 'POST' : 'GET', + headers: body ? { 'Content-Type': 'application/json' } : undefined, + body: body ? JSON.stringify(body) : undefined + }) .then(response => { if (!response.ok) { throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`); @@ -107,18 +116,21 @@ export class AppContextProvider extends Component<{ children: any }> { return response.json(); }) .catch(error => { + console.log('error', error); console.error('[LoadError]', error); - this.state.setNotification({ - visible: true, - message: error.message || 'Failed to load list. Please check connection.', - showSpinner: false, - type: 'error', + this.setState({ + notification: { + visible: true, + message: error.message || 'Failed to load data. Please check connection.', + showSpinner: false, + type: 'error', + } }); return null; }) .finally(() => { - this.state.setIsLoading(false); + this.setState({ isLoading: false }); }); }, logout: () => { diff --git a/frontend/components/ListContext.tsx b/frontend/components/ListContext.tsx index c26a02e..92da97c 100644 --- a/frontend/components/ListContext.tsx +++ b/frontend/components/ListContext.tsx @@ -174,13 +174,34 @@ export const useListState = () => { })); }; + // Recursively set level values based on position in the tree (depth) + const applyItemLevels = (items: ItemProps[], level: number = 0): ItemProps[] => { + return items.map(item => ({ + ...item, + level, + _children: applyItemLevels(item._children, level + 1) + })); + }; + + // Adjust an item's level (and its descendants) by a delta value + const adjustLevels = (item: ItemProps, delta: number): ItemProps => { + const newLevel = Math.max(0, item.level + delta); + return { + ...item, + level: newLevel, + __is_dirty: true, + _children: item._children.map(child => adjustLevels(child, delta)) + }; + }; + const fetchItems = async () => { try { const response = await fetchData('/list/fetch') as { data: ItemProps[] }; if (response?.data?.length > 0) { const itemsWithOrders = ensureItemOrders(response.data); - setItems(itemsWithOrders); - //setFocusedItemUuid(itemsWithOrders[0]._uuid); + const itemsWithLevels = applyItemLevels(itemsWithOrders); + setItems(itemsWithLevels); + //setFocusedItemUuid(itemsWithLevels[0]._uuid); } else { // Create default empty item const defaultItem: ItemProps = { @@ -246,12 +267,7 @@ export const useListState = () => { if (flatItems.length === 0) return; setSaveStatus('saving'); - const response = await fetch('/list/save', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - credentials: 'include', - body: JSON.stringify(flatItems) - }); + const response = await fetchData('/list/save', flatItems); if (!response.ok) throw new Error(`Save failed: ${response.status}`); @@ -299,7 +315,7 @@ export const useListState = () => { _type: 'item', _created_at: new Date().toISOString(), _updated_at: new Date().toISOString(), - _children: [], + _children: currentItem._children, // Inherit children from current item __is_new: true, __is_dirty: true, content: afterContent, @@ -310,10 +326,11 @@ export const useListState = () => { }; setItems(prevItems => { - // Update current item + // Update current item - remove children and update content const updatedItems = updateItemInTree(prevItems, currentItem._uuid, item => ({ ...item, content: beforeContent, + _children: [], // Remove children from current item __is_dirty: true })); @@ -446,8 +463,8 @@ export const useListState = () => { __is_dirty: true })); - // Insert after parent with updated level - const promotedItem = { ...currentItem, level: Math.max(0, currentItem.level - 1), __is_dirty: true }; + // Insert after parent with updated level (also adjust descendants) + const promotedItem = adjustLevels(currentItem, -1); const insertAfterParent = (items: ItemProps[], parentUuid: string): ItemProps[] => { for (let i = 0; i < items.length; i++) { if (items[i]._uuid === parentUuid) { @@ -491,11 +508,7 @@ export const useListState = () => { : reorderItems(prevItems.filter(item => item._uuid !== currentItem._uuid)); // Add as child of previous sibling - const demotedItem = { - ...currentItem, - level: currentItem.level + 1, - __is_dirty: true - }; + const demotedItem = adjustLevels(currentItem, 1); result = updateItemInTree(result, previousSibling._uuid, sibling => ({ ...sibling, diff --git a/frontend/views/Login.tsx b/frontend/views/Login.tsx index ef34034..1b08b95 100644 --- a/frontend/views/Login.tsx +++ b/frontend/views/Login.tsx @@ -1,6 +1,10 @@ import { h } from "preact"; +import { useContext } from "preact/hooks"; +import { AppContext } from "../app/AppContext"; const Login = () => { + const { fetchData } = useContext(AppContext); + const handleSubmit = async (e: Event) => { e.preventDefault(); const form = e.target as HTMLFormElement; @@ -12,17 +16,11 @@ const Login = () => { }; try { - const response = await fetch('/auth/login', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - credentials: 'include', - body: JSON.stringify(data) - }); + const response = await fetchData('/auth/login', data); - if (!response.ok) { - throw new Error('Login failed'); + if (!response) { + console.error('Login failed'); + return; } // Handle successful login here