diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index f08c19d..8239d4b 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -3515,119 +3515,6 @@ button { letter-spacing: 0.04em; text-transform: uppercase; } -.weather-camera-card { - padding-bottom: 12px; -} -.weather-camera-preview { - position: relative; - min-height: 146px; - overflow: hidden; - background: - radial-gradient(circle at 75% 20%, #bfe8ff9c, transparent 28%), - linear-gradient(160deg, #82b4d5 0%, #496783 52%, #27364c 100%); -} -.weather-camera-preview--rain, -.weather-camera-preview--thunder { - background: - radial-gradient(circle at 70% 18%, #9cb4c380, transparent 26%), - linear-gradient(160deg, #667d8d 0%, #34495f 50%, #1a2839 100%); -} -.weather-camera-preview--clear, -.weather-camera-preview--sunny { - background: - radial-gradient(circle at 76% 19%, #fff4c9 0 5%, #ffd78370 6%, transparent 29%), - linear-gradient(160deg, #72b7df 0%, #4d86b2 55%, #35506e 100%); -} -.weather-camera-preview::after { - position: absolute; - inset: -50% 0 auto; - height: 3px; - background: #ffffff38; - box-shadow: 0 0 12px #fff8; - content: ''; - animation: weather-camera-scan 4s linear infinite; -} -.weather-camera-skyline { - position: absolute; - right: -5%; - bottom: 0; - left: -5%; - height: 51%; - background: - linear-gradient(90deg, transparent 0 5%, #142333 5% 17%, transparent 17% 21%, #192b3c 21% 34%, transparent 34% 42%, #11202f 42% 60%, transparent 60% 65%, #182a3b 65% 76%, transparent 76% 81%, #102131 81% 95%, transparent 95%), - linear-gradient(0deg, #111c2a 0 18%, transparent 18%); - clip-path: polygon(0 43%, 9% 43%, 9% 22%, 18% 22%, 18% 57%, 27% 57%, 27% 10%, 37% 10%, 37% 48%, 48% 48%, 48% 30%, 61% 30%, 61% 62%, 71% 62%, 71% 16%, 83% 16%, 83% 49%, 100% 49%, 100% 100%, 0 100%); - opacity: 0.85; -} -.weather-camera-meta, -.weather-camera-copy { - position: absolute; - z-index: 1; - right: 12px; - left: 12px; - display: flex; - justify-content: space-between; - text-shadow: 0 2px 8px #000c; -} -.weather-camera-meta { - top: 11px; - align-items: center; - font-size: 10px; - font-weight: 700; - text-transform: uppercase; -} -.weather-camera-live { - display: flex; - align-items: center; - gap: 5px; -} -.weather-camera-live i { - width: 7px; - height: 7px; - border-radius: 50%; - background: #ff4e5b; - box-shadow: 0 0 0 4px #ff4e5b2e; - animation: weather-camera-pulse 1.6s ease-in-out infinite; -} -.weather-camera-copy { - bottom: 10px; - align-items: flex-end; - flex-direction: column; -} -.weather-camera-copy strong { - font-size: 16px; -} -.weather-camera-copy span { - color: #ffffffc4; - font-size: 10px; -} -.weather-camera-action { - padding: 11px 12px 0; -} -.weather-camera-action button span { - display: flex; - align-items: center; - justify-content: center; - gap: 7px; -} -.weather-camera-action p { - margin: 8px 3px 0; - color: #ffd2d2; - font-size: 10px; - text-align: center; -} -.weather-camera-unavailable { - padding: 16px; - color: #ffffffb3; - font-size: 12px; - text-align: center; -} -@keyframes weather-camera-scan { - to { transform: translateY(290px); } -} -@keyframes weather-camera-pulse { - 50% { opacity: 0.45; } -} .weather-hourly { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); diff --git a/frontend/src/stores/phone.ts b/frontend/src/stores/phone.ts index 7ab4eae..e9288b9 100644 --- a/frontend/src/stores/phone.ts +++ b/frontend/src/stores/phone.ts @@ -2338,28 +2338,6 @@ const defaultLocales: LocaleTree = { unavailable: 'Weather is unavailable', stale: 'Showing the last available weather update.', tryAgain: 'Try Again', - webcam: { - title: 'Weather Cameras', - live: 'LIVE', - view: 'View Live Camera', - subtitle: 'A live look at current conditions', - unavailable: 'No weather cameras are available.', - locations: { - legion_square: 'Legion Square', - sandy_shores: 'Sandy Shores', - cayo_airstrip: 'Cayo Perico Airstrip', - }, - errors: { - camera_disabled: 'Weather cameras are currently disabled.', - camera_not_found: 'This weather camera is no longer available.', - camera_requires_safe_position: - 'Stand still and exit your vehicle before opening a camera.', - camera_active: 'A weather camera is already open.', - rate_limited: 'Too many camera requests. Try again shortly.', - request_failed: 'The weather camera could not be opened.', - not_authenticated: 'Unlock the phone before opening a camera.', - }, - }, regions: { los_santos: 'Los Santos', blaine_county: 'Blaine County', diff --git a/frontend/src/stores/weather.test.ts b/frontend/src/stores/weather.test.ts index b89b26a..53e3526 100644 --- a/frontend/src/stores/weather.test.ts +++ b/frontend/src/stores/weather.test.ts @@ -61,24 +61,4 @@ describe('weather store', () => { expect(weather.error).toBe('offline') }) - it('loads the server-approved camera list and opens a camera by id', async () => { - vi.mocked(nuiCall) - .mockResolvedValueOnce({ - data: { cameras: [{ id: 'legion_square', region: 'los_santos' }] }, - success: true, - }) - .mockResolvedValueOnce({ success: true }) - const weather = useWeatherStore() - - await weather.loadCameras() - const opened = await weather.openCamera('legion_square') - - expect(weather.cameras).toEqual([ - { id: 'legion_square', region: 'los_santos' }, - ]) - expect(nuiCall).toHaveBeenLastCalledWith('weather:camera-open', { - id: 'legion_square', - }) - expect(opened).toBe(true) - }) }) diff --git a/frontend/src/stores/weather.ts b/frontend/src/stores/weather.ts index e94dbe6..103c378 100644 --- a/frontend/src/stores/weather.ts +++ b/frontend/src/stores/weather.ts @@ -1,10 +1,6 @@ import { defineStore } from 'pinia' -import type { - RawWeatherSnapshot, - WeatherCamera, - WeatherForecast, -} from '@/types/weather' +import type { RawWeatherSnapshot, WeatherForecast } from '@/types/weather' import { nuiCall } from '@/utils/nui' import { buildWeatherForecast } from '@/utils/weather' @@ -13,45 +9,12 @@ const REFRESH_INTERVAL = 30_000 export const useWeatherStore = defineStore('weather', { state: () => ({ error: null as string | null, - cameraError: null as string | null, - cameras: [] as WeatherCamera[], - camerasLoaded: false, forecast: null as WeatherForecast | null, intervalId: undefined as number | undefined, isLoading: false, - isOpeningCamera: false, lastFetchedAt: 0, }), actions: { - async loadCameras(force = false): Promise { - if (this.camerasLoaded && !force) return - const response = await nuiCall<{ cameras: WeatherCamera[] }>( - 'weather:cameras', - ) - this.camerasLoaded = true - if (!response.success || !Array.isArray(response.data?.cameras)) { - this.cameraError = response.error ?? 'request_failed' - return - } - this.cameras = response.data.cameras.filter( - (camera) => - typeof camera?.id === 'string' && - ['los_santos', 'blaine_county', 'cayo_perico'].includes( - camera.region, - ), - ) - this.cameraError = null - }, - async openCamera(id: string): Promise { - if (this.isOpeningCamera) return false - this.isOpeningCamera = true - const response = await nuiCall('weather:camera-open', { id }) - this.isOpeningCamera = false - this.cameraError = response.success - ? null - : (response.error ?? 'request_failed') - return response.success - }, async refresh(force = false): Promise { if (this.isLoading) return if (!force && Date.now() - this.lastFetchedAt < REFRESH_INTERVAL) return @@ -68,7 +31,6 @@ export const useWeatherStore = defineStore('weather', { }, start(): void { void this.refresh(true) - void this.loadCameras() if (this.intervalId !== undefined) return this.intervalId = window.setInterval(() => void this.refresh(true), REFRESH_INTERVAL) }, diff --git a/frontend/src/types/weather.ts b/frontend/src/types/weather.ts index 568ac93..14918a5 100644 --- a/frontend/src/types/weather.ts +++ b/frontend/src/types/weather.ts @@ -10,11 +10,6 @@ export type WeatherConditionId = export type WeatherRegionId = 'los_santos' | 'blaine_county' | 'cayo_perico' -export type WeatherCamera = { - id: string - region: WeatherRegionId -} - export type WeatherClock = { day: number hour: number diff --git a/frontend/src/views/apps/WeatherApp.vue b/frontend/src/views/apps/WeatherApp.vue index af217fd..9a3569b 100644 --- a/frontend/src/views/apps/WeatherApp.vue +++ b/frontend/src/views/apps/WeatherApp.vue @@ -1,5 +1,5 @@