mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-01-20 20:01:18 +00:00
Merge branch 'main' into lazy-loading-items-index
This commit is contained in:
commit
5a59265852
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@ -27,6 +27,10 @@ updates:
|
||||
- "@tiptap/pm"
|
||||
- "@tiptap/react"
|
||||
- "@tiptap/starter-kit"
|
||||
vitest-ecosystem:
|
||||
patterns:
|
||||
- "vitest"
|
||||
- "@vitest/*"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "javascript"
|
||||
|
||||
@ -21,8 +21,8 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-router-dom": "^6.23.0",
|
||||
"utopia-ui": "^3.0.111",
|
||||
"react-router-dom": "^7.11.0",
|
||||
"utopia-ui": "^3.0.112",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -11,6 +11,17 @@ import { directusClient } from './directus'
|
||||
import type { MyCollections } from './directus'
|
||||
import type { ItemsApi } from 'utopia-ui'
|
||||
|
||||
// Fields to request when fetching items to include all relational data
|
||||
const ITEM_FIELDS = [
|
||||
'*',
|
||||
'secrets.*',
|
||||
'to.*',
|
||||
'relations.*',
|
||||
'user_created.*',
|
||||
'markerIcon.*',
|
||||
{ offers: ['*'], needs: ['*'], gallery: ['*.*'] } as any,
|
||||
]
|
||||
|
||||
export class itemsApi<T> implements ItemsApi<T> {
|
||||
collectionName: keyof MyCollections
|
||||
filter: any
|
||||
@ -43,15 +54,7 @@ export class itemsApi<T> implements ItemsApi<T> {
|
||||
try {
|
||||
const result = await directusClient.request<T[]>(
|
||||
readItems(this.collectionName as never, {
|
||||
fields: [
|
||||
'*',
|
||||
'secrets.*',
|
||||
'to.*',
|
||||
'relations.*',
|
||||
'user_created.*',
|
||||
'markerIcon.*',
|
||||
{ offers: ['*'], needs: ['*'], gallery: ['*.*'] } as any,
|
||||
],
|
||||
fields: ITEM_FIELDS,
|
||||
filter: this.filter,
|
||||
limit: -1,
|
||||
}),
|
||||
@ -70,7 +73,11 @@ export class itemsApi<T> implements ItemsApi<T> {
|
||||
|
||||
async getItem(id: string): Promise<T> {
|
||||
try {
|
||||
const result = await directusClient.request(readItem(this.collectionName as never, id))
|
||||
const result = await directusClient.request(
|
||||
readItem(this.collectionName as never, id, {
|
||||
fields: ITEM_FIELDS,
|
||||
}),
|
||||
)
|
||||
return result as T
|
||||
} catch (error: any) {
|
||||
console.log(error)
|
||||
@ -82,13 +89,18 @@ export class itemsApi<T> implements ItemsApi<T> {
|
||||
async createItem(item: T & { id?: string }): Promise<T> {
|
||||
try {
|
||||
const result = await directusClient.request(
|
||||
createItem(this.collectionName, {
|
||||
...item,
|
||||
...(this.customParameter && this.customParameter),
|
||||
...(this.layerId && { layer: this.layerId }),
|
||||
...(this.layerId && { layer: this.layerId }),
|
||||
...(this.mapId && { map: this.mapId }),
|
||||
}),
|
||||
createItem(
|
||||
this.collectionName,
|
||||
{
|
||||
...item,
|
||||
...(this.customParameter && this.customParameter),
|
||||
...(this.layerId && { layer: this.layerId }),
|
||||
...(this.mapId && { map: this.mapId }),
|
||||
},
|
||||
{
|
||||
fields: ITEM_FIELDS,
|
||||
},
|
||||
),
|
||||
)
|
||||
return result as T
|
||||
} catch (error: any) {
|
||||
@ -100,7 +112,11 @@ export class itemsApi<T> implements ItemsApi<T> {
|
||||
|
||||
async updateItem(item: T & { id?: string }): Promise<T> {
|
||||
try {
|
||||
const result = await directusClient.request(updateItem(this.collectionName, item.id!, item))
|
||||
const result = await directusClient.request(
|
||||
updateItem(this.collectionName, item.id!, item, {
|
||||
fields: ITEM_FIELDS,
|
||||
}),
|
||||
)
|
||||
return result as T
|
||||
} catch (error: any) {
|
||||
console.log(error)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "utopia-ui",
|
||||
"version": "3.0.111",
|
||||
"version": "3.0.112",
|
||||
"description": "Reuseable React Components to build mapping apps for real life communities and networks",
|
||||
"repository": "https://github.com/utopia-os/utopia-ui",
|
||||
"homepage": "https://utopia-os.org/",
|
||||
@ -58,7 +58,7 @@
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"@vitest/coverage-v8": "^3.0.5",
|
||||
"@vitest/coverage-v8": "^4.0.16",
|
||||
"cypress": "^15.7.1",
|
||||
"daisyui": "^5.5.14",
|
||||
"eslint": "^9.39.2",
|
||||
@ -79,24 +79,24 @@
|
||||
"prettier": "^3.7.4",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"rollup": "^4.53.5",
|
||||
"rollup": "^4.54.0",
|
||||
"rollup-plugin-dts": "^6.3.0",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-svg": "^2.0.0",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"typedoc": "^0.27.6",
|
||||
"typedoc-plugin-coverage": "^3.4.1",
|
||||
"typedoc-plugin-missing-exports": "^3.1.0",
|
||||
"typedoc": "^0.28.15",
|
||||
"typedoc-plugin-coverage": "^4.0.2",
|
||||
"typedoc-plugin-missing-exports": "^4.1.2",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.9.0",
|
||||
"vite": "^7.3.0",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"vitest": "^3.0.5"
|
||||
"vitest": "^4.0.16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.23.0"
|
||||
"react-router-dom": "^7.10.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.0.17",
|
||||
@ -126,12 +126,12 @@
|
||||
"react-leaflet": "^4.2.1",
|
||||
"react-leaflet-cluster": "^3.1.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-photo-album": "^3.3.0",
|
||||
"react-photo-album": "^3.4.0",
|
||||
"react-qr-code": "^2.0.16",
|
||||
"react-toastify": "^9.1.3",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"tiptap-markdown": "^0.9.0",
|
||||
"yet-another-react-lightbox": "^3.27.0"
|
||||
"yet-another-react-lightbox": "^3.28.0"
|
||||
},
|
||||
"imports": {
|
||||
"#assets/*": "./src/assets/*",
|
||||
|
||||
@ -45,12 +45,12 @@ export const UserControl = () => {
|
||||
|
||||
const handleEdit = () => {
|
||||
if (!myProfile?.layer) {
|
||||
navigate(userProfile.id ? `/edit-item/${userProfile.id}` : '#')
|
||||
void navigate(userProfile.id ? `/edit-item/${userProfile.id}` : '#')
|
||||
return
|
||||
}
|
||||
|
||||
if (myProfile.layer.itemType.small_form_edit && myProfile.position) {
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
// Wait for navigation to complete before setting popup
|
||||
setTimeout(() => {
|
||||
if (myProfile.position && myProfile.layer) {
|
||||
@ -65,7 +65,7 @@ export const UserControl = () => {
|
||||
}
|
||||
}, 100)
|
||||
} else {
|
||||
navigate(userProfile.id ? `/edit-item/${userProfile.id}` : '#')
|
||||
void navigate(userProfile.id ? `/edit-item/${userProfile.id}` : '#')
|
||||
}
|
||||
}
|
||||
const avatar: string | undefined =
|
||||
|
||||
@ -51,9 +51,9 @@ export function LoginPage({ inviteApi, showRequestPassword }: Props) {
|
||||
invitingProfileId = await redeemInvite(inviteCode)
|
||||
}
|
||||
if (invitingProfileId) {
|
||||
navigate(`/item/${invitingProfileId}`)
|
||||
void navigate(`/item/${invitingProfileId}`)
|
||||
} else {
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
}
|
||||
}, [navigate, redeemInvite])
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ export function RequestPasswordPage({ resetUrl }: { resetUrl: string }) {
|
||||
await toast.promise(requestPasswordReset(email, resetUrl), {
|
||||
success: {
|
||||
render() {
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
return 'Check your mailbox'
|
||||
},
|
||||
// other options
|
||||
|
||||
@ -22,7 +22,7 @@ export function SetNewPasswordPage() {
|
||||
await toast.promise(passwordReset(token, password), {
|
||||
success: {
|
||||
render() {
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
return 'New password set'
|
||||
},
|
||||
},
|
||||
|
||||
@ -25,7 +25,7 @@ export function SignupPage() {
|
||||
await toast.promise(register({ email, password }, userName), {
|
||||
success: {
|
||||
render({ data }) {
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
return `Hi ${data?.first_name ? data.first_name : 'Traveler'}`
|
||||
},
|
||||
// other options
|
||||
|
||||
@ -14,7 +14,7 @@ export const GratitudeControl = () => {
|
||||
<div
|
||||
className='tw:card-body tw:hover:bg-slate-300 tw:card tw:p-2 tw:h-10 tw:w-10 tw:transition-all tw:duration-300 tw:hover:cursor-pointer'
|
||||
onClick={() => {
|
||||
navigate('/select-user')
|
||||
void navigate('/select-user')
|
||||
}}
|
||||
>
|
||||
<HeartIcon className='tw:stroke-[2.5]' />
|
||||
|
||||
@ -195,7 +195,7 @@ export const LocateControl = (): React.JSX.Element => {
|
||||
}
|
||||
|
||||
// Navigate to the profile to show the popup
|
||||
navigate(`/${result.id}`)
|
||||
void navigate(`/${result.id}`)
|
||||
|
||||
// Clean up and reset state
|
||||
setFoundLocation(null)
|
||||
|
||||
@ -73,11 +73,13 @@ export function EditMenu({
|
||||
className='tw:text-base-content! tw:tooltip tw:tooltip-top tw:cursor-pointer'
|
||||
data-tip='Edit'
|
||||
onClick={(e) => {
|
||||
item.layer?.customEditLink
|
||||
? navigate(
|
||||
`${item.layer.customEditLink}${item.layer.customEditParameter ? `/${item.id}${params.toString() ? '?' + params.toString() : ''}` : ''}`,
|
||||
)
|
||||
: editCallback(e)
|
||||
if (item.layer?.customEditLink) {
|
||||
void navigate(
|
||||
`${item.layer.customEditLink}${item.layer.customEditParameter ? `/${item.id}${params.toString() ? '?' + params.toString() : ''}` : ''}`,
|
||||
)
|
||||
} else {
|
||||
editCallback(e)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<PencilIcon className='tw:h-5 tw:w-5' />
|
||||
|
||||
@ -81,7 +81,7 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
|
||||
setLoading(false)
|
||||
map.closePopup()
|
||||
removeItemFromUrl()
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
}
|
||||
|
||||
return (
|
||||
@ -99,7 +99,7 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
|
||||
setPositionCallback={() => {
|
||||
map.closePopup()
|
||||
setSelectPosition(props.item)
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
}}
|
||||
loading={loading}
|
||||
/>
|
||||
|
||||
@ -117,7 +117,7 @@ function useFilterManager(initialTags: Tag[]): {
|
||||
params.set('layers', visibleNames.join(','))
|
||||
}
|
||||
|
||||
navigate(`${location.pathname}?${params.toString()}`, { replace: true })
|
||||
void navigate(`${location.pathname}?${params.toString()}`, { replace: true })
|
||||
}, [visibleLayers, allLayers, navigate])
|
||||
|
||||
const [visibleGroupTypes, dispatchGroupTypes] = useReducer(
|
||||
@ -152,8 +152,8 @@ function useFilterManager(initialTags: Tag[]): {
|
||||
params.set('tags', `${urlTags || ''}${urlTags ? ';' : ''}${tag.name}`)
|
||||
}
|
||||
if (windowDimensions.width < 786 && location.pathname.split('/').length > 2)
|
||||
navigate('/' + (params ? `?${params}` : ''))
|
||||
else navigate(location.pathname + (params ? `?${params}` : ''))
|
||||
void navigate('/' + (params ? `?${params}` : ''))
|
||||
else void navigate(location.pathname + (params ? `?${params}` : ''))
|
||||
|
||||
dispatchTags({
|
||||
type: 'ADD_TAG',
|
||||
@ -177,10 +177,10 @@ function useFilterManager(initialTags: Tag[]): {
|
||||
})
|
||||
if (newUrlTags !== '') {
|
||||
params.set('tags', newUrlTags)
|
||||
navigate(location.pathname + (params ? `?${params}` : ''))
|
||||
void navigate(location.pathname + (params ? `?${params}` : ''))
|
||||
} else {
|
||||
params.delete('tags')
|
||||
navigate(location.pathname + (params ? `?${params}` : ''))
|
||||
void navigate(location.pathname + (params ? `?${params}` : ''))
|
||||
}
|
||||
|
||||
dispatchTags({
|
||||
|
||||
@ -39,10 +39,10 @@ export function InvitePage({ inviteApi }: Props) {
|
||||
|
||||
if (invitingProfileId) {
|
||||
toast.success('Invite redeemed successfully!')
|
||||
navigate(`/item/${invitingProfileId}`)
|
||||
void navigate(`/item/${invitingProfileId}`)
|
||||
} else {
|
||||
toast.error('Failed to redeem invite')
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ export function InvitePage({ inviteApi }: Props) {
|
||||
localStorage.setItem('inviteCode', id)
|
||||
|
||||
// Redirect to login page
|
||||
navigate('/login')
|
||||
void navigate('/login')
|
||||
}
|
||||
}, [
|
||||
id,
|
||||
|
||||
@ -187,12 +187,12 @@ export function ProfileView({ attestationApi }: { attestationApi?: ItemsApi<any>
|
||||
})
|
||||
}}
|
||||
editCallback={() => {
|
||||
navigate('/edit-item/' + item.id)
|
||||
void navigate('/edit-item/' + item.id)
|
||||
}}
|
||||
setPositionCallback={() => {
|
||||
map.closePopup()
|
||||
setSelectPosition(item)
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
}}
|
||||
big
|
||||
truncateSubname={false}
|
||||
|
||||
@ -133,7 +133,7 @@ export const TabsForm = ({
|
||||
key={i.id}
|
||||
className='tw:cursor-pointer tw:card tw:bg-base-200 tw:border-[1px] tw:border-base-300 tw:card-body tw:shadow-xl tw:text-base-content tw:mx-4 tw:p-6 tw:mb-4'
|
||||
onClick={() => {
|
||||
navigate('/item/' + i.id)
|
||||
void navigate('/item/' + i.id)
|
||||
}}
|
||||
>
|
||||
<LinkedItemsHeaderView
|
||||
|
||||
@ -286,7 +286,7 @@ export const TabsView = ({
|
||||
key={i.id}
|
||||
className='tw:cursor-pointer tw:card tw:bg-base-200 tw:border-[1px] tw:border-base-300 tw:card-body tw:shadow-xl tw:text-base-content tw:p-6 tw:mr-4 tw:mb-4'
|
||||
onClick={() => {
|
||||
navigate('/item/' + i.id)
|
||||
void navigate('/item/' + i.id)
|
||||
}}
|
||||
>
|
||||
<LinkedItemsHeaderView
|
||||
|
||||
@ -48,7 +48,7 @@ export function UserSettings() {
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
})
|
||||
.catch((e) => {
|
||||
throw e
|
||||
|
||||
@ -71,7 +71,7 @@ export const AttestationForm = ({ api }: { api?: ItemsApi<unknown> }) => {
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
navigate(
|
||||
void navigate(
|
||||
'/item/' +
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
items.find(
|
||||
|
||||
@ -35,7 +35,7 @@ export const ItemCard = ({
|
||||
}
|
||||
|
||||
if (i.layer.itemType.small_form_edit && i.position) {
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
// Wait for navigation to complete before setting popup
|
||||
setTimeout(() => {
|
||||
if (i.position && i.layer) {
|
||||
@ -49,7 +49,7 @@ export const ItemCard = ({
|
||||
}
|
||||
}, 100)
|
||||
} else {
|
||||
navigate('/edit-item/' + i.id)
|
||||
void navigate('/edit-item/' + i.id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,8 +60,8 @@ export const ItemCard = ({
|
||||
// We could have an onClick callback instead
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
if (windowDimensions.width < 786 && i.position)
|
||||
navigate('/' + i.id + (params.size > 0 ? `?${params.toString()}` : ''))
|
||||
else navigate(url + i.id + (params.size > 0 ? `?${params.toString()}` : ''))
|
||||
void navigate('/' + i.id + (params.size > 0 ? `?${params.toString()}` : ''))
|
||||
else void navigate(url + i.id + (params.size > 0 ? `?${params.toString()}` : ''))
|
||||
}}
|
||||
>
|
||||
<HeaderView
|
||||
@ -74,7 +74,7 @@ export const ItemCard = ({
|
||||
setPositionCallback={() => {
|
||||
map.closePopup()
|
||||
setSelectPosition(i)
|
||||
navigate('/')
|
||||
void navigate('/')
|
||||
}}
|
||||
deleteCallback={() => {
|
||||
deleteCallback(i)
|
||||
|
||||
@ -17,7 +17,7 @@ export function MapOverlayPage({
|
||||
card?: boolean
|
||||
}) {
|
||||
const closeScreen = () => {
|
||||
navigate(`/${window.location.search ? window.location.search : ''}`)
|
||||
void navigate(`/${window.location.search ? window.location.search : ''}`)
|
||||
}
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
@ -70,7 +70,7 @@ export const MarketView = () => {
|
||||
{groupAndCount(offers).map((o) => (
|
||||
<TagView
|
||||
onClick={() => {
|
||||
navigate(`/?tags=${o.object.name}`)
|
||||
void navigate(`/?tags=${o.object.name}`)
|
||||
}}
|
||||
key={o.object.id}
|
||||
tag={o.object}
|
||||
@ -85,7 +85,7 @@ export const MarketView = () => {
|
||||
{groupAndCount(needs).map((o) => (
|
||||
<TagView
|
||||
onClick={() => {
|
||||
navigate(`/?tags=${o.object.name}`)
|
||||
void navigate(`/?tags=${o.object.name}`)
|
||||
}}
|
||||
key={o.object.id}
|
||||
tag={o.object}
|
||||
|
||||
@ -39,7 +39,7 @@ export const Tabs: React.FC<TabsProps> = ({ items, setUrlParams }: TabsProps) =>
|
||||
setUrlParams(params)
|
||||
const newUrl = location.pathname + '?' + params.toString()
|
||||
|
||||
navigate(newUrl, { replace: false })
|
||||
void navigate(newUrl, { replace: false })
|
||||
},
|
||||
[location.pathname, location.search, navigate, setUrlParams],
|
||||
)
|
||||
|
||||
@ -10,7 +10,6 @@ export default defineConfig({
|
||||
environment: 'happy-dom',
|
||||
setupFiles: ['setupTest.ts'],
|
||||
coverage: {
|
||||
all: true,
|
||||
include: ['src/**/*.{js,jsx,ts,tsx}'],
|
||||
exclude: [...configDefaults.exclude, 'src/**/*.cy.tsx'],
|
||||
reporter: ['html', 'json-summary'],
|
||||
|
||||
880
package-lock.json
generated
880
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user