Skip to content

Commit

Permalink
Add history manager
Browse files Browse the repository at this point in the history
  • Loading branch information
lucienbl committed Apr 18, 2019
1 parent c1d746f commit 0ab3cb9
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 28 deletions.
106 changes: 78 additions & 28 deletions App/Screens/Loading/Loading.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
// Copyright (c) 2018-2019, Amaury Martiny
// SPDX-License-Identifier: GPL-3.0

import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
import { Location, Permissions } from 'expo';
import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import {Location, Permissions, TaskManager} from 'expo';
import retry from 'async-retry';
import { StyleSheet, Text } from 'react-native';
import {StyleSheet, Text} from 'react-native';

import { Background } from './Background';
import {Background} from './Background';
import * as dataSources from '../../utils/dataSources';
import * as theme from '../../utils/theme';
import {i18n} from '../../localization';
import {AqiHistoryManager} from "../../managers";

const TASK_STORE_AQI_HISTORY = 'store-aqi-history';

@inject('stores')
@observer
Expand All @@ -21,8 +24,9 @@ export class Loading extends Component {

longWaitingTimeout = null; // The variable returned by setTimeout for longWaiting

componentDidMount () {
this.fetchData();
async componentDidMount () {
await this.fetchData();
await this._startRecordingAqiHistory();
}

componentWillUnmount () {
Expand All @@ -31,6 +35,37 @@ export class Loading extends Component {
}
}

_startRecordingAqiHistory = async () => {
await Location.startLocationUpdatesAsync(TASK_STORE_AQI_HISTORY, {
accuracy: Location.Accuracy.BestForNavigation,
timeInterval: AqiHistoryManager.SAVE_DATA_INTERVAL,

This comment has been minimized.

Copy link
@amaury1093

amaury1093 Apr 18, 2019

Member

This is android only. Do you know how often iOS fetches location? Permanently?

This comment has been minimized.

Copy link
@lucienbl

lucienbl Apr 18, 2019

Author Contributor

Uuh that's a good question, I never worked with iOS 🤔

Edit: But it shouldn't matter since the Manager also checks if the interval has been reached, no?

distanceInterval: 0,
});
};

_apiCall = async (currentPosition) => {

// We currently have 2 sources, aqicn, and windWaqi
// We put them in an array
const sources = [dataSources.aqicn, dataSources.windWaqi];

return retry(
async (_, attempt) => {
// Attempt starts at 1
console.log(
`<Loading> - fetchData - Attempt #${attempt}: ${
sources[(attempt - 1) % 2].name
}`
);
const result = await sources[(attempt - 1) % 2](currentPosition);
console.log('<Loading> - fetchData - Got result', result);

return result;
},
{retries: 3} // 2 attempts per source
);
};

async fetchData () {
const { stores } = this.props;
const { location } = stores;
Expand Down Expand Up @@ -70,34 +105,14 @@ export class Loading extends Component {
location.setGps(coords);
}

// We currently have 2 sources, aqicn, and windWaqi
// We put them in an array
const sources = [dataSources.aqicn, dataSources.windWaqi];

// Set a 2s timer that will set `longWaiting` to true. Used to show an
// additional "cough" message on the loading screen
this.longWaitingTimeout = setTimeout(
() => this.setState({ longWaiting: true }),
2000
);

const api = await retry(
async (_, attempt) => {
// Attempt starts at 1
console.log(
`<Loading> - fetchData - Attempt #${attempt}: ${
sources[(attempt - 1) % 2].name
}`
);
const result = await sources[(attempt - 1) % 2](currentPosition);
console.log('<Loading> - fetchData - Got result', result);

return result;
},
{ retries: 3 } // 2 attemps per source
);

stores.setApi(api);
stores.setApi(await this._apiCall(currentPosition));
} catch (error) {
console.log('<Loading> - fetchData - Error', error);
stores.setError(error.message);
Expand Down Expand Up @@ -145,6 +160,41 @@ export class Loading extends Component {
};
}

TaskManager.defineTask(TASK_STORE_AQI_HISTORY, async ({ data, error }) => {
if (error) {
console.log('<Loading> - TaskManager - defineTask - Error', error.message);
return;
}
if (data) {
const { locations } = data;
const { coords } = locations[0];

// We currently have 2 sources, aqicn, and windWaqi
// We put them in an array
const sources = [dataSources.aqicn, dataSources.windWaqi];

const api = await retry(
async (_, attempt) => {
// Attempt starts at 1
console.log(
`<Loading> - fetchData - Attempt #${attempt}: ${
sources[(attempt - 1) % 2].name
}`
);
const result = await sources[(attempt - 1) % 2](coords);
console.log('<Loading> - fetchData - Got result', result);

return result;
},
{retries: 3} // 2 attempts per source
);

if (await AqiHistoryManager.isSaveNeeded()) {
await AqiHistoryManager.saveData(api.city.name, api.rawPm25);
}
}
});

const styles = StyleSheet.create({
dots: {
color: theme.primaryColor
Expand Down
86 changes: 86 additions & 0 deletions App/managers/AqiHistoryManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { SQLite } from 'expo';

export const SAVE_DATA_INTERVAL = 3600000; // 1 hour
const DB_AQI_HISTORY = "aqi-history";

export const initDb = () => {
return SQLite.openDatabase(DB_AQI_HISTORY, '1.0', 'Aqi History', 5*1024*1024);
};

export const init = async () => {
const db = await initDb();

await db.transaction((tx) => {
tx.executeSql(
"create table if not exists history(id integer not null, location varchar(255) not null, rawPm25 decimal not null, creation_time timestamp not null, primary key (id))",

This comment has been minimized.

Copy link
@amaury1093

amaury1093 Apr 18, 2019

Member

Probably better to store lat/lng as 2 REAL columns

This comment has been minimized.

Copy link
@lucienbl

lucienbl Apr 18, 2019

Author Contributor

Actually this just stores the name of the location, for example: Paris, France. Indeed storing the coords is something which needs to be done 😄

[],
() => {},
(transaction, error) => console.log("DB init error", error)
);
});

return db;
};

export const isSaveNeeded = async () => {
const db = await init();

const promise = new Promise((resolve, reject) => {
db.readTransaction((tx) => {
tx.executeSql(
"select * from history order by id desc limit 1",
[],
(transaction, resultSet) => {
if (resultSet.rows.length === 0) {
resolve(true);
} else if ((resultSet.rows.item(0).creation_time + SAVE_DATA_INTERVAL) < Date.now()) {
resolve(true);
} else {
resolve(false);
}
},
(transaction, error) => reject(error)
);
});
});

return promise;
};

export const saveData = async (location, rawPm25) => {
const db = await init();

db.transaction((tx) => {
tx.executeSql(
"insert into history (id, location, rawPm25, creation_time) values (?, ?, ?, ?)",
[Date.now(), location, rawPm25, Date.now()],
() => {},
((transaction, error) => console.log("DB insert error", error))
);
});
};

export const getData = async (limit) => {
const db = await init();

const promise = new Promise((resolve, reject) => {
db.readTransaction((tx) => {
tx.executeSql(
"select * from history order by creation_time desc limit " + limit,
[],
((transaction, resultSet) => {
let data = [];

for (let i = 0; i < resultSet.rows.length; i++) {
data.push(resultSet.rows.item(i));
}

resolve(data);
}),
((transaction, error) => reject(error))
);
});
});

return promise;
};
5 changes: 5 additions & 0 deletions App/managers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as AqiHistoryManager from './AqiHistoryManager';

export {
AqiHistoryManager
}

0 comments on commit 0ab3cb9

Please sign in to comment.