drop-free.openapps.xyz

Source

This app’s code and revision history are public. Download a copy or explore the files below.

Public
The app’s code and revision history are public.
Owned app · rev b2e053fd98cb
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 80ddb8c5df822e290afdd3c7
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
← All files
src/main.tsx
// Drop's entry. "/" keeps the static landing and mounts the send and receive card into its hero;
// the app screens (/app home, /send, /r receive) hide the landing.
import { useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import { usePath } from "./lib/router";
import { picksFromDrop, picksFromInput } from "./lib/items";
import { Sender } from "./sender";
import { Start, send } from "./Start";
import { SendPage } from "./Send";
import { ReceivePage } from "./Receive";
import { AppBar, Toaster, toast } from "./ui";
import "./styles.css";

function Home() {
  return (
    <div className="app-page">
      <AppBar />
      <h1 className="app-h1">Send files to any device</h1>
      <div className="card start-card"><Start /></div>
    </div>
  );
}

/** The receive link's share id lives in the hash; opening another link in the same tab only changes the hash. */
function useHash(): string {
  const [hash, setHash] = useState(location.hash);
  useEffect(() => {
    const onHash = () => setHash(location.hash);
    window.addEventListener("hashchange", onHash);
    window.addEventListener("popstate", onHash);
    return () => { window.removeEventListener("hashchange", onHash); window.removeEventListener("popstate", onHash); };
  }, []);
  return hash;
}

function App() {
  const path = usePath();
  const hash = useHash();
  const screen =
    path === "/app" ? <Home />
      : path === "/send" ? <SendPage />
        : path === "/r" ? <ReceivePage key={hash} />
          : null;
  document.body.classList.toggle("app-mode", Boolean(screen));
  return <>{screen}<Toaster /></>;
}

createRoot(document.getElementById("app")!).render(<App />);
const start = document.getElementById("start");
if (start) createRoot(start).render(<Start />);

// A share this tab had open before a reload has lost its files; close it so its link stops.
void Sender.closeLeftover().then((had) => { if (had) toast("Sharing stopped when the page reloaded. Pick the files again to send them."); });

// Files shared to the installed Drop from another app (Android's share sheet) wait in the service worker.
if (location.pathname === "/send" && new URLSearchParams(location.search).has("shared") && "serviceWorker" in navigator) {
  history.replaceState(null, "", "/send");
  navigator.serviceWorker.addEventListener("message", (e) => {
    if (e.data?.t !== "drop:shared") return;
    const files = (e.data.files || []) as File[];
    if (files.length) send(picksFromInput(files));
    else toast("No files came through. Choose them here instead.");
  });
  void navigator.serviceWorker.ready.then((reg) => reg.active?.postMessage("drop:shared?"));
}

// Files dropped anywhere on the start screens or the sending screen are sent.
const hasFiles = (e: DragEvent) => Boolean(e.dataTransfer?.types.includes("Files"));
const accepts = () => ["/", "/app", "/send"].includes(location.pathname);
let depth = 0;
window.addEventListener("dragenter", (e) => { if (hasFiles(e) && accepts()) { depth++; document.body.classList.add("dragging"); } });
window.addEventListener("dragleave", () => { if (--depth <= 0) { depth = 0; document.body.classList.remove("dragging"); } });
window.addEventListener("dragover", (e) => { if (hasFiles(e)) { e.preventDefault(); if (e.dataTransfer) e.dataTransfer.dropEffect = accepts() ? "copy" : "none"; } });
window.addEventListener("drop", (e) => {
  if (!hasFiles(e)) return;
  e.preventDefault();
  depth = 0;
  document.body.classList.remove("dragging");
  if (!accepts() || !e.dataTransfer) return;
  void picksFromDrop(e.dataTransfer).then(send).catch(() => toast("Could not read those files. Try Choose files instead."));
});

if (import.meta.env.PROD && "serviceWorker" in navigator) {
  window.addEventListener("load", () => void navigator.serviceWorker.register("/sw.js").catch(() => null));
}

const head = document.querySelector(".site-head");
const onScroll = () => head?.classList.toggle("scrolled", window.scrollY > 4);
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();

Change history

Point OpenApps links to openapps.free
b2e053fd98 · 17 hours ago
Bind agent address in policy
ca424dd814 · 19 hours ago
Retry release capture
ae07f1a3fd · 19 hours ago
Use OpenApps service URLs and add contribution strip
0452ce136f · 19 hours ago
Bounded source sync
ca1bc00aa4 · 1 day ago
Bounded source sync
d4982911a0 · 1 day ago
Bounded source sync
18c97fe4ca · 1 day ago