mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 17:23:25 +00:00
ENH - polish weather refresh experience
Migrate the Weather app to the Sky UI primitives with richer condition effects, updated WebP icons, and pull-to-refresh feedback.\n\nAdd a shared reload cooldown for weather, banking, and housing actions, including localized status feedback and test-server coverage.
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
allowManualReload,
|
||||
isReloadCooldownActive,
|
||||
} from '@/utils/reload-cooldown'
|
||||
|
||||
describe('reload cooldown', () => {
|
||||
it('blocks after too many reloads in a short window and recovers', () => {
|
||||
const state = { cooldownUntil: 0, reloadAttempts: [] as number[] }
|
||||
|
||||
expect(allowManualReload(state, 1_000)).toBe(true)
|
||||
expect(allowManualReload(state, 2_000)).toBe(true)
|
||||
expect(allowManualReload(state, 3_000)).toBe(true)
|
||||
expect(allowManualReload(state, 4_000)).toBe(false)
|
||||
expect(isReloadCooldownActive(state, 13_999)).toBe(true)
|
||||
expect(isReloadCooldownActive(state, 14_000)).toBe(false)
|
||||
expect(allowManualReload(state, 14_000)).toBe(true)
|
||||
})
|
||||
|
||||
it('forgets reloads outside the rolling window', () => {
|
||||
const state = { cooldownUntil: 0, reloadAttempts: [] as number[] }
|
||||
|
||||
expect(allowManualReload(state, 1_000)).toBe(true)
|
||||
expect(allowManualReload(state, 12_000)).toBe(true)
|
||||
expect(state.reloadAttempts).toEqual([12_000])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,40 @@
|
||||
export const RELOAD_COOLDOWN_ERROR = 'reload_cooldown'
|
||||
|
||||
const RELOAD_LIMIT = 3
|
||||
const RELOAD_WINDOW_MS = 10_000
|
||||
const RELOAD_COOLDOWN_MS = 10_000
|
||||
|
||||
export interface ReloadCooldownState {
|
||||
cooldownUntil: number
|
||||
reloadAttempts: number[]
|
||||
}
|
||||
|
||||
export function isReloadCooldownActive(
|
||||
state: ReloadCooldownState,
|
||||
now = Date.now(),
|
||||
): boolean {
|
||||
if (state.cooldownUntil <= now) {
|
||||
state.cooldownUntil = 0
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export function allowManualReload(
|
||||
state: ReloadCooldownState,
|
||||
now = Date.now(),
|
||||
): boolean {
|
||||
if (isReloadCooldownActive(state, now)) return false
|
||||
|
||||
state.reloadAttempts = state.reloadAttempts.filter(
|
||||
(attemptedAt) => now - attemptedAt < RELOAD_WINDOW_MS,
|
||||
)
|
||||
if (state.reloadAttempts.length >= RELOAD_LIMIT) {
|
||||
state.cooldownUntil = now + RELOAD_COOLDOWN_MS
|
||||
state.reloadAttempts = []
|
||||
return false
|
||||
}
|
||||
|
||||
state.reloadAttempts.push(now)
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user