From 2137193935d0f498a09f2c2c80d10bf77b47db95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 8 Dec 2022 16:30:48 +0100 Subject: [PATCH] Revert "Remove 'show on map' for now" This reverts commit 46cb676822da65ba16ad138780382399abb65962. --- .../features/event/event-information.tsx | 14 ++++ app/soapbox/features/soapbox-config/index.tsx | 32 +++++++- .../features/ui/components/modal-root.tsx | 2 + .../ui/components/modals/event-map-modal.tsx | 75 +++++++++++++++++++ .../features/ui/util/async-components.ts | 4 + .../normalizers/soapbox/soapbox-config.ts | 2 + package.json | 2 + webpack/rules/assets.ts | 1 + yarn.lock | 17 +++++ 9 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 app/soapbox/features/ui/components/modals/event-map-modal.tsx diff --git a/app/soapbox/features/event/event-information.tsx b/app/soapbox/features/event/event-information.tsx index 986dff0f9..e6c2f4e72 100644 --- a/app/soapbox/features/event/event-information.tsx +++ b/app/soapbox/features/event/event-information.tsx @@ -46,6 +46,14 @@ const EventInformation: React.FC = ({ params }) => { setShowMedia(!showMedia); }; + const handleShowMap: React.MouseEventHandler = (e) => { + e.preventDefault(); + + dispatch(openModal('EVENT_MAP', { + statusId: status.id, + })); + }; + const renderEventLocation = useCallback(() => { const event = status!.event!; @@ -64,6 +72,12 @@ const EventInformation: React.FC = ({ params }) => {
)} {[event.location.get('postalCode'), event.location.get('locality'), event.location.get('country')].filter(text => text).join(', ')} + {event.location.get('latitude') && (<> +
+ + + + )} diff --git a/app/soapbox/features/soapbox-config/index.tsx b/app/soapbox/features/soapbox-config/index.tsx index 3c8b85d33..2ebb71500 100644 --- a/app/soapbox/features/soapbox-config/index.tsx +++ b/app/soapbox/features/soapbox-config/index.tsx @@ -22,7 +22,7 @@ import { Toggle, } from 'soapbox/components/ui'; import ThemeSelector from 'soapbox/features/ui/components/theme-selector'; -import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; +import { useAppSelector, useAppDispatch, useFeatures } from 'soapbox/hooks'; import { normalizeSoapboxConfig } from 'soapbox/normalizers'; import ColorWithPicker from './components/color-with-picker'; @@ -54,6 +54,8 @@ const messages = defineMessages({ singleUserModeProfileHint: { id: 'soapbox_config.single_user_mode_profile_hint', defaultMessage: '@handle' }, feedInjectionLabel: { id: 'soapbox_config.feed_injection_label', defaultMessage: 'Feed injection' }, feedInjectionHint: { id: 'soapbox_config.feed_injection_hint', defaultMessage: 'Inject the feed with additional content, such as suggested profiles.' }, + tileServerLabel: { id: 'soapbox_config.tile_server_label', defaultMessage: 'Map tile server' }, + tileServerAttributionLabel: { id: 'soapbox_config.tile_server_attribution_label', defaultMessage: 'Map tiles attribution' }, }); type ValueGetter = (e: React.ChangeEvent) => any; @@ -72,6 +74,8 @@ const SoapboxConfig: React.FC = () => { const intl = useIntl(); const dispatch = useAppDispatch(); + const features = useFeatures(); + const initialData = useAppSelector(state => state.soapbox); const [isLoading, setLoading] = useState(false); @@ -345,6 +349,32 @@ const SoapboxConfig: React.FC = () => { /> + {features.events && ( + <> + + } /> + + + + e.target.value)} + /> + + + + e.target.value)} + /> + + + )} + } /> diff --git a/app/soapbox/features/ui/components/modal-root.tsx b/app/soapbox/features/ui/components/modal-root.tsx index 5d299ccdd..41f8147ea 100644 --- a/app/soapbox/features/ui/components/modal-root.tsx +++ b/app/soapbox/features/ui/components/modal-root.tsx @@ -33,6 +33,7 @@ import { ComposeEventModal, JoinEventModal, AccountModerationModal, + EventMapModal, EventParticipantsModal, PolicyModal, } from 'soapbox/features/ui/util/async-components'; @@ -75,6 +76,7 @@ const MODAL_COMPONENTS = { 'COMPOSE_EVENT': ComposeEventModal, 'JOIN_EVENT': JoinEventModal, 'ACCOUNT_MODERATION': AccountModerationModal, + 'EVENT_MAP': EventMapModal, 'EVENT_PARTICIPANTS': EventParticipantsModal, 'POLICY': PolicyModal, }; diff --git a/app/soapbox/features/ui/components/modals/event-map-modal.tsx b/app/soapbox/features/ui/components/modals/event-map-modal.tsx new file mode 100644 index 000000000..b918d7204 --- /dev/null +++ b/app/soapbox/features/ui/components/modals/event-map-modal.tsx @@ -0,0 +1,75 @@ +import L from 'leaflet'; +import React, { useCallback, useEffect, useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; + +import { Button, Modal, Stack } from 'soapbox/components/ui'; +import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks'; +import { makeGetStatus } from 'soapbox/selectors'; + +import type { Status as StatusEntity } from 'soapbox/types/entities'; + +import 'leaflet/dist/leaflet.css'; + +L.Icon.Default.mergeOptions({ + iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), + iconUrl: require('leaflet/dist/images/marker-icon.png'), + shadowUrl: require('leaflet/dist/images/marker-shadow.png'), +}); + +interface IEventMapModal { + onClose: (type: string) => void, + statusId: string, +} + +const EventMapModal: React.FC = ({ onClose, statusId }) => { + const { tileServer, tileServerAttribution } = useSoapboxConfig(); + + const getStatus = useCallback(makeGetStatus(), []); + const status = useAppSelector(state => getStatus(state, { id: statusId })) as StatusEntity; + const location = status.event!.location!; + + const map = useRef(); + + useEffect(() => { + const latlng: [number, number] = [+location.get('latitude'), +location.get('longitude')]; + + map.current = L.map('event-map').setView(latlng, 15); + + L.marker(latlng, { + title: location.get('name'), + }).addTo(map.current); + + L.tileLayer(tileServer, { + attribution: tileServerAttribution, + }).addTo(map.current); + + return () => { + map.current?.remove(); + }; + }, []); + + const onClickClose = () => { + onClose('EVENT_MAP'); + }; + + const onClickNavigate = () => { + window.open(`https://www.openstreetmap.org/directions?from=&to=${location.get('latitude')},${location.get('longitude')}#map=14/${location.get('latitude')}/${location.get('longitude')}`, '_blank'); + }; + + return ( + } + onClose={onClickClose} + width='2xl' + > + +
+ + + + ); +}; + +export default EventMapModal; diff --git a/app/soapbox/features/ui/util/async-components.ts b/app/soapbox/features/ui/util/async-components.ts index da320ae2f..396eea883 100644 --- a/app/soapbox/features/ui/util/async-components.ts +++ b/app/soapbox/features/ui/util/async-components.ts @@ -530,6 +530,10 @@ export function EventDiscussion() { return import(/* webpackChunkName: "features/event" */'../../event/event-discussion'); } +export function EventMapModal() { + return import(/* webpackChunkName: "modals/event-map-modal" */'../components/modals/event-map-modal'); +} + export function EventParticipantsModal() { return import(/* webpackChunkName: "modals/event-participants-modal" */'../components/modals/event-participants-modal'); } diff --git a/app/soapbox/normalizers/soapbox/soapbox-config.ts b/app/soapbox/normalizers/soapbox/soapbox-config.ts index 3cfedb7af..089833d7f 100644 --- a/app/soapbox/normalizers/soapbox/soapbox-config.ts +++ b/app/soapbox/normalizers/soapbox/soapbox-config.ts @@ -114,6 +114,8 @@ export const SoapboxConfigRecord = ImmutableRecord({ displayCta: true, /** Whether to inject suggested profiles into the Home feed. */ feedInjection: true, + tileServer: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + tileServerAttribution: '© OpenStreetMap Contributors', }, 'SoapboxConfig'); type SoapboxConfigMap = ImmutableMap; diff --git a/package.json b/package.json index 1284969a9..14ed5d916 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "@types/escape-html": "^1.0.1", "@types/http-link-header": "^1.0.3", "@types/jest": "^28.1.4", + "@types/leaflet": "^1.8.0", "@types/lodash": "^4.14.180", "@types/object-assign": "^4.0.30", "@types/object-fit-images": "^3.2.3", @@ -139,6 +140,7 @@ "intl-pluralrules": "^1.3.1", "is-nan": "^1.2.1", "jsdoc": "~3.6.7", + "leaflet": "^1.8.0", "libphonenumber-js": "^1.10.8", "line-awesome": "^1.3.0", "localforage": "^1.10.0", diff --git a/webpack/rules/assets.ts b/webpack/rules/assets.ts index e9dd0202a..0f5648987 100644 --- a/webpack/rules/assets.ts +++ b/webpack/rules/assets.ts @@ -13,6 +13,7 @@ const rules: RuleSetRule[] = [{ include: [ resolve('app', 'assets', 'images'), resolve('node_modules', 'emoji-datasource'), + resolve('node_modules', 'leaflet'), ], generator: { filename: 'packs/images/[name]-[contenthash:8][ext]', diff --git a/yarn.lock b/yarn.lock index fe283b59c..10d918d46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2550,6 +2550,11 @@ dependencies: "@types/node" "*" +"@types/geojson@*": + version "7946.0.10" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" + integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA== + "@types/graceful-fs@^4.1.3": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -2658,6 +2663,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/leaflet@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.8.0.tgz#dc92d3e868fb6d5067b4b59fa08cd4441f84fabe" + integrity sha512-+sXFmiJTFdhaXXIGFlV5re9AdqtAODoXbGAvxx02e5SHXL3ir7ClP5J7pahO8VmzKY3dth4RUS1nf2BTT+DW1A== + dependencies: + "@types/geojson" "*" + "@types/lodash@^4.14.180": version "4.14.180" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" @@ -7953,6 +7965,11 @@ language-tags@^1.0.5: dependencies: language-subtag-registry "~0.3.2" +leaflet@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.8.0.tgz#4615db4a22a304e8e692cae9270b983b38a2055e" + integrity sha512-gwhMjFCQiYs3x/Sf+d49f10ERXaEFCPr+nVTryhAW8DWbMGqJqt9G4XuIaHmFW08zYvhgdzqXGr8AlW8v8dQkA== + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"