feat(pwa): register as an Android share target (#155)
public/manifest.json declares share_target (POST multipart to /share-target: title/text/url + image/video/audio/pdf/text files). The service worker answers that POST itself: it stashes the form in a Cache API bucket and 303s to the in-app /share page, which lists what arrived, offers a room search, and on pick writes the files into that room's upload-board atom (encrypting first for E2EE rooms via the composer's shared filesToUploadItems) and the title/text/url into its draft, then opens the room — the user still presses Send. The stash is cleared once placed; reopening /share afterwards says so. nginx/caddy examples and the prod image config gain a 303 for /share-target so a POST that reaches the origin before the worker controls the page lands on /share instead of a 405. iOS has no share target support and ignores the manifest entry. Verified headless against the built preview: SW-controlled page → POST /share-target (two PNGs + title + text) → /share lists both files and the text → pick the DM → composer shows both files on the upload board and the text in the draft → /share reports nothing pending. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { Room } from 'matrix-js-sdk';
|
||||
import { TUploadItem } from '../state/room/roomInputDrafts';
|
||||
import { encryptFile } from './matrix';
|
||||
import { safeFile } from './mimeTypes';
|
||||
import { fulfilledPromiseSettledResult } from './common';
|
||||
|
||||
/**
|
||||
* Files → composer upload items for `room`, encrypting first when the room
|
||||
* is E2EE. Shared by the composer's pick/drop/paste paths and the PWA share
|
||||
* target (#155).
|
||||
*/
|
||||
export async function filesToUploadItems(room: Room, files: File[]): Promise<TUploadItem[]> {
|
||||
const safeFiles = files.map(safeFile);
|
||||
const metadata = { markedAsSpoiler: false };
|
||||
if (room.hasEncryptionStateEvent()) {
|
||||
const encrypted = fulfilledPromiseSettledResult(
|
||||
await Promise.allSettled(safeFiles.map((f) => encryptFile(f))),
|
||||
);
|
||||
return encrypted.map((ef) => ({ ...ef, metadata }));
|
||||
}
|
||||
return safeFiles.map((f) => ({ file: f, originalFile: f, encInfo: undefined, metadata }));
|
||||
}
|
||||
Reference in New Issue
Block a user