169 lines
4.8 KiB
Vue
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>
|