myweather/src/views/HomeView.vue
Lance Edgar b983a34bc9 Make buttons half-wide on home screen (desktop mode)
having a button take up full screen width is a bit much
2024-06-08 19:35:45 -05:00

169 lines
4.8 KiB
Vue

<script setup>
import { mapStores } from 'pinia'
import { useWeatherStore } from '../stores/weather'
import { useLocationStore } from '../stores/location'
</script>
<script>
export default {
data() {
return {
coordinates: null,
loading: false,
locationAccessBlocked: false,
}
},
computed: {
...mapStores(useWeatherStore, useLocationStore),
},
methods: {
useCurrentLocation() {
if (this.locationAccessBlocked) {
alert("You must refresh the page first, then try again.")
return
}
navigator.geolocation.getCurrentPosition(loc => {
this.coordinates = `${loc.coords.latitude},${loc.coords.longitude}`
this.loadCoordinates()
}, error => {
if (error.code == 1) { // PERMISSION_DENIED
this.locationAccessBlocked = true
}
alert(`error.code = ${error.code}\n\n${error.message}`)
})
},
editLocations() {
this.$router.push('/edit-list')
},
async loadCoordinates() {
if (!this.coordinates) {
this.$refs.coordinates.focus()
return
}
const parts = this.coordinates.split(/(?: +| *\, *)/)
const pattern = /^ *-?\d+(?:\.\d+)? *$/
if (parts.length != 2
|| !parts[0].match(pattern)
|| !parts[1].match(pattern)) {
this.$oruga.notification.open({
variant: 'warning',
message: "Coordinates are not valid.",
position: 'bottom',
})
this.$refs.coordinates.focus()
return
}
this.coordinates = `${parts[0]},${parts[1]}`
this.loading = true
const url = `https://api.weather.gov/points/${this.coordinates}`
const response = await fetch(url)
const weather = await response.json()
this.loading = false
if (weather.status == 404) {
this.$oruga.notification.open({
variant: 'warning',
message: weather.title || "Data not found!",
position: 'bottom',
})
return
}
let coords = weather.geometry.coordinates
coords = `${coords[1].toFixed(4)},${coords[0].toFixed(4)}`
const city = weather.properties.relativeLocation.properties.city
const state = weather.properties.relativeLocation.properties.state
const cityState = `${city}, ${state}`
this.locationStore.addLocation(coords, cityState)
this.weatherStore.setCoordinates(coords)
this.weatherStore.setCityState(cityState)
this.weatherStore.setWeather(weather)
// clear this so user sees empty input when they return
this.coordinates = null
this.$router.push('/weather')
},
showWeather(location) {
this.weatherStore.clearWeather()
this.weatherStore.setCoordinates(location.coordinates)
this.weatherStore.setCityState(location.cityState)
this.$router.push('/weather')
},
},
}
</script>
<template>
<main>
<br />
<o-field grouped>
<o-input v-model="coordinates"
ref="coordinates"
placeholder="coordinates"
clearable />
<o-button variant="primary"
icon-left="arrow-right"
@click="loadCoordinates()"
:disabled="loading">
Go!
</o-button>
</o-field>
<div class="columns">
<div class="column is-half">
<div v-for="location in locationStore.locations"
:key="location.coordinates"
class="location">
<o-button variant="primary"
icon-right="arrow-right"
expanded
@click="showWeather(location)">
{{ location.cityState }}
</o-button>
</div>
<div v-if="locationStore.locations.length"
class="location">
<o-button icon-right="edit"
expanded
@click="editLocations()">
Edit List
</o-button>
</div>
<br />
<div class="location">
<o-button variant="primary"
icon-right="location-dot"
expanded
@click="useCurrentLocation()">
Use My Current Location
</o-button>
</div>
</div>
</div>
</main>
</template>
<style scoped>
div.location {
padding: 0.25rem;
}
</style>