Merge main into invites-2

This commit is contained in:
Anton Tranelis 2025-12-19 07:05:23 +01:00
commit 5017f1757e
24 changed files with 347 additions and 627 deletions

View File

@ -140,7 +140,7 @@ jobs:
- name: Upload test artifacts on failure
if: failure()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v4.6.2
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4.6.2
with:
name: cypress-test-results-${{ github.run_id }}
path: |
@ -170,7 +170,7 @@ jobs:
working-directory: ./cypress
- name: Download test artifacts
uses: actions/download-artifact@f093f21ca4cfa7c75ccbbc2be54da76a0c7e1f05 # v5.0.0
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v5.0.0
with:
name: cypress-test-results-${{ github.run_id }}
path: ./cypress
@ -191,7 +191,7 @@ jobs:
GITHUB_SHA: ${{ github.sha }}
- name: Upload consolidated test report
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v4.6.2
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4.6.2
with:
name: e2e-test-report-${{ github.run_id }}
path: cypress/results/html/
@ -200,7 +200,7 @@ jobs:
- name: Upload raw test data (for debugging)
if: failure()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v4.6.2
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4.6.2
with:
name: e2e-raw-data-${{ github.run_id }}
path: |

View File

@ -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.10.1",
"utopia-ui": "^3.0.112",
"vite-tsconfig-paths": "^5.1.4"
},
"devDependencies": {
@ -42,7 +42,7 @@
"eslint-plugin-promise": "^7.2.1",
"eslint-plugin-react": "^7.31.8",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"eslint-plugin-react-refresh": "^0.4.26",
"eslint-plugin-security": "^3.0.1",
"globals": "^16.3.0",
"postcss": "^8.4.30",

View File

@ -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/",
@ -49,9 +49,9 @@
"@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-typescript": "^12.3.0",
"@tailwindcss/postcss": "^4.1.17",
"@tailwindcss/postcss": "^4.1.18",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@testing-library/react": "^16.3.1",
"@types/geojson": "^7946.0.14",
"@types/leaflet": "^1.9.21",
"@types/leaflet.markercluster": "^1.5.5",
@ -71,7 +71,7 @@
"eslint-plugin-promise": "^7.2.1",
"eslint-plugin-react": "^7.31.8",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"eslint-plugin-react-refresh": "^0.4.26",
"eslint-plugin-security": "^3.0.1",
"globals": "^16.3.0",
"happy-dom": "^20.0.11",
@ -79,14 +79,14 @@
"prettier": "^3.7.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"rollup": "^4.53.3",
"rollup": "^4.53.5",
"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",
@ -96,7 +96,7 @@
"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",
@ -116,7 +116,7 @@
"classnames": "^2.5.1",
"leaflet": "^1.9.4",
"leaflet.locatecontrol": "^0.79.0",
"maplibre-gl": "^5.9.0",
"maplibre-gl": "^5.15.0",
"radash": "^12.1.0",
"react-colorful": "^5.6.1",
"react-dropzone": "^14.3.8",
@ -131,7 +131,7 @@
"react-toastify": "^9.1.3",
"remark-breaks": "^4.0.0",
"tiptap-markdown": "^0.9.0",
"yet-another-react-lightbox": "^3.26.0"
"yet-another-react-lightbox": "^3.27.0"
},
"imports": {
"#assets/*": "./src/assets/*",

View File

@ -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 =

View File

@ -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])

View File

@ -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

View File

@ -22,7 +22,7 @@ export function SetNewPasswordPage() {
await toast.promise(passwordReset(token, password), {
success: {
render() {
navigate('/')
void navigate('/')
return 'New password set'
},
},

View File

@ -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

View File

@ -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]' />

View File

@ -163,7 +163,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)

View File

@ -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' />

View File

@ -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}
/>

View File

@ -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({

View File

@ -42,10 +42,10 @@ export function InvitePage({ inviteApi, itemsApi }: Props) {
toast.success('Invite redeemed successfully!')
localStorage.removeItem('inviteCode')
setRedeemingDone(true)
navigate(`/item/${invitingProfileId}`)
void navigate(`/item/${invitingProfileId}`)
} else {
toast.error('Failed to redeem invite')
navigate('/')
void navigate('/')
}
}
@ -92,7 +92,7 @@ export function InvitePage({ inviteApi, itemsApi }: Props) {
if (!invitingProfileId) {
toast.error('Invalid invite code')
localStorage.removeItem('inviteCode')
navigate('/')
void navigate('/')
return
}
@ -102,7 +102,7 @@ export function InvitePage({ inviteApi, itemsApi }: Props) {
toast.error('You cannot invite yourself')
localStorage.removeItem('inviteCode')
// Navigate to own profile
navigate('/item/' + myProfile.id)
void navigate('/item/' + myProfile.id)
return
}
@ -113,14 +113,14 @@ export function InvitePage({ inviteApi, itemsApi }: Props) {
) {
toast.error('You are already following this profile')
localStorage.removeItem('inviteCode')
navigate('/item/' + invitingProfileId)
void navigate('/item/' + invitingProfileId)
return
}
if (!invitingProfile) {
toast.error('Inviting profile not found')
localStorage.removeItem('inviteCode')
navigate('/')
void navigate('/')
return
}
@ -156,15 +156,15 @@ export function InvitePage({ inviteApi, itemsApi }: Props) {
])
const goToSignup = () => {
navigate('/signup')
void navigate('/signup')
}
const goToLogin = () => {
navigate('/login')
void navigate('/login')
}
const goToStart = () => {
navigate('/')
void navigate('/')
}
if (isAuthenticated) {

View File

@ -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}

View File

@ -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

View File

@ -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

View File

@ -48,7 +48,7 @@ export function UserSettings() {
},
})
.then(() => {
navigate('/')
void navigate('/')
})
.catch((e) => {
throw e

View File

@ -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(

View File

@ -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)

View File

@ -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()

View File

@ -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}

View File

@ -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],
)

844
package-lock.json generated

File diff suppressed because it is too large Load Diff