@vixoniccom/footbal-score 1.4.1 → 1.4.2

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.4.2](https://github.com/Vixonic/store-football-score/compare/v1.4.2-dev.0...v1.4.2) (2026-06-13)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * load request when start match ([8b68820](https://github.com/Vixonic/store-football-score/commit/8b688204b15130642d9085838beeda75984435ff))
11
+
12
+ ### [1.4.2-dev.0](https://github.com/Vixonic/store-football-score/compare/v1.4.1...v1.4.2-dev.0) (2026-06-13)
13
+
5
14
  ### [1.4.1](https://github.com/Vixonic/store-football-score/compare/v1.4.1-dev.0...v1.4.1) (2026-06-11)
6
15
 
7
16
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vixoniccom/footbal-score",
3
3
  "alias": "Resultados Deportivos",
4
- "version": "1.4.1",
4
+ "version": "1.4.2",
5
5
  "description": "Muestra resultados en vivo de fútbol.",
6
6
  "main": "main.js",
7
7
  "author": "",
package/src/App.tsx CHANGED
@@ -1,6 +1,5 @@
1
- import React, { useEffect, useState } from 'react'
1
+ import React, { useEffect, useRef, useState } from 'react'
2
2
  import localforage from 'localforage'
3
- import moment from 'moment'
4
3
  import { FontLoader, FormattedText, Table } from './components'
5
4
  import { getFixture } from './helpers/getFixture'
6
5
 
@@ -9,74 +8,140 @@ export type Props = {
9
8
  start: boolean
10
9
  }
11
10
 
11
+ const LIVE_STATUSES = ['1H', 'HT', '2H', 'P', 'ET', 'BT', 'LIVE', 'INT']
12
+ const PRE_MATCH_MS = 5 * 60 * 1000
13
+
14
+ localforage.config({ name: 'request_api_football' })
15
+
16
+ const log = (msg: string, ...args: any[]) =>
17
+ console.log(`[⚽ football-score ${new Date().toLocaleTimeString()}]`, msg, ...args)
18
+
19
+ // Returns the timestamp (ms) when the next fetch should happen based on fixture states.
20
+ // Persisted in localStorage so re-mounts don't reset the schedule.
21
+ function computeNextFetchTime(fixtures: any[], pollIntervalMs: number): number {
22
+ const now = Date.now()
23
+
24
+ const liveMatches = fixtures.filter(f => LIVE_STATUSES.includes(f.fixture?.status?.short))
25
+ if (liveMatches.length > 0) {
26
+ const next = new Date(now + pollIntervalMs)
27
+ log(`🟢 ${liveMatches.length} partido(s) en vivo → próximo fetch en ${next.toLocaleTimeString()} (${pollIntervalMs / 1000}s)`)
28
+ return now + pollIntervalMs
29
+ }
30
+
31
+ const nextMatchAt = fixtures
32
+ .filter(f => f.fixture?.status?.short === 'NS')
33
+ .map(f => new Date(f.fixture.date).getTime())
34
+ .filter(t => t > now)
35
+ .sort((a, b) => a - b)[0]
36
+
37
+ if (nextMatchAt !== undefined) {
38
+ const wakeUp = nextMatchAt - PRE_MATCH_MS
39
+ if (wakeUp <= now) {
40
+ const next = new Date(now + pollIntervalMs)
41
+ log(`🟡 Dentro de ventana pre-partido → próximo fetch en ${next.toLocaleTimeString()}`)
42
+ return now + pollIntervalMs
43
+ }
44
+ log(`⏰ Próximo partido: ${new Date(nextMatchAt).toLocaleTimeString()} → fetch a las ${new Date(wakeUp).toLocaleTimeString()} (10 min antes)`)
45
+ return wakeUp
46
+ }
47
+
48
+ const tomorrow = new Date()
49
+ tomorrow.setDate(tomorrow.getDate() + 1)
50
+ tomorrow.setHours(0, 15, 0, 0)
51
+ log(`💤 Sin partidos pendientes → próximo check: ${tomorrow.toLocaleString()}`)
52
+ return tomorrow.getTime()
53
+ }
54
+
12
55
  export const App: React.FunctionComponent<Props> = ({ data, start }) => {
13
56
  const [dataFixture, setDataFixture] = useState<any[]>([])
14
57
  const [leagueState, setLeagueState] = useState<string>('')
15
58
  const { parameters, downloadsPath } = data
16
- const { backgroundImage, padding, msj0 = '', updateTime = 300000, league = '9' } = parameters
59
+ const { backgroundImage, padding, msj0 = '', league = '9' } = parameters
17
60
  const backgroundImageState = backgroundImage?.filename
18
61
  ? `url("${downloadsPath}/${backgroundImage.filename}")`
19
62
  : ''
20
63
 
21
- const getData = async () => {
22
- const dataLocal: any = await getLocalData()
23
- const momentLocalStorage = dataLocal ? moment().diff(dataLocal.date, 'millisecond') : undefined
24
- const timeNowFormat = moment().format('h:mma')
25
- const timeNow = moment(timeNowFormat, 'h:mma')
26
- const startTime = moment(parameters.timeStart, 'h:mma')
27
- const endTime = moment(parameters.timeEnd, 'h:mma')
28
- // When the end time is earlier than the start time the window crosses midnight,
29
- // e.g. 10am-1am means [10am, midnight) plus [midnight, 1am)
30
- const isWithinSchedule = endTime.isAfter(startTime)
31
- ? timeNow.isBetween(startTime, endTime)
32
- : timeNow.isSameOrAfter(startTime) || timeNow.isBefore(endTime)
33
-
34
- if (momentLocalStorage == undefined || (isWithinSchedule && momentLocalStorage > updateTime!)) {
35
- const dataRequest = await getFixture(league, parameters.matchesYesterday, parameters.matchesTomorrow)
36
- setDataLocal(dataRequest)
37
- setDataFixture(dataRequest)
38
- } else {
39
- setDataFixture(dataLocal.data)
64
+ const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
65
+ // Refs keep the timeout callback from closing over stale props
66
+ const leagueRef = useRef(league)
67
+ const paramsRef = useRef(parameters)
68
+ useEffect(() => {
69
+ leagueRef.current = league
70
+ paramsRef.current = parameters
71
+ })
72
+
73
+ const clearSchedule = () => {
74
+ if (timeoutRef.current !== null) {
75
+ clearTimeout(timeoutRef.current)
76
+ timeoutRef.current = null
40
77
  }
41
78
  }
42
79
 
43
- useEffect(() => {
44
- if (start) {
45
- setLeagueState(league)
46
- getData()
47
- }
48
- }, [data])
80
+ const fetchAndSchedule = async () => {
81
+ const { matchesYesterday, matchesTomorrow, updateTime: ut = 300000 } = paramsRef.current
82
+ const cached: any = await localforage.getItem('football-score')
49
83
 
50
- useEffect(() => {
51
- if (leagueState !== league) {
52
- setLeagueState(league)
53
- localforage.clear()
54
- getData()
84
+ log(`🔄 Fetching fixtures (liga ${leagueRef.current})...`)
85
+ let fixtures: any[]
86
+ try {
87
+ fixtures = await getFixture(leagueRef.current, matchesYesterday, matchesTomorrow)
88
+ log(`✅ ${fixtures.length} fixture(s) recibidos`)
89
+ } catch (err) {
90
+ console.error('[football-score] API error:', err)
91
+ fixtures = cached?.data ?? []
92
+ log(`❌ API falló → usando cache (${fixtures.length} fixtures)`)
55
93
  }
56
- }, [league])
57
94
 
58
- //Request data every 5 minutes
59
- useEffect(() => {
60
- const interval = setInterval(() => {
61
- getData()
62
- }, updateTime)
63
- return () => clearInterval(interval)
64
- }, [updateTime, data])
65
-
66
- const setDataLocal = async (data: any) => {
67
- localforage.config({ name: 'request_api_football' })
95
+ const nextFetchTime = computeNextFetchTime(fixtures, ut)
96
+
68
97
  try {
69
- await localforage.setItem('football-score', { date: Date.now(), data })
98
+ await localforage.setItem('football-score', { date: Date.now(), data: fixtures, nextFetchTime })
70
99
  } catch (err) {
71
- console.log('err', err)
100
+ console.error('[football-score] localStorage write error', err)
72
101
  }
102
+
103
+ setDataFixture(fixtures)
104
+
105
+ clearSchedule()
106
+ timeoutRef.current = setTimeout(fetchAndSchedule, Math.max(0, nextFetchTime - Date.now()))
73
107
  }
74
108
 
75
- const getLocalData = async () => {
76
- const dataLocal = await localforage.getItem('football-score')
77
- return dataLocal
109
+ const init = async () => {
110
+ const cached: any = await localforage.getItem('football-score')
111
+ const now = Date.now()
112
+
113
+ if (!cached?.data) {
114
+ log(`🆕 Sin cache → fetch inmediato`)
115
+ fetchAndSchedule()
116
+ return
117
+ }
118
+
119
+ setDataFixture(cached.data)
120
+
121
+ if (!cached.nextFetchTime || cached.nextFetchTime <= now) {
122
+ log(`🔁 Mount check: nextFetchTime ya pasó (${cached.nextFetchTime ? new Date(cached.nextFetchTime).toLocaleTimeString() : 'null'}) → fetch inmediato`)
123
+ fetchAndSchedule()
124
+ } else {
125
+ const mins = Math.round((cached.nextFetchTime - now) / 60000)
126
+ log(`💾 Mount check: usando cache → próximo fetch en ${new Date(cached.nextFetchTime).toLocaleTimeString()} (~${mins} min)`)
127
+ }
78
128
  }
79
129
 
130
+ useEffect(() => {
131
+ if (!start) return
132
+ setLeagueState(league)
133
+ init()
134
+ return clearSchedule
135
+ }, [data])
136
+
137
+ useEffect(() => {
138
+ if (leagueState === '' || leagueState === league) return
139
+ setLeagueState(league)
140
+ clearSchedule()
141
+ localforage.clear()
142
+ fetchAndSchedule()
143
+ }, [league])
144
+
80
145
  return (
81
146
  dataFixture.length > 0 ?
82
147
  <div style={{
@@ -27,11 +27,6 @@ export const getFixture = async (league: string, yesterday?: boolean, tomorrow?:
27
27
  headers: { 'X-RapidAPI-Key': FOOTBALL_API_KEY, 'X-RapidAPI-Host': FOOTBALL_API_HOST },
28
28
  }
29
29
 
30
- try {
31
- const response = await axios.request(options)
32
- return response.data.response
33
- } catch (error) {
34
- console.error(error)
35
- return []
36
- }
30
+ const response = await axios.request(options)
31
+ return response.data.response
37
32
  }
package/build/index.html DELETED
@@ -1 +0,0 @@
1
- <!doctype html><html lang="en" style="position:absolute;height:100%;width:100%;overflow:hidden"><head><title></title><meta charset="utf-8"/></head><body style="margin:0;overflow:hidden"><div id="root" style="position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden"></div><script src="./main.js"></script></body></html>