Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(maps): switch to self-hosted tiles #1105

Merged
merged 4 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@
"immer": "^10.0.2",
"lexical": "^0.7.5",
"mapbox-gl": "^2.7.0",
"maplibre-gl": "^4.0.2",
"nanoid": "^4.0.0",
"nanoid-dictionary": "^4.3.0",
"next": "^13.5.6",
"next-auth": "^4.22.1",
"nprogress": "^0.2.0",
"paper": "^0.12.17",
"paperjs-offset": "^1.0.8",
"pmtiles": "^3.0.3",
"rc-slider": "^10.0.0-alpha.5",
"react": "^18.2.0",
"react-content-loader": "^6.2.0",
Expand All @@ -61,7 +63,7 @@
"react-hook-form": "^7.34.2",
"react-hotkeys-hook": "^3.4.7",
"react-infinite-scroll-component": "^6.1.0",
"react-map-gl": "^7.0.10",
"react-map-gl": "^7.1.7",
"react-markdown": "^9.0.1",
"react-paginate": "^8.1.3",
"react-responsive": "^9.0.0-beta.6",
Expand Down
1 change: 0 additions & 1 deletion src/app/(default)/area/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Link from 'next/link'
import { Metadata } from 'next'
import { validate } from 'uuid'
import { MapPinLine, Lightbulb, ArrowRight } from '@phosphor-icons/react/dist/ssr'
import 'mapbox-gl/dist/mapbox-gl.css'
import Markdown from 'react-markdown'

import PhotoMontage, { UploadPhotoCTA } from '@/components/media/PhotoMontage'
Expand Down
2 changes: 1 addition & 1 deletion src/app/(default)/components/ui/AreaPageContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const AreaPageContainer: React.FC<{
{breadcrumbs == null ? <BreadCrumbsSkeleton /> : breadcrumbs}
{children == null ? <ContentSkeleton /> : children}
</div>
<div id='#map' className='w-full mt-16 relative h-[90vh] border-t snap-start snap-normal'>
<div id='map' className='w-full mt-16 relative h-[90vh] border-t snap-start snap-normal'>
{map != null && map}
</div>
</article>
Expand Down
34 changes: 34 additions & 0 deletions src/app/(maps)/components/FullScreenMap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use client'
import { useEffect, useState } from 'react'
import { GlobalMap } from '@/components/maps/GlobalMap'

export const FullScreenMap: React.FC = () => {
const [initialCenter, setInitialCenter] = useState<[number, number] | undefined>(undefined)

useEffect(() => {
getVisitorLocation().then((visitorLocation) => {
if (visitorLocation != null) {
setInitialCenter([visitorLocation.longitude, visitorLocation.latitude])
}
}).catch(() => {
console.log('Unable to determine user\'s location')
})
}, [])

return (
<GlobalMap
showFullscreenControl={false}
initialCenter={initialCenter}
/>
)
}

const getVisitorLocation = async (): Promise<{ longitude: number, latitude: number } | undefined> => {
try {
const res = await fetch('/api/geo')
return await res.json()
} catch (err) {
console.log('ERROR', err)
return undefined
}
}
2 changes: 1 addition & 1 deletion src/app/(maps)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'mapbox-gl/dist/mapbox-gl.css'
import 'maplibre-gl/dist/maplibre-gl.css'
import { Metadata } from 'next'
import '@/public/fonts/fonts.css'
import './../global.css'
Expand Down
6 changes: 2 additions & 4 deletions src/app/(maps)/maps/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { GlobalMap } from '@/components/maps/GlobalMap'
import { ProfileMenu } from '../components/ProfileMenu'
import { FullScreenMap } from '../components/FullScreenMap'

export const dynamic = 'force-dynamic'

export default async function MapPage (): Promise<any> {
return (
<div className='w-full h-full'>
<ProfileMenu />
<GlobalMap
showFullscreenControl={false}
/>
<FullScreenMap />
</div>
)
}
5 changes: 4 additions & 1 deletion src/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,12 @@ A slightly deemphasized dotted underline for a tag in order to not competing wit
}

/**
* Force mapbox-gl library to use our font otherwise components inside the map
* Force mapbox-gl/maplibre library to use our font otherwise components inside the map
* will use their font and look out of place.
*/
.mapboxgl-map {
font-family: inherit !important;
}
.maplibregl-map {
font-family: inherit !important;
}
81 changes: 47 additions & 34 deletions src/components/maps/AreaInfoDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,72 @@
import * as Popover from '@radix-ui/react-popover'

import { MapAreaFeatureProperties } from './AreaMap'
import { MapAreaFeatureProperties, SimpleClimbType } from './GlobalMap'
import { getAreaPageFriendlyUrl } from '@/js/utils'
import { Card } from '../core/Card'
import { EntityIcon } from '@/app/(default)/editArea/[slug]/general/components/AreaItem'
import { ArrowRight } from '@phosphor-icons/react/dist/ssr'

/**
* Area info panel
*/
export const AreaInfoDrawer: React.FC<{ data: MapAreaFeatureProperties | null, onClose?: () => void }> = ({ data, onClose }) => {
const parent = data?.parent == null ? null : JSON.parse(data.parent)
const parentName = parent?.name ?? ''
const parentId = parent?.id ?? null
return (
<Popover.Root open={data != null}>
<Popover.Anchor className='absolute top-3 left-3 z-50' />
<Popover.Content align='start'>
{data != null && <Content {...data} parentName={parentName} parentId={parentId} />}
{data != null && <Content {...data} />}
</Popover.Content>
</Popover.Root>
)
}

export const Content: React.FC<MapAreaFeatureProperties & { parentName: string, parentId: string | null }> = ({ id, name, parentName, parentId, content }) => {
const url = parentId == null
? (
<div className='inline-flex items-center gap-1.5'>
<EntityIcon type='area' size={16} />
<span className='text-secondary font-medium'>{parentName}</span>
</div>
)
: (
<a
href={getAreaPageFriendlyUrl(parentId, name)}
className='inline-flex items-center gap-1.5'
>
<EntityIcon type='area' size={16} /><span className='text-secondary font-medium hover:underline '>{parentName}</span>
</a>
)

const friendlyUrl = getAreaPageFriendlyUrl(id, name)
export const Content: React.FC<MapAreaFeatureProperties> = ({ id, areaName, climbs, content: { description } }) => {
const friendlyUrl = getAreaPageFriendlyUrl(id, areaName)
const editUrl = `/editArea/${id}/general`
return (
<Card>
<div className='flex flex-col gap-y-1 text-xs'>
<div>{url}</div>
<div className='ml-2'>
<span className='text-secondary'>&#8735;</span><a href={getAreaPageFriendlyUrl(id, name)} className='text-sm font-medium hover:underline'>{name}</a>
</div>
</div>
<hr className='mt-6' />
<div className='flex items-center justify-end gap-2'>
<a className='btn btn-link btn-sm no-underline' href={`/editArea/${id}`}>Edit</a>
<a className='btn btn-primary btn-sm' href={friendlyUrl}>Visit area <ArrowRight /></a>
<div className='flex flex-col gap-4'>
<section className='flex flex-col gap-y-2'>
<div className='text-lg font-medium leading-snug tracking-tight'>{areaName}</div>
<div className='font-sm text-secondary flex items-center gap-1'>
<EntityIcon type='crag' size={16} />
·
<span className='text-xs font-medium'>{climbs.length} climbs</span>
<a href={friendlyUrl} className='text-info text-xs font-semibold ml-auto hover:underline'>Visit page</a>
</div>
</section>

<a className='btn btn-primary btn-outline btn-sm' href={editUrl}>Edit area</a>

<hr />

<section className='text-xs'>
{description == null || description.trim() === ''
? <p>No description available. <a className='text-info hover:underline' href={editUrl}>[Add]</a></p>
: <p>{description}</p>}
</section>

<hr />

<MicroClimbList climbs={climbs} />
</div>
</Card>
)
}

const MicroClimbList: React.FC<{ climbs: SimpleClimbType[] }> = ({ climbs }) => {
return (
<section>
<h3 className='text-base font-semibold text-secondary'>Climbs</h3>
<ol>
{climbs.map((climb) => {
const url = `/climb/${climb.id}`
return (
<li key={climb.id} className='text-xs'>
<a href={url} className='hover:underline'>{climb.name}</a>
</li>
)
})}
</ol>
</section>
)
}
33 changes: 11 additions & 22 deletions src/components/maps/AreaInfoHover.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,40 @@
import * as Popover from '@radix-ui/react-popover'
import { HoverInfo, MapAreaFeatureProperties } from './AreaMap'
import { getAreaPageFriendlyUrl } from '@/js/utils'
import { Card } from '../core/Card'
import { EntityIcon } from '@/app/(default)/editArea/[slug]/general/components/AreaItem'
import { SelectedPolygon } from './AreaActiveMarker'
import { HoverInfo, MapAreaFeatureProperties } from './GlobalMap'

/**
* Area info panel
*/
export const AreaInfoHover: React.FC<HoverInfo> = ({ data, geometry, mapInstance }) => {
const parent = data?.parent == null ? null : JSON.parse(data.parent)
const parentName = parent?.name ?? 'Unknown'
const parentId = parent?.id ?? null

let screenXY
if (geometry.type === 'Point') {
screenXY = mapInstance.project(geometry.coordinates)
} else {
return <SelectedPolygon geometry={geometry} />
}

return (
<Popover.Root defaultOpen>
<Popover.Anchor style={{ position: 'absolute', left: screenXY.x, top: screenXY.y }} />
<Popover.Content align='center' side='top' alignOffset={12}>
{data != null && <Content {...data} parentName={parentName} parentId={parentId} />}
<Popover.Arrow />
<Popover.Content align='center' side='top' sideOffset={8} collisionPadding={24} className='z-50'>
{data != null && <Content {...data} />}
</Popover.Content>
</Popover.Root>
)
}

export const Content: React.FC<MapAreaFeatureProperties & { parentName: string, parentId: string | null }> = ({ id, name, parentName, parentId }) => {
const url = parentId == null
? parentName
: (
<a
href={getAreaPageFriendlyUrl(parentId, name)}
className='inline-flex items-center gap-1.5'
>
<EntityIcon type='area' size={16} /><span className='text-secondary font-medium hover:underline '>{parentName}</span>
</a>
)
export const Content: React.FC<MapAreaFeatureProperties> = ({ id, areaName, climbs }) => {
return (
<Card>
<div className='flex flex-col gap-y-1 text-xs'>
<div>{url}</div>
<div className='ml-2'>
<span className='text-secondary'>&#8735;</span><a href={getAreaPageFriendlyUrl(id, name)} className='text-sm font-medium hover:underline'>{name}</a>
<a href={getAreaPageFriendlyUrl(id, areaName)} className='text-base font-medium hover:underline'>{areaName}</a>
<div className='font-sm text-secondary flex items-center gap-1'>
<EntityIcon type='crag' size={16} />
·
<span className='text-xs'>{climbs.length} climbs</span>
</div>
</div>
</Card>
Expand Down
Loading
Loading