mirror of
https://github.com/mue/mue.git
synced 2026-06-08 14:10:42 +02:00
feat: individual widget zoom, hot reload for font and custom js/css etc
This commit is contained in:
@@ -47,10 +47,8 @@ export default class Autocomplete extends React.Component {
|
||||
autocomplete = (
|
||||
<ul className='suggestions'>
|
||||
{this.state.filtered.map((suggestion) => {
|
||||
let className;
|
||||
|
||||
return (
|
||||
<li className={className} key={suggestion} onClick={this.onClick}>
|
||||
<li key={suggestion} onClick={this.onClick}>
|
||||
{suggestion}
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -9,8 +9,8 @@ export default class Slider extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: localStorage.getItem(this.props.name) || '',
|
||||
numberWidth: ((localStorage.getItem(this.props.name).length + 1) * ((this.props.toast === true) ? 7.75 : 7))
|
||||
value: localStorage.getItem(this.props.name) || this.props.default,
|
||||
numberWidth: localStorage.getItem(this.props.name) ? ((localStorage.getItem(this.props.name).length + 1) * ((this.props.toast === true) ? 7.75 : 7)) : 32
|
||||
};
|
||||
this.language = window.language.modals.main.settings;
|
||||
this.widthCalculation = (this.props.toast === true) ? 7.75 : 7;
|
||||
|
||||
@@ -46,8 +46,8 @@ export default class AdvancedSettings extends React.PureComponent {
|
||||
|
||||
<h3>{advanced.customisation}</h3>
|
||||
<Text title={advanced.tab_name} name='tabName' default={window.language.tabname} category='other'/>
|
||||
<Text title={advanced.custom_js} name='customjs' textarea={true} element='.other'/>
|
||||
<Text title={advanced.custom_css} name='customcss' textarea={true} element='.other'/>
|
||||
<Text title={advanced.custom_js} name='customjs' textarea={true} category='other'/>
|
||||
<Text title={advanced.custom_css} name='customcss' textarea={true} category='other'/>
|
||||
|
||||
<h3>{this.language.sections.experimental.title}</h3>
|
||||
<p style={{ 'maxWidth': '75%'}}>{advanced.experimental_warning}</p>
|
||||
|
||||
@@ -36,10 +36,10 @@ export default function AppearanceSettings() {
|
||||
<Checkbox name='refresh' text={appearance.navbar.refresh} element='.other' />
|
||||
|
||||
<h3>{appearance.font.title}</h3>
|
||||
<Text title={appearance.font.custom} name='font' upperCaseFirst={true} element='.other' />
|
||||
<Text title={appearance.font.custom} name='font' upperCaseFirst={true} category='other' />
|
||||
<br/>
|
||||
<Checkbox name='fontGoogle' text={appearance.font.google} element='.other' />
|
||||
<Dropdown label={appearance.font.weight.title} name='fontweight' element='.other'>
|
||||
<Checkbox name='fontGoogle' text={appearance.font.google} category='other' />
|
||||
<Dropdown label={appearance.font.weight.title} name='fontweight' category='other'>
|
||||
{/* names are taken from https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight */}
|
||||
<option value='100'>{appearance.font.weight.thin}</option>
|
||||
<option value='200'>{appearance.font.weight.extra_light}</option>
|
||||
@@ -51,7 +51,7 @@ export default function AppearanceSettings() {
|
||||
<option value='800'>{appearance.font.weight.extra_bold}</option>
|
||||
</Dropdown>
|
||||
<br/><br/>
|
||||
<Dropdown label={appearance.font.style.title} name='fontstyle' element='.other'>
|
||||
<Dropdown label={appearance.font.style.title} name='fontstyle' category='other'>
|
||||
<option value='normal'>{appearance.font.style.normal}</option>
|
||||
<option value='italic'>{appearance.font.style.italic}</option>
|
||||
<option value='oblique'>{appearance.font.style.oblique}</option>
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Switch from '../Switch';
|
||||
import Text from '../Text';
|
||||
import Slider from '../Slider';
|
||||
|
||||
import DayPickerInput from 'react-day-picker/DayPickerInput';
|
||||
import 'react-day-picker/lib/style.css';
|
||||
@@ -34,6 +35,7 @@ export default class GreetingSettings extends React.PureComponent {
|
||||
<Checkbox name='events' text={greeting.events} category='greeting' element='.greeting'/>
|
||||
<Checkbox name='defaultGreetingMessage' text={greeting.default} category='greeting' element='.greeting'/>
|
||||
<Text title={greeting.name} name='greetingName' category='greeting' element='.greeting'/>
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomGreeting' min='10' max='400' default='100' display='%' category='greeting' element='.greeting' />
|
||||
|
||||
<h3>{greeting.birthday}</h3>
|
||||
<Switch name='birthdayenabled' text={this.language.enabled} category='greeting' element='.greeting'/>
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Text from '../Text';
|
||||
import Switch from '../Switch';
|
||||
import Slider from '../Slider';
|
||||
|
||||
export default function QuoteSettings() {
|
||||
const { quote } = window.language.modals.main.settings.sections;
|
||||
@@ -14,6 +15,7 @@ export default function QuoteSettings() {
|
||||
<Checkbox name='authorLink' text={quote.author_link} element='.other' />
|
||||
<Text title={quote.custom} name='customQuote' element='.other' />
|
||||
<Text title={quote.custom_author} name='customQuoteAuthor' element='.other'/>
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomQuote' min='10' max='400' default='100' display='%' category='quote' element='.quotediv' />
|
||||
|
||||
<h3>{quote.buttons.title}</h3>
|
||||
<Checkbox name='copyButton' text={quote.buttons.copy} category='quote'/>
|
||||
|
||||
@@ -78,10 +78,6 @@ export default class SearchSettings extends React.PureComponent {
|
||||
<h2>{search.title}</h2>
|
||||
<Switch name='searchBar' text={language.enabled} category='widgets' />
|
||||
{isChrome ? <Checkbox name='voiceSearch' text={search.voice_search} /> : null}
|
||||
<Checkbox name='autocomplete' text={search.autocomplete} category='search'/>
|
||||
<Radio title={search.autocomplete_provider} options={autocompleteProviders} name='autocompleteProvider' category='search'/>
|
||||
<br/>
|
||||
|
||||
<Dropdown label={search.search_engine} name='searchEngine' onChange={(value) => this.setSearchEngine(value)}>
|
||||
{searchEngines.map((engine) => (
|
||||
<option key={engine.name} value={engine.settingsName}>{engine.name}</option>
|
||||
@@ -89,11 +85,14 @@ export default class SearchSettings extends React.PureComponent {
|
||||
<option value='custom'>{search.custom.split(' ')[0]}</option>
|
||||
</Dropdown>
|
||||
|
||||
<br/><br/>
|
||||
<ul style={{ display: this.state.customDisplay }}>
|
||||
<br/>
|
||||
<p style={{ 'marginTop': '0px' }}>{search.custom} <span className='modalLink' onClick={() => this.resetSearch()}>{language.buttons.reset}</span></p>
|
||||
<input type='text' value={this.state.customValue} onInput={(e) => this.setState({ customValue: e.target.value })}></input>
|
||||
</ul>
|
||||
<br/>
|
||||
<Checkbox name='autocomplete' text={search.autocomplete} category='search' element='.other'/>
|
||||
<Radio title={search.autocomplete_provider} options={autocompleteProviders} name='autocompleteProvider' category='search'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import Checkbox from '../Checkbox';
|
||||
import Dropdown from '../Dropdown';
|
||||
import Switch from '../Switch';
|
||||
import Radio from '../Radio';
|
||||
import Slider from '../Slider';
|
||||
|
||||
export default class TimeSettings extends React.PureComponent {
|
||||
constructor() {
|
||||
@@ -101,6 +102,7 @@ export default class TimeSettings extends React.PureComponent {
|
||||
<option value='percentageComplete'>{time.percentage_complete}</option>
|
||||
</Dropdown>
|
||||
{timeSettings}
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomClock' min='10' max='400' default='100' display='%' category='clock' element='.clock-container' />
|
||||
|
||||
<h3>{time.date.title}</h3>
|
||||
<Switch name='date' text={this.language.enabled} category='date' element='.date'/>
|
||||
@@ -112,6 +114,7 @@ export default class TimeSettings extends React.PureComponent {
|
||||
<Checkbox name='datezero' text={time.digital.zero} category='date' element='.date' />
|
||||
<Checkbox name='weeknumber' text={time.date.week_number} category='date' element='.date'/>
|
||||
{dateSettings}
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomDate' min='10' max='400' default='100' display='%' category='date' element='.date' />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export default class TimeSettings extends React.PureComponent {
|
||||
<Radio name='tempformat' title={language.temp_format.title} options={tempFormat} category='weather'/>
|
||||
<h3>{language.extra_info.title}</h3>
|
||||
<Checkbox name='showlocation' text={language.extra_info.show_location} category='weather'/>
|
||||
<Checkbox name='showtext' text={language.extra_info.show_text} category='weather'/>
|
||||
<Checkbox name='weatherdescription' text={language.extra_info.show_description} category='weather'/>
|
||||
<Checkbox name='cloudiness' text={language.extra_info.cloudiness} category='weather'/>
|
||||
<Checkbox name='humidity' text={language.extra_info.humidity} category='weather'/>
|
||||
<Checkbox name='visibility' text={language.extra_info.visibility} category='weather'/>
|
||||
|
||||
@@ -51,6 +51,7 @@ export default class Widgets extends React.PureComponent {
|
||||
elements.push(<React.Fragment key={element}>{this.widgets[element]}</React.Fragment>);
|
||||
});
|
||||
} else {
|
||||
// prevent error
|
||||
elements = ['greeting', 'time', 'quicklinks', 'quote', 'date'];
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ export default class Background extends React.PureComponent {
|
||||
const url = (localStorage.getItem('ddgProxy') === 'true') ? window.constants.DDG_PROXY + this.state.url : this.state.url;
|
||||
const photoInformation = document.querySelector('.photoInformation');
|
||||
|
||||
// just set the background
|
||||
if (localStorage.getItem('bgtransition') === 'false') {
|
||||
if (photoInformation) {
|
||||
photoInformation.style.display = 'block';
|
||||
@@ -79,14 +80,16 @@ export default class Background extends React.PureComponent {
|
||||
return backgroundImage.style.background = `url(${url})`;
|
||||
}
|
||||
|
||||
// firstly we set the background as hidden and make sure there is no background set currently
|
||||
backgroundImage.classList.add('backgroundPreload');
|
||||
backgroundImage.style.background = null;
|
||||
|
||||
// same with photo information if not using custom background
|
||||
if (photoInformation) {
|
||||
photoInformation.classList.add('backgroundPreload');
|
||||
}
|
||||
|
||||
// preloader for background transition
|
||||
// preloader for background transition, required so it loads in nice
|
||||
const preloader = document.createElement('img');
|
||||
preloader.src = url;
|
||||
|
||||
@@ -95,7 +98,9 @@ export default class Background extends React.PureComponent {
|
||||
backgroundImage.classList.remove('backgroundPreload');
|
||||
backgroundImage.classList.add('fade-in');
|
||||
|
||||
// this doesn't make it fetch again which is nice
|
||||
backgroundImage.style.background = `url(${url})`;
|
||||
// remove the preloader element we created earlier
|
||||
preloader.remove();
|
||||
|
||||
if (photoInformation) {
|
||||
@@ -104,6 +109,7 @@ export default class Background extends React.PureComponent {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// custom colour
|
||||
backgroundImage.setAttribute('style', this.state.style);
|
||||
}
|
||||
}
|
||||
@@ -243,6 +249,7 @@ export default class Background extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
const element = document.getElementById('backgroundImage');
|
||||
|
||||
// this resets it so the fade in and getting background all works properly
|
||||
const refresh = () => {
|
||||
element.classList.remove('fade-in');
|
||||
this.setState({
|
||||
@@ -260,10 +267,12 @@ export default class Background extends React.PureComponent {
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'background') {
|
||||
if (localStorage.getItem('background') === 'false') {
|
||||
// user is using custom colour or image
|
||||
if (this.state.photoInfo.hidden === false) {
|
||||
document.querySelector('.photoInformation').style.display = 'none';
|
||||
}
|
||||
|
||||
// video backgrounds
|
||||
if (this.state.video === true) {
|
||||
return document.getElementById('backgroundVideo').style.display = 'none';
|
||||
} else {
|
||||
@@ -271,6 +280,7 @@ export default class Background extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
// video backgrounds
|
||||
if (this.state.video === true) {
|
||||
document.getElementById('backgroundVideo').style.display = 'block';
|
||||
} else {
|
||||
@@ -284,11 +294,13 @@ export default class Background extends React.PureComponent {
|
||||
const backgroundType = localStorage.getItem('backgroundType');
|
||||
|
||||
if (this.state.photoInfo.offline !== true) {
|
||||
// basically check to make sure something has changed before we try getting another background
|
||||
if (backgroundType !== this.state.type || localStorage.getItem('backgroundAPI') !== this.state.currentAPI || (this.state.type === 'custom' && localStorage.getItem('customBackground') !== this.state.url)) {
|
||||
return refresh();
|
||||
}
|
||||
}
|
||||
|
||||
// background effects so we don't get another image again
|
||||
if (this.state.video === true) {
|
||||
document.getElementById('backgroundVideo').style.webkitFilter = `blur(${localStorage.getItem('blur')}px) brightness(${localStorage.getItem('brightness')}%)`;
|
||||
} else {
|
||||
@@ -296,6 +308,7 @@ export default class Background extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
// uninstall photo pack reverts your background to what you had previously
|
||||
if (data === 'marketplacebackgrounduninstall') {
|
||||
refresh();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function PhotoInformation(props) {
|
||||
let credit = props.info.credit;
|
||||
let photo = language.credit;
|
||||
|
||||
// unsplash
|
||||
// unsplash credit
|
||||
if (props.info.photographerURL && props.info.photographerURL !== '' && !props.info.offline) {
|
||||
photo = <a href={props.info.photoURL} target='_blank' rel='noopener noreferrer'>{language.credit}</a>;
|
||||
credit = <><a href={props.info.photographerURL} target='_blank' rel='noopener noreferrer'>{photographer}</a> <a href='https://unsplash.com?utm_source=mue' target='_blank' rel='noopener noreferrer'>{language.unsplash}</a></>;
|
||||
|
||||
@@ -114,9 +114,14 @@ export default class Greeting extends React.PureComponent {
|
||||
this.getGreeting(0);
|
||||
|
||||
element.style.display = 'block';
|
||||
element.style.fontSize = `${1.6 * Number(localStorage.getItem('zoomGreeting') / 100)}em`;
|
||||
}
|
||||
});
|
||||
|
||||
// this comment can apply to all widget zoom features apart from the general one in the Accessibility section
|
||||
// in a nutshell: 1.6 is the current font size and we do "localstorage || 100" so we don't have to try that 4.0 -> 5.0 thing again
|
||||
document.querySelector('.greeting').style.fontSize = `${1.6 * Number((localStorage.getItem('zoomGreeting') || 100) / 100)}em`;
|
||||
|
||||
this.getGreeting(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ export default class QuickLinks extends React.PureComponent {
|
||||
}
|
||||
});
|
||||
|
||||
// allows you to add a link by pressing enter
|
||||
document.querySelector('.topbarquicklinks').onkeydown = (e) => {
|
||||
e = e || window.event;
|
||||
let code = e.which || e.keyCode;
|
||||
|
||||
@@ -172,10 +172,15 @@ export default class Quote extends React.PureComponent {
|
||||
}
|
||||
|
||||
element.style.display = 'block';
|
||||
document.querySelector('.quote').style.fontSize = `${0.8 * Number((localStorage.getItem('zoomQuote') || 100) / 100)}em`;
|
||||
document.querySelector('.quoteauthor').style.fontSize = `${0.9 * Number((localStorage.getItem('zoomQuote') || 100) / 100)}em`;
|
||||
this.init();
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector('.quote').style.fontSize = `${0.8 * Number((localStorage.getItem('zoomQuote') || 100) / 100)}em`;
|
||||
document.querySelector('.quoteauthor').style.fontSize = `${0.9 * Number((localStorage.getItem('zoomQuote') || 100) / 100)}em`;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
|
||||
@@ -100,9 +100,12 @@ export default class Clock extends React.PureComponent {
|
||||
this.startTime(0);
|
||||
|
||||
element.style.display = 'block';
|
||||
element.style.fontSize = `${4 * Number((localStorage.getItem('zoomClock') || 100) / 100)}em`;
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector('.clock-container').style.fontSize = `${4 * Number((localStorage.getItem('zoomClock') || 100) / 100)}em`;
|
||||
|
||||
this.startTime(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -108,10 +108,13 @@ export default class DateWidget extends React.PureComponent {
|
||||
}
|
||||
|
||||
element.style.display = 'block';
|
||||
element.style.fontSize = `${Number((localStorage.getItem('zoomDate') || 100) / 100)}em`;
|
||||
this.getDate();
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector('.date').style.fontSize = `${Number((localStorage.getItem('zoomDate') || 100) / 100)}em`;
|
||||
|
||||
this.getDate();
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ export default class Weather extends React.PureComponent {
|
||||
<div className='weather'>
|
||||
<WeatherIcon name={this.state.icon}/>
|
||||
<span>{this.state.weather.temp + this.state.temp_text}</span>
|
||||
<span className='loc' style={{ 'textTransform': 'capitalize' }}><br/>{this.state.weather.description}</span>
|
||||
{enabled('weatherdescription') ? <span className='loc' style={{ 'textTransform': 'capitalize' }}><br/>{this.state.weather.description}</span> : null}
|
||||
<span className='minmax'>{minmax()}</span>
|
||||
{enabled('humidity') ? <span className='loc'><br/><WiHumidity/>{this.state.weather.humidity}%</span> : null}
|
||||
{enabled('windspeed') ? <span className='loc'><br/><WiWindy/>{this.state.weather.wind_speed}<span className='minmax'> m/s</span> {enabled('windDirection') ? <WindDirectionIcon degrees={this.state.weather.wind_degrees}/> : null}</span> : null}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { WiDirectionDownLeft, WiDirectionDownRight, WiDirectionDown, WiDirection
|
||||
export default function WindDirectionIcon(props) {
|
||||
let icon;
|
||||
|
||||
// convert the number openweathermap gives us to closest direction or something
|
||||
const getDirection = (angle) => {
|
||||
const directions = ['North', 'North-West', 'West', 'South-West', 'South', 'South-East', 'East', 'North-East'];
|
||||
return directions[Math.round(((angle %= 360) < 0 ? angle + 360 : angle) / 45) % 8];
|
||||
|
||||
@@ -91,24 +91,32 @@ export default class SettingsFunctions {
|
||||
document.title = tabName;
|
||||
|
||||
if (hotreload === true) {
|
||||
return;
|
||||
const custom = ['customcss', 'customjs', 'customfont'];
|
||||
custom.forEach((element) => {
|
||||
try {
|
||||
document.head.removeChild(document.getElementById(element));
|
||||
} catch (e) {}
|
||||
});
|
||||
}
|
||||
|
||||
const css = localStorage.getItem('customcss');
|
||||
if (css) {
|
||||
document.head.insertAdjacentHTML('beforeend', '<style>' + css + '</style>');
|
||||
document.head.insertAdjacentHTML('beforeend', '<style id="customcss">' + css + '</style>');
|
||||
}
|
||||
|
||||
const js = localStorage.getItem('customjs');
|
||||
if (js) {
|
||||
document.body.insertAdjacentHTML('beforeend', '<script>' + js + '</script>');
|
||||
document.head.insertAdjacentHTML('beforeend', '<script id="customjs">' + js + '</script>');
|
||||
}
|
||||
|
||||
const font = localStorage.getItem('font');
|
||||
if (font) {
|
||||
const google = localStorage.getItem('fontGoogle');
|
||||
|
||||
let url, fontweight, fontstyle = '';
|
||||
let url = '';
|
||||
let fontweight = '';
|
||||
let fontstyle = '';
|
||||
|
||||
if (google === 'true') {
|
||||
url = `@import url('https://fonts.googleapis.com/css2?family=${font}&display=swap');`;
|
||||
}
|
||||
@@ -124,7 +132,7 @@ export default class SettingsFunctions {
|
||||
}
|
||||
|
||||
document.head.insertAdjacentHTML('beforeend', `
|
||||
<style>
|
||||
<style id='customfont'>
|
||||
${url}
|
||||
* {
|
||||
font-family: '${font}', 'Lexend Deca', 'Montserrat' !important;
|
||||
@@ -135,6 +143,10 @@ export default class SettingsFunctions {
|
||||
`);
|
||||
}
|
||||
|
||||
if (hotreload === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (localStorage.getItem('experimental') === 'true') {
|
||||
experimentalInit();
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"extra_info": {
|
||||
"title": "Zusätzliche informationen",
|
||||
"show_location": "Standort anzeigen",
|
||||
"show_text": "Show text",
|
||||
"show_description": "Show description",
|
||||
"cloudiness": "Cloudiness",
|
||||
"humidity": "Luftfeuchtigkeit",
|
||||
"visibility": "Visibility",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"extra_info": {
|
||||
"title": "Extra information",
|
||||
"show_location": "Show location",
|
||||
"show_text": "Show text",
|
||||
"show_description": "Show description",
|
||||
"cloudiness": "Cloudiness",
|
||||
"humidity": "Humidity",
|
||||
"visibility": "Visibility",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"extra_info": {
|
||||
"title": "Extra information",
|
||||
"show_location": "Show location",
|
||||
"show_text": "Show text",
|
||||
"show_description": "Show description",
|
||||
"cloudiness": "Cloudiness",
|
||||
"humidity": "Humidity",
|
||||
"visibility": "Visibility",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"extra_info": {
|
||||
"title": "Información extra",
|
||||
"show_location": "Mostrar ubicación",
|
||||
"show_text": "Show text",
|
||||
"show_description": "Show description",
|
||||
"cloudiness": "Cloudiness",
|
||||
"humidity": "Humedad",
|
||||
"visibility": "Visibility",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"extra_info": {
|
||||
"title": "Informations supplémentaires",
|
||||
"show_location": "Afficher l'emplacement",
|
||||
"show_text": "Show text",
|
||||
"show_description": "Show description",
|
||||
"cloudiness": "Cloudiness",
|
||||
"humidity": "Humidité",
|
||||
"visibility": "Visibility",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"extra_info": {
|
||||
"title": "Extra information",
|
||||
"show_location": "Show location",
|
||||
"show_text": "Show text",
|
||||
"show_description": "Show description",
|
||||
"cloudiness": "Cloudiness",
|
||||
"humidity": "Humidity",
|
||||
"visibility": "Visibility",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"extra_info": {
|
||||
"title": "Extra information",
|
||||
"show_location": "Show location",
|
||||
"show_text": "Show text",
|
||||
"show_description": "Show description",
|
||||
"cloudiness": "Cloudiness",
|
||||
"humidity": "Humidity",
|
||||
"visibility": "Visibility",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"extra_info": {
|
||||
"title": "Extra information",
|
||||
"show_location": "Show location",
|
||||
"show_text": "Show text",
|
||||
"show_description": "Show description",
|
||||
"cloudiness": "Cloudiness",
|
||||
"humidity": "Humidity",
|
||||
"visibility": "Visibility",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"extra_info": {
|
||||
"title": "Extra information",
|
||||
"show_location": "Show location",
|
||||
"show_text": "Show text",
|
||||
"show_description": "Show description",
|
||||
"cloudiness": "Cloudiness",
|
||||
"humidity": "Humidity",
|
||||
"visibility": "Visibility",
|
||||
|
||||
Reference in New Issue
Block a user