Oh the places we'll go.

Implementing Places API with Copilot

QUICK NOTE: I've used block quotes to reflect which parts of the below text are copied from copilot's output.

Now we need to use the map to set up our target location. I think searching would be helpful for this to keep it in app. So let's go!

Copilot, Make a new component to list the results of the places search

import React from 'react'

type SearchResult = {
  name: string
  geometry: {
    location: {
      lat: () => number
      lng: () => number
    }
  }
}

type SearchResultsProps = {
  results: SearchResult[]
  onSelect: (location: { lat: number; lng: number }) => void
}

const SearchResults: React.FC<SearchResultsProps> = ({ results, onSelect }) => {
  return (
    <div>
      <h2>Search Results</h2>
      <ul>
        {results.map((result, index) => (
          <li
            key={index}
            onClick={() =>
              onSelect({
                lat: result.geometry.location.lat(),
                lng: result.geometry.location.lng(),
              })
            }
          >
            {result.name}
          </li>
        ))}
      </ul>
    </div>
  )
}

Looks like we have a jsxa11y error

Copilot, Redo that last result with jsxA11y

{
  results.map((result, index) => (
    <li
      key={index}
      role="button"
      tabIndex={0}
      onClick={() =>
        onSelect({
          lat: result.geometry.location.lat(),
          lng: result.geometry.location.lng(),
        })
      }
      onKeyPress={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          onSelect({
            lat: result.geometry.location.lat(),
            lng: result.geometry.location.lng(),
          })
        }
      }}
      style={{ cursor: 'pointer' }}
    >
      {result.name}
    </li>
  ))
}

<li> elements are still non-interactive so we need to go inline to fix!

{
  results.map((result, index) => (
    <button
      key={index}
      onClick={() =>
        onSelect({
          lat: result.geometry.location.lat(),
          lng: result.geometry.location.lng(),
        })
      }
      style={{
        cursor: 'pointer',
        display: 'block',
        width: '100%',
        textAlign: 'left',
        background: 'none',
        border: 'none',
        padding: '0',
        margin: '0',
      }}
    >
      {result.name}
    </button>
  ))
}

commit f373de3266724cd3799c43e3325d19789f6a8964

When looking at the above commit you'll notice that copilot inferred what I wanted to do when a result is clicked on: it centers the map on the target location. I was expecting that to be a separate request!

There were still some issues with types which resolved with inline copilot, but I had to be careful copilot didn't force custom types instead of using existing ones.

No, I don't need you to iterate over all results to filter out ones that don't have a specific key value. (Though that could cause errors down the line, I'm sure we'll have types yell at us when it matters.)

commit 9d9fa7c624628f1e228ba4a08eeef03c0c448b0f

Hmm, we center the map, but can we set a pin down?

Copilot, place a pin on the map when a location is selected

The result does not work. I think it's supposed to have been placing the pin this whole time with the <Marker>, but that's never worked.

I'll manually dive into google's advanced marker docs.

And there we go. Check it out on this commit:

commit 87de3312cdbd3efea06c9a82a3ed523ef25bb4cd Next we'll have to sort out what we're saving and if we have steps or single puzzles to solve.

Think this naive schema will work?

interface IScavengerHunt {
    steps:{
        location: google.maps.places.PlaceResult
        clues: {type: 'img'|'string', value: string}[]
        solution: string
    }[]
}

Should we refactor some of the initial loading logic into a custom hook?