Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/6303 restrict live monitoring on expert mode #6314

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading