AppContent.jsx 18.2 KB
Newer Older
1
import React, { useEffect, useReducer, useState } from 'react';
2
3
4
import { useTranslation } from 'react-i18next';
import { latLngBounds } from 'leaflet';
import { useQuery } from "@apollo/react-hooks";
5
import {
6
    DashboardTile, DataSources, Histogram, ImageContents, Layout, OurMap, PageHeader, ResultsTable, ShowNext, Timeline
7
} from "..";
8
import { LinearProgress } from "@material-ui/core";
9
// Queries
10
11
12
13
import {
    byRegion as GET_SITES_BY_REGION, searchArchaeoSites as GET_ARCHAEOLOGICAL_SITES,
    searchObjectContext as GET_OBJECT_CONTEXT, searchObjects as GET_OBJECTS
} from "./queries.graphql";
14
import { timelineAdapter, timelineMapper, useDebounce } from "../../utils";
15
import { useStyles } from '../../styles';
16
import Container from "@material-ui/core/Container";
17

18
19
20
const initialInput = {
    mapBounds: latLngBounds([28.906303, -11.146792], [-3.355435, 47.564145]),
    zoomLevel: 5,
21
    clusterMarkers: true,
22
23
24
    objectId: 0,
    regionId: 0,
    regionTitle: null,
amarcic's avatar
amarcic committed
25
    searchStr: "brosche",
26
    catalogIdsList: [{"catalogLabel": "All SPP 2143 Arachne data", "catalogId": 123},
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
27
        {"catalogLabel": "AAArC - Fundplätze", "catalogId": 942},],
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
28
29
    checkedCatalogIds: [],
    checkedCatalogLabels: [],
30
    mode: "objects",
31
    sitesMode: "",
32
33
    showSearchResults: true,
    showArchaeoSites: false,
34
35
36
37
38
    showRelatedObjects: false,
    chronOntologyTerm: null,
    boundingBoxCorner1: [],
    boundingBoxCorner2: [],
    drawBBox: false,
39
    mapControlsExpanded: false,
40
    resultsListExpanded: true,
41
    selectedMarker: undefined,
42
    timelineSort: "period",
43
    highlightedTimelineObject: undefined,
44
    areaA: 1,
45
    areaB: 0,
46
    bigTileArea: ""
47
48
49
50
51
52
};


export const AppContent = () => {
    const { t, i18n } = useTranslation();

53
54
    const classes = useStyles();

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
    // State
    function inputReducer(state, action) {
        const {type, payload} = action;
        switch (type) {
            case 'UPDATE_INPUT':
                return {
                    ...state,
                    [payload.field]: payload.value
                };
            case 'CHECK_ITEM':
                return {
                    ...state,
                    [payload.field]: [...state[payload.field], payload.toggledItem]
                };
            case 'UNCHECK_ITEM':
                return {
                    ...state,
                    [payload.field]: state[payload.field].filter(checked => checked !== payload.toggledItem)
                };
            case 'TOGGLE_STATE':
                return {
                    ...state,
                    [payload.toggledField]: !state[payload.toggledField]
                };
            case 'DRAW_BBOX':
                return {
                    ...state,
                    boundingBoxCorner1: state.boundingBoxCorner1.length === 0 ? [String(payload.lat), String(payload.lng)] : state.boundingBoxCorner1,
                    boundingBoxCorner2: state.boundingBoxCorner1.length === 0 ? state.boundingBoxCorner2 : [String(payload.lat), String(payload.lng)]
                };
            case 'MANUAL_BBOX':
                payload.value = payload.valueString.split(",").map(coordinateString => {
                    return parseFloat(coordinateString).toFixed(coordinateString.split(".")[1].length)
                });
                return {
                    ...state,
                    [payload.field]: payload.value
                };
            default:
            //return { ...state, [type]: payload };
        }
    }

    const [input, dispatch] = useReducer(inputReducer, initialInput);

Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
100
101
102
    // debounce input.searchStr
    const debouncedSearchStr = useDebounce(input.searchStr, 500);

103
104
105
106
107
108
    const [mapDataContext, setMapDataContext] = useState({});
    const [mapDataObjects, setMapDataObjects] = useState({});
    const [mapDataArchaeoSites, setMapDataArchaeoSites] = useState({});
    const [mapDataSitesByRegion, setMapDataSitesByRegion] = useState({});

    // Queries
109
    const {data: dataContext, loading: loadingContext, error: errorContext} = useQuery(GET_OBJECT_CONTEXT, input.mode === "objects"
110
111
112
113
114
115
116
        ? {variables: {arachneId: input.objectId}}
        : {variables: {arachneId: 0}});

    const {data: dataObjects, loading: loadingObjects, error: errorObjects} =
        useQuery(GET_OBJECTS, input.mode === "objects"
            ? {
                variables: {
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
117
                    searchTerm: debouncedSearchStr, catalogIds: input.checkedCatalogIds,
118
119
120
121
122
123
124
                    // only send coordinates if entered values have valid format (floats with at least one decimal place)
                    bbox: (/-?\d{1,2}\.\d+,-?\d{1,3}\.\d+/.test(input.boundingBoxCorner1)) && (/-?\d{1,2}\.\d+,-?\d{1,3}\.\d+/.test(input.boundingBoxCorner2))
                        ? input.boundingBoxCorner1.concat(input.boundingBoxCorner2)
                        : [],
                    periodTerm: input.chronOntologyTerm
                }
            }
125
            : {variables: {searchTerm: "", catalogIds: [], bbox: [], periodTerm: ""}});
126
127
128
129

    const {data: dataArchaeoSites, loading: loadingArchaeoSites, error: errorArchaeoSites} = useQuery(GET_ARCHAEOLOGICAL_SITES, input.mode === "archaeoSites"
        ? {
            variables: {
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
130
                searchTerm: debouncedSearchStr,
131
132
133
134
135
136
                // only send coordinates if entered values have valid format (floats with at least one decimal place)
                bbox: (/-?\d{1,2}\.\d+,-?\d{1,3}\.\d+/.test(input.boundingBoxCorner1)) && (/-?\d{1,2}\.\d+,-?\d{1,3}\.\d+/.test(input.boundingBoxCorner2))
                    ? input.boundingBoxCorner1.concat(input.boundingBoxCorner2)
                    : []
            }
        }
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
137
138
        //: {variables: {searchTerm: "", bbox: []}});
        : {variables: {searchTerm: "''", bbox: []}});
139
140

    const {data: dataSitesByRegion, loading: loadingSitesByRegion, error: errorSitesByRegion} = useQuery(GET_SITES_BY_REGION, input.sitesMode === "region"
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
141
        ? {variables: {searchTerm: debouncedSearchStr, idOfRegion: input.regionId}}
142
143
144
145
146
147
        : {variables: {searchTerm: "", idOfRegion: 0}});


    const chronOntologyTerms = [
        'antoninisch', 'archaisch', 'augusteisch', 'FM III', 'frühkaiserzeitlich', 'geometrisch', 'hadrianisch',
        'hellenistisch', 'hochhellenistisch', 'kaiserzeitlich', 'klassisch', 'MM II', 'MM IIB', 'römisch', 'SB II',
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
148
149
        'severisch', 'SH IIIB', 'SM I', 'SM IB', 'trajanisch',
        'Altes Reich', 'Neues Reich', 'Erste Zwischenzeit', 'Holocene', 'Early Holocene', 'Middle Holocene', 'Late Holocene', 'Pleistocene'
150
151
152
153
    ];

    const regions = [
        {title: 'Africa', id: 2042601},
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
154
155
        {title: 'Benin', id: 2353200},
        {title: 'East Africa', id: 2359915},
156
        {title: 'Egypt', id: 2042786},
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
157
158
        {title: 'Horn of Africa', id: 2379066},
        {title: 'Maghreb', id: 2042694},
159
        {title: 'Meroe', id: 2293921},
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
160
        {title: 'Nubien', id: 2042608},
161
        {title: 'Republic of Namibia', id: 2293917},
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
162
        {title: 'Senegambia', id: 2348444},
163
164
165
        {title: 'Sudan', id: 2042707},
        {title: 'Tschad', id: 2128989},
        {title: 'Wadi Howar Region Sudan', id: 2042736},
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
166
        {title: 'West Africa', id: 2379014}
167
168
169
170
171
172
173
174
175
176
    ];


    const handleRelatedObjects = (id) => {
        dispatch({type: "UPDATE_INPUT", payload: {field: "objectId", value: id ? Number(id) : input.objectId}});
        dispatch({type: "TOGGLE_STATE", payload: {toggledField: "showSearchResults"}})
        dispatch({type: "TOGGLE_STATE", payload: {toggledField: "showRelatedObjects"}})
        console.log("handleRelatedObjects!");
    };

177
    const openPopup = (index) => {
178
179
180
181
182
        dispatch({type: "UPDATE_INPUT", payload: {field: "selectedMarker", value: index}});
    }


    useEffect( () => {
183
        if(dataContext && input.mode === "objects" && input.showRelatedObjects) {
184
185
186
187
188
            setMapDataContext(dataContext);
            console.log("rerender dataContext!");
            console.log("rerender dataContext --> dataContext: ", dataContext);
            console.log("rerender dataContext --> input:", input);
        }
189
    }, [dataContext, input.showRelatedObjects, input.mode]);
190
191

    useEffect( () => {
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
192
        if (dataObjects && input.mode === "objects" && input.showSearchResults && (debouncedSearchStr || input.checkedCatalogIds.length!==0 || input.chronOntologyTerm
193
194
195
196
197
198
            ||(input.boundingBoxCorner1.length!==0 && input.boundingBoxCorner2.length!==0))) {
            setMapDataObjects(dataObjects);
            console.log("rerender dataObjects!");
            console.log("rerender dataObjects --> dataObjects: ", dataObjects);
            console.log("rerender dataObjects --> input:", input);
        }
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
199
    }, [dataObjects, input.showSearchResults, debouncedSearchStr, input.checkedCatalogIds, input.chronOntologyTerm, input.boundingBoxCorner1, input.boundingBoxCorner2, input.mode]);
200
201

    useEffect( () => {
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
202
        if (dataArchaeoSites && input.showArchaeoSites && input.mode === "archaeoSites" && input.sitesMode!=="region" && (debouncedSearchStr || (input.boundingBoxCorner1.length!==0 && input.boundingBoxCorner2.length!==0))) {
203
204
205
206
207
            setMapDataArchaeoSites(dataArchaeoSites);
            console.log("rerender dataArchaeoSites!");
            console.log("rerender dataArchaeoSites --> dataArchaeoSites: ", dataArchaeoSites);
            console.log("rerender dataArchaeoSites --> input:", input);
        }
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
208
    }, [dataArchaeoSites, input.showArchaeoSites, debouncedSearchStr, input.boundingBoxCorner1, input.boundingBoxCorner2, input.sitesMode, input.mode]);
209
210

    useEffect( () => {
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
211
        if (dataSitesByRegion && input.showArchaeoSites && input.mode === "archaeoSites" && input.sitesMode==="region" && (debouncedSearchStr || input.regionId)) {
212
213
214
215
216
            setMapDataSitesByRegion(dataSitesByRegion);
            console.log("rerender dataSitesByRegion!");
            console.log("rerender dataSitesByRegion --> dataSitesByRegion: ", dataSitesByRegion);
            console.log("rerender dataSitesByRegion --> input:", input);
        }
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
217
    }, [dataSitesByRegion, input.showArchaeoSites, debouncedSearchStr, input.regionId, input.sitesMode, input.mode]);
218
219
220
221
222
223
224
225


    /* Conditions used to determine whether to render certain data (objects, related objects, sites, sites by region) */
    /* TODO: better names? use a function to check this instead? */
    const renderingConditionObjects =
        // this mode is selected
        input.showSearchResults
        // at least one relevant input not empty
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
226
        && (debouncedSearchStr || input.checkedCatalogIds.length!==0 || input.chronOntologyTerm
227
            || (input.boundingBoxCorner1.length!==0 && input.boundingBoxCorner2.length!==0))
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
        // query result not empty
        && mapDataObjects && mapDataObjects.entitiesMultiFilter;

    const renderingConditionRelatedObjects =
        // this mode is selected
        input.showRelatedObjects
        // relevant input not empty
        && input.objectId
        // query result not empty
        && mapDataContext && mapDataContext.entity;

    const renderingConditionSites =
        // this mode is selected
        input.showArchaeoSites && input.sitesMode!=="region"
        // at least one relevant input not empty
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
243
        && (debouncedSearchStr || (input.boundingBoxCorner1.length!==0 && input.boundingBoxCorner2.length!==0))
244
245
246
247
248
249
250
        // query result not empty
        && mapDataArchaeoSites && mapDataArchaeoSites.archaeologicalSites;

    const renderingConditionSitesByRegion =
        // this mode is selected
        input.showArchaeoSites && input.sitesMode==="region"
        // at least one relevant input not empty
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
251
        && (debouncedSearchStr || input.regionId)
252
253
254
        // query result not empty
        && mapDataSitesByRegion && mapDataSitesByRegion.sitesByRegion;

255
256
    const getMapData = () => {
        let mapData;
257

258
259
260
261
262
263
264
        if(renderingConditionObjects) mapData = mapDataObjects?.entitiesMultiFilter;
        else if(renderingConditionRelatedObjects) mapData = {original: mapDataContext?.entity?.spatial, related: mapDataContext?.entity?.related};
        else if(renderingConditionSites) mapData = mapDataArchaeoSites?.archaeologicalSites;
        else if(renderingConditionSitesByRegion) mapData = mapDataSitesByRegion?.sitesByRegion;

        return mapData;
    }
265

266
267
268
    const getMapDataType = () => {
        let type = null;
        let handler = false;
269

270
271
272
273
274
275
276
277
278
279
        if(renderingConditionObjects) handler = true;
        else if(renderingConditionRelatedObjects) {
            type = "related";
            handler = true;
        }

        return {type: type, handler: handler};
    }


280
281
    //const setWidth = () => window.innerWidth
    //const setOneTwelfthWidth = () => setWidth() / 12
282

283
    //window.addEventListener('resize', setOneTwelfthWidth)
284

285

286
    const renderAreaA = () => {
287
288
289
290
291
292
293
294
        const area = "areaA";

        return(
            <DashboardTile
                reducer={[input, dispatch]}
                area={area}
                content={
                    input[area]===0 && <ResultsTable
295
                        handleRelatedObjects={handleRelatedObjects}
296
297
298
299
300
301
302
303
304
305
                        mapDataObjects={mapDataObjects}
                        mapDataContext={mapDataContext}
                        mapDataArchaeoSites={mapDataArchaeoSites}
                        mapDataSitesByRegion={mapDataSitesByRegion}
                        reducer={[input, dispatch]}
                        renderingConditionObjects={renderingConditionObjects}
                        renderingConditionRelatedObjects={renderingConditionRelatedObjects}
                        renderingConditionSites={renderingConditionSites}
                        renderingConditionSitesByRegion={renderingConditionSitesByRegion}
                        openPopup={openPopup}
306
307
                    />
                    || input[area]===1 && <ImageContents
308
309
310
                        contents={dataObjects
                        && [dataObjects?.entitiesMultiFilter?.map(entity => entity?.categoryOfDepicted),
                            dataObjects?.entitiesMultiFilter?.map(entity => entity?.materialOfDepicted)]}
311
312
313
314
                    />
                    || input[area]===2 && <DataSources/>
                }
                showNext={
315
                    <ShowNext
316
                        area={area}
317
318
                        labels={["Results table", "Image contents", "Data sources"]}
                        reducer={[input, dispatch]}
319
                    />
320
321
                }
            />
322
323
324
325
        )
    }

    const renderAreaB = () => {
326
        const area = "areaB";
327

328
329
330
331
332
        return(
            <DashboardTile
                reducer={[input, dispatch]}
                area={area}
                content={
amarcic's avatar
amarcic committed
333
                    input[area]===0 && <Timeline
334
                        reducer={[input, dispatch]}
335
                        timelineObjectsData={dataObjects?.entitiesMultiFilter.flatMap(timelineAdapter)}
336
                    />
amarcic's avatar
amarcic committed
337
                    || input[area]===1 && <Histogram
338
                        timelineData={dataObjects?.entitiesMultiFilter.map(timelineMapper)}
amarcic's avatar
amarcic committed
339
                    />
340
341
                }
                showNext={
342
                    <ShowNext
343
                        area={area}
344
345
                        labels={["Timeline", "Histogram"]}
                        reducer={[input, dispatch]}
346
                    />
347
348
                }
            />
349
350
        )
    }
351

352
    const renderAreaC = () => {
353
354
355
356
357
358
359
360
361
        const area = "areaC";

        return(
            <DashboardTile
                reducer={[input, dispatch]}
                area={area}
                content={
                    <OurMap
                        handleRelatedObjects={handleRelatedObjects}
Elisabeth Reuhl's avatar
Elisabeth Reuhl committed
362
363
                        data={getMapData()}
                        dataType={getMapDataType()}
364
365
366
367
                        reducer={[input, dispatch]}
                    />
                }
            />
368
369
        )
    }
370

371
372
373
374
375
    return (
        /* Layout schema:
            F = filters, M = map, A = area A, B = area B; two rows = 100% height, four columns = 100 % width

            Size md/lg:
376
            default:      |    big M:        |    big A:        |    big B:        |    with expanded filters: (?)
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
            ------------------------------------------------------------------------------------------------------
            F             |    F             |    F             |    F             |    F  F  F  F
            M  M  A  A    |    M  M  M  M    |    A  A  A  A    |    B  B  B  B    |    ...
            M  M  B  B    |    M  M  M  M    |    A  A  A  A    |    B  B  B  B    |
                          |    A  A  B  B    |    M  M  B  B    |    M  M  A  A    |
                          |                  |    M  M          |    M  M          |
            Size xs:
            default: (?)  |   with expanded filters: (?)
            --------------------------------------------
            F  .  .  .    |    F  F  F  F
            M  M  M  M    |    F  F  F  F
            M  M  M  M    |    M  M  M  M
            A  A  A  A    |    M  M  M  M
            A  A  A  A    |    A  A  A  A
            B  B  B  B    |    A  A  A  A
            B  B  B  B    |    B  B  B  B
                          |    B  B  B  B
        */
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
        <>
            <PageHeader
                chronOntologyTerms={chronOntologyTerms}
                reducer={[input, dispatch]}
                regions={regions}
            />
            <Container maxWidth={"xl"}>
                <Layout
                    bigTile={
                        (input.bigTileArea === "areaA" && renderAreaA())
                        || (input.bigTileArea === "areaB" && renderAreaB())
                        || (input.bigTileArea === "areaC" && renderAreaC())
                    }
                    leftOrTopTile={
                        input.bigTileArea !== "areaC" && renderAreaC()
                    }
                    topRightOrMiddleTile={
                        input.bigTileArea !== "areaA"
                            ? renderAreaA()
                            : renderAreaB()
                    }
                    bottomRightOrBottomTile={
                        input.bigTileArea !== "areaA" && input.bigTileArea !== "areaB" && renderAreaB()
                    }
                    loadingIndicator={
                        (loadingContext || loadingObjects || loadingArchaeoSites || loadingSitesByRegion)
                        && <LinearProgress/>
                    }
                    rightTileIsMovedToBottomInstead={input.bigTileArea === "areaC" ? "true" : false}
                />
            </Container>
        </>
427
    )
428
};