Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion apps/mobile/src/components/LeafletView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@ import { View } from "react-native";
import { WebView as RNWebView, type WebViewMessageEvent, type WebViewProps } from "react-native-webview";
import { useOnline } from "../net";
import { Empty, IconButton, makeStyles, useTheme } from "../ui";
import { API_URL } from "../config";
import { mapPage } from "./mapHtml";

/**
* The map page's origin: the instance's own (issue #281). It is the Referer of the tile requests, and
* OpenStreetMap's usage policy asks for a real one; a made-up host got the tiles blocked.
*/
const PAGE_ORIGIN = API_URL ? `${new URL(API_URL).origin}/` : "about:blank";

// react-native-webview's class typings collapse to `never` under this TS/React combination; use the props type directly.
const WebView = RNWebView as unknown as ComponentType<WebViewProps & { ref?: Ref<{ injectJavaScript: (js: string) => void }> }>;

Expand All @@ -19,7 +26,7 @@ export function LeafletView({ script, onMessage, fitLabel = "Show all", offlineA
const { c, name } = useTheme();
const online = useOnline();
const ref = useRef<{ injectJavaScript: (js: string) => void } | null>(null);
const source = useMemo(() => ({ html: mapPage({ dark: name === "set", bg: c.bg, script }), baseUrl: "https://openclapper.local/" }), [name, c.bg, script]);
const source = useMemo(() => ({ html: mapPage({ dark: name === "set", bg: c.bg, script }), baseUrl: PAGE_ORIGIN }), [name, c.bg, script]);
const run = (js: string) => ref.current?.injectJavaScript(`${js};true;`);
if (handle) handle.current = { run };
if (!online) {
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/components/mapHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function mapPage({ dark, bg, script }: { dark: boolean; bg: string; scrip
</head><body><div id="m"></div><script>
function post(m){window.ReactNativeWebView&&window.ReactNativeWebView.postMessage(JSON.stringify(m));}
var map=L.map('m',{zoomControl:false});
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',{maxZoom:19,attribution:'&copy; OpenStreetMap'}).addTo(map);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',{maxZoom:19,attribution:'&copy; OpenStreetMap',referrerPolicy:'strict-origin-when-cross-origin'}).addTo(map);
function zoomIn(){map.zoomIn();} function zoomOut(){map.zoomOut();}
${script}
</script></body></html>`;
Expand Down
3 changes: 2 additions & 1 deletion apps/studio/src/Locations.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from "preact/hooks";
import L from "leaflet";
import { osmTiles } from "./mapTiles";
import "leaflet/dist/leaflet.css";
import { ApiError, deleteLocation, deleteSpot, existingIdOf, putLocation, putSpot, type Location, type Shot, type Spot } from "./api";
import { binned } from "./apps/trashOps";
Expand Down Expand Up @@ -187,7 +188,7 @@ function PinDialog({ title, place, shots, onClose, onSave }: { title: string; pl
useEffect(() => {
if (!el.current) return;
const map = L.map(el.current, { zoomControl: true }).setView(centre, at || own.length ? 17 : 12);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, attribution: "&copy; OpenStreetMap contributors" }).addTo(map);
osmTiles().addTo(map);
for (const { s, p } of own) {
L.circleMarker([p.lat, p.lon], { radius: 6, color: "#0B0B0C", weight: 2, fillColor: s.position_from_location ? "#FFB000" : "#FFFFFF", fillOpacity: 1 })
.bindTooltip(`${shotTitle(s)}${s.position_from_location ? " · synced to the place" : ""}`).addTo(map);
Expand Down
3 changes: 2 additions & 1 deletion apps/studio/src/MapView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { render } from "preact";
import { useEffect, useRef } from "preact/hooks";
import L from "leaflet";
import { osmTiles } from "./mapTiles";
import "leaflet/dist/leaflet.css";
import { label, STATE_ICONS } from "@openclapper/vocab";
import type { Shot } from "./api";
Expand Down Expand Up @@ -66,7 +67,7 @@ function LeafletMap({ shots, mask, selectedId, onSelect, onOpen }: Props) {
useEffect(() => {
if (!el.current) return;
const m = L.map(el.current, { zoomControl: false });
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(m);
osmTiles().addTo(m);
L.control.zoom({ position: "topleft" }).addTo(m);
map.current = m;
m.setView([51, 10], 5);
Expand Down
3 changes: 2 additions & 1 deletion apps/studio/src/PositionEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from "preact/hooks";
import L from "leaflet";
import { osmTiles } from "./mapTiles";
import "leaflet/dist/leaflet.css";
import { patchPhotoPosition, type Photo, type Shot } from "./api";
import { positionOf } from "./format";
Expand Down Expand Up @@ -31,7 +32,7 @@ export function PositionStage({ shot, photo, onSaved, onCancel, onError }: Props
useEffect(() => {
if (!el.current) return;
const map = L.map(el.current, { zoomControl: false }).setView(start, had || positionOf(shot) ? 18 : 12);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, attribution: "&copy; OpenStreetMap contributors" }).addTo(map);
osmTiles().addTo(map);
L.control.zoom({ position: "topleft" }).addTo(map);
// Reported accuracy: dashed ring with a faint accent fill, readable on light and (filtered) dark tiles.
if (had && photo.gps_accuracy_m) L.circle(start, { radius: photo.gps_accuracy_m, color: "#0B0B0C", weight: 2, dashArray: "6 4", fillColor: "#0040D8", fillOpacity: 0.12 }).addTo(map);
Expand Down
12 changes: 12 additions & 0 deletions apps/studio/src/mapTiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import L from "leaflet";

/**
* OpenStreetMap's tiles with what its usage policy asks of a web page (issue #281): attribution and a
* valid Referer. The page's own policy is `same-origin` (service-kit), which sends no Referer to the
* tile server and gets the tiles blocked; the tiles send the page's origin, nothing more.
*/
export const osmTiles = () => L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 19,
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
referrerPolicy: "strict-origin-when-cross-origin",
});
2 changes: 1 addition & 1 deletion docs/infrastructure.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Everything runs on one Cloudflare account per instance; nothing else is needed.
| Cloudflare Access | The admin plane's sign-in. |
| Android signing key | Per deployment, made once with `scripts/keystore.sh` (into a folder outside the checkout); in the deployment's GitHub secrets and a backup, never in git or a checkout. The staging app (`org.openclapper.staging`) uses the same key. Builds need no other service; the only build-time setting is `EXPO_PUBLIC_API_URL` ([self-hosting](self-hosting.md#the-phone)) |
| Open-Meteo | Weather forecast, called from the Studio browser directly (free, no key); the only origin besides the tiles in the Studio's CSP `connect-src` |
| OpenStreetMap tiles | Maps in the Studio (Leaflet) and the phone (Leaflet in a WebView, loaded from unpkg) |
| OpenStreetMap tiles | Maps in the Studio (Leaflet, `mapTiles.ts`) and the phone (Leaflet in a WebView, loaded from unpkg). Straight from `tile.openstreetmap.org` under its [usage policy](https://operations.osmfoundation.org/policies/tiles/): attribution and a valid Referer. The Studio's page policy is `same-origin`, so the tile layers send the origin themselves; the phone's map page takes the instance's origin as its base URL (issue #281) |

## Auth

Expand Down
Loading