Skip to content

Receive a shared file on Android, and share a chat URL on iOS #253

Description

@HMarzban

Parent

#249 Deepen the installed PWA. Sibling of #247, #248, #250, #251, and #252. Not blocked by them.

What to build

On an installed Android Chrome PWA, accept a shared Word or Markdown file and open it as a new pad.

iOS has no Share Target. Do not claim it does. On iOS, Chat Share must open the OS share sheet for the chat URL, the same way Document Share already shares a pad URL.

Acceptance criteria

  • /receive is a Next page (same shape as /new and /privacy). It is not a pad named receive. Add /receive to UTILITY_PATHS and receive to RESERVED_SLUGS.
  • /share is unchanged. It still opens a pad named share if someone visits it.
  • Manifest share_target action is same-origin /receive. Scope stays /. Do not change manifest id (docs-plus-pwa).
  • Installed Android Chrome: Share to docs.plus with a .docx or .md file lands on /receive, then a new pad, then the existing import convert and apply flow.
  • The custom service worker intercepts POST /receive, keeps one file that existing Import would accept (same types, 10 MB cap), and replies with a redirect to GET /receive. Any other POST is ignored.
  • /receive then location.replace('/new'). That leaves the pad that was already open. Never setContent on that prior document id.
  • After /new lands, the new pad is the only reader of that stash. It waits until the slug has a DocumentMetadata row the same way a normal first open does, and until a session JWT exists. Then it calls existing importDocument and the Settings apply path, then deletes the stash. One consumer. /receive does not import after the replace.
  • Signed-out: show Sign in on /receive and drop the stash. Do not keep the file across sign-in. The user shares again after they sign in.
  • A tab that is not the installed Android app does not have to show the target.
  • iOS Safari and the iOS Home Screen app: docs.plus does not appear as a receive target. No fake target.
  • iOS inbound file stays the existing Import picker. That click stays sync. No capture.
  • iOS and other browsers with Web Share: Chat Share opens navigator.share with the chat URL. Copy stays the fallback.
  • Private pad: Document Share still blocks a public post. Chat Share of a URL may use the sheet or copy. It must not add social posts.
  • No new Next pages/api route. No rest-api share endpoint. Health, Validate, and Status stay as they are.
  • file_handlers is not added.

Agent Brief

Category: enhancement
Summary: Android Share Target into a new pad. iOS can share a chat URL out. iOS cannot receive.

Current behavior:
UTILITY_PATHS is /privacy and /terms only. /new is a real page that redirects to a random slug. /share is not reserved. [...slugs] would treat it as a pad.

The manifest has no share_target and no file_handlers. launch_handler is navigate-existing.

Document Share uses navigator.share with title, text, and URL when the pad is not Private. Chat Share only copies the chat URL. Gallery Save on iOS shares a file.

Next pages/api keeps only Health. A share action cannot live on rest-api if that host is outside the PWA scope.

Import already converts .docx and .md on a live pad. Media insert needs a live editor. A share must not land on Home with no editor. Import needs a DocumentMetadata row and a signed-in token. /new only redirects; it does not stamp ownerId.

Desired behavior:
Add /receive as a utility page. Register share_target for files only, accept Word and Markdown, same as Import.

The worker holds the file from the Android share POST, then the page goes through /new so navigate-existing leaves the current room. The new pad is the only consumer of that stash. Then run existing Import on that new slug. Do not insert into the open document. Do not import on /receive after the replace.

Signed-out users sign in, then share the file again. Do not keep the blob across an auth change.

iOS: leave receive unimplemented. The honest inbound path is Import. Cover iOS on the way out: Chat Share uses the OS sheet when navigator.share exists, and copy when it does not. Document Share already does this for the pad URL. Do not route Chat Share through gallery file helpers.

Key interfaces:

  • /receive — utility page for Share Target. Not a pad. Redirects; does not import.
  • Share Target — Android Chromium installed PWA only
  • Stash — one-shot file in the worker; the new pad reads it once and deletes it
  • Import — existing Word and Markdown convert and apply
  • /new — leave the open room, then land on a new slug
  • Document Share — pad URL out; already uses the OS sheet
  • Chat Share — chat URL out; must use the OS sheet on iOS
  • Editing lock — import still needs a writable pad on the new document

Out of scope

  • iOS Share Target (the OS does not offer it)
  • file_handlers
  • Sharing into the already-open pad
  • Images, plain text, or a URL as the first receive payload
  • Chat Share as a file
  • Save export with a Chromium picker and iOS Files #252 export save picker
  • Command jump
  • Folders
  • Changing manifest id

Activity

  1. HMarzban commented on Sep 9, 2026

    @HMarzban
    CollaboratorAuthor

    Sibling export save is #252. Both sit under #249.

  2. HMarzban commented on Sep 9, 2026

    @HMarzban
    CollaboratorAuthor

    iOS and macOS Safari (WebKit)

    Neither iOS nor macOS Safari has Share Target. Import stays the inbound path.

    Chat Share and Document Share use navigator.share when it exists, including macOS Safari. Copy is the fallback.

    Do not add file_handlers.

  3. HMarzban commented on Sep 9, 2026

    @HMarzban
    CollaboratorAuthor

    Spec review: concrete receive path — worker POST stash, GET /receive, replace /new, then existing Import. Signed-out drops the file. Do not change manifest id.

  4. HMarzban commented on Sep 11, 2026

    @HMarzban
    CollaboratorAuthor

    Fresh-eyes hole closed: the new pad is the only stash reader. /receive redirects. Import runs once after /new, then the stash is deleted.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions