diff --git a/package.json b/package.json index 5a3d624d..f0554a67 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "react-dom": "17.0.2", "react-modal": "3.12.1", "react-sortable-hoc": "2.0.0", - "react-toastify": "7.0.3" + "react-toastify": "7.0.3", + "weather-icons-react": "^1.2.0" }, "devDependencies": { "@babel/core": "^7.13.14", diff --git a/src/components/modals/main/settings/sections/QuickLinks.jsx b/src/components/modals/main/settings/sections/QuickLinks.jsx new file mode 100644 index 00000000..1de9abc9 --- /dev/null +++ b/src/components/modals/main/settings/sections/QuickLinks.jsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import Switch from '../Switch'; +import Checkbox from '../Checkbox'; + +export default function QuickLinks() { + const language = window.language.modals.main.settings.sections.quicklinks; + + return ( + <> +

{language.title}

+ + + + + + ); +} diff --git a/src/components/modals/main/settings/sections/Time.jsx b/src/components/modals/main/settings/sections/Time.jsx index 433b6a9d..4c3fadc8 100644 --- a/src/components/modals/main/settings/sections/Time.jsx +++ b/src/components/modals/main/settings/sections/Time.jsx @@ -88,6 +88,7 @@ export default class TimeSettings extends React.PureComponent { switch (this.state.dateType) { case 'short': dateSettings = shortSettings; break; case 'long': dateSettings = longSettings; break; + default: break; } return ( diff --git a/src/components/modals/main/settings/sections/Weather.jsx b/src/components/modals/main/settings/sections/Weather.jsx new file mode 100644 index 00000000..7be719ca --- /dev/null +++ b/src/components/modals/main/settings/sections/Weather.jsx @@ -0,0 +1,55 @@ +import React from 'react'; + +import Switch from '../Switch'; +import Radio from '../Radio'; + +export default class TimeSettings extends React.PureComponent { + constructor() { + super(); + this.state = { + location: localStorage.getItem('location') || 'London' + }; + this.language = window.language.modals.main.settings; + } + + getLocation() { + if (window.navigator.geolocation) { + window.navigator.geolocation.getCurrentPosition(console.log, console.log); + } + } + + componentDidUpdate() { + localStorage.setItem('location', this.state.location); + } + + render() { + const language = window.language.modals.main.settings.sections.weather; + + const tempFormat = [ + { + 'name': 'Celsius', + 'value': 'celsius' + }, + { + 'name': 'Fahrenheit', + 'value': 'fahrenheit' + }, + { + 'name': 'Kelvin', + 'value': 'kelvin' + } + ]; + + return ( + <> +

{language.title}

+ + +
    +

    Location this.getLocation()}>Auto

    + this.setState({ location: e.target.value })}> +
+ + ); + } +} diff --git a/src/components/modals/main/tabs/Settings.jsx b/src/components/modals/main/tabs/Settings.jsx index 44be6f7f..13f0fca4 100644 --- a/src/components/modals/main/tabs/Settings.jsx +++ b/src/components/modals/main/tabs/Settings.jsx @@ -12,6 +12,8 @@ import Advanced from '../settings/sections/Advanced'; import Changelog from '../settings/sections/Changelog'; import Order from '../settings/sections/Order'; import Experimental from '../settings/sections/Experimental'; +import QuickLinks from '../settings/sections/QuickLinks'; +import Weather from '../settings/sections/Weather'; import Tabs from './backend/Tabs'; @@ -25,6 +27,8 @@ export default function Settings() {
+
+
diff --git a/src/components/modals/main/tabs/backend/Tab.jsx b/src/components/modals/main/tabs/backend/Tab.jsx index 05e51a7a..d6713063 100644 --- a/src/components/modals/main/tabs/backend/Tab.jsx +++ b/src/components/modals/main/tabs/backend/Tab.jsx @@ -7,16 +7,19 @@ import Marketplace from '@material-ui/icons/ShoppingBasket'; // Settings import Time from '@material-ui/icons/AccessAlarm'; -import Greeting from '@material-ui/icons/EmojiPeople'; -import Quote from '@material-ui/icons/FormatQuote'; -import Background from '@material-ui/icons/Photo'; +import Greeting from '@material-ui/icons/EmojiPeopleOutlined'; +import Quote from '@material-ui/icons/FormatQuoteOutlined'; +import Background from '@material-ui/icons/PhotoOutlined'; import Search from '@material-ui/icons/Search'; -import Appearance from '@material-ui/icons/FormatPaint'; +import Appearance from '@material-ui/icons/FormatPaintOutlined'; import Language from '@material-ui/icons/Translate'; -import Changelog from '@material-ui/icons/NewReleasesRounded'; -import About from '@material-ui/icons/Info'; -import Experimental from '@material-ui/icons/BugReport'; +import Changelog from '@material-ui/icons/NewReleasesOutlined'; +import About from '@material-ui/icons/InfoOutlined'; +import Experimental from '@material-ui/icons/BugReportOutlined'; import Order from '@material-ui/icons/List'; +import Weather from '@material-ui/icons/CloudOutlined'; +import Advanced from '@material-ui/icons/SettingsOutlined'; +import QuickLinks from '@material-ui/icons/Link'; // Store import Added from '@material-ui/icons/AddCircle'; @@ -50,10 +53,12 @@ export default function Tab(props) { case language.quote.title: icon = ; break; case language.background.title: icon = ; break; case language.search.title: icon = ; break; + case language.weather.title: icon = ; break; + case language.quicklinks.title: icon = ; break; case language.appearance.title: icon = ; break; case language.order.title: icon = ; break; case language.language.title: icon = ; divider = true; break; - case language.advanced.title: icon = ; break; + case language.advanced.title: icon = ; break; case language.experimental.title: icon = ; divider = true; break; case language.changelog: icon = ; break; case language.about.title: icon = ; break; diff --git a/src/components/widgets/Widgets.jsx b/src/components/widgets/Widgets.jsx index dcf99504..0cfbf869 100644 --- a/src/components/widgets/Widgets.jsx +++ b/src/components/widgets/Widgets.jsx @@ -4,10 +4,9 @@ import Clock from './time/Clock'; import Greeting from './greeting/Greeting'; import Quote from './quote/Quote'; import Search from './search/Search'; -import Maximise from './background/Maximise'; -import Favourite from './background/Favourite'; import Date from './time/Date'; import QuickLinks from './quicklinks/QuickLinks'; +import Weather from './weather/Weather'; export default class Widgets extends React.PureComponent { constructor() { @@ -65,6 +64,7 @@ export default class Widgets extends React.PureComponent { e.preventDefault(); e.stopPropagation(); break; + default: break; } }; } @@ -86,6 +86,7 @@ export default class Widgets extends React.PureComponent {
{this.enabled('searchBar') ? : null} {elements} + {this.enabled('weatherEnabled') ? : null}
); } diff --git a/src/components/widgets/background/Background.jsx b/src/components/widgets/background/Background.jsx index b25181a2..c4aa4f85 100644 --- a/src/components/widgets/background/Background.jsx +++ b/src/components/widgets/background/Background.jsx @@ -24,11 +24,13 @@ export default class Background extends React.PureComponent { gradientStyleBuilder(gradientSettings) { const { type, angle, gradient } = gradientSettings; let style = `background: ${gradient[0].colour};`; + if (gradient.length > 1) { // Note: Append the gradient for additional browser support. const stepStyles = gradient.map(g => ` ${g.colour} ${g.stop}%`).join(); style += ` background: ${type}-gradient(${(type === 'linear' ? (`${angle}deg,`) : '')}${stepStyles})`; } + this.setState({ style: style }); @@ -132,7 +134,7 @@ export default class Background extends React.PureComponent { case 'colour': // background colour - const customBackgroundColour = localStorage.getItem('customBackgroundColour'); + const customBackgroundColour = localStorage.getItem('customBackgroundColour') || {"angle":"180","gradient":[{"colour":"#ffb032","stop":0}],"type":"linear"}; let gradientSettings = ''; try { gradientSettings = JSON.parse(customBackgroundColour); diff --git a/src/components/widgets/background/Maximise.jsx b/src/components/widgets/background/Maximise.jsx index 8f4e58eb..a7966a66 100644 --- a/src/components/widgets/background/Maximise.jsx +++ b/src/components/widgets/background/Maximise.jsx @@ -21,7 +21,7 @@ export default class View extends React.PureComponent { viewStuff() { // elements to hide - const elements = ['.searchBar', '.clock', '.greeting', '.quotediv', 'time']; + const elements = ['.searchBar', '.clock', '.greeting', '.quotediv', 'time', '.quicklinks-container', '.weather']; elements.forEach((element) => { try { diff --git a/src/components/widgets/background/scss/_photoinformation.scss b/src/components/widgets/background/scss/_photoinformation.scss index 8765f320..a1771e02 100644 --- a/src/components/widgets/background/scss/_photoinformation.scss +++ b/src/components/widgets/background/scss/_photoinformation.scss @@ -29,7 +29,7 @@ box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); color: var(--modal-text); position: fixed; - bottom: 3.25rem; + bottom: 2.9rem; left: 0.7em; padding: 1rem; border-radius: 24px 24px 24px 0; diff --git a/src/components/widgets/quicklinks/QuickLinks.jsx b/src/components/widgets/quicklinks/QuickLinks.jsx index 2362f171..39039930 100644 --- a/src/components/widgets/quicklinks/QuickLinks.jsx +++ b/src/components/widgets/quicklinks/QuickLinks.jsx @@ -3,7 +3,7 @@ import React from 'react'; import Tooltip from '@material-ui/core/Tooltip'; import TextareaAutosize from '@material-ui/core/TextareaAutosize'; -import './scss/index.scss'; +import './quicklinks.scss'; export default class QuickLinks extends React.PureComponent { constructor() { @@ -11,7 +11,8 @@ export default class QuickLinks extends React.PureComponent { this.state = { items: JSON.parse(localStorage.getItem('quicklinks')), name: '', - url: '' + url: '', + showAddLink: 'hidden' }; } @@ -21,6 +22,18 @@ export default class QuickLinks extends React.PureComponent { }); } + deleteLink(name, event) { + event.preventDefault(); + + let data = JSON.parse(localStorage.getItem('quicklinks')); + data = data.filter((i) => i.name !== name); + + localStorage.setItem('quicklinks', JSON.stringify(data)); + this.setState({ + items: data + }); + } + addLink = () => { let data = JSON.parse(localStorage.getItem('quicklinks')); data.push({ @@ -31,33 +44,51 @@ export default class QuickLinks extends React.PureComponent { localStorage.setItem('quicklinks', JSON.stringify(data)); this.setState({ - items: JSON.parse(localStorage.getItem('quicklinks')), + items: data, name: '', url: '' }); + + this.toggleAdd(); + } + + toggleAdd = () => { + if (this.state.showAddLink === 'hidden') { + this.setState({ + showAddLink: 'visible' + }); + } else { + this.setState({ + showAddLink: 'hidden' + }); + } } render() { + let target, rel = null; + if (localStorage.getItem('quicklinksnewtab') === 'true') { + target ='_blank'; + rel ='noopener noreferrer'; + } + return (
{this.state.items.map((item) => ( - {item.name}/ + this.deleteLink(item.name, e)} href={item.url} target={target} rel={rel}>{item.name}/ ))} -
- - -
-

New Link

- this.updateLink('name', e.target.value)} /> -
- this.updateLink('url', e.target.value)} /> -
- -
-
-
+ + +
+

New Link

+ this.updateLink('name', e.target.value)} /> +
+ this.updateLink('url', e.target.value)} /> +
+ +
+
); } diff --git a/src/components/widgets/quicklinks/scss/index.scss b/src/components/widgets/quicklinks/quicklinks.scss similarity index 81% rename from src/components/widgets/quicklinks/scss/index.scss rename to src/components/widgets/quicklinks/quicklinks.scss index fae72413..41d5462e 100644 --- a/src/components/widgets/quicklinks/scss/index.scss +++ b/src/components/widgets/quicklinks/quicklinks.scss @@ -9,13 +9,12 @@ .quicklinkscontainer { padding: 15px; - visibility: hidden; background-color: var(--background); color: var(--modal-text); text-align: center; border-radius: 12px; position: absolute; - top: 80%; + top: 56%; margin-left: -140px; margin-top: 5px; @@ -29,10 +28,6 @@ } } -.quicklinks:hover .quicklinkscontainer { - visibility: visible; -} - textarea { border: none; width: 200px; @@ -62,27 +57,31 @@ textarea { color: white; } -.quicklinks > button { +.quicklinks { border: none; color: #fff; font-size: 22px; background: none; + cursor: pointer; } -.quicklinks-container > a, .quicklinks-container > .quicklinks > button { +.quicklinks-container>a, +.quicklinks-container>.quicklinks>button { display: inline; } -.quicklinks-container { +.quicklinks-container { img { height: 32px; width: auto; transition: transform .2s; + &:hover { transform: scale(1.1); } } + a { margin: 5px; } -} \ No newline at end of file +} diff --git a/src/components/widgets/weather/Weather.jsx b/src/components/widgets/weather/Weather.jsx new file mode 100644 index 00000000..5ed570b1 --- /dev/null +++ b/src/components/widgets/weather/Weather.jsx @@ -0,0 +1,52 @@ +import React from 'react'; +import WeatherIcon from './WeatherIcon'; + +import './weather.scss'; + +export default class Weather extends React.PureComponent { + constructor() { + super(); + this.state = { + icon: '', + weather: { + title: '', + temp: '', + temp_min: '', + temp_max: '', + humidity: '' + } + }; + } + + async getWeather() { + const data = await (await fetch (window.constants.WEATHER_URL + '?city=London')).json(); + this.setState({ + icon: data.weather[0].icon, + weather: { + title: data.weather[0].main, + temp: Math.round(data.main.temp - 273.15), + temp_min: Math.round(data.main.temp_min - 273.15), + temp_max: Math.round(data.main.temp_max - 273.15), + humidity: data.main.humidity + } + }); + } + + componentDidMount() { + this.getWeather(); + } + + render() { + return ( +
+ + {this.state.weather.temp}°C +
+ {this.state.weather.temp_min}°C, {this.state.weather.temp_max}°C +
+ London + {/*{this.state.weather.title}*/} +
+ ); + } +} \ No newline at end of file diff --git a/src/components/widgets/weather/WeatherIcon.jsx b/src/components/widgets/weather/WeatherIcon.jsx new file mode 100644 index 00000000..fe9bb837 --- /dev/null +++ b/src/components/widgets/weather/WeatherIcon.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { WiDaySunny, WiNightClear, WiDayCloudy, WiNightCloudy, WiCloud, WiCloudy, WiDayShowers, WiNightShowers, WiRain, WiThunderstorm, WiSnow, WiFog } from 'weather-icons-react'; + +import './weather.scss'; + +export default function WeatherIcon(props) { + let icon; + + // props.name is the openweathermap icon name, see https://openweathermap.org/weather-conditions + switch (props.name) { + case '01d': icon = ; break; + case '01n': icon = ; break; + case '02d': icon = ; break; + case '02n': icon = ; break; + case '03d': case '03n': icon = ; break; + case '04d': case '04n': icon = ; break; + case '09d': icon = ; break; + case '09n': icon = ; break; + case '10d': case '10n': icon = ; break; + case '11d': case '11n': icon = ; break; + case '13d': case '13n': icon = ; break; + case '50d': case '50n': icon = ; break; + default: icon = null; break; + } + + return icon; +} \ No newline at end of file diff --git a/src/components/widgets/weather/weather.scss b/src/components/widgets/weather/weather.scss new file mode 100644 index 00000000..59c3693a --- /dev/null +++ b/src/components/widgets/weather/weather.scss @@ -0,0 +1,26 @@ +.weather { + position: absolute; + bottom: 1rem; + right: 1rem; + + span { + text-shadow: 0 0 10px rgb(0 0 0 / 50%); + } + + svg { + filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.3)); + + font-size: 0.8em; + + } + + .loc { + font-size: 0.7em; + margin: 0; + padding: 0; + } + + .minmax { + font-size: 0.5em; + } +} diff --git a/src/index.js b/src/index.js index 4b01c920..e52e850c 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; +import * as Constants from './modules/constants'; import './scss/index.scss'; // the toast css is based on default so we need to import it @@ -23,7 +24,6 @@ if (window.languagecode !== 'en_GB' || window.languagecode !== 'en_US') { } // window.constants -import * as Constants from './modules/constants'; window.constants = Constants; ReactDOM.render( diff --git a/src/modules/constants.js b/src/modules/constants.js index 463c1acd..e7632142 100644 --- a/src/modules/constants.js +++ b/src/modules/constants.js @@ -1,6 +1,7 @@ export const API_URL = 'https://api.muetab.com'; export const UNSPLASH_URL = 'https://unsplash.muetab.com'; export const MARKETPLACE_URL = 'https://marketplace.muetab.com'; +export const WEATHER_URL = 'https://mueweather.ohlookitsderpy.workers.dev'; export const WEBSITE_URL = 'https://muetab.com'; export const SPONSORS_URL = 'https://sponsors.muetab.com'; export const GITHUB_URL = 'https://api.github.com'; diff --git a/src/modules/default_settings.json b/src/modules/default_settings.json index 2bf43aa2..3c4dda76 100644 --- a/src/modules/default_settings.json +++ b/src/modules/default_settings.json @@ -125,7 +125,7 @@ }, { "name": "order", - "value": "[\"greeting\", \"time\", \"quote\", \"date\"]" + "value": "[\"greeting\", \"time\", \"quicklinks\", \"quote\", \"date\"]" }, { "name": "theme", @@ -146,5 +146,17 @@ { "name": "debugtimeout", "value": 0 + }, + { + "name": "quicklinks", + "value": "[]" + }, + { + "name": "quicklinksenabled", + "value": false + }, + { + "name": "weatherEnabled", + "value": false } ] diff --git a/src/modules/helpers/experimental.js b/src/modules/helpers/experimental.js index 2d26bdf4..ab647dc3 100644 --- a/src/modules/helpers/experimental.js +++ b/src/modules/helpers/experimental.js @@ -20,6 +20,7 @@ export default function ExperimentalInit() { debugger; } break; + default: break; } }; } diff --git a/src/translations/en_GB.json b/src/translations/en_GB.json index a2ce5711..a85887a4 100644 --- a/src/translations/en_GB.json +++ b/src/translations/en_GB.json @@ -142,6 +142,12 @@ "custom": "Custom Search URL", "voice_search": "Voice Search" }, + "weather": { + "title": "Weather" + }, + "quicklinks": { + "title": "Quick Links" + }, "appearance": { "title": "Appearance", "theme": {