From 346eafc821a0e7478cfb4c0f399ed77bf7a54185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Brugarolas?= Date: Sun, 13 Jan 2019 19:39:49 +0100 Subject: [PATCH] Added local time --- CHANGELOG.md | 3 +- package.json | 14 ++++----- src/api/time/time-update.js | 54 ++++++++++++++++++++++++++++++++++ src/api/time/timezone-api.js | 19 ++++++++++++ src/api/time/timezone-date.js | 35 ++++++++++++++++++++++ src/api/weather/adapt.js | 5 ++-- src/api/weather/openweather.js | 7 +---- src/ui/components/weather.js | 2 ++ src/ui/components/weather.less | 9 ++++++ src/ui/containers/time.js | 47 +++++++++++++++++++++++++++++ webpack.config.js | 4 +++ 11 files changed, 183 insertions(+), 16 deletions(-) create mode 100644 src/api/time/time-update.js create mode 100644 src/api/time/timezone-api.js create mode 100644 src/api/time/timezone-date.js create mode 100644 src/ui/containers/time.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aca2d2..aa2d62c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ -## Working on +## v1.1.0 Responsive design +Added local time ## v1.0.1 Support for public paths on webpack build diff --git a/package.json b/package.json index 92205c8..b7007fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bruga-weather", - "version": "1.0.1", + "version": "1.1.0", "description": "A React.js personal project made for learning purposes", "main": "webpack.config.js", "scripts": { @@ -22,19 +22,19 @@ "@fortawesome/fontawesome-free": "^5.6.3" }, "devDependencies": { - "@babel/core": "^7.2.0", - "@babel/plugin-proposal-class-properties": "^7.2.1", - "@babel/polyfill": "7.0.0", - "@babel/preset-env": "^7.2.0", + "@babel/core": "^7.2.2", + "@babel/plugin-proposal-class-properties": "^7.2.3", + "@babel/polyfill": "^7.2.5", + "@babel/preset-env": "^7.2.3", "@babel/preset-react": "^7.0.0", - "babel-loader": "^8.0.4", + "babel-loader": "^8.0.5", "babel-preset-minify": "0.5.0", "less": "^3.9.0", "style-loader": "^0.23.1", "css-loader": "^2.1.0", "css-hot-loader": "^1.4.3", "less-loader": "^4.1.0", - "file-loader": "^2.0.0", + "file-loader": "^3.0.1", "html-loader": "^0.5.5", "mini-css-extract-plugin": "^0.5.0", "html-webpack-plugin": "^3.2.0", diff --git a/src/api/time/time-update.js b/src/api/time/time-update.js new file mode 100644 index 0000000..4fbdbae --- /dev/null +++ b/src/api/time/time-update.js @@ -0,0 +1,54 @@ +function* counter() { + let index = 0; + while (index < Number.MAX_SAFE_INTEGER) { + yield index++; + } +} + +class TimeUpdate { + constructor () { + this.counter = counter(); + this.times = []; + } + + addTime (date, callback) { + let id = this.counter.next().value; + this.times.push({ id, date, callback }); + + if (this.times.length === 1) { + this.startTimer(); + } + + return id; + } + + removeTime (id) { + let index = this.times.findIndex(time => time.id === id); + if (index !== -1) { + this.times.splice(index, 1); + } + + if (this.times.length === 0) { + this.stopTimer(); + } + } + + timer () { + const now = Date.now(); + + this.times.forEach(time => { + time.date.timeUpdate(now); + time.callback(time.date); + }) + } + + startTimer () { + this.interval = setInterval(this.timer.bind(this), 5000); + } + + stopTimer () { + clearInterval(this.interval); + } +} + +export default new TimeUpdate(); diff --git a/src/api/time/timezone-api.js b/src/api/time/timezone-api.js new file mode 100644 index 0000000..2f84da0 --- /dev/null +++ b/src/api/time/timezone-api.js @@ -0,0 +1,19 @@ +const BASE_URL = TIMEZONE_URL || 'http://localhost/timezone'; + +/* API Calls */ +const search = async (url) => { + let response = await fetch(url, { cache: 'force-cache' }); + + let json = await response.json(); + + return json; +} + +/* Public API */ +const searchTimezone = async (lat, lon) => { + let url = `${BASE_URL}?lat=${lat}&lon=${lon}`; + + return await search(url); +} + +export default { searchTimezone }; diff --git a/src/api/time/timezone-date.js b/src/api/time/timezone-date.js new file mode 100644 index 0000000..11c8f03 --- /dev/null +++ b/src/api/time/timezone-date.js @@ -0,0 +1,35 @@ +const twoDigits = (number) => number < 10 ? '0' + number : number; + +class TimezoneDate extends Date { + constructor (timezone) { + super(); + + this.diffTimezoneOffset = this.getTimezoneOffset() - timezone.offset; + this.addMinutes(this.diffTimezoneOffset); + + this.timezone = timezone.timezone; + this.timezoneOffset = timezone.offset; + } + + timeUpdate (timestamp) { + this.setTime(timestamp + this.diffTimezoneOffset * 60000); // 1000 * 60 + } + + addMinutes(minutes) { + if (minutes !== 0) { + this.setTime(this.getTime() + minutes * 60000); // 1000 * 60 + } + } + + simpleTime() { + let hours = twoDigits(this.getHours()); + let minutes = twoDigits(this.getMinutes()); + return `${hours}:${minutes}`; + } + + getTimezoneOffset () { + return this.timezoneOffset || super.getTimezoneOffset(); + } +} + +export default TimezoneDate; diff --git a/src/api/weather/adapt.js b/src/api/weather/adapt.js index c36fb7c..9a6c9b9 100644 --- a/src/api/weather/adapt.js +++ b/src/api/weather/adapt.js @@ -31,7 +31,8 @@ const transformSimple = (weather) => { sunset: new Date(weather.sys.sunset * 1000), wind_speed: roundTo2(weather.wind.speed * (3600 / 1000)), //'km/h' time: new Date(weather.dt * 1000), - daytime: weather.dt > weather.sys.sunrise && weather.dt < weather.sys.sunset + daytime: weather.dt > weather.sys.sunrise && weather.dt < weather.sys.sunset, + location: weather.coord } } @@ -48,7 +49,7 @@ const transformCity = (city) => { id: city.id, name: city.name, country: city.sys.country, - flag: flag(city.sys.country), + flag: flag(city.sys.country, 'shiny', 32), temp: kelvinToCelsius(city.main.temp), main: city.weather[0].main, descr: city.weather[0].description diff --git a/src/api/weather/openweather.js b/src/api/weather/openweather.js index 9d42ee7..fde542b 100644 --- a/src/api/weather/openweather.js +++ b/src/api/weather/openweather.js @@ -41,12 +41,7 @@ const searchCityById = async (id) => { const searchCitiesByIds = async (ids) => { let url = buildApiUrl('group', { id: ids.join(',') }); - return await search(url, true); - /*let response = await fetch(url, { cache: 'default' }); - - let json = await response.json(); - - console.log(json);*/ + return await search(url); } /* Exports */ diff --git a/src/ui/components/weather.js b/src/ui/components/weather.js index 67c011c..77fa36e 100644 --- a/src/ui/components/weather.js +++ b/src/ui/components/weather.js @@ -1,5 +1,6 @@ import React from 'react'; import { connect } from "react-redux"; +import Time from '@/ui/containers/time.js'; import Icon from './icon.js'; import CancelButton from '@/ui/components/cancel-button.js'; import Actions from '@/store/actions/index.js'; @@ -23,6 +24,7 @@ const Weather = (props) => {
{ weather.city } ()
{ weather.main }
+
diff --git a/src/ui/components/weather.less b/src/ui/components/weather.less index d699a51..00ac1f7 100644 --- a/src/ui/components/weather.less +++ b/src/ui/components/weather.less @@ -71,11 +71,20 @@ } .desc { font-size: 14px; + display: inline-block; color: @color-text-light; @media @tablet-above { font-size: 16px; } + + & + .desc { + margin-left: 5px; + + &::before { + content: ' - '; + } + } } .weather-icon-wrapper { diff --git a/src/ui/containers/time.js b/src/ui/containers/time.js new file mode 100644 index 0000000..337a615 --- /dev/null +++ b/src/ui/containers/time.js @@ -0,0 +1,47 @@ +import React, { PureComponent } from 'react'; +import TimezoneAPI from '@/api/time/timezone-api.js'; +import TimezoneDate from '@/api/time/timezone-date.js'; +import TimeUpdate from '@/api/time/time-update.js'; + +class Time extends PureComponent { + constructor (props) { + super(props); + this.searchTimezone(); + } + + state = { + hasTimezone: false + } + + searchTimezone = () => { + let { lat, lon } = this.props.location; + if (!lat || !lon) return; + + TimezoneAPI.searchTimezone(lat, lon).then(timezone => { + const date = new TimezoneDate(timezone); + + this.setState({ + hasTimezone: true, + date: date.simpleTime() + }); + + this.timerId = TimeUpdate.addTime(date, (updatedDate) => { + this.setState({ + date: updatedDate.simpleTime() + }); + }); + }); + } + + componentWillUnmount() { + TimeUpdate.removeTime(this.timerId); + } + + render () { + if (!this.state.hasTimezone) return (null); + + return (
{ this.state.date }
) + } +} + +export default Time; diff --git a/webpack.config.js b/webpack.config.js index e4fb7ba..e4e99f4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,5 @@ const path = require('path'); +const webpack = require('webpack'); const HtmlWebPackPlugin = require('html-webpack-plugin'); const RemoteFilePlugin = require('remote-file-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); @@ -74,6 +75,9 @@ module.exports = (env, args) => { ] }, plugins: [ + new webpack.DefinePlugin({ + 'TIMEZONE_URL': JSON.stringify('https://brugarolas.openode.io/timezone') + }), new RemoteFilePlugin([ { url: 'https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700,800|Roboto:100,300,400,500,700,900',