Skip to content

Conversation

@gmaclennan
Copy link
Member

@gmaclennan gmaclennan commented Nov 25, 2025

See src/hooks/maps.ts for the API with JS Doc comments. The usage is envisaged to be similar to the invite API.

To test in app code, put this comapeo-core-react-7.3.0-mapShare.0.tgz in the comapeo-mobile project root and check it into git, then run npm install comapeo-core-react-7.3.0-mapShare.0.tgz. Remember to remove the .tgz and switch to a published version before merging any PR.

Feedback welcome on this API as I implement it on the backend.

useManyMapShares()

List all received map shares.

function App() {
  const { data: shares } = useManyMapShares()
  
  useEffect(() => {
    if (!shares.length || !canShowShareOnThisScreen) return
    navigate.push('MapShareReceived', { shareId: share[0] })
  }, [shares])
}

useSingleMapShare({ shareId })

Get a specific map share by ID. Useful for tracking download progress.

function MapShareDetail({ shareId }) {
  const { data: share } = useSingleMapShare({ shareId })
  return <div>Download progress: {share.progress}%</div>
}

useAcceptMapShare()

Accept and download a received map share. Promise resolves when download starts (not when it completes).

function MapShareInvite({ shareId }) {
  const { mutate: accept } = useAcceptMapShare()
  return <button onClick={() => accept({ shareId })}>Accept Map</button>
}

useRejectMapShare()

Reject a received map share.

function MapShareInvite({ shareId }) {
  const { mutate: reject } = useRejectMapShare()
  return <button onClick={() => reject({ shareId })}>Decline</button>
}

useSendMapShare({ projectId })

Share a map with another device. Promise resolves when recipient accepts, rejects, or already has the map.

function ShareMapButton({ projectId, mapId, deviceId }) {
  const { mutate: sendShare } = useSendMapShare({ projectId })

  return (
    <button onClick={() => sendShare({ mapId, deviceId })}>
      Send Map
    </button>
  )
}

useRequestCancelInvite({ projectId })

Cancel a previously sent map share invite.

function CancelShareButton({ projectId, inviteId }) {
  const { mutate: cancelInvite } = useRequestCancelInvite({ projectId })

  return (
    <button onClick={() => cancelInvite({ inviteId })}>
      Cancel Invite
    </button>
  )
}

@gmaclennan gmaclennan self-assigned this Nov 25, 2025
@gmaclennan
Copy link
Member Author

I think maybe the useSendMapShare() API needs work - let me know @ErikSin & @cimigree how you are planning to implement this from the sender's side and we can maybe adjust this API so it works for that.

Copy link
Contributor

@ErikSin ErikSin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few event emitters/listeners needed.

  1. The ui is designed to constantly be listening in the background and notify the user when a map has been shared. So there needs to be some listener function that invalidates the useManyMapShares function when a map get shared that lives at the top of the app
  2. Similar to invites, the person receiving the map needs to also listen in the background for when the person sending the map has cancelled sending the map. This has to be listener because it can happen at any time during the process.
  3. As mentioned in the comments, if we want to show progress of the map being downloaded, the map sharing function needs to be an event emitter. As it is now, there is no way to show the progress in the ui as it an async function that will just return one object when resolved at the end.

Copy link
Contributor

@cimigree cimigree left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with what Erik said. Thanks for the thorough review, Erik. I added a few things that I noticed as well.

}) {
return {
...baseMutationOptions(),
mutationFn: async ({ shareId }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there could be a parameter mismatch here? projectId? shareId? or like in invites, deviceId? Which should it be?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes good question, I'll think about this more tomorrow.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think it's possible to implement either shareId or deviceId, where if we used deviceId it would cancel all active map downloads from a device. I think it's better to do shareId like we have right now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I'm further into the implementation I have an answer for this. The code is only going to allow one download per map share (although retries will be allowed in the case of failure). The cancel on the sender side will be with the shareId.

@cimigree
Copy link
Contributor

cimigree commented Dec 9, 2025

Hi, @gmaclennan
I tried using the tgz file but found a few issues that I think need code changes in the PR before they are ready to really use.

  1. The map share hooks (useSendMapShare, useRequestCancelMapShare, useManyMapShares, useSingleMapShare, useAcceptMapShare, useRejectMapShare) exist in src/hooks/maps.ts but aren't exported from src/index.ts - only useMapStyleUrl is exported. Could you add them to the main index exports?
  2. Missing useSetUpInvitesListeners: This hooks seems to have disappeared? And that is causing a crash. I can't remember why that is and what I should do about it. It's not in any changes in here...
  3. Naming correction: The tgz file I have still has the old useRequestCancelInvite name.
    Once these are fixed, could you generate an updated tgz? For now, I'm importing the hooks directly from dist/esm/hooks/maps.js with a @ts-expect-error comment to test the UI. Thanks! Or please advise if there is anything I should be doing differently.

@achou11
Copy link
Member

achou11 commented Dec 9, 2025

@cimigree

1. The map share hooks (useSendMapShare, useRequestCancelMapShare, useManyMapShares, useSingleMapShare, useAcceptMapShare, useRejectMapShare) exist in src/hooks/maps.ts but aren't exported from src/index.ts - only useMapStyleUrl is exported. Could you add them to the main index exports?

I can update this branch to export those hooks!

2. Missing useSetUpInvitesListeners: This hooks seems to have disappeared? And that is causing a crash. I can't remember why that is and what I should do about it. It's not in any changes in here...

Yes this is branch is based off of main, which includes the changes from #141 . You no longer need to use that hook as it's done automatically when setting up the api client provider.

3. Naming correction: The tgz file I have still has the old useRequestCancelInvite name.
   Once these are fixed, could you generate an updated tgz? For now, I'm importing the hooks directly from dist/esm/hooks/maps.js with a @ts-expect-error comment to test the UI. Thanks! Or please advise if there is anything I should be doing differently.

Can fix this for you. Also worth noting that Gregor included instructions about generating the tarball yourself in the PR description, if you want to take it into your own hands. EDIT: Think I was confusing this for a different conversation I was having with Erik where I instructed him on how to generate it. In a nutshell:

  1. Clone this branch
  2. Run npm pack
  3. Use the generated tarball file based on the instructions Gregor wrote in the PR description

@achou11
Copy link
Member

achou11 commented Dec 9, 2025

@cimigree Updated the package exports via 371e486.

Shared a tarball with you on Slack, but if you need/prefer to generate your own, you can follow the steps I outlined in my previous comment

@gmaclennan
Copy link
Member Author

I didn't catch this today until now, but thank you @achou11 for responding to these questions. I think @cimigree you have what you need for now, but let me know otherwise.

@gmaclennan
Copy link
Member Author

I will try and re-visit this API tomorrow now that I've got further into the implementation. We will actually need two similar APIs I think for managing the map shares from the sender side and from the receiver side. They are subtly different and I think this current API design maybe conflates the two. It could be possible to make this current API design work for both I guess - I'll look into this.

@gmaclennan
Copy link
Member Author

@cimigree & @ErikSin : So after implementing this, I realize there is a slight confusion with this API with the distinction between sent map shares and received map shares, which I imagine you have encountered when you are implementing this? For the API we have a couple of options:

  1. We keep the current API, but add a discriminator on the MapShare objects, so that you can filter by it to get only sent or received map shares as needed.
  2. We have separate APIs for sent and received map shares, e.g. useSentMapShares() and useReceivedMapShares().

Which do you think would be preferable to work with? I am thinking (2) is better because you there would never be a use-case for reading both sent and received map shares at the same time, and it would complicate the effects (e.g. showing the share offer screen) when the value updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create map share API in @comapeo/core-react

5 participants