-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vue Migration - fixes #80 #92
base: master
Are you sure you want to change the base?
Conversation
This looks very nice, thanks for your hard work! Do you think it is ready to be merged already? |
i like the new additions you did too, didn't reviewed the code |
Thank you ! What I didn't pay much attention to is what happens when adding / removing cities, and some of the current behavior may be considered a regression: |
This looks quite awesome! Do you think we should wait and include it in a possible release for Nextcloud 20 or make a release for Nextcloud 20 anyway with the legacy Angular.js interface? |
This PR doesn't depend on Nextcloud 20 APIs – it may be released whenever it's considered ready. Nextcloud 20 may warrant a rewrite on the backend, though: it comes with its built-in Dashboard including weather data from met.no's API. It makes sense to investigate piggybacking on it. |
@berdosi The old dashboard code should be removed because it is not useful anymore. Do you have an idea on how to integrate the best way with the Weather Status app by @eneiluj? I am just thinking that maybe it shouldn't send the user to an external page but rather automatically add the preferred location to the Weather app if it is installed. Maybe a separate menu in the settings could allow the user to set their preferred option. |
Indeed it should be – unless someone's faster, I may open a PR for that soonish. I know that currently the link in the Dashboard points to Windy.com, for the current coordinates. I see two ways of changing the link in the widget, in case the app is installed - and both are ugly:
There are probably more civilized ways of apps interopping with one another – I assume the Widget could expose an interface to let its forecast provider be changed by the apps (which would be similar to 1) above, but has a way better PR). |
Whoops, this has been closed in error. |
@berdosi Do you think this is ready for merging? |
@berdosi It might be necessary to rebase on the latest master to get the most recent changes from there. |
082dac1
to
2f670f0
Compare
@e-alfred Thanks for the tip – I like what I'm seeing now. |
It appears this PR is mergeable. Is there anything we can do as the community to support the maintainers in reviewing this PR, in so it can be merged? |
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Signed-off-by: Balint Erdosi <[email protected]>
Update Dependencies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good, mostly nitpicks
- Use async/await
- Move store definition to another file
- Forgot to translate a few strings
context.commit('setApiKeyNeeded', false) | ||
} else if (reason.status === 401) { | ||
const error = t( | ||
'weather', | ||
'Your OpenWeatherMap API key is invalid. Contact your administrator to configure a valid API key in Additional Settings of the Administration') | ||
showError(error) | ||
|
||
context.commit('setCityLoadError', error) | ||
context.commit('setApiKeyNeeded', true) | ||
} else { | ||
const error = context.getters.fatalError | ||
|
||
showError(error) | ||
|
||
logger.error('Error getting weather information for city', { reason }) | ||
context.commit('setCityLoadError', error) | ||
context.commit('setApiKeyNeeded', false) | ||
} | ||
|
||
}) | ||
}, | ||
deleteCity(context, cityId) { | ||
axios.post(generateUrl('/apps/weather/city/delete'), { id: cityId }) | ||
.then((response) => { | ||
if (response.data != null && !!response.data.deleted) { | ||
context.commit('removeCity', cityId) | ||
// If current city is the removed city, close it | ||
if (context.state.selectedCityId === cityId) { | ||
context.commit('unsetSelected') | ||
// if there are still cities on the list, and the selected was deleted, select the home city. | ||
// if the home city was deleted, select the first one instead | ||
if (context.state.allCities.cities.length > 0) { | ||
context.dispatch( | ||
'loadCity', | ||
cityId !== context.state.allCities.home | ||
? context.state.allCities.home | ||
: context.state.allCities.cities[0].id) | ||
} | ||
} | ||
} else { | ||
alert(t('weather', 'Failed to remove city. Please contact your administrator')) | ||
} | ||
}).catch((reason) => { | ||
showError(context.getters.fatalError) | ||
logger.error('Error deleting city', { reason }) | ||
}) | ||
}, | ||
setHome(context, cityId) { | ||
axios.post(generateUrl('/apps/weather/settings/home/set'), { city: cityId }) | ||
.then(response => { | ||
if (response.data != null && !!response.data.set) { | ||
context.commit('setHome', cityId) | ||
} else { | ||
showError(t('weather', 'Failed to set home. Please contact your administrator')) | ||
} | ||
}) | ||
.catch((reason) => { | ||
logger.error('Error setting home city', { reason }) | ||
showError(context.getters.fatalError) | ||
}) | ||
}, | ||
selectCity(context, cityId) { | ||
context.dispatch('loadCity', cityId) | ||
.then(() => { | ||
context.commit('setSelected', cityId) | ||
}) | ||
}, | ||
loadMetric(context) { | ||
axios.get(generateUrl('/apps/weather/settings/metric/get')) | ||
.then((result) => { | ||
if (result.data.metric) { | ||
context.commit('setMetric', result.data.metric) | ||
context.commit('setMetricRepresentation', mapMetric(result.data.metric)) | ||
} | ||
}).catch((reason) => { | ||
logger.error('Error setting metric', { reason }) | ||
showError(context.getters.fatalError) | ||
}) | ||
}, | ||
modifyMetric(context, metric) { | ||
axios.post(generateUrl('/apps/weather/settings/metric/set'), { metric }) | ||
.then((result) => { | ||
if (result.data != null && !!(result.data.set)) { | ||
context.commit('setMetric', metric) | ||
context.commit('setMetricRepresentation', mapMetric(metric)) | ||
context.dispatch('loadCity', context.state.selectedCityId) | ||
} else { | ||
showError(t('weather', 'Failed to set metric. Please contact your administrator')) | ||
} | ||
}).catch( | ||
(reason) => { | ||
if (reason.status === 404) { | ||
showError(t('weather', 'This metric is not known.')) | ||
} else { | ||
logger.error('Error setting metric', { reason }) | ||
showError(context.getters.fatalError) | ||
} | ||
}) | ||
}, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addCity(context, newCityName) { | |
axios.post(generateUrl('/apps/weather/city/add'), { name: newCityName }) | |
.then((response) => { | |
showSuccess(`City added: ${newCityName}`) | |
if (response.data.id) { | |
// response contains the id, but not the name | |
context.commit('addCity', { name: newCityName, id: response.data.id }) | |
} | |
// otherwise one may send a new API call : context.dispatch('loadCities') | |
}) | |
.catch((reason) => { | |
logger.error('Error adding city', { reason }) | |
showError(context.getters.fatalError) | |
}) | |
}, | |
loadCities(context) { | |
axios.get(generateUrl('/apps/weather/city/getall')) | |
.then((response) => { | |
const responseData = response.data | |
if (responseData.home) { | |
context.commit('setHome', responseData.home) | |
} | |
if (responseData.cities) { | |
context.commit('setCities', responseData.cities) | |
} | |
if (!context.state.loaded) { | |
if (responseData.home && !!responseData.cities.find(city => city.id === responseData.home)) { | |
// there is a home city set, and it hasnn't been removed before -> load it | |
context.dispatch('loadCity', responseData.home) | |
.then(() => { context.commit('setLoaded', true) }) | |
} else if (responseData.cities && responseData.cities.length > 0) { | |
// load the first city, if no home city is set | |
context.dispatch('loadCity', responseData.cities[0].id) | |
} | |
} | |
}) | |
.catch(reason => { | |
showError(context.getters.fatalError) | |
logger.error('Cannot load cities', { reason }) | |
}) | |
}, | |
loadCity(context, cityId) { | |
const cityToLoad = context.state.allCities.cities.find(city => city.id === cityId) | |
if (!cityToLoad) return | |
axios.get( | |
generateUrl('/apps/weather/weather/get'), | |
{ params: { name: cityToLoad.name } }) | |
.then((response) => { | |
const currentCityData = response.data | |
currentCityData.wind.desc = windMapper(currentCityData.wind.deg) | |
currentCityData.image = imageMapper(currentCityData.weather[0].main) | |
context.commit('setCurrentCity', currentCityData) | |
context.commit('setSelected', cityToLoad.id) | |
}) | |
.catch((reason) => { | |
showError(`Cannot load city: ${reason}`) | |
if (reason.status === 404) { | |
const error = t('weather', 'No city with this name found.') | |
showError(error) | |
context.commit('setCityLoadError', error) | |
context.commit('setApiKeyNeeded', false) | |
} else if (reason.status === 401) { | |
const error = t( | |
'weather', | |
'Your OpenWeatherMap API key is invalid. Contact your administrator to configure a valid API key in Additional Settings of the Administration') | |
showError(error) | |
context.commit('setCityLoadError', error) | |
context.commit('setApiKeyNeeded', true) | |
} else { | |
const error = context.getters.fatalError | |
showError(error) | |
logger.error('Error getting weather information for city', { reason }) | |
context.commit('setCityLoadError', error) | |
context.commit('setApiKeyNeeded', false) | |
} | |
}) | |
}, | |
deleteCity(context, cityId) { | |
axios.post(generateUrl('/apps/weather/city/delete'), { id: cityId }) | |
.then((response) => { | |
if (response.data != null && !!response.data.deleted) { | |
context.commit('removeCity', cityId) | |
// If current city is the removed city, close it | |
if (context.state.selectedCityId === cityId) { | |
context.commit('unsetSelected') | |
// if there are still cities on the list, and the selected was deleted, select the home city. | |
// if the home city was deleted, select the first one instead | |
if (context.state.allCities.cities.length > 0) { | |
context.dispatch( | |
'loadCity', | |
cityId !== context.state.allCities.home | |
? context.state.allCities.home | |
: context.state.allCities.cities[0].id) | |
} | |
} | |
} else { | |
alert(t('weather', 'Failed to remove city. Please contact your administrator')) | |
} | |
}).catch((reason) => { | |
showError(context.getters.fatalError) | |
logger.error('Error deleting city', { reason }) | |
}) | |
}, | |
setHome(context, cityId) { | |
axios.post(generateUrl('/apps/weather/settings/home/set'), { city: cityId }) | |
.then(response => { | |
if (response.data != null && !!response.data.set) { | |
context.commit('setHome', cityId) | |
} else { | |
showError(t('weather', 'Failed to set home. Please contact your administrator')) | |
} | |
}) | |
.catch((reason) => { | |
logger.error('Error setting home city', { reason }) | |
showError(context.getters.fatalError) | |
}) | |
}, | |
selectCity(context, cityId) { | |
context.dispatch('loadCity', cityId) | |
.then(() => { | |
context.commit('setSelected', cityId) | |
}) | |
}, | |
loadMetric(context) { | |
axios.get(generateUrl('/apps/weather/settings/metric/get')) | |
.then((result) => { | |
if (result.data.metric) { | |
context.commit('setMetric', result.data.metric) | |
context.commit('setMetricRepresentation', mapMetric(result.data.metric)) | |
} | |
}).catch((reason) => { | |
logger.error('Error setting metric', { reason }) | |
showError(context.getters.fatalError) | |
}) | |
}, | |
modifyMetric(context, metric) { | |
axios.post(generateUrl('/apps/weather/settings/metric/set'), { metric }) | |
.then((result) => { | |
if (result.data != null && !!(result.data.set)) { | |
context.commit('setMetric', metric) | |
context.commit('setMetricRepresentation', mapMetric(metric)) | |
context.dispatch('loadCity', context.state.selectedCityId) | |
} else { | |
showError(t('weather', 'Failed to set metric. Please contact your administrator')) | |
} | |
}).catch( | |
(reason) => { | |
if (reason.status === 404) { | |
showError(t('weather', 'This metric is not known.')) | |
} else { | |
logger.error('Error setting metric', { reason }) | |
showError(context.getters.fatalError) | |
} | |
}) | |
}, | |
}, | |
async addCity(context, newCityName) { | |
try { | |
const { data } = await axios.post(generateUrl('/apps/weather/city/add'), { name: newCityName }) | |
showSuccess(`City added: ${newCityName}`) | |
if (data.id) { | |
// response contains the id, but not the name | |
context.commit('addCity', { name: newCityName, id: response.data.id }) | |
} | |
// otherwise one may send a new API call : context.dispatch('loadCities') | |
} catch (reason) { | |
logger.error('Error adding city', { reason }) | |
showError(context.getters.fatalError) | |
} | |
}, | |
async loadCities(context) { | |
const { data } = await axios.get(generateUrl('/apps/weather/city/getall')) | |
try { | |
if (data.home) { | |
context.commit('setHome', data.home) | |
} | |
if (data.cities) { | |
context.commit('setCities', data.cities) | |
} | |
if (!context.state.loaded) { | |
if (data.home && !!data.cities.find(city => city.id === data.home)) { | |
// there is a home city set, and it hasnn't been removed before -> load it | |
context.dispatch('loadCity', responseData.home) | |
.then(() => { context.commit('setLoaded', true) }) | |
} else if (data.cities && data.cities.length > 0) { | |
// load the first city, if no home city is set | |
context.dispatch('loadCity', data.cities[0].id) | |
} | |
} | |
} catch (reason) { | |
showError(context.getters.fatalError) | |
logger.error('Cannot load cities', { reason }) | |
} | |
}, | |
async loadCity(context, cityId) { | |
const cityToLoad = context.state.allCities.cities.find(city => city.id === cityId) | |
if (!cityToLoad) { | |
return | |
} | |
try { | |
const { data } = await axios.get(generateUrl('/apps/weather/weather/get'), { | |
params: { name: cityToLoad.name } | |
}) | |
const currentCityData = data | |
currentCityData.wind.desc = windMapper(currentCityData.wind.deg) | |
currentCityData.image = imageMapper(currentCityData.weather[0].main) | |
context.commit('setCurrentCity', currentCityData) | |
context.commit('setSelected', cityToLoad.id) | |
} catch (reason) { | |
showError(`Cannot load city: ${reason}`) | |
if (reason.status === 404) { | |
const error = t('weather', 'No city with this name found.') | |
showError(error) | |
context.commit('setCityLoadError', error) | |
context.commit('setApiKeyNeeded', false) | |
} else if (reason.status === 401) { | |
const error = t( | |
'weather', | |
'Your OpenWeatherMap API key is invalid. Contact your administrator to configure a valid API key in Additional Settings of the Administration') | |
showError(error) | |
context.commit('setCityLoadError', error) | |
context.commit('setApiKeyNeeded', true) | |
} else { | |
const error = context.getters.fatalError | |
showError(error) | |
logger.error('Error getting weather information for city', { reason }) | |
context.commit('setCityLoadError', error) | |
context.commit('setApiKeyNeeded', false) | |
} | |
} | |
}, | |
async deleteCity(context, cityId) { | |
try { | |
const { data } = await axios.post(generateUrl('/apps/weather/city/delete'), { id: cityId }) | |
if (data != null && !!data.deleted) { | |
context.commit('removeCity', cityId) | |
// If current city is the removed city, close it | |
if (context.state.selectedCityId === cityId) { | |
context.commit('unsetSelected') | |
// if there are still cities on the list, and the selected was deleted, select the home city. | |
// if the home city was deleted, select the first one instead | |
if (context.state.allCities.cities.length > 0) { | |
context.dispatch( | |
'loadCity', | |
cityId !== context.state.allCities.home | |
? context.state.allCities.home | |
: context.state.allCities.cities[0].id) | |
} | |
} | |
} else { | |
alert(t('weather', 'Failed to remove city. Please contact your administrator')) | |
} | |
} catch (reason) { | |
showError(context.getters.fatalError) | |
logger.error('Error deleting city', { reason }) | |
} | |
}, | |
async setHome(context, cityId) { | |
try { | |
const { data } = await axios.post(generateUrl('/apps/weather/settings/home/set'), { city: cityId }) | |
if (data != null && !!data.set) { | |
context.commit('setHome', cityId) | |
} else { | |
showError(t('weather', 'Failed to set home. Please contact your administrator')) | |
} | |
}) | |
} catch (reason) { | |
logger.error('Error setting home city', { reason }) | |
showError(context.getters.fatalError) | |
} | |
}, | |
async selectCity(context, cityId) { | |
await context.dispatch('loadCity', cityId) | |
context.commit('setSelected', cityId) | |
}, | |
async loadMetric(context) { | |
try { | |
const { data } = await axios.get(generateUrl('/apps/weather/settings/metric/get')) | |
if (data.metric) { | |
context.commit('setMetric', data.metric) | |
context.commit('setMetricRepresentation', mapMetric(data.metric)) | |
} | |
} catch (reason) { | |
logger.error('Error setting metric', { reason }) | |
showError(context.getters.fatalError) | |
} | |
}, | |
async modifyMetric(context, metric) { | |
try { | |
const { data } = axios.post(generateUrl('/apps/weather/settings/metric/set'), { metric }) | |
if (data != null && !!(data.set)) { | |
context.commit('setMetric', metric) | |
context.commit('setMetricRepresentation', mapMetric(metric)) | |
context.dispatch('loadCity', context.state.selectedCityId) | |
} else { | |
showError(t('weather', 'Failed to set metric. Please contact your administrator')) | |
} | |
} catch (reason) { | |
if (reason.status === 404) { | |
showError(t('weather', 'This metric is not known.')) | |
} else { | |
logger.error('Error setting metric', { reason }) | |
showError(context.getters.fatalError) | |
} | |
} | |
}, | |
}, |
|
||
Vue.use(Vuex) | ||
|
||
const store = new Vuex.Store({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
store definition should be moved to another file
Co-authored-by: Carl Schwan <[email protected]>
Co-authored-by: Carl Schwan <[email protected]>
This is quite a mouthful, for a full rewrite of the frontend template (API calls were kept pretty much as-is)
It builds with
npm install && npm build
.The bundle is around 1MB, which seems large - but comparable to all the other Nextcloud applications.
Also, I've added a blur to the content panels, which could work around #90 (only works on WebKit):