mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-04-06 01:25:33 +00:00
use server response for local state updates
This commit is contained in:
parent
94fa6321ba
commit
89ff940b74
@ -1,11 +1,9 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable security/detect-object-injection */
|
||||||
import { useContext, useEffect, useRef, useState } from 'react'
|
/* eslint-disable no-catch-all/no-catch-all */
|
||||||
|
|
||||||
|
import { useCallback, useContext, useEffect, useRef, useState } from 'react'
|
||||||
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
@ -50,95 +48,170 @@ export function ItemFormPopup(props: Props) {
|
|||||||
|
|
||||||
const { user } = useAuth()
|
const { user } = useAuth()
|
||||||
|
|
||||||
const handleSubmit = async (evt: any) => {
|
// Extract form data into Item object
|
||||||
if (!popupForm) {
|
const parseFormData = useCallback(
|
||||||
throw new Error('Popup form is not defined')
|
(evt: React.FormEvent<HTMLFormElement>): Item => {
|
||||||
}
|
if (!popupForm) {
|
||||||
const formItem: Item = {} as Item
|
throw new Error('Popup form is not defined')
|
||||||
Array.from(evt.target).forEach((input: HTMLInputElement) => {
|
|
||||||
if (input.name) {
|
|
||||||
formItem[input.name] = input.value
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
formItem.position = {
|
const formItem: Item = {} as Item
|
||||||
type: 'Point',
|
const formData = new FormData(evt.currentTarget)
|
||||||
coordinates: [popupForm.position.lng, popupForm.position.lat],
|
|
||||||
}
|
for (const [key, value] of formData.entries()) {
|
||||||
|
if (key && typeof value === 'string') {
|
||||||
|
;(formItem as unknown as Record<string, unknown>)[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formItem.position = {
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: [popupForm.position.lng, popupForm.position.lat],
|
||||||
|
}
|
||||||
|
|
||||||
|
return formItem
|
||||||
|
},
|
||||||
|
[popupForm],
|
||||||
|
)
|
||||||
|
|
||||||
|
// Process hashtags in text and create new tags if needed
|
||||||
|
const processHashtags = useCallback(
|
||||||
|
(text: string) => {
|
||||||
|
if (!text) return
|
||||||
|
|
||||||
|
text
|
||||||
|
.toLocaleLowerCase()
|
||||||
|
.match(hashTagRegex)
|
||||||
|
?.forEach((tag) => {
|
||||||
|
const tagName = tag.slice(1).toLocaleLowerCase()
|
||||||
|
if (!tags.find((t) => t.name.toLocaleLowerCase() === tagName)) {
|
||||||
|
addTag({ id: crypto.randomUUID(), name: tag.slice(1), color: randomColor() })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[tags, addTag],
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handle API operations with consistent error handling and return response data
|
||||||
|
const handleApiOperation = useCallback(
|
||||||
|
async (
|
||||||
|
operation: () => Promise<Item>,
|
||||||
|
successMessage: string,
|
||||||
|
): Promise<{ success: boolean; data?: Item }> => {
|
||||||
|
try {
|
||||||
|
const data = await operation()
|
||||||
|
toast.success(successMessage)
|
||||||
|
return { success: true, data }
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error instanceof Error ? error.message : String(error))
|
||||||
|
return { success: false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
|
// Update existing item
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
async (formItem: Item) => {
|
||||||
|
if (!popupForm?.item) return false
|
||||||
|
|
||||||
|
const result = await handleApiOperation(
|
||||||
|
() =>
|
||||||
|
popupForm.layer.api?.updateItem!({ ...formItem, id: popupForm.item!.id }) ??
|
||||||
|
Promise.resolve({} as Item),
|
||||||
|
'Item updated',
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result.success && result.data) {
|
||||||
|
// Ensure the item has the layer object attached
|
||||||
|
const itemWithLayer = { ...result.data, layer: popupForm.layer }
|
||||||
|
updateItem(itemWithLayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.success
|
||||||
|
},
|
||||||
|
[popupForm, handleApiOperation, updateItem],
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create new item or update existing user profile
|
||||||
|
const handleCreateItem = useCallback(
|
||||||
|
async (formItem: Item) => {
|
||||||
|
if (!popupForm) return false
|
||||||
|
|
||||||
|
const existingUserItem = items.find(
|
||||||
|
(i) => i.user_created?.id === user?.id && i.layer === popupForm.layer,
|
||||||
|
)
|
||||||
|
|
||||||
|
const itemName = formItem.name || user?.first_name
|
||||||
|
if (!itemName) {
|
||||||
|
toast.error('Name must be defined')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const isUserProfileUpdate = popupForm.layer.userProfileLayer && existingUserItem
|
||||||
|
const uuid = crypto.randomUUID()
|
||||||
|
|
||||||
|
const operation = isUserProfileUpdate
|
||||||
|
? () =>
|
||||||
|
popupForm.layer.api?.updateItem!({ ...formItem, id: existingUserItem.id }) ??
|
||||||
|
Promise.resolve({} as Item)
|
||||||
|
: () =>
|
||||||
|
popupForm.layer.api?.createItem!({ ...formItem, name: itemName, id: uuid }) ??
|
||||||
|
Promise.resolve({} as Item)
|
||||||
|
|
||||||
|
const result = await handleApiOperation(
|
||||||
|
operation,
|
||||||
|
isUserProfileUpdate ? 'Profile updated' : 'New item created',
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result.success && result.data) {
|
||||||
|
// Ensure the item has the layer object attached
|
||||||
|
const itemWithLayer = { ...result.data, layer: popupForm.layer }
|
||||||
|
|
||||||
|
if (isUserProfileUpdate) {
|
||||||
|
updateItem(itemWithLayer)
|
||||||
|
} else {
|
||||||
|
addItem(itemWithLayer)
|
||||||
|
}
|
||||||
|
resetFilterTags()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.success
|
||||||
|
},
|
||||||
|
[popupForm, items, user, handleApiOperation, updateItem, addItem, resetFilterTags],
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleSubmit = async (evt: React.FormEvent<HTMLFormElement>) => {
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
|
|
||||||
const name = formItem.name ? formItem.name : user?.first_name
|
if (!popupForm) {
|
||||||
if (!name) {
|
throw new Error('Popup form is not defined')
|
||||||
toast.error('Name is must be defined')
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setSpinner(true)
|
setSpinner(true)
|
||||||
|
|
||||||
formItem.text &&
|
try {
|
||||||
formItem.text
|
const formItem = parseFormData(evt)
|
||||||
.toLocaleLowerCase()
|
|
||||||
.match(hashTagRegex)
|
|
||||||
?.map((tag) => {
|
|
||||||
if (!tags.find((t) => t.name.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase())) {
|
|
||||||
addTag({ id: crypto.randomUUID(), name: tag.slice(1), color: randomColor() })
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
if (popupForm.item) {
|
// Process hashtags if text exists
|
||||||
let success = false
|
if (formItem.text) {
|
||||||
try {
|
processHashtags(formItem.text)
|
||||||
await popupForm.layer.api?.updateItem!({ ...formItem, id: popupForm.item.id })
|
|
||||||
success = true
|
|
||||||
// eslint-disable-next-line no-catch-all/no-catch-all
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(error.toString())
|
|
||||||
}
|
}
|
||||||
if (success) {
|
|
||||||
updateItem({ ...popupForm.item, ...formItem })
|
|
||||||
toast.success('Item updated')
|
|
||||||
}
|
|
||||||
setSpinner(false)
|
|
||||||
map.closePopup()
|
|
||||||
} else {
|
|
||||||
const item = items.find((i) => i.user_created?.id === user?.id && i.layer === popupForm.layer)
|
|
||||||
|
|
||||||
const uuid = crypto.randomUUID()
|
let success: boolean
|
||||||
let success = false
|
if (popupForm.item) {
|
||||||
try {
|
success = await handleUpdateItem(formItem)
|
||||||
popupForm.layer.userProfileLayer &&
|
} else {
|
||||||
item &&
|
success = await handleCreateItem(formItem)
|
||||||
(await popupForm.layer.api?.updateItem!({ ...formItem, id: item.id }))
|
|
||||||
;(!popupForm.layer.userProfileLayer || !item) &&
|
|
||||||
(await popupForm.layer.api?.createItem!({
|
|
||||||
...formItem,
|
|
||||||
name,
|
|
||||||
id: uuid,
|
|
||||||
}))
|
|
||||||
success = true
|
|
||||||
// eslint-disable-next-line no-catch-all/no-catch-all
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(error.toString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
if (popupForm.layer.userProfileLayer && item) updateItem({ ...item, ...formItem })
|
map.closePopup()
|
||||||
if (!popupForm.layer.userProfileLayer || !item) {
|
setPopupForm(null)
|
||||||
addItem({
|
|
||||||
...formItem,
|
|
||||||
name: (formItem.name ? formItem.name : user?.first_name) ?? '',
|
|
||||||
user_created: user ?? undefined,
|
|
||||||
id: uuid,
|
|
||||||
layer: popupForm.layer,
|
|
||||||
public_edit: !user,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
toast.success('New item created')
|
|
||||||
resetFilterTags()
|
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
setSpinner(false)
|
setSpinner(false)
|
||||||
map.closePopup()
|
|
||||||
}
|
}
|
||||||
setPopupForm(null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetPopup = () => {
|
const resetPopup = () => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user