Skip to content

feat: Add map sharing API#143

Open
gmaclennan wants to merge 35 commits intomainfrom
feat/map-sharing
Open

feat: Add map sharing API#143
gmaclennan wants to merge 35 commits intomainfrom
feat/map-sharing

Conversation

@gmaclennan
Copy link
Member

@gmaclennan gmaclennan commented Nov 25, 2025

Map Sharing Hooks

Adds React hooks for sharing maps between devices. The API separates receiver and sender functionality.

Receiver Hooks

  • useManyReceivedMapShares() - List all received map shares. Updates when shares arrive or status changes (but not during download progress updates).
  • useSingleReceivedMapShare({ shareId }) - Get a specific received share with real-time updates including download progress.
  • useDownloadReceivedMapShare() - Accept and start downloading a share. Resolves when download starts, not when complete.
  • useDeclineReceivedMapShare() - Decline a pending share. Notifies the sender.
  • useAbortReceivedMapShareDownload() - Cancel an in-progress download.

Sender Hooks

  • useSendMapShare({ projectId }) - Send a map to another device. Resolves immediately with the share object (doesn't wait for recipient response).
  • useSingleSentMapShare({ shareId }) - Track a sent share's status in real-time (pending, downloading, declined, canceled).
  • useCancelSentMapShare() - Cancel a sent share before the recipient completes the download.

Example Usage

// Receiving a map share
function ReceivedShareScreen({ shareId }: { shareId: string }) {
  const share = useSingleReceivedMapShare({ shareId })
  const { mutate: download } = useDownloadReceivedMapShare()
  const { mutate: decline } = useDeclineReceivedMapShare()

  if (share.status === 'downloading') {
    return <div>Downloading: {share.downloadProgress}%</div>
  }

  return (
    <div>
      <div>{share.mapName} from {share.senderDeviceName}</div>
      <button onClick={() => download({ shareId })}>Accept</button>
      <button onClick={() => decline({ shareId, reason: 'user_rejected' })}>Decline</button>
    </div>
  )
}

// Sending a map share
function SendMapScreen({ projectId, deviceId }: { projectId: string; deviceId: string }) {
  const [shareId, setShareId] = useState<string | null>(null)
  const { mutate: send } = useSendMapShare({ projectId })

  const handleSend = () => {
    send(
      { receiverDeviceId: deviceId, mapId: 'custom' },
      { onSuccess: (share) => setShareId(share.shareId) }
    )
  }

  if (shareId) {
    return <SentShareStatus shareId={shareId} />
  }

  return <button onClick={handleSend}>Send Map</button>
}

function SentShareStatus({ shareId }: { shareId: string }) {
  const share = useSingleSentMapShare({ shareId })

  return (
    <div>
      {share.status === 'pending' && <div>Waiting for recipient...</div>}
      {share.status === 'downloading' && <div>Recipient downloading: {share.downloadProgress}%</div>}
      {share.status === 'completed' && <div>Map shared successfully</div>}
      {share.status === 'declined' && <div>Recipient declined</div>}
    </div>
  )
}

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

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

@awana-lockfile-bot
Copy link

awana-lockfile-bot bot commented Feb 11, 2026

package-lock.json changes

Summary

Status Count
ADDED 57
UPDATED 85
Click to toggle table visibility
Name Status Previous Current
@acemir/cssom ADDED - 0.9.31
@asamuzakjp/css-color ADDED - 4.1.2
@asamuzakjp/dom-selector ADDED - 6.7.8
@asamuzakjp/nwsapi ADDED - 2.3.9
@augment-vir/common ADDED - 26.4.0
@babel/code-frame UPDATED 7.27.1 7.29.0
@babel/compat-data UPDATED 7.28.5 7.29.0
@babel/core UPDATED 7.28.5 7.29.0
@babel/generator UPDATED 7.28.5 7.29.1
@babel/helper-compilation-targets UPDATED 7.27.2 7.28.6
@babel/helper-module-imports UPDATED 7.27.1 7.28.6
@babel/helper-module-transforms UPDATED 7.28.3 7.28.6
@babel/helpers UPDATED 7.28.4 7.28.6
@babel/parser UPDATED 7.28.5 7.29.0
@babel/template UPDATED 7.27.2 7.28.6
@babel/traverse UPDATED 7.28.5 7.29.0
@babel/types UPDATED 7.28.5 7.29.0
@comapeo/core UPDATED 5.2.1 5.4.0
@comapeo/map-server ADDED - 1.0.0-pre.6
@csstools/color-helpers ADDED - 6.0.1
@csstools/css-calc ADDED - 3.0.1
@csstools/css-color-parser ADDED - 4.0.1
@csstools/css-parser-algorithms ADDED - 4.0.0
@csstools/css-syntax-patches-for-csstree ADDED - 1.0.27
@csstools/css-tokenizer ADDED - 4.0.0
@emnapi/core UPDATED 1.5.0 1.8.1
@emnapi/runtime UPDATED 1.5.0 1.8.1
@esbuild/aix-ppc64 UPDATED 0.25.12 0.27.3
@esbuild/android-arm UPDATED 0.25.12 0.27.3
@esbuild/android-arm64 UPDATED 0.25.12 0.27.3
@esbuild/android-x64 UPDATED 0.25.12 0.27.3
@esbuild/darwin-arm64 UPDATED 0.25.12 0.27.3
@esbuild/darwin-x64 UPDATED 0.25.12 0.27.3
@esbuild/freebsd-arm64 UPDATED 0.25.12 0.27.3
@esbuild/freebsd-x64 UPDATED 0.25.12 0.27.3
@esbuild/linux-arm UPDATED 0.25.12 0.27.3
@esbuild/linux-arm64 UPDATED 0.25.12 0.27.3
@esbuild/linux-ia32 UPDATED 0.25.12 0.27.3
@esbuild/linux-loong64 UPDATED 0.25.12 0.27.3
@esbuild/linux-mips64el UPDATED 0.25.12 0.27.3
@esbuild/linux-ppc64 UPDATED 0.25.12 0.27.3
@esbuild/linux-riscv64 UPDATED 0.25.12 0.27.3
@esbuild/linux-s390x UPDATED 0.25.12 0.27.3
@esbuild/linux-x64 UPDATED 0.25.12 0.27.3
@esbuild/netbsd-arm64 UPDATED 0.25.12 0.27.3
@esbuild/netbsd-x64 UPDATED 0.25.12 0.27.3
@esbuild/openbsd-arm64 UPDATED 0.25.12 0.27.3
@esbuild/openbsd-x64 UPDATED 0.25.12 0.27.3
@esbuild/openharmony-arm64 UPDATED 0.25.12 0.27.3
@esbuild/sunos-x64 UPDATED 0.25.12 0.27.3
@esbuild/win32-arm64 UPDATED 0.25.12 0.27.3
@esbuild/win32-ia32 UPDATED 0.25.12 0.27.3
@esbuild/win32-x64 UPDATED 0.25.12 0.27.3
@eslint-community/eslint-utils UPDATED 4.9.0 4.9.1
@eslint/core UPDATED 1.0.0 1.1.0
@exodus/bytes ADDED - 1.13.0
@fastify/forwarded ADDED - 3.0.1
@fastify/proxy-addr ADDED - 5.1.0
@jridgewell/source-map ADDED - 0.3.11
@pinojs/redact ADDED - 0.4.0
@rollup/rollup-android-arm-eabi UPDATED 4.53.3 4.57.1
@rollup/rollup-android-arm64 UPDATED 4.53.3 4.57.1
@rollup/rollup-darwin-arm64 UPDATED 4.53.3 4.57.1
@rollup/rollup-darwin-x64 UPDATED 4.53.3 4.57.1
@rollup/rollup-freebsd-arm64 UPDATED 4.53.3 4.57.1
@rollup/rollup-freebsd-x64 UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-arm-gnueabihf UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-arm-musleabihf UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-arm64-gnu UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-arm64-musl UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-loong64-gnu UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-loong64-musl ADDED - 4.57.1
@rollup/rollup-linux-ppc64-gnu UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-ppc64-musl ADDED - 4.57.1
@rollup/rollup-linux-riscv64-gnu UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-riscv64-musl UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-s390x-gnu UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-x64-gnu UPDATED 4.53.3 4.57.1
@rollup/rollup-linux-x64-musl UPDATED 4.53.3 4.57.1
@rollup/rollup-openbsd-x64 ADDED - 4.57.1
@rollup/rollup-openharmony-arm64 UPDATED 4.53.3 4.57.1
@rollup/rollup-win32-arm64-msvc UPDATED 4.53.3 4.57.1
@rollup/rollup-win32-ia32-msvc UPDATED 4.53.3 4.57.1
@rollup/rollup-win32-x64-gnu UPDATED 4.53.3 4.57.1
@rollup/rollup-win32-x64-msvc UPDATED 4.53.3 4.57.1
@standard-schema/spec UPDATED 1.0.0 1.1.0
@types/jsdom ADDED - 27.0.0
@types/tough-cookie ADDED - 4.0.5
@typescript-eslint/scope-manager UPDATED 8.49.0 8.55.0
@typescript-eslint/types UPDATED 8.49.0 8.55.0
@typescript-eslint/visitor-keys UPDATED 8.49.0 8.55.0
agent-base ADDED - 7.1.4
ansi-escapes UPDATED 7.2.0 7.3.0
baseline-browser-mapping UPDATED 2.9.7 2.9.19
bidi-js ADDED - 1.0.3
browser-or-node ADDED - 2.1.1
buffer-from ADDED - 1.1.2
caniuse-lite UPDATED 1.0.30001760 1.0.30001769
chai UPDATED 6.2.1 6.2.2
commander UPDATED 14.0.2 14.0.3
cssstyle ADDED - 5.3.7
data-urls ADDED - 7.0.0
decimal.js ADDED - 10.6.0
electron-to-chromium UPDATED 1.5.267 1.5.286
ensure-error UPDATED 4.0.0 5.0.0
esbuild UPDATED 0.25.12 0.27.3
eventsource-client ADDED - 1.2.0
eventsource-parser ADDED - 3.0.6
html-encoding-sniffer ADDED - 6.0.0
http-proxy-agent ADDED - 7.0.2
https-proxy-agent ADDED - 7.0.6
is-potential-custom-element-name ADDED - 1.0.1
itty-router ADDED - 5.0.22
jsdom ADDED - 28.0.0
ky UPDATED 1.10.0 1.14.3
p-mutex ADDED - 0.1.0
parse5 ADDED - 8.0.0
rollup UPDATED 4.53.3 4.57.1
run-time-assertions ADDED - 1.5.2
saxes ADDED - 6.0.0
secret-stream-http ADDED - 1.0.1
semver UPDATED 7.7.2 7.7.4
source-map-support ADDED - 0.5.21
symbol-tree ADDED - 3.2.4
terser ADDED - 5.46.0
tldts-core ADDED - 7.0.23
tldts ADDED - 7.0.23
tough-cookie ADDED - 6.0.0
tr46 ADDED - 6.0.0
ts-api-utils UPDATED 2.1.0 2.4.0
type-fest UPDATED 4.41.0 5.4.3
typebox ADDED - 1.0.79
typed-event-target ADDED - 3.4.0
uint8array-extras ADDED - 1.5.0
update-browserslist-db UPDATED 1.2.2 1.2.3
vite UPDATED 7.2.7 7.3.1
w3c-xmlserializer ADDED - 5.0.0
webidl-conversions ADDED - 8.0.1
whatwg-url ADDED - 16.0.0
xml-name-validator ADDED - 5.0.0
xmlchars ADDED - 2.2.0
zod UPDATED 4.1.13 4.3.6

@socket-security
Copy link

socket-security bot commented Feb 11, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​jsdom@​27.0.01001007481100
Added@​comapeo/​map-server@​1.0.0-pre.6761009994100
Updated@​comapeo/​core@​5.2.1 ⏵ 5.4.07710084 +197 -1100
Addeduint8array-extras@​1.5.010010010081100
Addedeventsource-client@​1.2.010010010082100
Addedensure-error@​5.0.0981009183100
Updatedky@​1.10.0 ⏵ 1.14.3100100100 +190 +1100
Addedjsdom@​28.0.09610010094100
Updatedtype-fest@​5.1.0 ⏵ 5.4.3100100100 +195100

View full report

@gmaclennan gmaclennan changed the title feat: Add stubbed map sharing API feat: Add map sharing API Feb 11, 2026
@gmaclennan
Copy link
Member Author

I've updated the PR description with the completed API. This is now ready for review.

@gmaclennan gmaclennan requested a review from achou11 February 11, 2026 18:30
@gmaclennan
Copy link
Member Author

@achou11 there are a couple more hooks tests still pending (download progress related) and I think maybe I can delete the unit tests for the stores, which are now testing an implementation detail, once I verify that the hooks tests are covering the same things (I created the stores tests first so I could fix bugs before writing the hooks). I added a helper filterResult to reduce some boilerplate but I might update that to a more generic pick(result, keys). I didn't use that helper for any existing hooks - you can do that in a separate PR if you like.
What do you think about exporting a general provider CoMapeoCoreProvider that combines all the providers into one (including tanstack query) ?

@socket-security
Copy link

socket-security bot commented Feb 13, 2026

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

@gmaclennan
Copy link
Member Author

The latest changes may/should address the react native Request compatibility issues by removing ky and using a simpler wrapper for reducing fetch boiler plate. 0430f7a also adds hooks for importing, deleting and getting map info about the custom map. These replace the hooks currently in the mobile app code.

@cimigree
Copy link
Contributor

The latest changes may/should address the react native Request compatibility issues by removing ky and using a simpler wrapper for reducing fetch boiler plate. 0430f7a also adds hooks for importing, deleting and getting map info about the custom map. These replace the hooks currently in the mobile app code.

So sorry but it doesn't seem to be working or I need more guidance on how to use.
digidem/comapeo-mobile#1693 (comment)

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

4 participants