diff --git a/frontend/src/components/projectDetail/index.js b/frontend/src/components/projectDetail/index.js
index 47068b8c3f..71f43535a5 100644
--- a/frontend/src/components/projectDetail/index.js
+++ b/frontend/src/components/projectDetail/index.js
@@ -23,7 +23,8 @@ import { ProjectInfoPanel } from './infoPanel';
import { OSMChaButton } from './osmchaButton';
import { LiveViewButton } from './liveViewButton';
import { useSetProjectPageTitleTag } from '../../hooks/UseMetaTags';
-import { useProjectContributionsQuery, useProjectTimelineQuery, useAvailableCountriesQuery } from '../../api/projects';
+import useHasLiveMonitoringFeature from '../../hooks/UseHasLiveMonitoringFeature';
+import { useProjectContributionsQuery, useProjectTimelineQuery } from '../../api/projects';
import { Alert } from '../alert';
import './styles.scss';
@@ -145,6 +146,8 @@ export const ProjectDetail = (props) => {
props.project.projectId,
);
+ const hasLiveMonitoringFeature = useHasLiveMonitoringFeature();
+
const htmlDescription =
props.project.projectInfo && htmlFromMarkdown(props.project.projectInfo.description);
const h2Classes = 'pl4 f3 f2-ns fw5 mt2 mb3 mb4-ns ttu barlow-condensed blue-dark';
@@ -154,16 +157,6 @@ export const ProjectDetail = (props) => {
);
- const { data } = useAvailableCountriesQuery();
-
- // check if the project has live monitoring feature enabled
- // based on the country list provided by available.json
- const hasLiveMonitoringFeature = !data
- ? false
- : props.project.countryTag.some((country) =>
- data.countries.some((item) => country.toLowerCase() === item.toLowerCase()),
- );
-
return (
@@ -358,11 +351,8 @@ export const ProjectDetail = (props) => {
className="bg-white blue-dark ba b--grey-light pa3"
/>
- {/*
- show live view button only for published projects &
- when the project has live monitoring feature
- */}
- {props.project.status === 'PUBLISHED' && hasLiveMonitoringFeature && (
+ {/* show live view button only when the project has live monitoring feature */}
+ {hasLiveMonitoringFeature && (
state.auth.userDetails);
+ const [hasLiveMonitoringFeature, setHasLiveMonitoringFeature] = useState(null);
+
+ const { data: project } = useProjectQuery(projectId);
+ const { data: availableCountries } = useAvailableCountriesQuery();
+
+ useEffect(() => {
+ if (!availableCountries || !project || !userDetails) return;
+
+ // set hasLiveMonitoringFeature to false if project is not published
+ // or expert mode is not enabled
+ if (project.data.status !== 'PUBLISHED' || !userDetails.isExpert) {
+ setHasLiveMonitoringFeature(false);
+ return;
+ }
+
+ // check if the project has live monitoring feature enabled
+ // based on the country list provided by available.json
+ const isLiveMonitoringAvailableInCountry = project.data.countryTag.some((country) =>
+ availableCountries.countries.some((item) => country.toLowerCase() === item.toLowerCase()),
+ );
+
+ setHasLiveMonitoringFeature(isLiveMonitoringAvailableInCountry);
+ }, [availableCountries, project, userDetails]);
+
+ return hasLiveMonitoringFeature;
+}
diff --git a/frontend/src/views/projectLiveMonitoring.js b/frontend/src/views/projectLiveMonitoring.js
index 188ef00a4e..4a1647391d 100644
--- a/frontend/src/views/projectLiveMonitoring.js
+++ b/frontend/src/views/projectLiveMonitoring.js
@@ -1,6 +1,8 @@
import React, { useState, useRef, useEffect } from 'react';
import ReactPlaceholder from 'react-placeholder';
import Select from 'react-select';
+import { useNavigate, useParams, Link } from 'react-router-dom';
+import { useDispatch } from 'react-redux';
import centroid from '@turf/centroid';
import {
UnderpassFeatureList,
@@ -9,95 +11,34 @@ import {
UnderpassFeatureStats,
UnderpassValidationStats,
} from '@hotosm/underpass-ui';
-import { Link } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
import { ProjectVisibilityBox } from '../components/projectDetail/visibilityBox';
import { ProjectStatusBox } from '../components/projectDetail/statusBox';
import { useSetTitleTag } from '../hooks/UseMetaTags';
-import { useParams } from 'react-router-dom';
import { useFetch } from '../hooks/UseFetch';
+import useHasLiveMonitoringFeature from '../hooks/UseHasLiveMonitoringFeature';
+import {
+ underpassConfig,
+ availableImageryOptions,
+ statusList,
+ mappingTypesTags,
+ mappingTypesFeatureTypes,
+} from '../config/underpass';
import './projectLiveMonitoring.css';
-import { MAPBOX_TOKEN, UNDERPASS_URL } from '../config';
-
-const availableImageryOptions = [
- { label: 'OSM', value: 'osm' },
- { label: 'Bing', value: 'Bing' },
- { label: 'Mapbox Satellite', value: 'Mapbox' },
- { label: 'ESRI World Imagery', value: 'EsriWorldImagery' },
-];
const availableImageryValues = availableImageryOptions.map((item) => item.value);
-const config = {
- API_URL: UNDERPASS_URL,
- MAPBOX_TOKEN: MAPBOX_TOKEN,
- // set default sources of Tasking Manager
- sources: {
- osm: {
- type: 'raster',
- tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
- tileSize: 256,
- attribution: '© OpenStreetMap Contributors',
- maxzoom: 19,
- },
- Bing: {
- type: 'raster',
- tiles: ['https://ecn.t3.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=1'],
- tileSize: 256,
- attribution: '© OpenStreetMap Contributors',
- maxzoom: 18,
- },
- Mapbox: {
- type: 'raster',
- tiles: [
- `https://api.mapbox.com/styles/v1/mapbox/satellite-v9/tiles/{z}/{x}/{y}?access_token=${MAPBOX_TOKEN}`,
- ],
- tileSize: 512,
- attribution: '© OpenStreetMap Contributors © Mapbox',
- maxzoom: 19,
- },
- EsriWorldImagery: {
- type: 'raster',
- tiles: [
- 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
- ],
- tileSize: 256,
- attribution: '© OpenStreetMap Contributors © ESRI',
- maxzoom: 18,
- },
- },
-};
-
-const statusList = {
- ALL: '',
- UNSQUARED: 'badgeom',
- OVERLAPPING: 'overlapping',
- BADVALUE: 'badvalue',
-};
-
-const mappingTypesTags = {
- ROADS: 'highway',
- BUILDINGS: 'building',
- WATERWAYS: 'waterway',
-};
-
-const mappingTypesFeatureTypes = {
- ROADS: 'line',
- BUILDINGS: 'polygon',
- WATERWAYS: 'line',
-};
-
export function ProjectLiveMonitoring() {
const { id } = useParams();
const dispatch = useDispatch();
+ const navigate = useNavigate();
const [coords, setCoords] = useState([0, 0]);
const [activeFeature, setActiveFeature] = useState(null);
const [tags, setTags] = useState('building');
const [featureType, setFeatureType] = useState('polygon');
const [mapSource, setMapSource] = useState('osm');
const [imageryOptions, setImageryOptions] = useState(availableImageryOptions);
- const [mapConfig, setMapConfig] = useState(config);
+ const [mapConfig, setMapConfig] = useState(underpassConfig);
const [realtimeList, setRealtimeList] = useState(false);
const [realtimeMap, setRealtimeMap] = useState(false);
const [listAll, setListAll] = useState(false);
@@ -113,6 +54,15 @@ export function ProjectLiveMonitoring() {
const [areaOfInterest, setAreaOfInterest] = useState(null);
const [project, setProject] = useState(null);
+ const hasLiveMonitoringFeature = useHasLiveMonitoringFeature();
+
+ // navigate to homepage when a project without live monitoring feature
+ // is accessed directly from the route
+ useEffect(() => {
+ if (hasLiveMonitoringFeature === null || hasLiveMonitoringFeature) return;
+ navigate('/');
+ }, [navigate, hasLiveMonitoringFeature]);
+
useEffect(() => {
if (!Object.keys(data).length) return;
setProject(data);
@@ -322,14 +272,14 @@ export function ProjectLiveMonitoring() {
tags={tags}
hashtag={'hotosm-project-' + id}
featureType={featureType}
- apiUrl={config.API_URL}
+ apiUrl={underpassConfig.API_URL}
area={areaOfInterest}
/>
@@ -382,7 +332,7 @@ export function ProjectLiveMonitoring() {
setActiveFeature({ properties: { tags, status }, ...feature });
}}
realtime={realtimeList}
- config={config}
+ config={underpassConfig}
status={listAll ? '' : status}
orderBy="created_at"
onFetchFirstTime={(mostRecentFeature) => {