Wednesday, 11 March 2020

expo gps2 redux persist

app opens click new record

press stop record, press show record
time, latitude, longitude are recorded

press continue record

data append to the existing

close app, reopen app, press show record

location data persist, didn't lose when app close

press continue record, list keep growing

//app.js

import React from 'react';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';
import GPS from './gps';
import { store, persistor } from './Redux/store';
import { View, ActivityIndicator } from 'react-native';

export default function App() {
  renderLoading = () => {
    return (
      <View>
        <ActivityIndicator size={"large"} />
      </View>
    );
  };

  return (
    // Redux: Global Store
    <Provider store={store}>
      <PersistGate loading={() => renderLoading()} persistor={persistor}>
        <GPS />
      </PersistGate>
    </Provider>
  );
};

----------------------------------------------
//Redux/store.js

import createSecureStore from "redux-persist-expo-securestore";
import { createStore, applyMiddleware } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import reducers from "./reducers";
import { createLogger } from 'redux-logger';

// Secure storage
const storage = createSecureStore();

const config = {
    key: "root",
    storage
};

const persistedReducer = persistReducer(config, reducers);

const store = createStore(
    persistedReducer,
    applyMiddleware(createLogger())
);
const persistor = persistStore(store);

export { store, persistor }

-------------------------------
//Redux/reducers/index.js

import { combineReducers } from 'redux';

import locationReducer from "./locationReducer";

export default combineReducers(
    {
        locationReducer
    })

----------------------------
//Redux/reducers/locationReducer.js

export default function reducer(
    state = {
        locations: []
    },
    action
) {
    switch (action.type) {

        case "add": {
            return { ...state, locations: state.locations.concat(action.payload) }
        }

        case "delete": {
            return { ...state, locations: [] }
        }
    }
    return state;
}

----------------------------
//Redux/locationActions.js

export function addLocations(locations) {

    return {
        type: 'add',
        payload: locations
    }
}

export function deleteLocations() {
    return {
        type: 'delete'
    }
}

-----------------------------
//gps.js

import React, { useState, useEffect } from 'react';
import { Clipboard, ShadowPropTypesIOS } from 'react-native';
import {
    Container, Header, Title, Content, Footer,
    FooterTab, Button, Left, Right, Body, Icon, Text,
    Accordion, Card, CardItem, Thumbnail, ListItem,
    CheckBox, DatePicker, DeckSwiper, Fab, View,
    Badge, Form, Item, Input, Label, Picker, Textarea,
    Switch, Radio, Spinner, Tab, Tabs, TabHeading,
    ScrollableTab, H1, H2, H3, Drawer,
} from 'native-base';
import * as Font from 'expo-font'
import * as Location from 'expo-location'
import * as TaskManager from 'expo-task-manager'
import useInterval from 'react-useinterval'
import openMap from 'react-native-open-maps'
import { connect } from 'react-redux';
import { addLocations, deleteLocations } from './Redux/locationActions'

const LOCATION_TASK_NAME = 'background-location-task';
let background_location = null

function GPS(props) {
    const [loadfont, setloadfont] = useState(true)
    const [permission_status, setpermission_status] = useState('undetermined')
    const [acc, setacc] = useState(-1)
    const [altitude, setaltitude] = useState(-1)
    const [heading, setheading] = useState(-1)
    const [latitude, setlatitude] = useState(-1)
    const [longitude, setlonggitude] = useState(-1)
    const [speed, setspeed] = useState(-1)
    const [time, settime] = useState('')
    const [updatePeriod, setupdatePeriod] = useState(null)
    const [address, setaddress] = useState(null)
    const [card_collapse1, setcard_collapse1] = useState(false)
    const [card_collapse2, setcard_collapse2] = useState(false)
    const [record, setrecord] = useState(false)

    useEffect(() => {
        initializeApp()
        return () => stop_update_location()
    }, [])

    initializeApp = async () => {
        const { status } = await Location.requestPermissionsAsync()
        setpermission_status(status)

        await Font.loadAsync({
            Roboto: require("native-base/Fonts/Roboto.ttf"),
            Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf")
        })
        setloadfont(false)
    }

    useInterval(() => {
        update_location()

        if (record && time !== '') {
            props.dispatch(addLocations({
                latitude: latitude,
                longitude: longitude,
                time: time,
            }))
        }
    }, updatePeriod);

    update_location = async () => {

        await Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, {
            accuracy: Location.Accuracy.High,
        })

        if (background_location) {
            const e = background_location.coords
            setacc(e.accuracy)
            setaltitude(e.altitude)
            setheading(e.heading)
            setlatitude(e.latitude)
            setlonggitude(e.longitude)
            setspeed(e.speed)
            settime(Date(background_location.timestamp).toLocaleString())
        }
    }

    stop_update_location = async () => {
        setupdatePeriod(null)
        const taskRegistered = await TaskManager.isTaskRegisteredAsync(LOCATION_TASK_NAME)
        if (taskRegistered) {
            await TaskManager.unregisterTaskAsync(LOCATION_TASK_NAME)
        }
    }

    search_address = async () => {
        if (background_location) {
            const _address = await Location.reverseGeocodeAsync(background_location.coords)
            setaddress(_address[0])
        }
    }

    open_map = () => {
        if (background_location) {
            const e = background_location.coords
            openMap({ latitude: e.latitude, longitude: e.longitude })
        }
    }

    write_to_clipboard = (text) => {
        Clipboard.setString(text);
    }

    display_record = () => {
        let text = ''
        props.locations_store.map(item => {
            text = text + item.time + ',' + item.latitude.toString() + ',' + item.longitude.toString() + '\n'
        })
        alert(text)
    }

    if (permission_status !== 'granted') {
        <Container style={{ backgroundColor: '#1C2833' }}>
            <Text>App needs location permission granted</Text>
        </Container>
    }

    if (loadfont) {
        return <Container style={{ backgroundColor: '#1C2833' }}><Spinner /></Container>
    }

    return (
        <Container style={{ backgroundColor: '#1C2833' }}>
            <Content style={{ marginTop: 25 }}>
                <Card style={{ backgroundColor: '#1C2833' }}>
                    <CardItem header bordered button style={{ backgroundColor: "#1C2833" }}
                        onPress={() => setcard_collapse1(!card_collapse1)}>
                        <View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between' }}>
                            <H3 style={{ color: '#D0D3D4' }}>Realtime GPS</H3>
                            <H3 style={{ color: "#D0D3D4" }}>+</H3>
                        </View>
                    </CardItem>
                    {card_collapse1 ? null :
                        <CardItem bordered style={{ backgroundColor: '#1C2833' }}>
                            <Text style={{ color: "#D0D3D4", fontStyle: 'italic' }}>
                                Accuracy: {acc} Meters{'\n'}
                                Altitude: {altitude} Meters{'\n'}
                                Heading: {heading} Degree{'\n'}
                                Latitude: {latitude} Degree{'\n'}
                                Longitude: {longitude} Degree{'\n'}
                                Speed: {speed} M/S{'\n'}
                                Time: {time}
                            </Text>
                            <Button light small bordered icon style={{ position: 'absolute', right: 10, top: 10 }}
                                onPress={() => { write_to_clipboard(latitude + ',' + longitude) }}>
                                <Icon name='copy'></Icon>
                            </Button>
                        </CardItem>
                    }
                    <CardItem header bordered button style={{ backgroundColor: '#1C2833' }}
                        onPress={() => setcard_collapse2(!card_collapse2)}>
                        <View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between' }}>
                            <H3 style={{ color: '#D0D3D4' }}>Address (update location first)</H3>
                            <H3 style={{ color: '#D0D3D4' }}>+</H3>
                        </View>
                    </CardItem>
                    {card_collapse2 ? null :
                        <CardItem style={{ backgroundColor: '#1C2833' }}>
                            {address ?
                                <Text style={{ color: '#D0D3D4' }}>
                                    {address.name}  {address.street} {'\n'}
                                    {address.city} {address.region} {address.country}  {address.postalCode}
                                </Text>
                                : <Text style={{ color: '#D0D3D4' }}>Waiting for GPS location</Text>}
                            <Button light small bordered icon style={{ position: 'absolute', right: 10, top: 10 }}
                                onPress={() => {
                                    write_to_clipboard(address.name + ' ' + address.street + ' ' +
                                        address.city + ' ' + address.region + ' ' + address.country + ' ' + address.postalCode)
                                }}>
                                <Icon name='copy'></Icon>
                            </Button>
                        </CardItem>
                    }
                </Card>

                <View style={{ flexDirection: 'row', justifyContent: "space-around", marginTop: 10 }}>
                    <Button small style={{ width: 150, justifyContent: "center" }} onPress={() => setupdatePeriod(1000)}>
                        <Text>Update Location</Text>
                    </Button>
                    <Button small style={{ width: 150, justifyContent: "center" }} onPress={() => stop_update_location()}>
                        <Text>Stop Update</Text>
                    </Button>
                </View>
                <View style={{ flexDirection: 'row', justifyContent: "space-around", marginTop: 10 }}>
                    <Button small style={{ width: 150, justifyContent: "center" }} onPress={() => search_address()}>
                        <Text>Search Address</Text>
                    </Button>
                    <Button small style={{ width: 150, justifyContent: "center" }} onPress={() => open_map()}>
                        <Text>Open Map</Text>
                    </Button>
                </View>
                <View style={{ flexDirection: 'row', justifyContent: "space-around", marginTop: 10 }}>
                    <Button small style={{ width: 150, justifyContent: "center" }} onPress={() => {
                        props.dispatch(deleteLocations()); setupdatePeriod(1000); setrecord(true)
                    }}>
                        <Text>New Record</Text>
                    </Button>
                    <Button small style={{ width: 150, justifyContent: "center" }}
                        onPress={() => { setupdatePeriod(1000); setrecord(true) }}>
                        <Text>Continue Record</Text>
                    </Button>
                </View>
                <View style={{ flexDirection: 'row', justifyContent: "space-around", marginTop: 10 }}>
                    <Button small style={{ width: 150, justifyContent: "center" }}
                        onPress={() => { stop_update_location(); setrecord(false) }}>
                        <Text>Stop Record</Text>
                    </Button>
                    <Button small style={{ width: 150, justifyContent: "center" }}
                        onPress={() => display_record()}>
                        <Text>Show Record</Text>
                    </Button>
                </View>
            </Content>
        </Container>
    );
}

TaskManager.defineTask(LOCATION_TASK_NAME, ({ data, error }) => {
    if (error) {
        // Error occurred - check `error.message` for more details.
        console.log(error.message)
        return;
    }
    if (data) {
        const { locations } = data;
        // do something with the locations captured in the background
        background_location = locations[locations.length - 1]
    }
});

export default connect(
    (store) => {
        return {
            locations_store: store.locationReducer.locations,
        };
    }
)(GPS);

reference:
https://chuanshuoge2.blogspot.com/2018/05/react-redux.html
https://chuanshuoge2.blogspot.com/2020/03/expo-location.html
http://chuanshuoge2.blogspot.com/2020/03/expo-gps-1.html

No comments:

Post a Comment