mv element.io @types __mocks__/ debian docker module_system/ playwright res src test webapp Dockerfile .dockerignore .eslintignore .stylelintrc.cjs babel.config.cjs recorder-worklet-loader.cjs .modernizr.json components.json config.json config.sample.json package.json project.json tsconfig.json tsconfig.module_system.json jest.config.ts playwright.config.ts webpack.config.ts build_config.sample.yaml apps/web/
mkdir apps/web/scripts
mv scripts/{cleanup.sh,ci_package.sh,copy-res.ts,deploy.py,package.sh} apps/web/scripts
And a couple of gitignore tweaks
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { _t } from "../../languageHandler";
|
||||
|
||||
export enum LocationShareError {
|
||||
MapStyleUrlNotConfigured = "MapStyleUrlNotConfigured",
|
||||
MapStyleUrlNotReachable = "MapStyleUrlNotReachable",
|
||||
WebGLNotEnabled = "WebGLNotEnabled",
|
||||
Default = "Default",
|
||||
}
|
||||
|
||||
export const getLocationShareErrorMessage = (errorType?: LocationShareError): string => {
|
||||
switch (errorType) {
|
||||
case LocationShareError.MapStyleUrlNotConfigured:
|
||||
return _t("location_sharing|MapStyleUrlNotConfigured");
|
||||
case LocationShareError.WebGLNotEnabled:
|
||||
return _t("location_sharing|WebGLNotEnabled");
|
||||
case LocationShareError.MapStyleUrlNotReachable:
|
||||
default:
|
||||
return _t("location_sharing|MapStyleUrlNotReachable");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import SdkConfig from "../../SdkConfig";
|
||||
import { getTileServerWellKnown } from "../WellKnownUtils";
|
||||
import { LocationShareError } from "./LocationShareErrors";
|
||||
|
||||
/**
|
||||
* Look up what map tile server style URL was provided in the homeserver's
|
||||
* .well-known location, or, failing that, in our local config, or, failing
|
||||
* that, defaults to the same tile server listed by matrix.org.
|
||||
*/
|
||||
export function findMapStyleUrl(matrixClient: MatrixClient): string {
|
||||
const mapStyleUrl = getTileServerWellKnown(matrixClient)?.map_style_url ?? SdkConfig.get().map_style_url;
|
||||
|
||||
if (!mapStyleUrl) {
|
||||
logger.error("'map_style_url' missing from homeserver .well-known area, and missing from from config.json.");
|
||||
throw new Error(LocationShareError.MapStyleUrlNotConfigured);
|
||||
}
|
||||
|
||||
return mapStyleUrl;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
export * from "./findMapStyleUrl";
|
||||
export * from "./isSelfLocation";
|
||||
export * from "./locationEventGeoUri";
|
||||
export * from "./LocationShareErrors";
|
||||
export * from "./links";
|
||||
export * from "./parseGeoUri";
|
||||
export * from "./positionFailureMessage";
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type ILocationContent, LocationAssetType, M_ASSET } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
export const isSelfLocation = (locationContent: ILocationContent): boolean => {
|
||||
const asset = M_ASSET.findIn(locationContent) as { type: string };
|
||||
const assetType = asset?.type ?? LocationAssetType.Self;
|
||||
return assetType == LocationAssetType.Self;
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type MatrixEvent, M_LOCATION } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { parseGeoUri } from "./parseGeoUri";
|
||||
|
||||
export const makeMapSiteLink = (coords: GeolocationCoordinates): string => {
|
||||
return (
|
||||
"https://www.openstreetmap.org/" +
|
||||
`?mlat=${coords.latitude}` +
|
||||
`&mlon=${coords.longitude}` +
|
||||
`#map=16/${coords.latitude}/${coords.longitude}`
|
||||
);
|
||||
};
|
||||
|
||||
export const createMapSiteLinkFromEvent = (event: MatrixEvent): string | null => {
|
||||
const content = event.getContent();
|
||||
const mLocation = content[M_LOCATION.name];
|
||||
if (mLocation !== undefined) {
|
||||
const uri = mLocation["uri"];
|
||||
if (uri !== undefined) {
|
||||
const geoCoords = parseGeoUri(uri);
|
||||
return geoCoords ? makeMapSiteLink(geoCoords) : null;
|
||||
}
|
||||
} else {
|
||||
const geoUri = content["geo_uri"];
|
||||
if (geoUri) {
|
||||
const geoCoords = parseGeoUri(geoUri);
|
||||
return geoCoords ? makeMapSiteLink(geoCoords) : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type MatrixEvent, M_LOCATION, type MLocationEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
type LocationEvent = { geo_uri: string } & MLocationEvent;
|
||||
|
||||
/**
|
||||
* Find the geo-URI contained within a location event.
|
||||
*/
|
||||
export const locationEventGeoUri = (mxEvent: MatrixEvent): string => {
|
||||
// unfortunately we're stuck supporting legacy `content.geo_uri`
|
||||
// events until the end of days, or until we figure out mutable
|
||||
// events - so folks can read their old chat history correctly.
|
||||
// https://github.com/matrix-org/matrix-doc/issues/3516
|
||||
const content = mxEvent.getContent<LocationEvent>();
|
||||
const loc = M_LOCATION.findIn(content);
|
||||
return loc?.uri ?? content["geo_uri"];
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import * as maplibregl from "maplibre-gl";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { _t } from "../../languageHandler";
|
||||
import { findMapStyleUrl } from "./findMapStyleUrl";
|
||||
import { LocationShareError } from "./LocationShareErrors";
|
||||
|
||||
export const createMap = (
|
||||
client: MatrixClient,
|
||||
interactive: boolean,
|
||||
bodyId: string,
|
||||
onError?: (error: Error) => void,
|
||||
): maplibregl.Map => {
|
||||
try {
|
||||
const styleUrl = findMapStyleUrl(client);
|
||||
|
||||
const map = new maplibregl.Map({
|
||||
container: bodyId,
|
||||
style: styleUrl,
|
||||
zoom: 15,
|
||||
interactive,
|
||||
attributionControl: false,
|
||||
locale: {
|
||||
"AttributionControl.ToggleAttribution": _t("location_sharing|toggle_attribution"),
|
||||
"AttributionControl.MapFeedback": _t("location_sharing|map_feedback"),
|
||||
"FullscreenControl.Enter": _t("action|enter_fullscreen"),
|
||||
"FullscreenControl.Exit": _t("action|exit_fullscreeen"),
|
||||
"GeolocateControl.FindMyLocation": _t("location_sharing|find_my_location"),
|
||||
"GeolocateControl.LocationNotAvailable": _t("location_sharing|location_not_available"),
|
||||
"LogoControl.Title": _t("location_sharing|mapbox_logo"),
|
||||
"NavigationControl.ResetBearing": _t("location_sharing|reset_bearing"),
|
||||
"NavigationControl.ZoomIn": _t("action|zoom_in"),
|
||||
"NavigationControl.ZoomOut": _t("action|zoom_out"),
|
||||
},
|
||||
});
|
||||
map.addControl(new maplibregl.AttributionControl(), "top-right");
|
||||
|
||||
map.on("error", (e) => {
|
||||
logger.error("Failed to load map: check map_style_url in config.json has a valid URL and API key", e.error);
|
||||
onError?.(new Error(LocationShareError.MapStyleUrlNotReachable));
|
||||
});
|
||||
|
||||
return map;
|
||||
} catch (e) {
|
||||
logger.error("Failed to render map", e);
|
||||
const errorMessage = (e as Error)?.message;
|
||||
if (errorMessage.includes("Failed to initialize WebGL")) throw new Error(LocationShareError.WebGLNotEnabled);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const createMarker = (coords: GeolocationCoordinates, element: HTMLElement): maplibregl.Marker => {
|
||||
const marker = new maplibregl.Marker({
|
||||
element,
|
||||
anchor: "bottom",
|
||||
offset: [0, -1],
|
||||
}).setLngLat({ lon: coords.longitude, lat: coords.latitude });
|
||||
return marker;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
export const parseGeoUri = (uri: string): GeolocationCoordinates | undefined => {
|
||||
function parse(s: string): number | null {
|
||||
const ret = parseFloat(s);
|
||||
if (Number.isNaN(ret)) {
|
||||
return null;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
const m = uri.match(/^\s*geo:(.*?)\s*$/);
|
||||
if (!m) return;
|
||||
const parts = m[1].split(";");
|
||||
const coords = parts[0].split(",");
|
||||
let uncertainty: number | null | undefined = undefined;
|
||||
for (const param of parts.slice(1)) {
|
||||
const m = param.match(/u=(.*)/);
|
||||
if (m) uncertainty = parse(m[1]);
|
||||
}
|
||||
const latitude = parse(coords[0]);
|
||||
const longitude = parse(coords[1]);
|
||||
|
||||
if (latitude === null || longitude === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const geoCoords = {
|
||||
latitude: latitude!,
|
||||
longitude: longitude!,
|
||||
altitude: parse(coords[2]),
|
||||
accuracy: uncertainty!,
|
||||
altitudeAccuracy: null,
|
||||
heading: null,
|
||||
speed: null,
|
||||
};
|
||||
|
||||
return {
|
||||
toJSON: () => geoCoords,
|
||||
...geoCoords,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { _t } from "../../languageHandler";
|
||||
import SdkConfig from "../../SdkConfig";
|
||||
|
||||
/**
|
||||
* Get a localised error message for GeolocationPositionError error codes
|
||||
* @param code - error code from GeolocationPositionError
|
||||
* @returns
|
||||
*/
|
||||
export const positionFailureMessage = (code: number): string | undefined => {
|
||||
const brand = SdkConfig.get().brand;
|
||||
switch (code) {
|
||||
case 1:
|
||||
return _t("location_sharing|failed_permission", { brand });
|
||||
case 2:
|
||||
return _t("location_sharing|failed_generic");
|
||||
case 3:
|
||||
return _t("location_sharing|failed_timeout");
|
||||
case 4:
|
||||
return _t("location_sharing|failed_unknown");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import type { Map as MapLibreMap } from "maplibre-gl";
|
||||
import { createMap } from "./map";
|
||||
import { useMatrixClientContext } from "../../contexts/MatrixClientContext";
|
||||
|
||||
interface UseMapProps {
|
||||
bodyId: string;
|
||||
onError?: (error: Error) => void;
|
||||
interactive?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a map instance
|
||||
* Add listeners for errors
|
||||
* Make sure `onError` has a stable reference
|
||||
* As map is recreated on changes to it
|
||||
*/
|
||||
export const useMap = ({ interactive, bodyId, onError }: UseMapProps): MapLibreMap | undefined => {
|
||||
const cli = useMatrixClientContext();
|
||||
const [map, setMap] = useState<MapLibreMap>();
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
let map: MapLibreMap | undefined;
|
||||
try {
|
||||
map = createMap(cli, !!interactive, bodyId, onError);
|
||||
setMap(map);
|
||||
} catch (error) {
|
||||
console.error("Error encountered in useMap", error);
|
||||
if (error instanceof Error) {
|
||||
onError?.(error);
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
if (map) {
|
||||
map.remove();
|
||||
setMap(undefined);
|
||||
}
|
||||
};
|
||||
},
|
||||
// map is excluded as a dependency
|
||||
[cli, interactive, bodyId, onError],
|
||||
);
|
||||
|
||||
return map;
|
||||
};
|
||||
Reference in New Issue
Block a user