Skip to content

Commit

Permalink
Merge pull request #6316 from hotosm/feature/6303-restrict-live-monit…
Browse files Browse the repository at this point in the history
…oring-on-expert-mode

Feature/6303 restrict live monitoring on expert mode
  • Loading branch information
royallsilwallz committed Apr 4, 2024
2 parents 3836590 + bb9a5ed commit 43b8d85
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 90 deletions.
22 changes: 6 additions & 16 deletions frontend/src/components/projectDetail/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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';
Expand All @@ -154,16 +157,6 @@ export const ProjectDetail = (props) => {
</Link>
);

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 (
<div className={`${props.className || 'blue-dark'}`}>
<div className="db flex-l tasks-map-height">
Expand Down Expand Up @@ -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 && (
<LiveViewButton
projectId={props.project.projectId}
className="bg-white blue-dark ba b--grey-light pa3"
Expand Down
67 changes: 67 additions & 0 deletions frontend/src/config/underpass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { MAPBOX_TOKEN, UNDERPASS_URL } from '.';

export const underpassConfig = {
API_URL: UNDERPASS_URL,
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: '&copy; OpenStreetMap Contributors',
maxzoom: 19,
},
Bing: {
type: 'raster',
tiles: ['https://ecn.t3.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=1'],
tileSize: 256,
attribution: '&copy; 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: '&copy; OpenStreetMap Contributors &copy; Mapbox',
maxzoom: 19,
},
EsriWorldImagery: {
type: 'raster',
tiles: [
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
],
tileSize: 256,
attribution: '&copy; OpenStreetMap Contributors &copy; ESRI',
maxzoom: 18,
},
},
};

export const availableImageryOptions = [
{ label: 'OSM', value: 'osm' },
{ label: 'Bing', value: 'Bing' },
{ label: 'Mapbox Satellite', value: 'Mapbox' },
{ label: 'ESRI World Imagery', value: 'EsriWorldImagery' },
];

export const statusList = {
ALL: '',
UNSQUARED: 'badgeom',
OVERLAPPING: 'overlapping',
BADVALUE: 'badvalue',
};

export const mappingTypesTags = {
ROADS: 'highway',
BUILDINGS: 'building',
WATERWAYS: 'waterway',
};

export const mappingTypesFeatureTypes = {
ROADS: 'line',
BUILDINGS: 'polygon',
WATERWAYS: 'line',
};
42 changes: 42 additions & 0 deletions frontend/src/hooks/UseHasLiveMonitoringFeature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';

import { useProjectQuery } from '../api/projects';
import { useAvailableCountriesQuery } from '../api/projects';

/**
* A React custom hook that checks if a project can view live monitoring feature
*
* Returns null if the status is unknown, else returns boolean
*
*/
export default function useHasLiveMonitoringFeature() {
const { id: projectId } = useParams();
const userDetails = useSelector((state) => 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;
}
98 changes: 24 additions & 74 deletions frontend/src/views/projectLiveMonitoring.js
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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: '&copy; OpenStreetMap Contributors',
maxzoom: 19,
},
Bing: {
type: 'raster',
tiles: ['https://ecn.t3.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=1'],
tileSize: 256,
attribution: '&copy; 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: '&copy; OpenStreetMap Contributors &copy; Mapbox',
maxzoom: 19,
},
EsriWorldImagery: {
type: 'raster',
tiles: [
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
],
tileSize: 256,
attribution: '&copy; OpenStreetMap Contributors &copy; 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);
Expand All @@ -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);
Expand Down Expand Up @@ -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}
/>
<UnderpassValidationStats
tags={tags}
hashtag={'hotosm-project-' + id}
featureType={featureType}
apiUrl={config.API_URL}
apiUrl={underpassConfig.API_URL}
status="badgeom"
area={areaOfInterest}
/>
Expand Down Expand Up @@ -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) => {
Expand Down

0 comments on commit 43b8d85

Please sign in to comment.