@vixoniccom/aqi 0.0.1 → 0.0.2-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,46 @@
1
+ /*!
2
+ localForage -- Offline Storage, Improved
3
+ Version 1.10.0
4
+ https://localforage.github.io/localForage
5
+ (c) 2013-2017 Mozilla, Apache License 2.0
6
+ */
7
+
8
+ /**
9
+ * @license React
10
+ * react-dom.production.min.js
11
+ *
12
+ * Copyright (c) Facebook, Inc. and its affiliates.
13
+ *
14
+ * This source code is licensed under the MIT license found in the
15
+ * LICENSE file in the root directory of this source tree.
16
+ */
17
+
18
+ /**
19
+ * @license React
20
+ * react-jsx-runtime.production.min.js
21
+ *
22
+ * Copyright (c) Facebook, Inc. and its affiliates.
23
+ *
24
+ * This source code is licensed under the MIT license found in the
25
+ * LICENSE file in the root directory of this source tree.
26
+ */
27
+
28
+ /**
29
+ * @license React
30
+ * react.production.min.js
31
+ *
32
+ * Copyright (c) Facebook, Inc. and its affiliates.
33
+ *
34
+ * This source code is licensed under the MIT license found in the
35
+ * LICENSE file in the root directory of this source tree.
36
+ */
37
+
38
+ /**
39
+ * @license React
40
+ * scheduler.production.min.js
41
+ *
42
+ * Copyright (c) Facebook, Inc. and its affiliates.
43
+ *
44
+ * This source code is licensed under the MIT license found in the
45
+ * LICENSE file in the root directory of this source tree.
46
+ */
Binary file
@@ -0,0 +1,36 @@
1
+ {
2
+ "parameters": {
3
+ "backgroundImage": {
4
+ "id": "earthquakes",
5
+ "filename": "Earthquakes.png",
6
+ "__isAsset": true
7
+ },
8
+ "cityInput": "Santiago",
9
+ "topSeparationCard": 200,
10
+ "leftSeparationCard": 600,
11
+ "cardWidth": 700,
12
+ "cardHeight": 600,
13
+ "cardGap": 2,
14
+ "aqiFormat": {
15
+ "fontSize": 18,
16
+ "fontColor": "#000000",
17
+ "alignment": {
18
+ "horizontal": "center"
19
+ }
20
+ },
21
+ "stationFormat": {
22
+ "fontSize": 8,
23
+ "fontColor": "#000000",
24
+ "alignment": {
25
+ "horizontal": "center"
26
+ }
27
+ },
28
+ "qualityFormat": {
29
+ "fontSize": 4,
30
+ "fontColor": "#000000",
31
+ "alignment": {
32
+ "horizontal": "center"
33
+ }
34
+ }
35
+ }
36
+ }
package/build.zip CHANGED
Binary file
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "author": {
9
9
  "name": "Daniel Alvayay"
10
10
  },
11
- "version": "0.0.1",
11
+ "version": "0.0.2-dev.0",
12
12
  "scripts": {
13
13
  "prepublishOnly": "vixonic-module-packager --mode=build",
14
14
  "watch": "vixonic-module-packager --mode=watch",
@@ -22,14 +22,14 @@
22
22
  "dependencies": {
23
23
  "axios": "^1.6.0",
24
24
  "localforage": "^1.10.0",
25
- "react": "^17.0.2",
26
- "react-dom": "^17.0.2"
25
+ "react": "^18.3.1",
26
+ "react-dom": "^18.3.1"
27
27
  },
28
28
  "devDependencies": {
29
- "@types/react": "^17.0.35",
30
- "@types/react-dom": "^17.0.11",
31
- "@vixoniccom/module-packager": "^2.10.0",
32
- "@vixoniccom/modules": "^2.19.0",
33
- "standard-version": "^9.3.1"
29
+ "@types/react": "^18.3.23",
30
+ "@types/react-dom": "^18.3.7",
31
+ "@vixoniccom/module-packager": "^2.13.0-dev.1",
32
+ "@vixoniccom/modules": "^2.20.5-dev.1",
33
+ "standard-version": "^9.5.0"
34
34
  }
35
35
  }
@@ -0,0 +1 @@
1
+ sonar.projectKey=Vixonic_store-air-quality-index_de6b8e55-8f87-4d53-b9d5-7e514a2702d9
package/src/App.tsx CHANGED
@@ -1,11 +1,9 @@
1
- import axios from 'axios'
2
- import localforage from 'localforage'
3
1
  import React, { useEffect, useState } from 'react'
4
2
  import { Card } from './components/Card'
5
3
  import { FontLoader } from './components/FontLoader'
6
4
  import { FormattedText } from './components/FormattedText'
7
- import { AirQuality, AqiData, StorageData } from './types'
8
- import { API_RESPONSE_STATUS, TOKEN } from './utils'
5
+ import { AqiData } from './types'
6
+ import { getData } from './services'
9
7
 
10
8
  interface Props {
11
9
  data: VixonicData
@@ -19,65 +17,25 @@ export const App: React.FunctionComponent<Props> = ({ data, start }) => {
19
17
  const cityInput = parameters?.cityInput || "santiago"
20
18
  const updateTime = 600000
21
19
 
22
- const refetchData = (referenceDate: Date): boolean => {
23
- const now = new Date()
24
-
25
- const sameYear = referenceDate.getFullYear() === now.getFullYear()
26
- const sameMonth = referenceDate.getMonth() === now.getMonth()
27
- const sameDay = referenceDate.getDate() === now.getDate()
28
-
29
- if (sameYear && sameMonth && sameDay) {
30
- const differenceMinutes = Math.abs(now.getTime() - referenceDate.getTime())
31
- return differenceMinutes >= updateTime
32
- }
33
- return true
34
- }
35
-
36
- const requestData = async (): Promise<AirQuality | null> => {
37
- const URL = `https://api.waqi.info/feed/${cityInput}/?token=${TOKEN}`
38
- try {
39
- const response = await axios.get<AirQuality>(URL)
40
- if (response?.status === 200) {
41
- return response.data
42
- }
43
- return null
44
- } catch (error) {
45
- return null
46
- }
47
- }
20
+ useEffect(() => {
21
+ if (!start) return
48
22
 
49
- const processData = (response: AirQuality | null): AqiData => {
50
- let data: AqiData = { city: cityInput, station: "Estación desconocida" }
51
- if (response && response.status === API_RESPONSE_STATUS.OK) {
52
- data = {
53
- aqi: response.data.aqi,
54
- city: cityInput,
55
- station: response.data.city.name
23
+ const fetchData = async () => {
24
+ try {
25
+ const data = await getData(cityInput, updateTime)
26
+ setFormattedData(data)
27
+ } catch (error) {
28
+ console.error('Error al obtener datos:', error)
29
+ setFormattedData(undefined)
56
30
  }
57
31
  }
58
- return data
59
- }
60
32
 
61
- const getData = async () => {
62
- const storageData: StorageData | null = await localforage.getItem('aqi')
63
- let data
64
- if (!storageData?.item || storageData?.item.city !== parameters.cityInput || !storageData?.date || refetchData(new Date(storageData.date))) {
65
- data = await requestData()
66
- const processedData = processData(data)
67
- await localforage.setItem('aqi', { item: processedData, date: new Date() })
68
- setFormattedData(processedData)
69
- } else {
70
- data = storageData.item
71
- setFormattedData(data)
72
- }
73
- }
33
+ fetchData()
74
34
 
75
- useEffect(() => {
76
- if (!start) return
77
- getData()
78
35
  const interval = setInterval(() => {
79
- getData()
36
+ fetchData()
80
37
  }, updateTime)
38
+
81
39
  return () => clearInterval(interval)
82
40
  }, [start, updateTime, parameters.cityInput])
83
41
 
@@ -92,11 +50,7 @@ export const App: React.FunctionComponent<Props> = ({ data, start }) => {
92
50
  backgroundImage: backgroundImageState,
93
51
  backgroundSize: '100% 100%',
94
52
  }}>
95
- <div style={{
96
- width: "100%",
97
- height: "100%",
98
- position: "relative"
99
- }}>
53
+ <div style={{ width: "100%", height: "100%", position: "relative" }}>
100
54
  {formattedData ? (
101
55
  <div style={{
102
56
  position: "absolute",
@@ -122,12 +76,7 @@ export const App: React.FunctionComponent<Props> = ({ data, start }) => {
122
76
  left: `${parameters.leftSeparationCard || 0}px`
123
77
  }}>
124
78
  <FontLoader paths={['formatMjs.font']} parameters={parameters} downloadsPath={downloadsPath} />
125
- <div style={{
126
- display: 'flex',
127
- position: 'relative',
128
- flex: '1 1 0%',
129
- flexDirection: 'column',
130
- }}>
79
+ <div style={{ display: 'flex', position: 'relative', flex: '1 1 0%', flexDirection: 'column' }}>
131
80
  <FormattedText text={parameters?.msj0 || 'No hay datos para mostrar'} format={parameters?.formatMjs} />
132
81
  </div>
133
82
  </div>
@@ -135,4 +84,4 @@ export const App: React.FunctionComponent<Props> = ({ data, start }) => {
135
84
  </div>
136
85
  </div>
137
86
  )
138
- }
87
+ }
@@ -52,4 +52,4 @@ export const Card: React.FunctionComponent<Props> = ({ data, format }) => {
52
52
  </div>
53
53
  </div>
54
54
  )
55
- }
55
+ }
@@ -38,4 +38,4 @@ export const FontLoader: React.FunctionComponent<Props> = ({ paths, parameters,
38
38
  return (
39
39
  <style>{fonts}</style>
40
40
  )
41
- }
41
+ }
@@ -23,10 +23,10 @@ interface Props {
23
23
  }
24
24
 
25
25
  export const FormattedText: React.FunctionComponent<Props> = ({ text, format, maxChar, lineHeight, style, unit, paddingBottom, paddingTop }) => {
26
- const trimText = (text: any, maxChar: any) => {
26
+ const trimText = (text: string, maxChar?: number) => {
27
27
  const isValid = maxChar && maxChar >= 3
28
- if (isValid && (text && text.length > maxChar) || false) {
29
- let returnText = text.substring(0, maxChar - 3)
28
+ if (isValid && (text && text.length > maxChar)) {
29
+ const returnText = text.substring(0, maxChar - 3)
30
30
  returnText.substr(-1, 3)
31
31
  return `${returnText.trim()}...`
32
32
  }
@@ -34,7 +34,7 @@ export const FormattedText: React.FunctionComponent<Props> = ({ text, format, ma
34
34
  }
35
35
 
36
36
  const checkNested = (obj: any, path: any): any => {
37
- let arr = path.split('.')
37
+ const arr = path.split('.')
38
38
  if (arr.length > 0) {
39
39
  if (obj.hasOwnProperty(arr[0])) {
40
40
  if (arr.length > 1) return checkNested(obj[arr[0]], arr.splice(1).join('.'))
@@ -43,12 +43,11 @@ export const FormattedText: React.FunctionComponent<Props> = ({ text, format, ma
43
43
  }
44
44
  }
45
45
 
46
- const getHorizontalAlignment = (alignment: Aligment) => {
47
- if (alignment) {
48
- let hA = alignment.horizontal
49
- return alignments.hasOwnProperty(hA) ? alignments[alignment.horizontal] : 'flex-start'
50
- }
51
- return 'flex-start'
46
+ const getHorizontalAlignment = (alignment?: Aligment) => {
47
+ if (!alignment) return 'flex-start'
48
+
49
+ const hA = alignment.horizontal
50
+ return alignments.hasOwnProperty(hA) ? alignments[hA] : 'flex-start'
52
51
  }
53
52
 
54
53
  const renderText = maxChar ? trimText(text, maxChar) : text
@@ -78,4 +77,4 @@ FormattedText.defaultProps = {
78
77
  lineHeight: 1,
79
78
  unit: 'vh',
80
79
  maxChar: undefined
81
- }
80
+ }
package/src/index.html CHANGED
@@ -1,11 +1,14 @@
1
1
  <!DOCTYPE html>
2
- <html style='position: absolute; height:100%; width: 100%; overflow: hidden;'>
3
- <head>
4
- <title></title>
5
- <meta charset='utf-8'>
6
- </head>
7
- <body style='margin:0; overflow: hidden;'>
8
- <div id='root' style='position: absolute; top: 0; right: 0; bottom: 0; left: 0; overflow: hidden;'>
9
- </div>
10
- </body>
11
- </html>
2
+ <html
3
+ lang="en"
4
+ style="position: absolute; height: 100%; width: 100%; overflow: hidden">
5
+ <head>
6
+ <title></title>
7
+ <meta charset="utf-8" />
8
+ </head>
9
+ <body style="margin: 0; overflow: hidden">
10
+ <div
11
+ id="root"
12
+ style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; overflow: hidden"></div>
13
+ </body>
14
+ </html>
package/src/main.ts CHANGED
@@ -1,6 +1,6 @@
1
- import React from 'react';
2
- import ReactDOM from 'react-dom';
3
- import { App } from './App';
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom'
3
+ import { App } from './App'
4
4
 
5
5
  const { ipcRenderer } = require('electron')
6
6
  let start: boolean = false
@@ -0,0 +1,64 @@
1
+ import axios from 'axios'
2
+ import localforage from 'localforage'
3
+ import { AirQuality, AqiData, StorageData } from '../types'
4
+ import { API_RESPONSE_STATUS } from '../utils'
5
+
6
+ const TOKEN = process.env.AQI_TOKEN ?? ''
7
+
8
+ const refetchData = (referenceDate: Date, updateTime: number): boolean => {
9
+ const now = new Date()
10
+
11
+ const sameYear = referenceDate.getFullYear() === now.getFullYear()
12
+ const sameMonth = referenceDate.getMonth() === now.getMonth()
13
+ const sameDay = referenceDate.getDate() === now.getDate()
14
+
15
+ if (sameYear && sameMonth && sameDay) {
16
+ const differenceMinutes = Math.abs(now.getTime() - referenceDate.getTime())
17
+ return differenceMinutes >= updateTime
18
+ }
19
+ return true
20
+ }
21
+
22
+ const requestData = async (cityInput: string): Promise<AirQuality | null> => {
23
+ if (!TOKEN) {
24
+ console.warn('VITE_AQI_TOKEN no está definido en el entorno. Se omitirá la llamada a la API.')
25
+ return null
26
+ }
27
+ const URL = `https://api.waqi.info/feed/${cityInput}/?token=${TOKEN}`
28
+ try {
29
+ const response = await axios.get<AirQuality>(URL)
30
+ if (response?.status === 200) {
31
+ return response.data
32
+ }
33
+ return null
34
+ } catch (error) {
35
+ console.error('Error al obtener datos de AQI:', error)
36
+ return null
37
+ }
38
+ }
39
+
40
+ const processData = (response: AirQuality | null, cityInput: string): AqiData => {
41
+ let data: AqiData = { city: cityInput, station: "Estación desconocida" }
42
+ if (response?.status === API_RESPONSE_STATUS.OK) {
43
+ data = {
44
+ aqi: response.data.aqi,
45
+ city: cityInput,
46
+ station: response.data.city.name
47
+ }
48
+ }
49
+ return data
50
+ }
51
+
52
+ export const getData = async (cityInput: string, updateTime: number): Promise<AqiData> => {
53
+ const storageData: StorageData | null = await localforage.getItem('aqi')
54
+ let data
55
+ if (!storageData?.item || storageData?.item.city !== cityInput || !storageData?.date || refetchData(new Date(storageData.date), updateTime)) {
56
+ data = await requestData(cityInput)
57
+ const processedData = processData(data, cityInput)
58
+ await localforage.setItem('aqi', { item: processedData, date: new Date() })
59
+ return processedData
60
+ } else {
61
+ data = storageData.item
62
+ return data
63
+ }
64
+ }
package/src/utils.ts CHANGED
@@ -1,11 +1,9 @@
1
- const TOKEN = '9fa7d8df1a621c9d518a351a13ff9b94093b9dac'
2
-
3
- const API_RESPONSE_STATUS = {
1
+ export const API_RESPONSE_STATUS = {
4
2
  OK: 'ok',
5
3
  ERROR: 'error',
6
4
  }
7
5
 
8
- const assingAirQuality = (magnitude: number): { color: string; quality: string } => {
6
+ export const assingAirQuality = (magnitude: number): { color: string; quality: string } => {
9
7
  let color = '#9D9F93'
10
8
  let quality = ''
11
9
  if (0 <= magnitude && magnitude <= 50) {
@@ -29,5 +27,3 @@ const assingAirQuality = (magnitude: number): { color: string; quality: string }
29
27
  }
30
28
  return { color, quality }
31
29
  }
32
-
33
- export { TOKEN, API_RESPONSE_STATUS, assingAirQuality }
package/tsconfig.json CHANGED
@@ -1,37 +1,28 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "es2015",
4
- "module": "es2015",
5
- "moduleResolution": "node",
6
- "jsx": "preserve",
7
- "allowJs": true,
8
- "checkJs": true,
9
- "allowSyntheticDefaultImports": true,
10
- "emitDecoratorMetadata": true,
11
- "experimentalDecorators": true,
12
- "downlevelIteration": true,
13
- "strict": true,
14
- "forceConsistentCasingInFileNames": true,
15
- "noFallthroughCasesInSwitch": true,
16
- "noImplicitReturns": true,
17
- "noImplicitAny": true,
18
- "noImplicitThis": true,
19
- "noUnusedLocals": true,
20
- "noUnusedParameters": true,
21
- "sourceMap": true,
22
- "types": [
23
- "node"
24
- ],
25
- "typeRoots": [
26
- "./node_modules/@types",
27
- "./src",
28
- "./src/global.d.ts"
29
- ]
30
- },
31
- "include": [
32
- "./src/**/*"
33
- ],
34
- "exclude": [
35
- "./node_modules/**/*"
36
- ]
37
- }
2
+ "compilerOptions": {
3
+ "target": "es6",
4
+ "module": "esnext",
5
+ "moduleResolution": "node",
6
+ "jsx": "react-jsx",
7
+ "allowJs": true,
8
+ "checkJs": true,
9
+ "allowSyntheticDefaultImports": true,
10
+ "emitDecoratorMetadata": true,
11
+ "experimentalDecorators": true,
12
+ "downlevelIteration": true,
13
+ "strict": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "noFallthroughCasesInSwitch": true,
16
+ "noImplicitReturns": true,
17
+ "noImplicitAny": true,
18
+ "noImplicitThis": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "sourceMap": true,
22
+ "skipLibCheck": true,
23
+ "types": ["node"],
24
+ "typeRoots": ["./node_modules/@types", "./src"]
25
+ },
26
+ "include": ["./src/**/*"],
27
+ "exclude": ["./node_modules/**/*"]
28
+ }