fix(privacy): location map loads on click; CollapsibleBody tracks reduced motion live
m.location mounted an openstreetmap.org iframe on render, acting as a read beacon; it's now behind a "Load map" button. CollapsibleBody uses useReducedMotion() instead of a one-time matchMedia snapshot. Fixes #66 Fixes #85 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
|||||||
MessageVerificationRequestContent,
|
MessageVerificationRequestContent,
|
||||||
} from './content';
|
} from './content';
|
||||||
import { useMessageTranslation } from '../../hooks/useMessageTranslation';
|
import { useMessageTranslation } from '../../hooks/useMessageTranslation';
|
||||||
|
import { useReducedMotion } from '../../hooks/useReducedMotion';
|
||||||
import { languageName } from '../../utils/translation/langUtils';
|
import { languageName } from '../../utils/translation/langUtils';
|
||||||
import {
|
import {
|
||||||
IAudioContent,
|
IAudioContent,
|
||||||
@@ -62,8 +63,9 @@ function CollapsibleBody({ eventId, children }: CollapsibleBodyProps) {
|
|||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const prefersReducedMotion =
|
// A one-time matchMedia() read never updated if the OS setting changed mid-session
|
||||||
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
// (Gitea #85); useReducedMotion subscribes to the change event instead.
|
||||||
|
const prefersReducedMotion = useReducedMotion();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -633,6 +635,13 @@ type MLocationProps = {
|
|||||||
};
|
};
|
||||||
export function MLocation({ content }: MLocationProps) {
|
export function MLocation({ content }: MLocationProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
// The OpenStreetMap iframe used to mount unconditionally on render, silently
|
||||||
|
// handing the sender's coordinates (and this client's IP/UA) to
|
||||||
|
// openstreetmap.org for every location message rendered, autoplay or not.
|
||||||
|
// Gate it behind an explicit click instead (Gitea #66). There's no
|
||||||
|
// location-specific auto-load setting in settings.ts to opt back into this,
|
||||||
|
// so it always requires a click.
|
||||||
|
const [mapLoaded, setMapLoaded] = useState(false);
|
||||||
// Prefer the legacy top-level geo_uri, but fall back to the MSC3488 extensible
|
// Prefer the legacy top-level geo_uri, but fall back to the MSC3488 extensible
|
||||||
// location block so events from clients that only send the new shape (uri under
|
// location block so events from clients that only send the new shape (uri under
|
||||||
// org.matrix.msc3488.location / m.location) still render instead of appearing
|
// org.matrix.msc3488.location / m.location) still render instead of appearing
|
||||||
@@ -660,20 +669,33 @@ export function MLocation({ content }: MLocationProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box direction="Column" alignItems="Start" gap="200">
|
<Box direction="Column" alignItems="Start" gap="200">
|
||||||
<iframe
|
{mapLoaded ? (
|
||||||
title="Location"
|
<iframe
|
||||||
src={mapSrc}
|
title="Location"
|
||||||
style={{
|
src={mapSrc}
|
||||||
width: '280px',
|
style={{
|
||||||
height: '160px',
|
width: '280px',
|
||||||
border: `${config.borderWidth.B300} solid ${color.SurfaceVariant.ContainerLine}`,
|
height: '160px',
|
||||||
borderRadius: '8px',
|
border: `${config.borderWidth.B300} solid ${color.SurfaceVariant.ContainerLine}`,
|
||||||
display: 'block',
|
borderRadius: '8px',
|
||||||
}}
|
display: 'block',
|
||||||
scrolling="no"
|
}}
|
||||||
loading="lazy"
|
scrolling="no"
|
||||||
sandbox="allow-scripts"
|
loading="lazy"
|
||||||
/>
|
sandbox="allow-scripts"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
size="400"
|
||||||
|
radii="300"
|
||||||
|
variant="Secondary"
|
||||||
|
fill="Soft"
|
||||||
|
onClick={() => setMapLoaded(true)}
|
||||||
|
before={<Icon src={Icons.Pin} size="50" />}
|
||||||
|
>
|
||||||
|
<Text size="B300">{t('Organisms.Message.load_map', 'Load map')}</Text>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{description && (
|
{description && (
|
||||||
<Text size="T300" style={{ wordBreak: 'break-word', maxWidth: '280px' }}>
|
<Text size="T300" style={{ wordBreak: 'break-word', maxWidth: '280px' }}>
|
||||||
{description}
|
{description}
|
||||||
|
|||||||
Reference in New Issue
Block a user