mirror of
https://github.com/mue/mue.git
synced 2026-06-18 06:27:36 +02:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30768053eb | ||
|
|
1d99622123 | ||
|
|
66ac192c09 | ||
|
|
37464acf85 | ||
|
|
c687b2fb67 | ||
|
|
6bf6cca8c6 | ||
|
|
096df4f073 | ||
|
|
490f42a9ad | ||
|
|
ef2f666ccf | ||
|
|
afcead634b | ||
|
|
98c857e7ad | ||
|
|
34cf696bb4 | ||
|
|
2f3252d15e | ||
|
|
7ce2afb773 | ||
|
|
203ff27ee1 | ||
|
|
50b1d16171 | ||
|
|
13825c5525 | ||
|
|
a91d5843b9 | ||
|
|
fb15a1037b | ||
|
|
c66add33d2 | ||
|
|
6ec57c75d4 | ||
|
|
54e7a3a33f | ||
|
|
7e2432b9b4 | ||
|
|
640b83e1c3 | ||
|
|
4d3c7cbbe6 | ||
|
|
23251d248b | ||
|
|
ba31ea5841 | ||
|
|
4f5233fab9 | ||
|
|
5e4a14ba2c | ||
|
|
e82ac7da9e | ||
|
|
16485c3d2c | ||
|
|
9a8a360e21 | ||
|
|
cd5364dd36 | ||
|
|
70273798b6 | ||
|
|
baaa780ade | ||
|
|
39751736ca | ||
|
|
1a8bb69288 | ||
|
|
b8c793741f | ||
|
|
0f20983c37 | ||
|
|
9c3b8c7f59 | ||
|
|
943d817e71 | ||
|
|
564b82c427 | ||
|
|
26df915801 | ||
|
|
eefc594ec1 |
@@ -2,5 +2,5 @@ module.exports = {
|
||||
presets: ['@babel/preset-env', ['@babel/preset-react', {
|
||||
runtime: 'automatic'
|
||||
}]],
|
||||
plugins: ['@babel/plugin-proposal-class-properties', '@babel/transform-runtime', 'babel-plugin-transform-react-class-to-function', '@babel/plugin-transform-react-constant-elements']
|
||||
plugins: ['@babel/plugin-proposal-class-properties', '@babel/transform-runtime', '@babel/plugin-transform-react-inline-elements', 'babel-plugin-transform-react-class-to-function', '@babel/plugin-transform-react-constant-elements']
|
||||
};
|
||||
|
||||
10
manifest/background-chrome.js
Normal file
10
manifest/background-chrome.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable no-undef */
|
||||
chrome.runtime.setUninstallURL('https://muetab.com/uninstall');
|
||||
|
||||
chrome.runtime.onInstalled.addListener((details) => {
|
||||
if (details.reason === 'install') {
|
||||
chrome.tabs.create({
|
||||
url: chrome.runtime.getURL('index.html')
|
||||
});
|
||||
}
|
||||
});
|
||||
10
manifest/background-firefox.js
Normal file
10
manifest/background-firefox.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable no-undef */
|
||||
browser.runtime.setUninstallURL('https://muetab.com/uninstall');
|
||||
|
||||
browser.runtime.onInstalled.addListener((details) => {
|
||||
if (details.reason === 'install') {
|
||||
browser.tabs.create({
|
||||
url: browser.runtime.getURL('index.html')
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -4,7 +4,7 @@
|
||||
"default_locale": "en",
|
||||
"name": "__MSG_name__",
|
||||
"description": "__MSG_description__",
|
||||
"version": "5.2.0",
|
||||
"version": "5.3.2",
|
||||
"homepage_url": "https://muetab.com",
|
||||
"browser_action": {
|
||||
"default_icon": "icons/128x128.png"
|
||||
@@ -17,5 +17,9 @@
|
||||
"48": "icons/48x48.png",
|
||||
"128": "icons/128x128.png"
|
||||
},
|
||||
"content_security_policy": "script-src 'self' https://api.bing.com https://www.google.com; object-src 'self'"
|
||||
"content_security_policy": "script-src 'self' https://api.bing.com https://www.google.com; object-src 'self'",
|
||||
"background": {
|
||||
"persistent": false,
|
||||
"scripts": [ "background-chrome.js" ]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "Mue",
|
||||
"description": "Fast, open and free-to-use new tab page for modern browsers.",
|
||||
"version": "5.2.0",
|
||||
"version": "5.3.2",
|
||||
"homepage_url": "https://muetab.com",
|
||||
"browser_action": {
|
||||
"default_icon": "icons/128x128.png"
|
||||
|
||||
33
package.json
33
package.json
@@ -9,15 +9,14 @@
|
||||
"homepage": "https://muetab.com",
|
||||
"bugs": "https://github.com/mue/mue/issues/new?assignees=&labels=bug&template=bug-report.md&title=%5BBUG%5D",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "5.2.0",
|
||||
"version": "5.3.2",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.4.0",
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@fontsource/lexend-deca": "4.4.5",
|
||||
"@fontsource/montserrat": "4.4.5",
|
||||
"@material-ui/core": "5.0.0-beta.2",
|
||||
"@material-ui/icons": "5.0.0-beta.1",
|
||||
"date-fns-tz": "^1.1.6",
|
||||
"@material-ui/core": "5.0.0-beta.4",
|
||||
"@material-ui/icons": "5.0.0-beta.4",
|
||||
"react": "17.0.2",
|
||||
"react-clock": "3.0.0",
|
||||
"react-color-gradient-picker": "0.1.2",
|
||||
@@ -28,34 +27,34 @@
|
||||
"weather-icons-react": "1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.8",
|
||||
"@babel/eslint-parser": "^7.14.9",
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/eslint-parser": "^7.15.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||
"@babel/plugin-transform-react-constant-elements": "^7.13.15",
|
||||
"@babel/plugin-transform-runtime": "^7.14.5",
|
||||
"@babel/preset-env": "^7.14.9",
|
||||
"@babel/plugin-transform-react-constant-elements": "^7.14.5",
|
||||
"@babel/plugin-transform-react-inline-elements": "^7.14.5",
|
||||
"@babel/plugin-transform-runtime": "^7.15.0",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@eartharoid/deep-merge": "^0.0.1",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-plugin-transform-react-class-to-function": "^1.2.2",
|
||||
"copy-webpack-plugin": "^9.0.1",
|
||||
"css-loader": "^6.2.0",
|
||||
"date-fns": "^2.23.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-react-app": "^6.0.0",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"mini-css-extract-plugin": "^2.1.0",
|
||||
"sass": "^1.37.0",
|
||||
"mini-css-extract-plugin": "^2.2.0",
|
||||
"sass": "^1.38.0",
|
||||
"sass-loader": "^12.1.0",
|
||||
"source-map-loader": "^3.0.0",
|
||||
"webpack": "^5.47.1",
|
||||
"webpack-cli": "^4.7.2",
|
||||
"webpack-dev-server": "^4.0.0-rc.0"
|
||||
"webpack": "^5.51.1",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack serve",
|
||||
"build": "webpack --mode=production",
|
||||
"chrome": "cp manifest/chrome.json build/manifest.json && cp -r manifest/_locales build/_locales",
|
||||
"chrome": "cp manifest/chrome.json build/manifest.json && cp -r manifest/_locales build/_locales && cp manifest/background-chrome.js build/background-chrome.js",
|
||||
"firefox": "rm -rf build/_locales && cp manifest/firefox.json build/manifest.json"
|
||||
},
|
||||
"browserslist": {
|
||||
|
||||
16
src/App.jsx
16
src/App.jsx
@@ -1,29 +1,29 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
|
||||
import Background from './components/widgets/background/Background';
|
||||
import Widgets from './components/widgets/Widgets';
|
||||
import Modals from './components/modals/Modals';
|
||||
|
||||
import { loadSettings, moveSettings } from './modules/helpers/settings';
|
||||
|
||||
import EventBus from './modules/helpers/eventbus';
|
||||
import SettingsFunctions from './modules/helpers/settings';
|
||||
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
|
||||
export default class App extends React.PureComponent {
|
||||
export default class App extends PureComponent {
|
||||
componentDidMount() {
|
||||
// 4.0 -> 5.0 (the key below is only on 5.0)
|
||||
// now featuring 5.0 -> 5.1
|
||||
// the firstRun check was moved here because the old function was useless
|
||||
if (!localStorage.getItem('firstRun') || !localStorage.getItem('stats')) {
|
||||
SettingsFunctions.moveSettings();
|
||||
moveSettings();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
SettingsFunctions.loadSettings();
|
||||
loadSettings();
|
||||
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'other') {
|
||||
SettingsFunctions.loadSettings(true);
|
||||
loadSettings(true);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import './autocomplete.scss';
|
||||
|
||||
export default class Autocomplete extends React.PureComponent {
|
||||
export default class Autocomplete extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
filtered: [],
|
||||
input: ''
|
||||
input: '',
|
||||
autocompleteDisabled: (localStorage.getItem('autocomplete') !== 'true')
|
||||
};
|
||||
}
|
||||
|
||||
onChange = (e) => {
|
||||
if (localStorage.getItem('autocomplete') !== 'true') {
|
||||
if (this.state.autocompleteDisabled) {
|
||||
return this.setState({
|
||||
input: e.target.value
|
||||
});
|
||||
@@ -35,6 +38,20 @@ export default class Autocomplete extends React.PureComponent {
|
||||
this.props.onClick(e);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'search') {
|
||||
this.setState({
|
||||
autocompleteDisabled: (localStorage.getItem('autocomplete') !== 'true')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnount() {
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
let autocomplete = null;
|
||||
|
||||
|
||||
@@ -47,4 +47,4 @@
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
display: inline-block;
|
||||
|
||||
.tooltipTitle {
|
||||
width: 60px;
|
||||
min-width: 60px;
|
||||
background-color: rgba(255, 255, 255, 0.89);
|
||||
color: #000000;
|
||||
text-align: center;
|
||||
@@ -20,7 +20,7 @@
|
||||
cursor: initial;
|
||||
user-select: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.8s;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { ErrorOutline } from '@material-ui/icons';
|
||||
|
||||
import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline';
|
||||
|
||||
export default class ErrorBoundary extends React.PureComponent {
|
||||
export default class ErrorBoundary extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -24,7 +23,7 @@ export default class ErrorBoundary extends React.PureComponent {
|
||||
return (
|
||||
<div className='emptyitems'>
|
||||
<div className='emptyMessage'>
|
||||
<ErrorOutlineIcon/>
|
||||
<ErrorOutline/>
|
||||
<h1>{this.language.title}</h1>
|
||||
<p>{this.language.message}</p>
|
||||
<button className='refresh' onClick={() => window.location.reload()}>{this.language.refresh}</button>
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import React from 'react';
|
||||
import { PureComponent, Suspense, lazy } from 'react';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import Main from './main/Main';
|
||||
import Feedback from './feedback/Feedback';
|
||||
import Navbar from '../widgets/navbar/Navbar';
|
||||
|
||||
import EventBus from '../../modules/helpers/eventbus';
|
||||
|
||||
import Main from './main/Main';
|
||||
import Navbar from '../widgets/navbar/Navbar';
|
||||
|
||||
import Modal from 'react-modal';
|
||||
|
||||
// These modals are lazy loaded as the user won't use them every time they open a tab
|
||||
// We used to lazy load the main modal, but doing so broke the main modal open animation on first click
|
||||
const Welcome = React.lazy(() => import('./welcome/Welcome'));
|
||||
const Feedback = React.lazy(() => import('./feedback/Feedback'));
|
||||
// Welcome modal is lazy loaded as the user won't use it every time they open a tab
|
||||
// We used to lazy load the main and feedback modals, but doing so broke the modal open animation on first click
|
||||
const Welcome = lazy(() => import('./welcome/Welcome'));
|
||||
const renderLoader = () => <></>;
|
||||
|
||||
export default class Modals extends React.PureComponent {
|
||||
export default class Modals extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -58,18 +57,18 @@ export default class Modals extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Navbar openModal={(modal) => this.toggleModal(modal, true)}/>
|
||||
{this.state.welcomeModal === false ? <Navbar openModal={(modal) => this.toggleModal(modal, true)}/> : null}
|
||||
<Modal closeTimeoutMS={300} id='modal' onRequestClose={() => this.toggleModal('mainModal', false)} isOpen={this.state.mainModal} className='Modal mainModal' overlayClassName='Overlay' ariaHideApp={false}>
|
||||
<Main modalClose={() => this.toggleModal('mainModal', false)}/>
|
||||
</Modal>
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Modal closeTimeoutMS={300} onRequestClose={() => this.closeWelcome()} isOpen={this.state.welcomeModal} className='Modal welcomemodal mainModal' overlayClassName='Overlay welcomeoverlay' shouldCloseOnOverlayClick={false} ariaHideApp={false}>
|
||||
<Welcome modalClose={() => this.closeWelcome()}/>
|
||||
</Modal>
|
||||
<Modal closeTimeoutMS={300} onRequestClose={() => this.toggleModal('feedbackModal', false)} isOpen={this.state.feedbackModal} className='Modal mainModal' overlayClassName='Overlay' ariaHideApp={false}>
|
||||
<Feedback modalClose={() => this.toggleModal('feedbackModal', false)}/>
|
||||
</Modal>
|
||||
</React.Suspense>
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import './feedback.scss';
|
||||
|
||||
export default class FeedbackModal extends React.PureComponent {
|
||||
export default class FeedbackModal extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -38,7 +38,7 @@ export default class FeedbackModal extends React.PureComponent {
|
||||
});
|
||||
|
||||
await fetch(window.constants.FEEDBACK_FORM, {
|
||||
'method': 'POST'
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
this.setState({
|
||||
@@ -47,7 +47,7 @@ export default class FeedbackModal extends React.PureComponent {
|
||||
|
||||
setTimeout(() => {
|
||||
this.props.modalClose();
|
||||
}, 3000);
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export default class FeedbackModal extends React.PureComponent {
|
||||
<textarea name='question4' id='questionfour'/>
|
||||
<p className='feedbackerror'>{this.state.questionfourerror}</p>
|
||||
</>
|
||||
{this.state.formsubmit}
|
||||
<p>{this.state.formsubmit}</p>
|
||||
<button onClick={() => this.submitForm()}>{this.language.submit}</button>
|
||||
</>
|
||||
</div>
|
||||
|
||||
@@ -31,10 +31,14 @@
|
||||
border: none;
|
||||
padding: 15px 20px;
|
||||
font-size: 1.5em;
|
||||
background: linear-gradient(90deg, #ffb032 0%, #dd3b67 100%);
|
||||
background: #5352ed;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: rgba(83, 82, 237, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
@@ -54,7 +58,8 @@
|
||||
|
||||
textarea {
|
||||
width: 80%;
|
||||
background-color: var(--sidebar);
|
||||
padding: 10px;
|
||||
background-color: var(--sidebar) !important;
|
||||
}
|
||||
|
||||
.feedbackerror {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Suspense, lazy } from 'react';
|
||||
|
||||
import Tabs from './tabs/backend/Tabs';
|
||||
|
||||
import './scss/index.scss';
|
||||
|
||||
// Lazy load all the tabs instead of the modal itself
|
||||
const Settings = React.lazy(() => import('./tabs/Settings'));
|
||||
const Addons = React.lazy(() => import('./tabs/Addons'));
|
||||
const Marketplace = React.lazy(() => import('./tabs/Marketplace'));
|
||||
const Settings = lazy(() => import('./tabs/Settings'));
|
||||
const Addons = lazy(() => import('./tabs/Addons'));
|
||||
const Marketplace = lazy(() => import('./tabs/Marketplace'));
|
||||
|
||||
const renderLoader = () => (
|
||||
<Tabs>
|
||||
@@ -30,19 +30,19 @@ export default function MainModal(props) {
|
||||
<span className='closeModal' onClick={props.modalClose}>×</span>
|
||||
<Tabs navbar={true}>
|
||||
<div label={language.settings} name='settings'>
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Settings/>
|
||||
</React.Suspense>
|
||||
</Suspense>
|
||||
</div>
|
||||
<div label={language.addons} name='addons'>
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Addons/>
|
||||
</React.Suspense>
|
||||
</Suspense>
|
||||
</div>
|
||||
<div label={language.marketplace} name='marketplace'>
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Marketplace/>
|
||||
</React.Suspense>
|
||||
</Suspense>
|
||||
</div>
|
||||
</Tabs>
|
||||
</>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
import { PureComponent } from 'react';
|
||||
import { ArrowBack } from '@material-ui/icons';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import Lightbox from './Lightbox';
|
||||
|
||||
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
||||
|
||||
export default class Item extends React.PureComponent {
|
||||
export default class Item extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -42,7 +40,7 @@ export default class Item extends React.PureComponent {
|
||||
return (
|
||||
<div id='item'>
|
||||
<br/>
|
||||
<ArrowBackIcon className='backArrow' onClick={this.props.toggleFunction}/>
|
||||
<ArrowBack className='backArrow' onClick={this.props.toggleFunction}/>
|
||||
<br/>
|
||||
<h1>{this.props.data.display_name}</h1>
|
||||
{this.props.button}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { LocalMall } from '@material-ui/icons';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import LocalMallIcon from '@material-ui/icons/LocalMall';
|
||||
import Item from '../Item';
|
||||
import Items from '../Items';
|
||||
import Dropdown from '../../settings/Dropdown';
|
||||
|
||||
import MarketplaceFunctions from '../../../../../modules/helpers/marketplace';
|
||||
import { uninstall, urlParser } from '../../../../../modules/helpers/marketplace';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export default class Added extends React.PureComponent {
|
||||
export default class Added extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -34,7 +33,7 @@ export default class Added extends React.PureComponent {
|
||||
name: data,
|
||||
display_name: info.name,
|
||||
author: info.author,
|
||||
description: MarketplaceFunctions.urlParser(info.description.replace(/\n/g, '<br>')),
|
||||
description: urlParser(info.description.replace(/\n/g, '<br>')),
|
||||
//updated: info.updated,
|
||||
version: info.version,
|
||||
icon: info.screenshot_url,
|
||||
@@ -51,7 +50,7 @@ export default class Added extends React.PureComponent {
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
MarketplaceFunctions.uninstall(this.state.item.type, this.state.item.display_name);
|
||||
uninstall(this.state.item.type, this.state.item.display_name);
|
||||
|
||||
toast(window.language.toasts.uninstalled);
|
||||
|
||||
@@ -100,7 +99,7 @@ export default class Added extends React.PureComponent {
|
||||
return (
|
||||
<div className='emptyitems'>
|
||||
<div className='emptyMessage'>
|
||||
<LocalMallIcon/>
|
||||
<LocalMall/>
|
||||
<h1>{this.language.empty.title}</h1>
|
||||
<p className='description'>{this.language.empty.description}</p>
|
||||
</div>
|
||||
|
||||
220
src/components/modals/main/marketplace/sections/Create.jsx
Normal file
220
src/components/modals/main/marketplace/sections/Create.jsx
Normal file
@@ -0,0 +1,220 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { PureComponent } from 'react';
|
||||
import {
|
||||
SettingsRounded as Settings,
|
||||
PhotoOutlined as Photos,
|
||||
FormatQuoteOutlined as Quotes
|
||||
} from '@material-ui/icons';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { saveFile } from '../../../../../modules/helpers/settings/modals';
|
||||
|
||||
import FileUpload from '../../settings/FileUpload';
|
||||
|
||||
import '../../../welcome/welcome.scss';
|
||||
|
||||
export default class Create extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
currentTab: 1,
|
||||
addonMetadata: {
|
||||
name: '',
|
||||
description: '',
|
||||
type: '',
|
||||
version: '',
|
||||
author: '',
|
||||
icon_url: '',
|
||||
screenshot_url: ''
|
||||
},
|
||||
addonData: '',
|
||||
settingsClasses: {
|
||||
current: 'toggle lightTheme',
|
||||
json: 'toggle lightTheme'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
changeTab(tab, type) {
|
||||
if (type) {
|
||||
return this.setState({
|
||||
currentTab: tab,
|
||||
addonMetadata: {
|
||||
type: type
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
currentTab: tab
|
||||
});
|
||||
}
|
||||
|
||||
importSettings(input) {
|
||||
const data = input || localStorage;
|
||||
let settings = {};
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (key === 'statsData' || key === 'firstRun' || key === 'showWelcome' || key === 'language' || key === 'installed' || key === 'stats') {
|
||||
return;
|
||||
}
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
|
||||
this.setState({
|
||||
addonData: settings,
|
||||
settingsClasses: {
|
||||
current: input ? 'toggle lightTheme' : 'toggle lightTheme active',
|
||||
json: input ? 'toggle lightTheme active' : 'toggle lightTheme'
|
||||
}
|
||||
});
|
||||
|
||||
toast('Imported settings!');
|
||||
}
|
||||
|
||||
downloadAddon() {
|
||||
saveFile({
|
||||
name: this.state.addonMetadata.name,
|
||||
description: this.state.addonMetadata.description,
|
||||
type: this.state.addonMetadata.type,
|
||||
version: this.state.addonMetadata.version,
|
||||
author: this.state.addonMetadata.author,
|
||||
icon_url: this.state.addonMetadata.icon_url,
|
||||
screenshot_url: this.state.addonMetadata.screenshot_url,
|
||||
[this.state.addonMetadata.type]: this.state.addonData
|
||||
}, this.state.addonMetadata.name + '.json');
|
||||
}
|
||||
|
||||
render() {
|
||||
let tabContent;
|
||||
|
||||
const { time } = window.language.modals.main.settings.sections;
|
||||
const { marketplace, addons } = window.language.modals.main;
|
||||
const { welcome } = window.language.modals;
|
||||
|
||||
const chooseType = (
|
||||
<>
|
||||
<h3>{time.type}</h3>
|
||||
<div className='themesToggleArea'>
|
||||
<div className='options'>
|
||||
{/* <div className='toggle lightTheme' onClick={() => this.changeTab(2, 'photos')}>
|
||||
<Photos/>
|
||||
<span>{marketplace.photo_packs}</span>
|
||||
</div>
|
||||
<div className='toggle lightTheme' onClick={() => this.changeTab(2, 'quotes')}>
|
||||
<Quotes/>
|
||||
<span>{marketplace.quote_packs}</span>
|
||||
</div>
|
||||
*/}
|
||||
<div className='toggle lightTheme' onClick={() => this.changeTab(2, 'settings')}>
|
||||
<Settings/>
|
||||
<span>{marketplace.preset_settings}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
// todo: find a better way to do all this
|
||||
const nextDescriptionDisabled = (this.state.addonMetadata.name !== undefined &&
|
||||
this.state.addonMetadata.description !== undefined &&
|
||||
this.state.addonMetadata.version !== undefined && this.state.addonMetadata.author !== undefined &&
|
||||
this.state.addonMetadata.icon_url !== undefined && this.state.addonMetadata.screenshot_url !== undefined)
|
||||
? false : true;
|
||||
|
||||
const setMetadata = (data, type) => {
|
||||
this.setState({
|
||||
addonMetadata: {
|
||||
name: (type === 'name') ? data : this.state.addonMetadata.name,
|
||||
description: (type === 'description') ? data : this.state.addonMetadata.description,
|
||||
version: (type === 'version') ? data : this.state.addonMetadata.version,
|
||||
author: (type === 'author') ? data : this.state.addonMetadata.author,
|
||||
icon_url: (type === 'icon_url') ? data : this.state.addonMetadata.icon_url,
|
||||
screenshot_url: (type === 'screenshot_url') ? data : this.state.addonMetadata.screenshot_url,
|
||||
type: this.state.addonMetadata.type
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const writeDescription = (
|
||||
<>
|
||||
<h3>{marketplace.product.information}</h3>
|
||||
<p>{addons.create.metadata.name}</p>
|
||||
<input type='text' value={this.state.addonMetadata.name} onInput={(e) => setMetadata(e.target.value, 'name')}/>
|
||||
<p>{marketplace.product.version}</p>
|
||||
<input type='text' value={this.state.addonMetadata.version} onInput={(e) => setMetadata(e.target.value, 'version')}/>
|
||||
<p>{marketplace.product.author}</p>
|
||||
<input type='text' value={this.state.addonMetadata.author} onInput={(e) => setMetadata(e.target.value, 'author')}/>
|
||||
<p>{addons.create.metadata.icon_url}</p>
|
||||
<input type='text' value={this.state.addonMetadata.icon_url} onInput={(e) => setMetadata(e.target.value, 'icon_url')}/>
|
||||
<p>{addons.create.metadata.screenshot_url}</p>
|
||||
<input type='text' value={this.state.addonMetadata.screenshot_url} onInput={(e) => setMetadata(e.target.value, 'screenshot_url')}/>
|
||||
<p>{addons.create.metadata.description}</p>
|
||||
<textarea className='settingsTextarea' value={this.state.addonMetadata.description} onInput={(e) => setMetadata(e.target.value, 'description')}></textarea>
|
||||
<br/>
|
||||
<button onClick={() => this.changeTab(1)} className='uploadbg' style={{ marginRight: '10px' }}>{welcome.buttons.previous}</button>
|
||||
<button onClick={() => this.changeTab(this.state.addonMetadata.type)} className='uploadbg' disabled={nextDescriptionDisabled}>{welcome.buttons.next}</button>
|
||||
</>
|
||||
);
|
||||
|
||||
// settings
|
||||
const nextSettingsDisabled = (this.state.addonData === '') ? true : false;
|
||||
const importSettings = (
|
||||
<>
|
||||
<h3>{welcome.sections.settings.title}</h3>
|
||||
<div className='themesToggleArea'>
|
||||
<div className='options'>
|
||||
<div className={this.state.settingsClasses.current} onClick={() => this.importSettings()}>
|
||||
<span>{addons.create.settings.current}</span>
|
||||
</div>
|
||||
<div className={this.state.settingsClasses.json} onClick={() => document.getElementById('file-input').click()}>
|
||||
<span>{addons.create.settings.json}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FileUpload id='file-input' type='settings' accept='application/json' loadFunction={(e) => this.importSettings(JSON.parse(e.target.result))} />
|
||||
<br/><br/>
|
||||
<button onClick={() => this.changeTab(2)} className='uploadbg' style={{ marginRight: '10px' }}>{welcome.buttons.previous}</button>
|
||||
<button onClick={() => this.changeTab(3)} className='uploadbg' disabled={nextSettingsDisabled}>{welcome.buttons.next}</button>
|
||||
</>
|
||||
);
|
||||
|
||||
// quotes
|
||||
const addQuotes = (
|
||||
<>
|
||||
<h3>{addons.create.quotes.title}</h3>
|
||||
</>
|
||||
);
|
||||
|
||||
// photos
|
||||
const addPhotos = (
|
||||
<>
|
||||
<h3>{addons.create.photos.title}</h3>
|
||||
</>
|
||||
);
|
||||
|
||||
const downloadAddon = (
|
||||
<>
|
||||
<h3>{addons.create.finish.title}</h3>
|
||||
<button onClick={() => this.downloadAddon()} className='upload'>{addons.create.finish.download}</button>
|
||||
<br/><br/>
|
||||
<button onClick={() => this.changeTab(this.state.addonMetadata.type)} className='uploadbg' style={{ marginRight: '10px' }}>{welcome.buttons.previous}</button>
|
||||
</>
|
||||
);
|
||||
|
||||
switch (this.state.currentTab) {
|
||||
case 2: tabContent = writeDescription; break;
|
||||
case 'settings': tabContent = importSettings; break;
|
||||
case 'quotes': tabContent = addQuotes; break;
|
||||
case 'photos': tabContent = addPhotos; break;
|
||||
case 3: tabContent = downloadAddon; break;
|
||||
default: tabContent = chooseType;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{addons.create.other_title}</h2>
|
||||
{tabContent}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import WifiOffIcon from '@material-ui/icons/WifiOff';
|
||||
import LocalMallIcon from '@material-ui/icons/LocalMall';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { WifiOff, LocalMall } from '@material-ui/icons';
|
||||
|
||||
import Item from '../Item';
|
||||
import Items from '../Items';
|
||||
import Dropdown from '../../settings/Dropdown';
|
||||
|
||||
import MarketplaceFunctions from '../../../../../modules/helpers/marketplace';
|
||||
import { install, urlParser, uninstall } from '../../../../../modules/helpers/marketplace';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export default class Marketplace extends React.PureComponent {
|
||||
export default class Marketplace extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -59,7 +56,7 @@ export default class Marketplace extends React.PureComponent {
|
||||
type: info.data.type,
|
||||
display_name: info.data.name,
|
||||
author: info.data.author,
|
||||
description: MarketplaceFunctions.urlParser(info.data.description.replace(/\n/g, '<br>')),
|
||||
description: urlParser(info.data.description.replace(/\n/g, '<br>')),
|
||||
//updated: info.updated,
|
||||
version: info.data.version,
|
||||
icon: info.data.screenshot_url,
|
||||
@@ -96,9 +93,9 @@ export default class Marketplace extends React.PureComponent {
|
||||
|
||||
manage(type) {
|
||||
if (type === 'install') {
|
||||
MarketplaceFunctions.install(this.state.item.type, this.state.item.data);
|
||||
install(this.state.item.type, this.state.item.data);
|
||||
} else {
|
||||
MarketplaceFunctions.uninstall(this.state.item.type, this.state.item.display_name);
|
||||
uninstall(this.state.item.type, this.state.item.display_name);
|
||||
}
|
||||
|
||||
toast(window.language.toasts[type + 'ed']);
|
||||
@@ -162,23 +159,9 @@ export default class Marketplace extends React.PureComponent {
|
||||
);
|
||||
};
|
||||
|
||||
const featured = () => {
|
||||
const openFeatured = () => {
|
||||
window.stats.postEvent('marketplace', 'Featured clicked');
|
||||
window.open(this.state.featured.buttonLink);
|
||||
}
|
||||
return (
|
||||
<div className='featured' style={{ backgroundColor: this.state.featured.colour }}>
|
||||
<p>{this.state.featured.title}</p>
|
||||
<h1>{this.state.featured.name}</h1>
|
||||
<button className='addToMue' onClick={() => openFeatured()}>{this.state.featured.buttonText}</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return errorMessage(<>
|
||||
<WifiOffIcon/>
|
||||
<WifiOff/>
|
||||
<h1>{this.language.offline.title}</h1>
|
||||
<p className='description'>{this.language.offline.description}</p>
|
||||
</>);
|
||||
@@ -188,17 +171,32 @@ export default class Marketplace extends React.PureComponent {
|
||||
return errorMessage(<h1>{window.language.modals.main.loading}</h1>);
|
||||
}
|
||||
|
||||
const featured = () => {
|
||||
const openFeatured = () => {
|
||||
window.stats.postEvent('marketplace', 'Featured clicked');
|
||||
window.open(this.state.featured.buttonLink);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='featured' style={{ backgroundColor: this.state.featured.colour }}>
|
||||
<p>{this.state.featured.title}</p>
|
||||
<h1>{this.state.featured.name}</h1>
|
||||
<button className='addToMue' onClick={() => openFeatured()}>{this.state.featured.buttonText}</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.items.length === 0) {
|
||||
return (
|
||||
<>
|
||||
{featured()}
|
||||
{errorMessage(<>
|
||||
<LocalMallIcon/>
|
||||
<LocalMall/>
|
||||
<h1>{window.language.modals.main.addons.empty.title}</h1>
|
||||
<p className='description'>{this.language.no_items}</p>
|
||||
</>)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.item.display_name) {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import LocalMallIcon from '@material-ui/icons/LocalMall';
|
||||
import { LocalMall } from '@material-ui/icons';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import FileUpload from '../../settings/FileUpload';
|
||||
|
||||
import MarketplaceFunctions from '../../../../../modules/helpers/marketplace';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
import { install } from '../../../../../modules/helpers/marketplace';
|
||||
|
||||
export default function Sideload() {
|
||||
const install = (input) => {
|
||||
MarketplaceFunctions.install(input.type, input);
|
||||
const installAddon = (input) => {
|
||||
install(input.type, input);
|
||||
toast(window.language.toasts.installed);
|
||||
window.stats.postEvent('marketplace', 'Sideload');
|
||||
};
|
||||
@@ -16,8 +15,8 @@ export default function Sideload() {
|
||||
return (
|
||||
<div className='emptyitems'>
|
||||
<div className='emptyMessage'>
|
||||
<FileUpload id='file-input' type='settings' accept='application/json' loadFunction={(e) => install(JSON.parse(e.target.result))} />
|
||||
<LocalMallIcon/>
|
||||
<FileUpload id='file-input' type='settings' accept='application/json' loadFunction={(e) => installAddon(JSON.parse(e.target.result))} />
|
||||
<LocalMall/>
|
||||
<h1>{window.language.modals.main.addons.sideload}</h1>
|
||||
<button className='addToMue sideload' onClick={() => document.getElementById('file-input').click()}>{window.language.modals.main.settings.sections.background.source.upload}</button>
|
||||
</div>
|
||||
|
||||
@@ -89,14 +89,11 @@
|
||||
}
|
||||
|
||||
.ReactModal__Content {
|
||||
//min-height: calc(100vh - 30vh);
|
||||
//max-height: calc(100vh - 10vh);
|
||||
box-shadow: 0 0 30px 0 rgba(0, 0, 0, 0.25);
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
|
||||
// animation
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transition: all 300ms cubic-bezier(0.47, 1.64, 0.41, 0.8);
|
||||
}
|
||||
@@ -149,6 +146,24 @@ ul.sidebar {
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-height: 999px) and (min-height: 920px) {
|
||||
ul.sidebar {
|
||||
min-height: 160vh;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-height: 919px) and (min-height: 700px) {
|
||||
ul.sidebar {
|
||||
min-height: 200vh;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-height: 699px) and (min-height: 400px) {
|
||||
ul.sidebar {
|
||||
min-height: 260vh;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
font-size: 24px;
|
||||
@@ -165,17 +180,12 @@ li {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 80%;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1300px) {
|
||||
@media (max-width: 1700px) {
|
||||
#modal {
|
||||
width: 90% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1310px) {
|
||||
#modal {
|
||||
width: 60%;
|
||||
width: 80% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +262,7 @@ li {
|
||||
display: inline-flex;
|
||||
|
||||
&:hover {
|
||||
color: rgb(165, 165, 165);
|
||||
color: grey;
|
||||
background: none;
|
||||
}
|
||||
|
||||
@@ -291,7 +301,7 @@ li {
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1650px) {
|
||||
@media only screen and (max-width: 800px) {
|
||||
li.navbar-item {
|
||||
span {
|
||||
display: none;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
.items {
|
||||
display: inline-grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
margin-top: 15px;
|
||||
|
||||
.item {
|
||||
@@ -71,31 +71,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 2100px) {
|
||||
@media (max-width: 1920px) {
|
||||
.items {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1870px) {
|
||||
@media (max-width: 1680px) and (min-width: 1500px) {
|
||||
.items {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1079px), (max-width: 1869px) {
|
||||
@media (max-width: 1440px) {
|
||||
.items {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1680px) {
|
||||
.side {
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
.sidebr {
|
||||
display: none;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,4 +34,4 @@ select {
|
||||
.dark select {
|
||||
background: url("data:image/svg+xml;utf8,<svg fill='white' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>") right center no-repeat, var(--sidebar) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ input {
|
||||
-webkit-appearance: none;
|
||||
vertical-align: middle;
|
||||
background: none;
|
||||
|
||||
|
||||
&::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
&::-webkit-color-swatch {
|
||||
border: none;
|
||||
border-radius: 100%;
|
||||
@@ -37,11 +37,11 @@ input {
|
||||
-moz-appearance: none;
|
||||
vertical-align: middle;
|
||||
background: none;
|
||||
|
||||
|
||||
&::-moz-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
&::-moz-color-swatch {
|
||||
border: none;
|
||||
border-radius: 100%;
|
||||
@@ -106,11 +106,6 @@ ul {
|
||||
}
|
||||
}
|
||||
|
||||
.newFeature {
|
||||
color: #ff4757;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.settingsTextarea {
|
||||
font-family: Consolas !important;
|
||||
padding: 15px;
|
||||
@@ -181,7 +176,7 @@ legend {
|
||||
li {
|
||||
cursor: initial;
|
||||
font-size: 1rem;
|
||||
list-style-type:disc;
|
||||
list-style-type: disc;
|
||||
padding: 0;
|
||||
margin-left: 20px;
|
||||
}
|
||||
@@ -228,9 +223,13 @@ input[type=number] {
|
||||
h2 {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
h2, span, svg {
|
||||
|
||||
h2,
|
||||
span,
|
||||
svg {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
svg {
|
||||
vertical-align: sub;
|
||||
font-size: 1.4rem;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { Checkbox as CheckboxUI, FormControlLabel } from '@material-ui/core';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
|
||||
import CheckboxUI from '@material-ui/core/Checkbox';
|
||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||
|
||||
export default class Checkbox extends React.PureComponent {
|
||||
export default class Checkbox extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -34,19 +32,11 @@ export default class Checkbox extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
let text = this.props.text;
|
||||
|
||||
if (this.props.newFeature) {
|
||||
text = <>{this.props.text} <span className='newFeature'> NEW</span></>;
|
||||
} else if (this.props.betaFeature) {
|
||||
text = <>{this.props.text} <span className='newFeature'> BETA</span></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormControlLabel
|
||||
control={<CheckboxUI name={this.props.name} color='primary' className='checkbox' checked={this.state.checked} onChange={this.handleChange} />}
|
||||
label={text}
|
||||
label={this.props.text}
|
||||
/>
|
||||
<br/>
|
||||
</>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
|
||||
export default class Dropdown extends React.PureComponent {
|
||||
export default class Dropdown extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export default class FileUpload extends React.PureComponent {
|
||||
export default class FileUpload extends PureComponent {
|
||||
componentDidMount() {
|
||||
document.getElementById(this.props.id).onchange = (e) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { Radio as RadioUI, RadioGroup, FormControlLabel, FormControl, FormLabel } from '@material-ui/core';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
|
||||
import RadioUI from '@material-ui/core/Radio';
|
||||
import RadioGroup from '@material-ui/core/RadioGroup';
|
||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import FormLabel from '@material-ui/core/FormLabel';
|
||||
|
||||
export default class Radio extends React.PureComponent {
|
||||
export default class Radio extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -23,6 +18,13 @@ export default class Radio extends React.PureComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.name === 'language') {
|
||||
// old tab name
|
||||
if (localStorage.getItem('tabName') === window.language.tabname) {
|
||||
localStorage.setItem('tabName', require(`../../../../translations/${value.replace('-', '_')}.json`).tabname);
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem(this.props.name, value);
|
||||
|
||||
this.setState({
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
import DeleteIcon from '@material-ui/icons/Delete';
|
||||
|
||||
import SettingsFunctions from '../../../../modules/helpers/settings';
|
||||
import { Close, Delete } from '@material-ui/icons';
|
||||
import { setDefaultSettings } from '../../../../modules/helpers/settings';
|
||||
|
||||
export default function ResetModal(props) {
|
||||
const language = window.language.modals.main.settings.sections.advanced.reset_modal;
|
||||
|
||||
const reset = () => {
|
||||
window.stats.postEvent('setting', 'Reset');
|
||||
SettingsFunctions.setDefaultSettings('reset');
|
||||
setDefaultSettings('reset');
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
@@ -20,10 +18,10 @@ export default function ResetModal(props) {
|
||||
<span>{language.information}</span>
|
||||
<div className='resetfooter'>
|
||||
<button className='round reset' style={{ marginLeft: 0 }} onClick={() => reset()}>
|
||||
<DeleteIcon/>
|
||||
<Delete/>
|
||||
</button>
|
||||
<button className='round import' style={{ marginLeft: '5px' }} onClick={props.modalClose}>
|
||||
<CloseIcon/>
|
||||
<Close/>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// todo: find a better method to do width of number input
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export default class Slider extends React.PureComponent {
|
||||
export default class Slider extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { Switch as SwitchUI, FormControlLabel } from '@material-ui/core';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
|
||||
import SwitchUI from '@material-ui/core/Switch';
|
||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||
|
||||
export default class Switch extends React.PureComponent {
|
||||
export default class Switch extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -34,19 +32,11 @@ export default class Switch extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
let text = this.props.text;
|
||||
|
||||
if (this.props.newFeature) {
|
||||
text = <>{this.props.text} <span className='newFeature'> NEW</span></>;
|
||||
} else if (this.props.betaFeature) {
|
||||
text = <>{this.props.text} <span className='newFeature'> BETA</span></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormControlLabel
|
||||
control={<SwitchUI name={this.props.name} color='primary' checked={this.state.checked} onChange={this.handleChange} />}
|
||||
label={text}
|
||||
label={this.props.text}
|
||||
labelPlacement='start'
|
||||
/>
|
||||
<br/>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export default class Text extends React.PureComponent {
|
||||
export default class Text extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { Email, Twitter, Chat, Instagram, Facebook } from '@material-ui/icons';
|
||||
|
||||
import Tooltip from '../../../../helpers/tooltip/Tooltip';
|
||||
import EmailIcon from '@material-ui/icons/Email';
|
||||
import TwitterIcon from '@material-ui/icons/Twitter';
|
||||
import ChatIcon from '@material-ui/icons/Chat';
|
||||
import InstagramIcon from '@material-ui/icons/Instagram';
|
||||
import FacebookIcon from '@material-ui/icons/Facebook';
|
||||
|
||||
const other_contributors = require('../../../../../modules/other_contributors.json');
|
||||
|
||||
export default class About extends React.PureComponent {
|
||||
export default class About extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -94,11 +90,11 @@ export default class About extends React.PureComponent {
|
||||
<a href={window.constants.PRIVACY_URL} className='aboutLink' target='_blank' rel='noopener noreferrer' style={{ fontSize: '1rem' }}>{window.language.modals.welcome.sections.privacy.links.privacy_policy}</a>
|
||||
|
||||
<h3>{this.language.contact_us}</h3>
|
||||
<a href={'mailto:' + window.constants.EMAIL} className='aboutIcon' target='_blank' rel='noopener noreferrer'><EmailIcon/></a>
|
||||
<a href={'https://twitter.com/' + window.constants.TWITTER_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><TwitterIcon/></a>
|
||||
<a href={'https://instagram.com/' + window.constants.INSTAGRAM_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><InstagramIcon/></a>
|
||||
<a href={'https://facebook.com/' + window.constants.FACEBOOK_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><FacebookIcon/></a>
|
||||
<a href={'https://discord.gg/' + window.constants.DISCORD_SERVER} className='aboutIcon' target='_blank' rel='noopener noreferrer'><ChatIcon/></a>
|
||||
<a href={'mailto:' + window.constants.EMAIL} className='aboutIcon' target='_blank' rel='noopener noreferrer'><Email/></a>
|
||||
<a href={'https://twitter.com/' + window.constants.TWITTER_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><Twitter/></a>
|
||||
<a href={'https://instagram.com/' + window.constants.INSTAGRAM_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><Instagram/></a>
|
||||
<a href={'https://facebook.com/' + window.constants.FACEBOOK_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><Facebook/></a>
|
||||
<a href={'https://discord.gg/' + window.constants.DISCORD_SERVER} className='aboutIcon' target='_blank' rel='noopener noreferrer'><Chat/></a>
|
||||
|
||||
<h3>{this.language.support_mue}</h3>
|
||||
<p>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import { exportSettings, importSettings } from '../../../../../modules/helpers/settings/modals';
|
||||
|
||||
import Checkbox from '../Checkbox';
|
||||
import FileUpload from '../FileUpload';
|
||||
@@ -7,13 +10,9 @@ import Switch from '../Switch';
|
||||
import ResetModal from '../ResetModal';
|
||||
import Dropdown from '../Dropdown';
|
||||
|
||||
import SettingsFunctions from '../../../../../modules/helpers/settings/modals';
|
||||
|
||||
import Modal from 'react-modal';
|
||||
|
||||
const time_zones = require('../../../../widgets/time/timezones.json');
|
||||
|
||||
export default class AdvancedSettings extends React.PureComponent {
|
||||
export default class AdvancedSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -38,9 +37,9 @@ export default class AdvancedSettings extends React.PureComponent {
|
||||
|
||||
<h3>{advanced.data}</h3>
|
||||
<button className='reset' onClick={() => this.setState({ resetModal: true })}>{this.language.buttons.reset}</button>
|
||||
<button className='export' onClick={() => SettingsFunctions.exportSettings()}>{this.language.buttons.export}</button>
|
||||
<button className='export' onClick={() => exportSettings()}>{this.language.buttons.export}</button>
|
||||
<button className='import' onClick={() => document.getElementById('file-input').click()}>{this.language.buttons.import}</button>
|
||||
<FileUpload id='file-input' accept='application/json' type='settings' loadFunction={(e) => SettingsFunctions.importSettings(e)}/>
|
||||
<FileUpload id='file-input' accept='application/json' type='settings' loadFunction={(e) => importSettings(e)}/>
|
||||
|
||||
<h3>{advanced.customisation}</h3>
|
||||
<Text title={advanced.tab_name} name='tabName' default={window.language.tabname} category='other'/>
|
||||
|
||||
@@ -30,6 +30,7 @@ export default function AppearanceSettings() {
|
||||
<h3>{appearance.navbar.title}</h3>
|
||||
<Checkbox name='notesEnabled' text={appearance.navbar.notes} category='navbar' />
|
||||
<Checkbox name='refresh' text={appearance.navbar.refresh} category='navbar' />
|
||||
<Slider title={appearance.accessibility.widget_zoom} name='zoomNavbar' min='10' max='400' default='100' display='%' category='navbar' />
|
||||
|
||||
<h3>{appearance.font.title}</h3>
|
||||
<Text title={appearance.font.custom} name='font' upperCaseFirst={true} category='other' />
|
||||
@@ -54,6 +55,7 @@ export default function AppearanceSettings() {
|
||||
</Dropdown>
|
||||
|
||||
<h3>{appearance.accessibility.title}</h3>
|
||||
{/* not supported on firefox */}
|
||||
{(navigator.userAgent.includes('Chrome') && typeof InstallTrigger === 'undefined') ?
|
||||
<Slider title={appearance.accessibility.widget_zoom} name='widgetzoom' default='100' step='10' min='50' max='200' display='%' category='other'/>
|
||||
: null}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
import { PureComponent } from 'react';
|
||||
import { WifiOff } from '@material-ui/icons';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import Lightbox from '../../marketplace/Lightbox';
|
||||
|
||||
import WifiOffIcon from '@material-ui/icons/WifiOff';
|
||||
|
||||
export default class Changelog extends React.PureComponent {
|
||||
export default class Changelog extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -89,7 +87,7 @@ export default class Changelog extends React.PureComponent {
|
||||
const language = window.language.modals.main.marketplace;
|
||||
|
||||
return errorMessage(<>
|
||||
<WifiOffIcon/>
|
||||
<WifiOff/>
|
||||
<h1>{language.offline.title}</h1>
|
||||
<p className='description'>{language.offline.description}</p>
|
||||
</>);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Checkbox from '../Checkbox';
|
||||
import Slider from '../Slider';
|
||||
|
||||
import EventBus from '../../../../../modules/helpers/eventbus';
|
||||
|
||||
export default function ExperimentalSettings() {
|
||||
@@ -10,9 +11,6 @@ export default function ExperimentalSettings() {
|
||||
<h2>{experimental.title}</h2>
|
||||
<p>{experimental.warning}</p>
|
||||
<Checkbox name='animations' text={window.language.modals.main.settings.sections.appearance.animations} element='.other'/>
|
||||
<h3>Usage Stats</h3>
|
||||
<p>Allows you to see stats such as how many tabs you have opened, quotes favourited etc. It also sends this data anonymously to our<a className='modalLink' href='https://github.com/mue/umami'>umami</a> instance.</p>
|
||||
<Checkbox name='stats' text='Enable Usage Stats' element='.other'/>
|
||||
<h3>{experimental.developer}</h3>
|
||||
<Checkbox name='debug' text='Debug hotkey (Ctrl + #)' element='.other'/>
|
||||
<Slider title='Debug timeout' name='debugtimeout' min='0' max='5000' default='0' step='100' display=' miliseconds' element='.other' />
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Checkbox from '../Checkbox';
|
||||
import Switch from '../Switch';
|
||||
import Text from '../Text';
|
||||
import Slider from '../Slider';
|
||||
|
||||
export default class GreetingSettings extends React.PureComponent {
|
||||
export default class GreetingSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -29,14 +29,14 @@ export default class GreetingSettings extends React.PureComponent {
|
||||
<>
|
||||
<h2>{greeting.title}</h2>
|
||||
<Switch name='greeting' text={this.language.enabled} category='greeting' element='.greeting'/>
|
||||
<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' />
|
||||
<Checkbox name='events' text={greeting.events} category='greeting'/>
|
||||
<Checkbox name='defaultGreetingMessage' text={greeting.default} category='greeting'/>
|
||||
<Text title={greeting.name} name='greetingName' category='greeting'/>
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomGreeting' min='10' max='400' default='100' display='%' category='greeting' />
|
||||
|
||||
<h3>{greeting.birthday}</h3>
|
||||
<Switch name='birthdayenabled' text={this.language.enabled} category='greeting' element='.greeting'/>
|
||||
<Checkbox name='birthdayage' text={greeting.birthday_age} category='greeting' element='.greeting'/>
|
||||
<Switch name='birthdayenabled' text={this.language.enabled} category='greeting'/>
|
||||
<Checkbox name='birthdayage' text={greeting.birthday_age} category='greeting'/>
|
||||
<p>{greeting.birthday_date}</p>
|
||||
<input type='date' onChange={this.changeDate} value={this.state.birthday.toISOString().substr(0, 10)}/>
|
||||
</>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Radio from '../Radio';
|
||||
|
||||
const languages = require('../../../../../modules/languages.json');
|
||||
|
||||
export default class BackgroundSettings extends React.PureComponent {
|
||||
export default class BackgroundSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -60,7 +60,7 @@ export default class BackgroundSettings extends React.PureComponent {
|
||||
return (
|
||||
<>
|
||||
<h2>{language.title}</h2>
|
||||
<Radio name='language' options={languages} element='.language' />
|
||||
<Radio name='language' options={languages} element='.other' />
|
||||
<h3>{language.quote}</h3>
|
||||
<Radio name='quotelanguage' options={this.state.quoteLanguages} category='quote' />
|
||||
</>
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../../../modules/helpers/eventbus';
|
||||
|
||||
import DragHandleIcon from '@material-ui/icons/DragIndicator';
|
||||
|
||||
import { PureComponent } from 'react';
|
||||
import { DragIndicator } from '@material-ui/icons';
|
||||
import { sortableContainer, sortableElement } from 'react-sortable-hoc';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
const enabled = (setting) => {
|
||||
switch (setting) {
|
||||
case 'quicklinks':
|
||||
return (localStorage.getItem('quicklinksenabled') === 'true');
|
||||
default:
|
||||
return (localStorage.getItem(setting) === 'true');
|
||||
}
|
||||
};
|
||||
import EventBus from '../../../../../modules/helpers/eventbus';
|
||||
|
||||
const settings = window.language.modals.main.settings.sections;
|
||||
const widget_name = {
|
||||
@@ -26,8 +15,8 @@ const widget_name = {
|
||||
};
|
||||
|
||||
const SortableItem = sortableElement(({ value }) => (
|
||||
<li className='sortableitem' style={{ display: enabled(value) ? 'block' : 'none' }}>
|
||||
<DragHandleIcon style={{ verticalAlign: 'middle' }} />
|
||||
<li className='sortableitem'>
|
||||
<DragIndicator style={{ verticalAlign: 'middle' }} />
|
||||
{widget_name[value]}
|
||||
</li>
|
||||
));
|
||||
@@ -36,7 +25,7 @@ const SortableContainer = sortableContainer(({ children }) => {
|
||||
return <ul className='sortablecontainer'>{children}</ul>;
|
||||
});
|
||||
|
||||
export default class OrderSettings extends React.PureComponent {
|
||||
export default class OrderSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -79,6 +68,15 @@ export default class OrderSettings extends React.PureComponent {
|
||||
toast(window.language.toasts.reset);
|
||||
}
|
||||
|
||||
enabled = (setting) => {
|
||||
switch (setting) {
|
||||
case 'quicklinks':
|
||||
return (localStorage.getItem('quicklinksenabled') === 'true');
|
||||
default:
|
||||
return (localStorage.getItem(setting) === 'true');
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
localStorage.setItem('order', JSON.stringify(this.state.items));
|
||||
window.stats.postEvent('setting', 'Widget order');
|
||||
@@ -91,9 +89,15 @@ export default class OrderSettings extends React.PureComponent {
|
||||
<h2>{this.language.sections.order.title}</h2>
|
||||
<span className='modalLink' onClick={this.reset}>{this.language.buttons.reset}</span>
|
||||
<SortableContainer onSortEnd={this.onSortEnd} lockAxis='y' lockToContainerEdges disableAutoscroll>
|
||||
{this.state.items.map((value, index) => (
|
||||
<SortableItem key={`item-${value}`} index={index} value={value} />
|
||||
))}
|
||||
{this.state.items.map((value, index) => {
|
||||
if (!this.enabled(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SortableItem key={`item-${value}`} index={index} value={value} />
|
||||
);
|
||||
})}
|
||||
</SortableContainer>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -8,11 +8,11 @@ export default function QuickLinks() {
|
||||
return (
|
||||
<>
|
||||
<h2>{language.title}</h2>
|
||||
<Switch name='quicklinksenabled' text={window.language.modals.main.settings.enabled} category='quicklinks' element='.quicklinks-container' />
|
||||
<Checkbox name='quicklinksddgProxy' text={window.language.modals.main.settings.sections.background.ddg_image_proxy} element='.other' />
|
||||
<Checkbox name='quicklinksnewtab' text={language.open_new} category='quicklinks' />
|
||||
<Checkbox name='quicklinkstooltip' text={language.tooltip} category='quicklinks' />
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomQuicklinks' min='10' max='400' default='100' display='%' category='quicklinks' element='.quicklinks-container' />
|
||||
<Switch name='quicklinksenabled' text={window.language.modals.main.settings.enabled} category='quicklinks' element='.quicklinks-container'/>
|
||||
<Checkbox name='quicklinksddgProxy' text={window.language.modals.main.settings.sections.background.ddg_image_proxy} category='quicklinks'/>
|
||||
<Checkbox name='quicklinksnewtab' text={language.open_new} category='quicklinks'/>
|
||||
<Checkbox name='quicklinkstooltip' text={language.tooltip} category='quicklinks'/>
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomQuicklinks' min='10' max='400' default='100' display='%' category='quicklinks'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Checkbox from '../Checkbox';
|
||||
import Text from '../Text';
|
||||
@@ -6,7 +6,7 @@ import Switch from '../Switch';
|
||||
import Slider from '../Slider';
|
||||
import Dropdown from '../Dropdown';
|
||||
|
||||
export default class QuoteSettings extends React.PureComponent {
|
||||
export default class QuoteSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -27,8 +27,8 @@ export default class QuoteSettings extends React.PureComponent {
|
||||
if (this.state.quoteType === 'custom') {
|
||||
customSettings = (
|
||||
<>
|
||||
<Text title={quote.custom} name='customQuote' category='quote' element='.quotediv' />
|
||||
<Text title={quote.custom_author} name='customQuoteAuthor' category='quote' element='.quotediv'/>
|
||||
<Text title={quote.custom} name='customQuote' category='quote' />
|
||||
<Text title={quote.custom_author} name='customQuoteAuthor' category='quote'/>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
@@ -60,7 +60,7 @@ export default class QuoteSettings extends React.PureComponent {
|
||||
<option value='custom'>{quote.custom}</option>
|
||||
</Dropdown>
|
||||
{customSettings}
|
||||
<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' />
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomQuote' min='10' max='400' default='100' display='%' category='quote' />
|
||||
|
||||
<h3>{quote.buttons.title}</h3>
|
||||
<Checkbox name='copyButton' text={quote.buttons.copy} category='quote'/>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import Dropdown from '../Dropdown';
|
||||
import Checkbox from '../Checkbox';
|
||||
@@ -7,12 +8,10 @@ import Radio from '../Radio';
|
||||
|
||||
import EventBus from '../../../../../modules/helpers/eventbus';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
const searchEngines = require('../../../../widgets/search/search_engines.json');
|
||||
const autocompleteProviders = require('../../../../widgets/search/autocomplete_providers.json');
|
||||
|
||||
export default class SearchSettings extends React.PureComponent {
|
||||
export default class SearchSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -90,7 +89,7 @@ export default class SearchSettings extends React.PureComponent {
|
||||
<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'/>
|
||||
<Checkbox name='autocomplete' text={search.autocomplete} category='search' />
|
||||
<Radio title={search.autocomplete_provider} options={autocompleteProviders} name='autocompleteProvider' category='search'/>
|
||||
</>
|
||||
);
|
||||
|
||||
59
src/components/modals/main/settings/sections/Stats.jsx
Normal file
59
src/components/modals/main/settings/sections/Stats.jsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Switch from '../Switch';
|
||||
|
||||
import EventBus from '../../../../../modules/helpers/eventbus';
|
||||
|
||||
export default class Stats extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
stats: JSON.parse(localStorage.getItem('statsData')) || {}
|
||||
};
|
||||
this.language = window.language.modals.main.settings.sections.stats;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'stats') {
|
||||
if (localStorage.getItem('stats') === 'false') {
|
||||
localStorage.setItem('statsData', JSON.stringify({}));
|
||||
return this.setState({
|
||||
stats: {}
|
||||
});
|
||||
}
|
||||
this.forceUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
if (localStorage.getItem('stats') === 'false') {
|
||||
return (
|
||||
<>
|
||||
<h2>{window.language.modals.main.settings.reminder.title}</h2>
|
||||
<p>{this.language.warning}</p>
|
||||
<Switch name='stats' text={this.language.usage} category='stats'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{this.language.title}</h2>
|
||||
<p>{this.language.sections.tabs_opened}: {this.state.stats['tabs-opened'] || 0}</p>
|
||||
<p>{this.language.sections.backgrounds_favourited}: {this.state.stats.feature ? this.state.stats.feature['background-favourite'] || 0 : 0}</p>
|
||||
<p>{this.language.sections.backgrounds_downloaded}: {this.state.stats.feature ? this.state.stats.feature['background-download'] || 0 : 0}</p>
|
||||
<p>{this.language.sections.quotes_favourited}: {this.state.stats.feature ? this.state.stats.feature['quoted-favourite'] || 0 : 0}</p>
|
||||
<p>{this.language.sections.quicklinks_added}: {this.state.stats.feature ? this.state.stats.feature['quicklink-add'] || 0 : 0}</p>
|
||||
<p>{this.language.sections.settings_changed}: {this.state.stats.setting ? Object.keys(this.state.stats.setting).length : 0}</p>
|
||||
<p>{this.language.sections.addons_installed}: {this.state.stats.marketplace ? this.state.stats.marketplace['install'] : 0}</p>
|
||||
<Switch name='stats' text={this.language.usage} category='stats'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Checkbox from '../Checkbox';
|
||||
import Dropdown from '../Dropdown';
|
||||
@@ -6,7 +6,7 @@ import Switch from '../Switch';
|
||||
import Radio from '../Radio';
|
||||
import Slider from '../Slider';
|
||||
|
||||
export default class TimeSettings extends React.PureComponent {
|
||||
export default class TimeSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -35,21 +35,21 @@ export default class TimeSettings extends React.PureComponent {
|
||||
const digitalSettings = (
|
||||
<>
|
||||
<h3>{time.digital.title}</h3>
|
||||
<Radio title={time.format} name='timeformat' options={digitalOptions} smallTitle={true} category='clock' element='.clock-container' />
|
||||
<Radio title={time.format} name='timeformat' options={digitalOptions} smallTitle={true} category='clock' />
|
||||
<br/>
|
||||
<Checkbox name='seconds' text={time.digital.seconds} category='clock' element='.clock-container' />
|
||||
<Checkbox name='zero' text={time.digital.zero} category='clock' element='.clock-container' />
|
||||
<Checkbox name='seconds' text={time.digital.seconds} category='clock' />
|
||||
<Checkbox name='zero' text={time.digital.zero} category='clock' />
|
||||
</>
|
||||
);
|
||||
|
||||
const analogSettings = (
|
||||
<>
|
||||
<h3>{time.analogue.title}</h3>
|
||||
<Checkbox name='secondHand' text={time.analogue.second_hand} category='clock' element='.clock-container' />
|
||||
<Checkbox name='minuteHand' text={time.analogue.minute_hand} category='clock' element='.clock-container' />
|
||||
<Checkbox name='hourHand' text={time.analogue.hour_hand} category='clock' element='.clock-container' />
|
||||
<Checkbox name='hourMarks' text={time.analogue.hour_marks} category='clock' element='.clock-container' />
|
||||
<Checkbox name='minuteMarks' text={time.analogue.minute_marks} category='clock' element='.clock-container' />
|
||||
<Checkbox name='secondHand' text={time.analogue.second_hand} category='clock' />
|
||||
<Checkbox name='minuteHand' text={time.analogue.minute_hand} category='clock' />
|
||||
<Checkbox name='hourHand' text={time.analogue.hour_hand} category='clock' />
|
||||
<Checkbox name='hourMarks' text={time.analogue.hour_marks} category='clock' />
|
||||
<Checkbox name='minuteMarks' text={time.analogue.minute_marks} category='clock' />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -63,23 +63,23 @@ export default class TimeSettings extends React.PureComponent {
|
||||
|
||||
const longSettings = (
|
||||
<>
|
||||
<Checkbox name='dayofweek' text={time.date.day_of_week} category='date' element='.date' />
|
||||
<Checkbox name='datenth' text={time.date.datenth} category='date' element='.date' />
|
||||
<Checkbox name='dayofweek' text={time.date.day_of_week} category='date' />
|
||||
<Checkbox name='datenth' text={time.date.datenth} category='date' />
|
||||
</>
|
||||
);
|
||||
|
||||
const shortSettings = (
|
||||
<>
|
||||
<br/>
|
||||
<Dropdown label={time.date.short_format} name='dateFormat' category='date' element='.date'>
|
||||
<Dropdown label={time.date.short_format} name='dateFormat' category='date'>
|
||||
<option value='DMY'>DMY</option>
|
||||
<option value='MDY'>MDY</option>
|
||||
<option value='YMD'>YMD</option>
|
||||
</Dropdown>
|
||||
<br/><br/>
|
||||
<Dropdown label={time.date.short_separator.title} name='shortFormat' category='date' element='.date'>
|
||||
<option value='dots'>{time.date.short_separator.dots}</option>
|
||||
<Dropdown label={time.date.short_separator.title} name='shortFormat' category='date'>
|
||||
<option value='dash'>{time.date.short_separator.dash}</option>
|
||||
<option value='dots'>{time.date.short_separator.dots}</option>
|
||||
<option value='gaps'>{time.date.short_separator.gaps}</option>
|
||||
<option value='slashes'>{time.date.short_separator.slashes}</option>
|
||||
</Dropdown>
|
||||
@@ -96,27 +96,27 @@ export default class TimeSettings extends React.PureComponent {
|
||||
<>
|
||||
<h2>{time.title}</h2>
|
||||
<Switch name='time' text={this.language.enabled} category='clock' element='.clock-container' />
|
||||
<Dropdown label={time.type} name='timeType' onChange={(value) => this.setState({ timeType: value })} category='clock' element='.clock-container'>
|
||||
<Dropdown label={time.type} name='timeType' onChange={(value) => this.setState({ timeType: value })} category='clock'>
|
||||
<option value='digital'>{time.digital.title}</option>
|
||||
<option value='analogue'>{time.analogue.title}</option>
|
||||
<option value='percentageComplete'>{time.percentage_complete}</option>
|
||||
</Dropdown>
|
||||
{timeSettings}
|
||||
{this.state.timeType !== 'analogue' ?
|
||||
<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' />
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomClock' min='10' max='400' default='100' display='%' category='clock'/>
|
||||
: null}
|
||||
|
||||
<h3>{time.date.title}</h3>
|
||||
<Switch name='date' text={this.language.enabled} category='date' element='.date'/>
|
||||
<Dropdown label={time.type} name='dateType' onChange={(value) => this.setState({ dateType: value })} category='date' element='.date'>
|
||||
<Dropdown label={time.type} name='dateType' onChange={(value) => this.setState({ dateType: value })} category='date'>
|
||||
<option value='long'>{time.date.type.long}</option>
|
||||
<option value='short'>{time.date.type.short}</option>
|
||||
</Dropdown>
|
||||
<br/>
|
||||
<Checkbox name='datezero' text={time.digital.zero} category='date' element='.date' />
|
||||
<Checkbox name='weeknumber' text={time.date.week_number} category='date' element='.date'/>
|
||||
<Checkbox name='datezero' text={time.digital.zero} category='date'/>
|
||||
<Checkbox name='weeknumber' text={time.date.week_number} category='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' />
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomDate' min='10' max='400' default='100' display='%' category='date'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Switch from '../Switch';
|
||||
import Radio from '../Radio';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Slider from '../Slider';
|
||||
|
||||
export default class TimeSettings extends React.PureComponent {
|
||||
export default class TimeSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../../../../modules/helpers/eventbus';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import Checkbox from '../../Checkbox';
|
||||
import Dropdown from '../../Dropdown';
|
||||
@@ -11,9 +10,9 @@ import Radio from '../../Radio';
|
||||
|
||||
import ColourSettings from './Colour';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
import EventBus from '../../../../../../modules/helpers/eventbus';
|
||||
|
||||
export default class BackgroundSettings extends React.PureComponent {
|
||||
export default class BackgroundSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -122,7 +121,7 @@ export default class BackgroundSettings extends React.PureComponent {
|
||||
))}
|
||||
</Dropdown>
|
||||
<br/><br/>
|
||||
<Dropdown label={background.source.quality.title} name='apiQuality' category='background' element='.other'>
|
||||
<Dropdown label={background.source.quality.title} name='apiQuality' element='.other'>
|
||||
<option value='original'>{background.source.quality.original}</option>
|
||||
<option value='high'>{background.source.quality.high}</option>
|
||||
<option value='normal'>{background.source.quality.normal}</option>
|
||||
@@ -167,9 +166,9 @@ export default class BackgroundSettings extends React.PureComponent {
|
||||
<>
|
||||
<h2>{background.title}</h2>
|
||||
<Switch name='background' text={this.language.enabled} category='background' element='#backgroundImage' />
|
||||
<Checkbox name='ddgProxy' text={background.ddg_image_proxy} />
|
||||
<Checkbox name='bgtransition' text={background.transition} />
|
||||
<Checkbox name='photoInformation' text={background.photo_information} category='background' element='.other' />
|
||||
<Checkbox name='ddgProxy' text={background.ddg_image_proxy} element='.other' />
|
||||
<Checkbox name='bgtransition' text={background.transition} element='.other' />
|
||||
<Checkbox name='photoInformation' text={background.photo_information} element='.other' />
|
||||
|
||||
<h3>{background.source.title}</h3>
|
||||
<Dropdown label={background.type.title} name='backgroundType' onChange={(value) => this.setState({ backgroundType: value })} category='background'>
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import { PureComponent, Fragment } from 'react';
|
||||
import { ColorPicker } from 'react-color-gradient-picker';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import hexToRgb from '../../../../../../modules/helpers/background/hexToRgb';
|
||||
import rgbToHex from '../../../../../../modules/helpers/background/rgbToHex';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import 'react-color-gradient-picker/dist/index.css';
|
||||
import '../../../scss/settings/react-color-picker-gradient-picker-custom-styles.scss';
|
||||
|
||||
export default class ColourSettings extends React.PureComponent {
|
||||
export default class ColourSettings extends PureComponent {
|
||||
DefaultGradientSettings = { angle: '180', gradient: [{ colour: '#ffb032', stop: 0 }], type: 'linear' };
|
||||
GradientPickerInitalState = undefined;
|
||||
|
||||
@@ -166,10 +164,10 @@ export default class ColourSettings extends React.PureComponent {
|
||||
} else {
|
||||
gradientInputs = this.state.gradientSettings.gradient.map((g, i) => {
|
||||
return (
|
||||
<React.Fragment key={i}>
|
||||
<Fragment key={i}>
|
||||
<input id={'colour_' + i} type='color' name='colour' className='colour' onChange={(event) => this.onGradientChange(event, i)} value={g.colour}></input>
|
||||
<label htmlFor={'colour_' + i} className='customBackgroundHex'>{g.colour}</label>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import Tabs from './backend/Tabs';
|
||||
|
||||
import Added from '../marketplace/sections/Added';
|
||||
import Sideload from '../marketplace/sections/Sideload';
|
||||
|
||||
import Tabs from './backend/Tabs';
|
||||
import Create from '../marketplace/sections/Create';
|
||||
|
||||
export default function Addons() {
|
||||
const addons = window.language.modals.main.addons;
|
||||
@@ -10,6 +11,7 @@ export default function Addons() {
|
||||
<Tabs>
|
||||
<div label={addons.added} name='added'><Added/></div>
|
||||
<div label={addons.sideload} name='sideload'><Sideload/></div>
|
||||
<div label={addons.create.title} name='create'><Create/></div>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import MarketplaceTab from '../marketplace/sections/Marketplace';
|
||||
|
||||
import Tabs from './backend/Tabs';
|
||||
|
||||
import MarketplaceTab from '../marketplace/sections/Marketplace';
|
||||
|
||||
export default function Marketplace() {
|
||||
const marketplace = window.language.modals.main.marketplace;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import Tabs from './backend/Tabs';
|
||||
|
||||
import About from '../settings/sections/About';
|
||||
import Language from '../settings/sections/Language';
|
||||
import Search from '../settings/sections/Search';
|
||||
@@ -12,16 +14,11 @@ 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';
|
||||
import Stats from '../settings/sections/Stats';
|
||||
|
||||
export default function Settings() {
|
||||
const { reminder, sections } = window.language.modals.main.settings;
|
||||
|
||||
let display = 'none';
|
||||
if (localStorage.getItem('showReminder') === 'true') {
|
||||
display = 'block';
|
||||
}
|
||||
const display = (localStorage.getItem('showReminder') === 'true') ? 'block' : 'none';
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -37,6 +34,7 @@ export default function Settings() {
|
||||
<div label={sections.order.title} name='order'><Order/></div>
|
||||
<div label={sections.language.title} name='language'><Language/></div>
|
||||
<div label={sections.advanced.title} name='advanced'><Advanced/></div>
|
||||
<div label={sections.stats.title} name='stats'><Stats/></div>
|
||||
<div label={sections.experimental.title} name='experimental'><Experimental/></div>
|
||||
<div label={sections.changelog} name='changelog'><Changelog/></div>
|
||||
<div label={sections.about.title} name='about'><About/></div>
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
import React from 'react';
|
||||
|
||||
// Navbar
|
||||
import Settings from '@material-ui/icons/SettingsRounded';
|
||||
import Addons from '@material-ui/icons/Widgets';
|
||||
import Marketplace from '@material-ui/icons/ShoppingBasket';
|
||||
|
||||
// Settings
|
||||
import Time from '@material-ui/icons/AccessAlarm';
|
||||
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/FormatPaintOutlined';
|
||||
import Language from '@material-ui/icons/Translate';
|
||||
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';
|
||||
|
||||
// Addons
|
||||
import Sideload from '@material-ui/icons/Code';
|
||||
import Added from '@material-ui/icons/AddCircleOutline';
|
||||
import { memo } from 'react';
|
||||
import {
|
||||
SettingsRounded as Settings,
|
||||
Widgets as Addons,
|
||||
ShoppingBasket as Marketplace,
|
||||
AccessAlarm as Time,
|
||||
EmojiPeopleOutlined as Greeting,
|
||||
FormatQuoteOutlined as Quote,
|
||||
PhotoOutlined as Background,
|
||||
Search,
|
||||
FormatPaintOutlined as Appearance,
|
||||
Translate as Language,
|
||||
NewReleasesOutlined as Changelog,
|
||||
InfoOutlined as About,
|
||||
BugReportOutlined as Experimental,
|
||||
List as Order,
|
||||
CloudOutlined as Weather,
|
||||
SettingsOutlined as Advanced,
|
||||
Link as QuickLinks,
|
||||
AssessmentOutlined as Stats,
|
||||
Code as Sideload,
|
||||
AddCircleOutline as Added,
|
||||
CreateNewFolderOutlined as Create
|
||||
} from '@material-ui/icons';
|
||||
|
||||
function Tab(props) {
|
||||
let className = 'tab-list-item';
|
||||
@@ -60,6 +58,7 @@ function Tab(props) {
|
||||
case settings.order.title: icon = <Order/>; break;
|
||||
case settings.language.title: icon = <Language/>; divider = true; break;
|
||||
case settings.advanced.title: icon = <Advanced/>; break;
|
||||
case settings.stats.title: icon = <Stats/>; break;
|
||||
case settings.experimental.title: icon = <Experimental/>; divider = true; break;
|
||||
case settings.changelog: icon = <Changelog/>; break;
|
||||
case settings.about.title: icon = <About/>; break;
|
||||
@@ -67,6 +66,7 @@ function Tab(props) {
|
||||
// Addons
|
||||
case addons.added: icon = <Added/>; break;
|
||||
case addons.sideload: icon = <Sideload/>; break;
|
||||
case addons.create.title: icon = <Create/>; break;
|
||||
|
||||
// Marketplace
|
||||
case marketplace.photo_packs: icon = <Background/>; break;
|
||||
@@ -92,4 +92,4 @@ function Tab(props) {
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Tab);
|
||||
export default memo(Tab);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Tab from './Tab';
|
||||
import ErrorBoundary from '../../../ErrorBoundary';
|
||||
|
||||
export default class Tabs extends React.PureComponent {
|
||||
export default class Tabs extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -15,7 +15,7 @@ export default class Tabs extends React.PureComponent {
|
||||
|
||||
onClick = (tab, name) => {
|
||||
if (name !== this.state.currentName) {
|
||||
window.stats.postEvent('tab', `Changed ${this.state.currentName} to ${name}`);
|
||||
window.stats.postEvent('tab', `Opened ${name}`);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
@@ -7,7 +7,7 @@ import ProgressBar from './ProgressBar';
|
||||
|
||||
import './welcome.scss';
|
||||
|
||||
export default class WelcomeModal extends React.PureComponent {
|
||||
export default class WelcomeModal extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -81,6 +81,10 @@ export default class WelcomeModal extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='welcomeContent'>
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { CloudUpload, AutoAwesome, LightMode, DarkMode } from '@material-ui/icons';
|
||||
|
||||
import Radio from '../main/settings/Radio';
|
||||
import Checkbox from '../main/settings/Checkbox';
|
||||
import FileUpload from '../main/settings/FileUpload';
|
||||
|
||||
import UploadIcon from '@material-ui/icons/CloudUpload';
|
||||
import AutoIcon from '@material-ui/icons/AutoAwesome';
|
||||
import LightModeIcon from '@material-ui/icons/LightMode';
|
||||
import DarkModeIcon from '@material-ui/icons/DarkMode';
|
||||
|
||||
import SettingsFunctions from '../../../modules/helpers/settings';
|
||||
import SettingsFunctionsModal from '../../../modules/helpers/settings/modals';
|
||||
import { loadSettings } from '../../../modules/helpers/settings';
|
||||
import { importSettings } from '../../../modules/helpers/settings/modals';
|
||||
|
||||
const languages = require('../../../modules/languages.json');
|
||||
const default_settings = require('../../../modules/default_settings.json');
|
||||
|
||||
export default class WelcomeSections extends React.PureComponent {
|
||||
export default class WelcomeSections extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -40,7 +36,7 @@ export default class WelcomeSections extends React.PureComponent {
|
||||
});
|
||||
|
||||
localStorage.setItem('theme', type);
|
||||
SettingsFunctions.loadSettings(true);
|
||||
loadSettings(true);
|
||||
}
|
||||
|
||||
getSetting(name) {
|
||||
@@ -49,11 +45,12 @@ export default class WelcomeSections extends React.PureComponent {
|
||||
}
|
||||
|
||||
importSettings(e) {
|
||||
SettingsFunctionsModal.importSettings(e);
|
||||
importSettings(e);
|
||||
|
||||
let settings = [];
|
||||
const data = JSON.parse(e.target.result);
|
||||
Object.keys(data).forEach((setting) => {
|
||||
// language and theme already shown, the others are only used internally
|
||||
if (setting === 'language' || setting === 'theme'|| setting === 'firstRun' || setting === 'showWelcome' || setting === 'showReminder') {
|
||||
return;
|
||||
}
|
||||
@@ -124,12 +121,12 @@ export default class WelcomeSections extends React.PureComponent {
|
||||
const chooseLanguage = (
|
||||
<>
|
||||
<h1>{language.sections.language.title}</h1>
|
||||
<p>{language.sections.language.description}</p>
|
||||
<p>{language.sections.language.description} <a href={window.constants.TRANSLATIONS_URL} className='resetLink' target='_blank' rel='noopener noreferrer'>GitHub</a>!</p>
|
||||
<Radio name='language' options={languages} category='welcomeLanguage'/>
|
||||
</>
|
||||
);
|
||||
|
||||
const { appearance, advanced, background, quicklinks } = window.language.modals.main.settings.sections;
|
||||
const { appearance, advanced, background, quicklinks, stats } = window.language.modals.main.settings.sections;
|
||||
const languageSettings = window.language.modals.main.settings.sections.language;
|
||||
|
||||
const theme = (
|
||||
@@ -138,16 +135,16 @@ export default class WelcomeSections extends React.PureComponent {
|
||||
<p>{language.sections.theme.description}</p>
|
||||
<div className='themesToggleArea'>
|
||||
<div className={this.state.autoClass} onClick={() => this.changeTheme('auto')}>
|
||||
<AutoIcon/>
|
||||
<AutoAwesome/>
|
||||
<span>{appearance.theme.auto}</span>
|
||||
</div>
|
||||
<div className='options'>
|
||||
<div className={this.state.lightClass} onClick={() => this.changeTheme('light')}>
|
||||
<LightModeIcon/>
|
||||
<LightMode/>
|
||||
<span>{appearance.theme.light}</span>
|
||||
</div>
|
||||
<div className={this.state.darkClass} onClick={() => this.changeTheme('dark')}>
|
||||
<DarkModeIcon/>
|
||||
<DarkMode/>
|
||||
<span>{appearance.theme.dark}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -162,7 +159,7 @@ export default class WelcomeSections extends React.PureComponent {
|
||||
<h1>{language.sections.settings.title}</h1>
|
||||
<p>{language.sections.settings.description}</p>
|
||||
<button className='upload' onClick={() => document.getElementById('file-input').click()}>
|
||||
<UploadIcon/>
|
||||
<CloudUpload/>
|
||||
<br/>
|
||||
<span>{window.language.modals.main.settings.buttons.import}</span>
|
||||
</button>
|
||||
@@ -181,6 +178,8 @@ export default class WelcomeSections extends React.PureComponent {
|
||||
<Checkbox name='quicklinksddgProxy' text={background.ddg_image_proxy + ' (' + quicklinks.title + ')'}/>
|
||||
<Checkbox name='ddgProxy' text={background.ddg_image_proxy + ' (' + background.title + ')'}/>
|
||||
<p>{language.sections.privacy.ddg_proxy_description}</p>
|
||||
<Checkbox name='stats' text={stats.usage}/>
|
||||
<p>{language.sections.privacy.stats_description}</p>
|
||||
<h3 className='quicktip'>{language.sections.privacy.links.title}</h3>
|
||||
<a className='privacy' href={window.constants.PRIVACY_URL} target='_blank' rel='noopener noreferrer'>{language.sections.privacy.links.privacy_policy}</a>
|
||||
<br/><br/>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../modules/helpers/eventbus';
|
||||
import { PureComponent, Fragment, Suspense, lazy } from 'react';
|
||||
|
||||
import Clock from './time/Clock';
|
||||
import Greeting from './greeting/Greeting';
|
||||
@@ -9,10 +7,13 @@ import Search from './search/Search';
|
||||
import QuickLinks from './quicklinks/QuickLinks';
|
||||
import Date from './time/Date';
|
||||
|
||||
const Weather = React.lazy(() => import('./weather/Weather'));
|
||||
import EventBus from '../../modules/helpers/eventbus';
|
||||
|
||||
const Weather = lazy(() => import('./weather/Weather'));
|
||||
const renderLoader = () => <></>;
|
||||
|
||||
export default class Widgets extends React.PureComponent {
|
||||
export default class Widgets extends PureComponent {
|
||||
online = (localStorage.getItem('offlineMode') === 'false');
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -24,7 +25,7 @@ export default class Widgets extends React.PureComponent {
|
||||
greeting: this.enabled('greeting') ? <Greeting/> : null,
|
||||
quote: this.enabled('quote') ? <Quote/> : null,
|
||||
date: this.enabled('date') ? <Date/> : null,
|
||||
quicklinks: this.enabled('quicklinksenabled') ? <QuickLinks/> : null
|
||||
quicklinks: this.enabled('quicklinksenabled') && this.online ? <QuickLinks/> : null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,7 +54,7 @@ export default class Widgets extends React.PureComponent {
|
||||
|
||||
if (this.state.order) {
|
||||
this.state.order.forEach((element) => {
|
||||
elements.push(<React.Fragment key={element}>{this.widgets[element]}</React.Fragment>);
|
||||
elements.push(<Fragment key={element}>{this.widgets[element]}</Fragment>);
|
||||
});
|
||||
} else {
|
||||
// prevent error
|
||||
@@ -62,11 +63,11 @@ export default class Widgets extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<div id='widgets'>
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
{this.enabled('searchBar') ? <Search/> : null}
|
||||
{elements}
|
||||
{this.enabled('weatherEnabled') && (localStorage.getItem('offlineMode') === 'false') ? <Weather/> : null}
|
||||
</React.Suspense>
|
||||
{this.enabled('weatherEnabled') && this.online ? <Weather/> : null}
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
// todo: rewrite this mess
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
import Interval from '../../../modules/helpers/interval';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import PhotoInformation from './PhotoInformation';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
import Interval from '../../../modules/helpers/interval';
|
||||
import { videoCheck, offlineBackground, gradientStyleBuilder } from '../../../modules/helpers/background/widget';
|
||||
|
||||
import './scss/index.scss';
|
||||
|
||||
export default class Background extends React.PureComponent {
|
||||
export default class Background extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -25,49 +26,6 @@ export default class Background extends React.PureComponent {
|
||||
this.language = window.language.widgets.background;
|
||||
}
|
||||
|
||||
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({
|
||||
type: 'colour',
|
||||
style: style
|
||||
});
|
||||
}
|
||||
|
||||
videoCheck(url) {
|
||||
return url.startsWith('data:video/') || url.endsWith('.mp4') || url.endsWith('.webm') || url.endsWith('.ogg');
|
||||
}
|
||||
|
||||
offlineBackground() {
|
||||
const offlineImages = require('./offline_images.json');
|
||||
|
||||
// Get all photographers from the keys in offlineImages.json
|
||||
const photographers = Object.keys(offlineImages);
|
||||
const photographer = photographers[Math.floor(Math.random() * photographers.length)];
|
||||
|
||||
const randomImage = offlineImages[photographer].photo[
|
||||
Math.floor(Math.random() * offlineImages[photographer].photo.length)
|
||||
];
|
||||
|
||||
const object = {
|
||||
url: `./offline-images/${randomImage}.webp`,
|
||||
photoInfo: {
|
||||
offline: true,
|
||||
credit: photographer
|
||||
}
|
||||
}
|
||||
|
||||
this.setState(object);
|
||||
localStorage.setItem('currentBackground', JSON.stringify(object));
|
||||
}
|
||||
|
||||
setBackground() {
|
||||
const backgroundImage = document.getElementById('backgroundImage');
|
||||
|
||||
@@ -81,7 +39,7 @@ export default class Background extends React.PureComponent {
|
||||
photoInformation.style.display = 'block';
|
||||
}
|
||||
backgroundImage.style.background = null;
|
||||
return backgroundImage.style.background = `url(${url})`;
|
||||
return backgroundImage.style.background = `url(${url})`;
|
||||
}
|
||||
|
||||
// firstly we set the background as hidden and make sure there is no background set currently
|
||||
@@ -124,23 +82,26 @@ export default class Background extends React.PureComponent {
|
||||
offline = true;
|
||||
}
|
||||
|
||||
const setFavourited = (favourited) => {
|
||||
this.setState({
|
||||
url: favourited.url,
|
||||
photoInfo: {
|
||||
credit: favourited.credit,
|
||||
location: favourited.location,
|
||||
camera: favourited.camera
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
switch (localStorage.getItem('backgroundType')) {
|
||||
case 'api':
|
||||
if (offline) {
|
||||
return this.offlineBackground();
|
||||
return this.setState(offlineBackground());
|
||||
}
|
||||
|
||||
// favourite button
|
||||
const favourited = JSON.parse(localStorage.getItem('favourite'));
|
||||
if (favourited) {
|
||||
return this.setState({
|
||||
url: favourited.url,
|
||||
photoInfo: {
|
||||
credit: favourited.credit,
|
||||
location: favourited.location,
|
||||
camera: favourited.camera
|
||||
}
|
||||
});
|
||||
return setFavourited(favourited);
|
||||
}
|
||||
|
||||
// API background
|
||||
@@ -166,8 +127,8 @@ export default class Background extends React.PureComponent {
|
||||
data = await (await fetch(requestURL)).json();
|
||||
} catch (e) {
|
||||
// if requesting to the API fails, we get an offline image
|
||||
return this.offlineBackground();
|
||||
}
|
||||
return this.setState(offlineBackground());
|
||||
}
|
||||
|
||||
let credit = data.photographer;
|
||||
let photoURL, photographerURL;
|
||||
@@ -195,7 +156,8 @@ export default class Background extends React.PureComponent {
|
||||
photographerURL: photographerURL,
|
||||
photoURL: photoURL
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.setState(object);
|
||||
|
||||
localStorage.setItem('currentBackground', JSON.stringify(object));
|
||||
@@ -217,7 +179,7 @@ export default class Background extends React.PureComponent {
|
||||
}
|
||||
|
||||
if (typeof gradientSettings === 'object' && gradientSettings !== null) {
|
||||
return this.gradientStyleBuilder(gradientSettings);
|
||||
return this.setState(gradientStyleBuilder(gradientSettings));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -226,14 +188,14 @@ export default class Background extends React.PureComponent {
|
||||
|
||||
// allow users to use offline images
|
||||
if (offline && !customBackground.startsWith('data:')) {
|
||||
return this.offlineBackground();
|
||||
return this.setState(offlineBackground());
|
||||
}
|
||||
|
||||
if (customBackground !== '' && customBackground !== 'undefined') {
|
||||
this.setState({
|
||||
url: customBackground,
|
||||
type: 'custom',
|
||||
video: this.videoCheck(customBackground),
|
||||
video: videoCheck(customBackground),
|
||||
photoInfo: {
|
||||
hidden: true
|
||||
}
|
||||
@@ -243,7 +205,12 @@ export default class Background extends React.PureComponent {
|
||||
|
||||
case 'photo_pack':
|
||||
if (offline) {
|
||||
return this.offlineBackground();
|
||||
return this.setState(offlineBackground());
|
||||
}
|
||||
|
||||
const photofavourited = JSON.parse(localStorage.getItem('favourite'));
|
||||
if (photofavourited) {
|
||||
return setFavourited(photofavourited);
|
||||
}
|
||||
|
||||
const photoPack = JSON.parse(localStorage.getItem('photo_packs'));
|
||||
@@ -260,7 +227,7 @@ export default class Background extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -302,7 +269,7 @@ export default class Background extends React.PureComponent {
|
||||
return element.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// video backgrounds
|
||||
if (this.state.video === true) {
|
||||
document.getElementById('backgroundVideo').style.display = 'block';
|
||||
@@ -352,34 +319,36 @@ export default class Background extends React.PureComponent {
|
||||
}
|
||||
|
||||
const interval = localStorage.getItem('backgroundchange');
|
||||
if (interval && interval !== 'refresh' && localStorage.getItem('backgroundType') === 'api') {
|
||||
Interval(() => {
|
||||
try {
|
||||
document.getElementById('backgroundImage').classList.remove('fade-in');
|
||||
document.getElementsByClassName('photoInformation')[0].classList.remove('fade-in');
|
||||
} catch (e) {
|
||||
// Disregard exception
|
||||
}
|
||||
this.getBackground();
|
||||
}, Number(interval), 'background');
|
||||
|
||||
try {
|
||||
// todo: refactor this mess
|
||||
const current = JSON.parse(localStorage.getItem('currentBackground'));
|
||||
const offline = localStorage.getItem('offlineMode')
|
||||
if (current.url.startsWith('http') && offline === 'false') {
|
||||
this.setState(current);
|
||||
} else if (current.url.startsWith('http')) {
|
||||
this.offlineBackground();
|
||||
} else {
|
||||
if (offline === 'false') {
|
||||
localStorage.removeItem('currentBackground');
|
||||
return this.getBackground();
|
||||
if (interval && interval !== 'refresh') {
|
||||
if (localStorage.getItem('backgroundType') === 'api') {
|
||||
Interval(() => {
|
||||
try {
|
||||
document.getElementById('backgroundImage').classList.remove('fade-in');
|
||||
document.getElementsByClassName('photoInformation')[0].classList.remove('fade-in');
|
||||
} catch (e) {
|
||||
// Disregard exception
|
||||
}
|
||||
this.setState(current);
|
||||
this.getBackground();
|
||||
}, Number(interval), 'background');
|
||||
|
||||
try {
|
||||
// todo: refactor this mess
|
||||
const current = JSON.parse(localStorage.getItem('currentBackground'));
|
||||
const offline = localStorage.getItem('offlineMode');
|
||||
if (current.url.startsWith('http') && offline === 'false') {
|
||||
this.setState(current);
|
||||
} else if (current.url.startsWith('http')) {
|
||||
this.setState(offlineBackground());
|
||||
} else {
|
||||
if (offline === 'false') {
|
||||
localStorage.removeItem('currentBackground');
|
||||
return this.getBackground();
|
||||
}
|
||||
this.setState(current);
|
||||
}
|
||||
} catch (e) {
|
||||
this.setBackground();
|
||||
}
|
||||
} catch (e) {
|
||||
this.setBackground();
|
||||
}
|
||||
} else {
|
||||
this.getBackground();
|
||||
@@ -395,6 +364,10 @@ export default class Background extends React.PureComponent {
|
||||
this.setBackground();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.video === true) {
|
||||
const enabled = (setting) => {
|
||||
@@ -413,8 +386,8 @@ export default class Background extends React.PureComponent {
|
||||
return (
|
||||
<>
|
||||
<div style={{ WebkitFilter: `blur(${localStorage.getItem('blur')}px) brightness(${localStorage.getItem('brightness')}%) ${backgroundFilter ? backgroundFilter + '(' + localStorage.getItem('backgroundFilterAmount') + '%)' : ''}` }} id='backgroundImage'/>
|
||||
{(this.state.photoInfo.credit !== '') ?
|
||||
<PhotoInformation className={this.props.photoInformationClass} info={this.state.photoInfo} api={this.state.currentAPI} url={this.state.url}/>
|
||||
{(this.state.photoInfo.credit !== '') ?
|
||||
<PhotoInformation info={this.state.photoInfo} api={this.state.currentAPI} url={this.state.url}/>
|
||||
: null}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { Star, StarBorder } from '@material-ui/icons';
|
||||
|
||||
import Tooltip from '../../helpers/tooltip/Tooltip';
|
||||
|
||||
import StarIcon from '@material-ui/icons/Star';
|
||||
import StarIcon2 from '@material-ui/icons/StarBorder';
|
||||
|
||||
export default class Favourite extends React.PureComponent {
|
||||
export default class Favourite extends PureComponent {
|
||||
buttons = {
|
||||
favourited: <StarIcon onClick={() => this.favourite()} className='topicons' />,
|
||||
unfavourited: <StarIcon2 onClick={() => this.favourite()} className='topicons' />
|
||||
favourited: <Star onClick={() => this.favourite()} className='topicons' />,
|
||||
unfavourited: <StarBorder onClick={() => this.favourite()} className='topicons' />
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@@ -31,12 +29,17 @@ export default class Favourite extends React.PureComponent {
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// photo information now hides information if it isn't sent, unless if photoinformation hover is hidden
|
||||
const location = document.getElementById('infoLocation');
|
||||
const camera = document.getElementById('infoCamera');
|
||||
|
||||
localStorage.setItem('favourite', JSON.stringify({
|
||||
url: url,
|
||||
credit: document.getElementById('credit').textContent,
|
||||
location: document.getElementById('infoLocation').textContent,
|
||||
camera: document.getElementById('infoCamera').textContent
|
||||
location: location ? location.innerText : 'N/A',
|
||||
camera: camera ? camera.innerText : 'N/A',
|
||||
resolution: document.getElementById('infoResolution').textContent
|
||||
}));
|
||||
|
||||
this.setState({
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { Fullscreen } from '@material-ui/icons';
|
||||
|
||||
import Tooltip from '../../helpers/tooltip/Tooltip';
|
||||
|
||||
import FullscreenIcon from '@material-ui/icons/Fullscreen';
|
||||
|
||||
export default class Maximise extends React.PureComponent {
|
||||
export default class Maximise extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -57,7 +56,7 @@ export default class Maximise extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<Tooltip title={window.language.modals.main.settings.sections.background.buttons.view}>
|
||||
<FullscreenIcon onClick={this.maximise} className='topicons' />
|
||||
<Fullscreen onClick={this.maximise} className='topicons' />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import React from 'react';
|
||||
|
||||
import Info from '@material-ui/icons/Info';
|
||||
import Location from '@material-ui/icons/LocationOn';
|
||||
import Camera from '@material-ui/icons/PhotoCamera';
|
||||
import Resolution from '@material-ui/icons/Crop';
|
||||
import Photographer from '@material-ui/icons/Person';
|
||||
import Download from '@material-ui/icons/GetApp';
|
||||
import { useState, Fragment } from 'react';
|
||||
import { Info, LocationOn, PhotoCamera, Crop as Resolution, Person as Photographer, GetApp as Download } from '@material-ui/icons';
|
||||
|
||||
const toDataURL = async (url) => {
|
||||
const res = await fetch(url);
|
||||
return URL.createObjectURL(await res.blob());
|
||||
};
|
||||
|
||||
const formatText = (text) => {
|
||||
return text.toLowerCase().replaceAll(',', '').replaceAll(' ', '-');
|
||||
};
|
||||
|
||||
const downloadImage = async (info) => {
|
||||
const link = document.createElement('a');
|
||||
link.href = await toDataURL(info.url);
|
||||
// todo: make this a bit cleaner
|
||||
link.download = `mue-${info.credit.toLowerCase().replaceAll(' ', '-')}-${info.location.toLowerCase().replaceAll(',', '').replaceAll(' ', '-')}.jpg`;
|
||||
link.download = `mue-${formatText(info.credit)}-${formatText(info.location)}.jpg`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
@@ -24,8 +21,8 @@ const downloadImage = async (info) => {
|
||||
};
|
||||
|
||||
export default function PhotoInformation(props) {
|
||||
const [width, setWidth] = React.useState(0);
|
||||
const [height, setHeight] = React.useState(0);
|
||||
const [width, setWidth] = useState(0);
|
||||
const [height, setHeight] = useState(0);
|
||||
|
||||
const language = window.language.widgets.background;
|
||||
|
||||
@@ -55,34 +52,51 @@ export default function PhotoInformation(props) {
|
||||
img.onload = (event) => {
|
||||
setWidth(event.target.width);
|
||||
setHeight(event.target.height);
|
||||
}
|
||||
};
|
||||
img.src = (localStorage.getItem('ddgProxy') === 'true' && !props.info.offline && !props.url.startsWith('data:')) ? window.constants.DDG_IMAGE_PROXY + props.url : props.url;
|
||||
|
||||
// info is still there because we want the favourite button to work
|
||||
if (localStorage.getItem('photoInformation') === 'false') {
|
||||
return (
|
||||
<div className='photoInformation'>
|
||||
<h1>{photo} <span id='credit'>{credit}</span></h1>
|
||||
<div style={{ display: 'none' }}>
|
||||
<span id='infoLocation'>{props.info.location || 'N/A'}</span>
|
||||
<span id='infoCamera'>{props.info.camera || 'N/A'}</span>
|
||||
<span id='infoResolution'>{width}x{height}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='photoInformation'>
|
||||
<h1>{photo} <span id='credit'>{credit}</span></h1>
|
||||
{localStorage.getItem('photoInformation') !== 'false' ?
|
||||
<>
|
||||
<Info className='photoInformationHover'/>
|
||||
<div className={props.className || 'infoCard'}>
|
||||
<Info className='infoIcon'/>
|
||||
<h1>{language.information}</h1>
|
||||
<hr/>
|
||||
<Location/>
|
||||
<span id='infoLocation'>{props.info.location || 'N/A'}</span>
|
||||
<Camera/>
|
||||
<span id='infoCamera'>{props.info.camera || 'N/A'}</span>
|
||||
<Resolution/>
|
||||
<span id='infoResolution'>{width}x{height}</span>
|
||||
<Photographer/>
|
||||
<span>{photographer}</span>
|
||||
{(localStorage.getItem('downloadbtn') === 'true') && !props.info.offline && !props.info.photographerURL ?
|
||||
<>
|
||||
<Download/>
|
||||
<span className='download' onClick={() => downloadImage(props.info)}>{language.download}</span>
|
||||
</> : null}
|
||||
</div>
|
||||
</> : null}
|
||||
<Info className='photoInformationHover'/>
|
||||
<div className='infoCard'>
|
||||
<Info className='infoIcon'/>
|
||||
<h1>{language.information}</h1>
|
||||
<hr/>
|
||||
{/* fix console error by using fragment and key */}
|
||||
{props.info.location && props.info.location !== 'N/A' ? <Fragment key='location'>
|
||||
<LocationOn/>
|
||||
<span id='infoLocation'>{props.info.location}</span>
|
||||
</Fragment> : null}
|
||||
{props.info.camera && props.info.camera !== 'N/A' ? <Fragment key='camera'>
|
||||
<PhotoCamera/>
|
||||
<span id='infoCamera'>{props.info.camera}</span>
|
||||
</Fragment> : null}
|
||||
<Resolution/>
|
||||
<span id='infoResolution'>{width}x{height}</span>
|
||||
<Photographer/>
|
||||
<span>{photographer}</span>
|
||||
{(localStorage.getItem('downloadbtn') === 'true') && !props.info.offline && !props.info.photographerURL ?
|
||||
<>
|
||||
<Download/>
|
||||
<span className='download' onClick={() => downloadImage(props.info)}>{language.download}</span>
|
||||
</>
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { utcToZonedTime } from 'date-fns-tz';
|
||||
import { nth, convertTimezone } from '../../../modules/helpers/date';
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import dtf from '../../../modules/helpers/date';
|
||||
|
||||
import './greeting.scss';
|
||||
|
||||
export default class Greeting extends React.PureComponent {
|
||||
export default class Greeting extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -50,8 +48,8 @@ export default class Greeting extends React.PureComponent {
|
||||
this.timer = setTimeout(() => {
|
||||
let now = new Date();
|
||||
const timezone = localStorage.getItem('timezone');
|
||||
if (timezone) {
|
||||
now = utcToZonedTime(now, timezone);
|
||||
if (timezone && timezone !== 'auto') {
|
||||
now = convertTimezone(now, timezone);
|
||||
}
|
||||
|
||||
const hour = now.getHours();
|
||||
@@ -66,12 +64,12 @@ export default class Greeting extends React.PureComponent {
|
||||
message = this.language.afternoon;
|
||||
}
|
||||
|
||||
// Events
|
||||
message = this.doEvents(now, message);
|
||||
|
||||
// Events and custom
|
||||
const custom = localStorage.getItem('defaultGreetingMessage');
|
||||
if (custom === 'false') {
|
||||
message = '';
|
||||
} else {
|
||||
message = this.doEvents(now, message);
|
||||
}
|
||||
|
||||
// Name
|
||||
@@ -84,18 +82,23 @@ export default class Greeting extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
if (custom === 'false') {
|
||||
const birthday = localStorage.getItem('birthdayenabled');
|
||||
|
||||
if (custom === 'false' && birthday !== 'true') {
|
||||
name = name.replace(',', '');
|
||||
}
|
||||
|
||||
// Birthday
|
||||
const birth = new Date(localStorage.getItem('birthday'));
|
||||
if (localStorage.getItem('birthdayenabled') === 'true' && birth.getDate() === now.getDate() && birth.getMonth() === now.getMonth()) {
|
||||
if (localStorage.getItem('birthdayage') === 'true') {
|
||||
const text = this.language.birthday.split(' ');
|
||||
message = `${text[0]} ${dtf.nth(this.calculateAge(birth))} ${text[1]}`;
|
||||
} else {
|
||||
message = this.language.birthday;
|
||||
if (birthday === 'true') {
|
||||
const birth = new Date(localStorage.getItem('birthday'));
|
||||
|
||||
if (birth.getDate() === now.getDate() && birth.getMonth() === now.getMonth()) {
|
||||
if (localStorage.getItem('birthdayage') === 'true') {
|
||||
const text = this.language.birthday.split(' ');
|
||||
message = `${text[0]} ${nth(this.calculateAge(birth))} ${text[1]}`;
|
||||
} else {
|
||||
message = this.language.birthday;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +136,7 @@ export default class Greeting extends React.PureComponent {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.remove('refresh');
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import RefreshIcon from '@material-ui/icons/RefreshRounded';
|
||||
import Gear from '@material-ui/icons/SettingsRounded';
|
||||
import NotesIcon from '@material-ui/icons/AssignmentRounded';
|
||||
import Report from '@material-ui/icons/SmsFailed';
|
||||
import { PureComponent } from 'react';
|
||||
import { RefreshRounded, SettingsRounded, AssignmentRounded as NotesRounded, SmsFailed as Report } from '@material-ui/icons';
|
||||
|
||||
import Notes from './Notes';
|
||||
import Maximise from '../background/Maximise';
|
||||
@@ -14,20 +10,27 @@ import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import './scss/index.scss';
|
||||
|
||||
export default class Navbar extends React.PureComponent {
|
||||
export default class Navbar extends PureComponent {
|
||||
setZoom() {
|
||||
const zoomNavbar = Number((localStorage.getItem('zoomNavbar') || 100) / 100);
|
||||
const navbarIcons = document.querySelectorAll('.navbar-container');
|
||||
for (let i = 0; i < navbarIcons.length; i++) {
|
||||
navbarIcons[i].style.fontSize = `${zoomNavbar}em`;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'navbar') {
|
||||
if (data === 'navbar' || data === 'background') {
|
||||
this.forceUpdate();
|
||||
this.setZoom();
|
||||
}
|
||||
});
|
||||
|
||||
this.setZoom();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (localStorage.getItem('showWelcome') !== 'false') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const backgroundEnabled = (localStorage.getItem('background') === 'true');
|
||||
|
||||
return (
|
||||
@@ -37,7 +40,7 @@ export default class Navbar extends React.PureComponent {
|
||||
|
||||
{(localStorage.getItem('notesEnabled') === 'true') ?
|
||||
<div className='notes'>
|
||||
<NotesIcon className='topicons'/>
|
||||
<NotesRounded className='topicons'/>
|
||||
<Notes/>
|
||||
</div>
|
||||
: null}
|
||||
@@ -50,12 +53,12 @@ export default class Navbar extends React.PureComponent {
|
||||
|
||||
{(localStorage.getItem('refresh') === 'true') ?
|
||||
<Tooltip title={window.language.widgets.navbar.tooltips.refresh}>
|
||||
<RefreshIcon className='refreshicon topicons' onClick={() => window.location.reload()}/>
|
||||
<RefreshRounded className='refreshicon topicons' onClick={() => window.location.reload()}/>
|
||||
</Tooltip>
|
||||
: null}
|
||||
|
||||
<Tooltip title={window.language.modals.main.navbar.settings}>
|
||||
<Gear className='settings-icon topicons' onClick={() => this.props.openModal('mainModal')}/>
|
||||
<SettingsRounded className='settings-icon topicons' onClick={() => this.props.openModal('mainModal')}/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
import { PureComponent } from 'react';
|
||||
import { FileCopyRounded, AssignmentRounded as NotesRounded, PushPin }from '@material-ui/icons';
|
||||
import TextareaAutosize from '@material-ui/core/TextareaAutosize';
|
||||
|
||||
import CopyIcon from '@material-ui/icons/FileCopyRounded';
|
||||
import NotesIcon from '@material-ui/icons/AssignmentRounded';
|
||||
import Pin from '@material-ui/icons/PushPin';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export default class Notes extends React.PureComponent {
|
||||
export default class Notes extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -57,12 +52,12 @@ export default class Notes extends React.PureComponent {
|
||||
return (
|
||||
<span id='noteContainer' className='notescontainer' style={{ visibility: this.state.visibility }}>
|
||||
<div className='topbarnotes'>
|
||||
<NotesIcon/>
|
||||
<NotesRounded/>
|
||||
<h3>{this.language.title}</h3>
|
||||
</div>
|
||||
<TextareaAutosize rowsmax={50} placeholder={this.language.placeholder} value={this.state.notes} onChange={this.setNotes}/>
|
||||
<button onClick={() => this.pin()} className='pinNote'><Pin/></button>
|
||||
<button onClick={() => this.copy()} className='copyNote'><CopyIcon/></button>
|
||||
<button onClick={() => this.pin()} className='pinNote'><PushPin/></button>
|
||||
<button onClick={() => this.copy()} className='copyNote'><FileCopyRounded/></button>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
svg {
|
||||
float: left;
|
||||
font-size: 1em !important;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
@@ -45,7 +46,7 @@ textarea {
|
||||
|
||||
.topbarnotes {
|
||||
svg {
|
||||
font-size: 46px;
|
||||
font-size: 46px !important;
|
||||
padding: 9px;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
import { TextareaAutosize } from '@material-ui/core';
|
||||
|
||||
import Tooltip from '../../helpers/tooltip/Tooltip';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import Tooltip from '../../helpers/tooltip/Tooltip';
|
||||
import TextareaAutosize from '@material-ui/core/TextareaAutosize';
|
||||
|
||||
import './quicklinks.scss';
|
||||
|
||||
export default class QuickLinks extends React.PureComponent {
|
||||
export default class QuickLinks extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -24,8 +24,8 @@ export default class QuickLinks extends React.PureComponent {
|
||||
deleteLink(key, event) {
|
||||
event.preventDefault();
|
||||
|
||||
let data = JSON.parse(localStorage.getItem('quicklinks'));
|
||||
data = data.filter((i) => i.key !== key);
|
||||
// remove link from array
|
||||
const data = JSON.parse(localStorage.getItem('quicklinks')).filter((i) => i.key !== key);
|
||||
|
||||
localStorage.setItem('quicklinks', JSON.stringify(data));
|
||||
this.setState({
|
||||
@@ -36,7 +36,7 @@ export default class QuickLinks extends React.PureComponent {
|
||||
}
|
||||
|
||||
addLink = () => {
|
||||
let data = JSON.parse(localStorage.getItem('quicklinks'));
|
||||
const data = JSON.parse(localStorage.getItem('quicklinks'));
|
||||
let url = this.state.url;
|
||||
|
||||
let nameError, urlError;
|
||||
@@ -81,38 +81,25 @@ export default class QuickLinks extends React.PureComponent {
|
||||
|
||||
// make sure image is correct size
|
||||
const element = document.querySelector('.quicklinks-container');
|
||||
const images = element.getElementsByTagName('img');
|
||||
for (const img of images) {
|
||||
img.style.height = `${0.87 * Number((localStorage.getItem('zoomQuicklinks') || 100) / 100)}em`;
|
||||
};
|
||||
this.setZoom(element);
|
||||
}
|
||||
|
||||
toggleAdd = () => {
|
||||
if (this.state.showAddLink === 'hidden') {
|
||||
this.setState({
|
||||
showAddLink: 'visible'
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
showAddLink: 'hidden'
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
showAddLink: (this.state.showAddLink === 'hidden') ? 'visible' : 'hidden'
|
||||
});
|
||||
}
|
||||
|
||||
// widget zoom
|
||||
setZoom(element) {
|
||||
const images = element.getElementsByTagName('img');
|
||||
|
||||
for (const img of images) {
|
||||
img.style.height = `${0.87 * Number((localStorage.getItem('zoomQuicklinks') || 100) / 100)}em`;
|
||||
};
|
||||
element.querySelector('button').style.fontSize = `${1.15 * Number((localStorage.getItem('zoomQuicklinks') || 100) / 100)}em`;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (localStorage.getItem('offlineMode') === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'quicklinks') {
|
||||
const element = document.querySelector('.quicklinks-container');
|
||||
@@ -144,14 +131,10 @@ export default class QuickLinks extends React.PureComponent {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.remove('refresh');
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
if (localStorage.getItem('offlineMode') === 'true') {
|
||||
return null;
|
||||
}
|
||||
|
||||
let target, rel = null;
|
||||
if (localStorage.getItem('quicklinksnewtab') === 'true') {
|
||||
target = '_blank';
|
||||
@@ -159,9 +142,9 @@ export default class QuickLinks extends React.PureComponent {
|
||||
}
|
||||
|
||||
const tooltipEnabled = localStorage.getItem('quicklinkstooltip');
|
||||
const useProxy = (localStorage.getItem('quicklinksddgProxy') !== 'false');
|
||||
|
||||
const quickLink = (item) => {
|
||||
const useProxy = (localStorage.getItem('quicklinksddgProxy') !== 'false');
|
||||
const url = useProxy ? 'https://icons.duckduckgo.com/ip2/' : 'https://www.google.com/s2/favicons?sz=32&domain=';
|
||||
|
||||
const link = (
|
||||
@@ -171,19 +154,21 @@ export default class QuickLinks extends React.PureComponent {
|
||||
);
|
||||
|
||||
if (tooltipEnabled === 'true') {
|
||||
return <Tooltip title={item.name} key={item.name} draggable={false}>{link}</Tooltip>;
|
||||
return <Tooltip title={item.name}>{link}</Tooltip>;
|
||||
} else {
|
||||
return link;
|
||||
}
|
||||
};
|
||||
|
||||
const marginTop = (this.state.items.length > 0) ? '9vh' : '4vh';
|
||||
|
||||
return (
|
||||
<div className='quicklinks-container'>
|
||||
{this.state.items.map((item) => (
|
||||
quickLink(item)
|
||||
))}
|
||||
<button className='quicklinks' onClick={this.toggleAdd}>+</button>
|
||||
<span className='quicklinkscontainer' style={{ visibility: this.state.showAddLink }}>
|
||||
<span className='quicklinkscontainer' style={{ visibility: this.state.showAddLink, marginTop: marginTop }}>
|
||||
<div className='topbarquicklinks'>
|
||||
<h4>{this.language.new}</h4>
|
||||
<TextareaAutosize rowsmax={1} placeholder={this.language.name} value={this.state.name} onChange={(e) => this.setState({ name: e.target.value })} />
|
||||
|
||||
@@ -53,6 +53,7 @@ textarea {
|
||||
margin: 0;
|
||||
cursor: initial;
|
||||
user-select: none;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
p {
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
import Interval from '../../../modules/helpers/interval';
|
||||
|
||||
import FileCopy from '@material-ui/icons/FilterNone';
|
||||
import TwitterIcon from '@material-ui/icons/Twitter';
|
||||
import StarIcon from '@material-ui/icons/Star';
|
||||
import StarIcon2 from '@material-ui/icons/StarBorder';
|
||||
|
||||
import { PureComponent } from 'react';
|
||||
import { FilterNone as FileCopy, Twitter, Star, StarBorder } from '@material-ui/icons';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import Interval from '../../../modules/helpers/interval';
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import './quote.scss';
|
||||
|
||||
export default class Quote extends React.PureComponent {
|
||||
export default class Quote extends PureComponent {
|
||||
buttons = {
|
||||
tweet: <TwitterIcon className='copyButton' onClick={() => this.tweetQuote()} />,
|
||||
tweet: <Twitter className='copyButton' onClick={() => this.tweetQuote()} />,
|
||||
copy: <FileCopy className='copyButton' onClick={() => this.copyQuote()} />,
|
||||
unfavourited: <StarIcon2 className='copyButton' onClick={() => this.favourite()} />,
|
||||
favourited: <StarIcon className='copyButton' onClick={() => this.favourite()} />
|
||||
unfavourited: <StarBorder className='copyButton' onClick={() => this.favourite()} />,
|
||||
favourited: <Star className='copyButton' onClick={() => this.favourite()} />
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@@ -147,7 +142,7 @@ export default class Quote extends React.PureComponent {
|
||||
author: data.author,
|
||||
authorlink: this.getAuthorLink(data.author),
|
||||
quoteLanguage: quotelanguage
|
||||
}
|
||||
};
|
||||
|
||||
this.setState(object);
|
||||
localStorage.setItem('currentQuote', JSON.stringify(object));
|
||||
@@ -200,8 +195,9 @@ export default class Quote extends React.PureComponent {
|
||||
}
|
||||
|
||||
setZoom() {
|
||||
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`;
|
||||
const zoomQuote = Number((localStorage.getItem('zoomQuote') || 100) / 100);
|
||||
document.querySelector('.quote').style.fontSize = `${0.8 * zoomQuote}em`;
|
||||
document.querySelector('.quoteauthor').style.fontSize = `${0.9 * zoomQuote}em`;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -215,6 +211,13 @@ export default class Quote extends React.PureComponent {
|
||||
|
||||
element.style.display = 'block';
|
||||
this.init();
|
||||
|
||||
// buttons hot reload
|
||||
this.setState({
|
||||
favourited: this.useFavourite(),
|
||||
tweet: (localStorage.getItem('tweetButton') === 'false') ? null : this.buttons.tweet,
|
||||
copy: (localStorage.getItem('copyButton') === 'false') ? null : this.buttons.copy
|
||||
});
|
||||
}
|
||||
|
||||
// uninstall quote pack reverts the quote to what you had previously
|
||||
@@ -244,7 +247,7 @@ export default class Quote extends React.PureComponent {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.remove('refresh');
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
import { PureComponent } from 'react';
|
||||
import { Search as SearchIcon, Mic } from '@material-ui/icons';
|
||||
|
||||
import AutocompleteInput from '../../helpers/autocomplete/Autocomplete';
|
||||
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
import MicIcon from '@material-ui/icons/Mic';
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import './search.scss';
|
||||
|
||||
const searchEngines = require('./search_engines.json');
|
||||
const autocompleteProviders = require('./autocomplete_providers.json');
|
||||
|
||||
export default class Search extends React.PureComponent {
|
||||
export default class Search extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -50,14 +48,8 @@ export default class Search extends React.PureComponent {
|
||||
}
|
||||
|
||||
searchButton = (e) => {
|
||||
let value;
|
||||
|
||||
if (e.target.innerText !== undefined) {
|
||||
value = e.target.innerText;
|
||||
} else {
|
||||
value = document.getElementById('searchtext').value || 'mue fast';
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
const value = e.target.value || document.getElementById('searchtext').value || 'mue fast';
|
||||
window.stats.postEvent('feature', 'Search');
|
||||
window.location.href = this.state.url + `?${this.state.query}=` + value;
|
||||
}
|
||||
@@ -101,7 +93,7 @@ export default class Search extends React.PureComponent {
|
||||
}
|
||||
|
||||
if (localStorage.getItem('voiceSearch') === 'true') {
|
||||
microphone = <MicIcon className='micIcon' onClick={this.startSpeechRecognition}/>;
|
||||
microphone = <Mic className='micIcon' onClick={this.startSpeechRecognition}/>;
|
||||
}
|
||||
|
||||
let autocompleteURL, autocompleteQuery, autocompleteCallback;
|
||||
@@ -134,12 +126,12 @@ export default class Search extends React.PureComponent {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.remove('refresh');
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<form action={this.state.url} className='searchBar'>
|
||||
<form onSubmit={this.searchButton} className='searchBar'>
|
||||
{this.state.microphone}
|
||||
<SearchIcon onClick={this.searchButton}/>
|
||||
<AutocompleteInput placeholder={this.language} id='searchtext' suggestions={this.state.suggestions} onChange={(e) => this.getSuggestions(e)} onClick={this.searchButton}/>
|
||||
|
||||
@@ -71,4 +71,4 @@
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React from 'react';
|
||||
import { PureComponent, Suspense, lazy } from 'react';
|
||||
|
||||
import { utcToZonedTime } from 'date-fns-tz';
|
||||
import { convertTimezone } from '../../../modules/helpers/date';
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import './clock.scss';
|
||||
|
||||
const Analog = React.lazy(() => import('react-clock'));
|
||||
const Analog = lazy(() => import('react-clock'));
|
||||
const renderLoader = () => <></>;
|
||||
|
||||
export default class Clock extends React.PureComponent {
|
||||
export default class Clock extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -23,8 +23,8 @@ export default class Clock extends React.PureComponent {
|
||||
this.timer = setTimeout(() => {
|
||||
let now = new Date();
|
||||
const timezone = localStorage.getItem('timezone');
|
||||
if (timezone) {
|
||||
now = utcToZonedTime(now, timezone);
|
||||
if (timezone && timezone !== 'auto') {
|
||||
now = convertTimezone(now, timezone);
|
||||
}
|
||||
|
||||
switch (localStorage.getItem('timeType')) {
|
||||
@@ -58,7 +58,8 @@ export default class Clock extends React.PureComponent {
|
||||
}
|
||||
|
||||
this.setState({
|
||||
time: time
|
||||
time: time,
|
||||
ampm: ''
|
||||
});
|
||||
} else {
|
||||
// 12 hour
|
||||
@@ -66,6 +67,8 @@ export default class Clock extends React.PureComponent {
|
||||
|
||||
if (hours > 12) {
|
||||
hours -= 12;
|
||||
} else if (hours === 0) {
|
||||
hours = 12;
|
||||
}
|
||||
|
||||
if (zero === 'false') {
|
||||
@@ -111,7 +114,7 @@ export default class Clock extends React.PureComponent {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.remove('refresh');
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -123,7 +126,7 @@ export default class Clock extends React.PureComponent {
|
||||
|
||||
if (localStorage.getItem('timeType') === 'analogue') {
|
||||
clockHTML = (
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Analog
|
||||
className='analogclock clock-container'
|
||||
value={this.state.time}
|
||||
@@ -133,7 +136,7 @@ export default class Clock extends React.PureComponent {
|
||||
renderMinuteHand={enabled('minuteHand')}
|
||||
renderHourHand={enabled('hourHand')}
|
||||
/>
|
||||
</React.Suspense>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React from 'react';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { utcToZonedTime } from 'date-fns-tz';
|
||||
import { nth, convertTimezone } from '../../../modules/helpers/date';
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import dtf from '../../../modules/helpers/date';
|
||||
|
||||
import './date.scss';
|
||||
|
||||
export default class DateWidget extends React.PureComponent {
|
||||
export default class DateWidget extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -36,8 +34,8 @@ export default class DateWidget extends React.PureComponent {
|
||||
getDate() {
|
||||
let date = new Date();
|
||||
const timezone = localStorage.getItem('timezone');
|
||||
if (timezone) {
|
||||
date = utcToZonedTime(date, timezone);
|
||||
if (timezone && timezone !== 'auto') {
|
||||
date = convertTimezone(date, timezone);
|
||||
}
|
||||
|
||||
if (localStorage.getItem('weeknumber') === 'true') {
|
||||
@@ -96,15 +94,15 @@ export default class DateWidget extends React.PureComponent {
|
||||
});
|
||||
} else {
|
||||
// Long date
|
||||
const lang = localStorage.getItem('language').split('_')[0];
|
||||
const lang = window.languagecode.split('_')[0];
|
||||
|
||||
const nth = (localStorage.getItem('datenth') === 'true') ? dtf.nth(date.getDate()) : date.getDate();
|
||||
const datenth = (localStorage.getItem('datenth') === 'true') ? nth(date.getDate()) : date.getDate();
|
||||
|
||||
const day = (localStorage.getItem('dayofweek') === 'true') ? date.toLocaleDateString(lang, { weekday: 'long' }) : '';
|
||||
const month = date.toLocaleDateString(lang, { month: 'long' });
|
||||
|
||||
this.setState({
|
||||
date: `${day} ${nth} ${month} ${date.getFullYear()}`
|
||||
date: `${day} ${datenth} ${month} ${date.getFullYear()}`
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -130,7 +128,7 @@ export default class DateWidget extends React.PureComponent {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.remove('refresh');
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
import { PureComponent } from 'react';
|
||||
import { WiHumidity, WiWindy, WiBarometer, WiCloud } from 'weather-icons-react';
|
||||
|
||||
import WeatherIcon from './WeatherIcon';
|
||||
import WindDirectionIcon from './WindDirectionIcon';
|
||||
import { WiHumidity, WiWindy, WiBarometer, WiCloud } from 'weather-icons-react';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import './weather.scss';
|
||||
|
||||
export default class Weather extends React.PureComponent {
|
||||
export default class Weather extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -31,11 +31,8 @@ export default class Weather extends React.PureComponent {
|
||||
}
|
||||
|
||||
async getWeather() {
|
||||
if (localStorage.getItem('offlineMode') === 'true') {
|
||||
return null;
|
||||
}
|
||||
|
||||
document.querySelector('.weather').style.fontSize = `${Number((localStorage.getItem('zoomWeather') || 100) / 100)}em`;
|
||||
const zoomWeather = `${Number((localStorage.getItem('zoomWeather') || 100) / 100)}em`;
|
||||
document.querySelector('.weather').style.fontSize = zoomWeather;
|
||||
|
||||
let data = {
|
||||
weather: [
|
||||
@@ -62,7 +59,7 @@ export default class Weather extends React.PureComponent {
|
||||
};
|
||||
|
||||
if (!this.state.weather.temp) {
|
||||
data = await (await fetch(window.constants.PROXY_URL + `/weather/current?city=${this.state.location}&lang=${localStorage.getItem('language')}`)).json();
|
||||
data = await (await fetch(window.constants.PROXY_URL + `/weather/current?city=${this.state.location}&lang=${window.languagecode}`)).json();
|
||||
}
|
||||
|
||||
if (data.cod === '404') {
|
||||
@@ -114,7 +111,7 @@ export default class Weather extends React.PureComponent {
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector('.weather svg').style.fontSize = `${0.95 * Number((localStorage.getItem('zoomWeather') || 100) / 100)}em`;
|
||||
document.querySelector('.weather svg').style.fontSize = zoomWeather;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -123,11 +120,12 @@ export default class Weather extends React.PureComponent {
|
||||
this.getWeather();
|
||||
}
|
||||
});
|
||||
|
||||
this.getWeather();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.remove('refresh');
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -135,10 +133,6 @@ export default class Weather extends React.PureComponent {
|
||||
return (localStorage.getItem(setting) === 'true');
|
||||
};
|
||||
|
||||
if (enabled('offlineMode')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.state.location === window.language.widgets.weather.not_found) {
|
||||
return (<div className='weather'>
|
||||
<span className='loc'>{this.state.location}</span>
|
||||
|
||||
@@ -44,4 +44,4 @@
|
||||
vertical-align: middle;
|
||||
font-size: 35px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
src/index.js
17
src/index.js
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { render } from 'react-dom';
|
||||
|
||||
import App from './App';
|
||||
import * as Constants from './modules/constants';
|
||||
@@ -8,12 +7,10 @@ import './scss/index.scss';
|
||||
// the toast css is based on default so we need to import it
|
||||
import 'react-toastify/dist/ReactToastify.min.css';
|
||||
|
||||
// this is opt-in btw, allows you to see your stats etc
|
||||
// local stats
|
||||
import Stats from './modules/helpers/stats';
|
||||
|
||||
// language
|
||||
import merge from '@material-ui/utils/esm/deepmerge';
|
||||
|
||||
const languagecode = localStorage.getItem('language') || 'en_GB';
|
||||
|
||||
// we set things to window. so we avoid passing the translation strings as props to each component
|
||||
@@ -23,8 +20,7 @@ if (languagecode === 'en') {
|
||||
window.languagecode = 'en_GB';
|
||||
}
|
||||
|
||||
// these are merged so if a string is untranslated it doesn't break mue
|
||||
window.language = merge(require('./translations/en_GB.json'), require(`./translations/${window.languagecode}.json`));
|
||||
window.language = require(`./translations/${window.languagecode}.json`);
|
||||
|
||||
// set html language tag
|
||||
if (window.languagecode !== 'en_GB' || window.languagecode !== 'en_US') {
|
||||
@@ -32,17 +28,16 @@ if (window.languagecode !== 'en_GB' || window.languagecode !== 'en_US') {
|
||||
}
|
||||
|
||||
window.constants = Constants;
|
||||
// doesn't send to umami when offline mode is on
|
||||
if (localStorage.getItem('stats') === 'true') {
|
||||
window.stats = new Stats(window.constants.UMAMI_ID);
|
||||
window.stats = Stats;
|
||||
} else {
|
||||
window.stats = {
|
||||
tabLoad: () => '',
|
||||
postEvent: () => ''
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
render(
|
||||
<App/>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
@@ -9,8 +9,9 @@ export const DDG_IMAGE_PROXY = 'https://external-content.duckduckgo.com/iu/?u=';
|
||||
// Mue URLs
|
||||
export const WEBSITE_URL = 'https://muetab.com';
|
||||
export const PRIVACY_URL = 'https://muetab.com/privacy';
|
||||
export const BLOG_POST = 'https://blog.muetab.com/posts/version-5-2';
|
||||
export const BLOG_POST = 'https://blog.muetab.com/posts/version-5-3';
|
||||
export const FEEDBACK_FORM = 'https://api.formcake.com/api/form/349b56cb-7e2b-4004-b32b-e8964d217dd1/submission';
|
||||
export const TRANSLATIONS_URL = 'https://docs.muetab.com/translations/';
|
||||
|
||||
// Mue Info
|
||||
export const ORG_NAME = 'mue';
|
||||
@@ -25,13 +26,9 @@ export const COPYRIGHT_YEAR = '2018';
|
||||
export const COPYRIGHT_LICENSE = 'BSD-3 License';
|
||||
export const DONATE_USERNAME = 'davidjcralph'; // this only works if you use the same username for Patreon, GitHub and Ko-Fi
|
||||
|
||||
// umami
|
||||
export const UMAMI_DOMAIN = 'https://umami.muetab.com';
|
||||
export const UMAMI_ID = '1b97e723-199c-48d8-8992-17c4e22d4f3c';
|
||||
|
||||
// Offline images
|
||||
export const OFFLINE_IMAGES = 20;
|
||||
|
||||
// Version
|
||||
export const BETA_VERSION = false;
|
||||
export const VERSION = '5.2.0';
|
||||
export const VERSION = '5.3.2';
|
||||
|
||||
39
src/modules/helpers/background/widget.js
Normal file
39
src/modules/helpers/background/widget.js
Normal file
@@ -0,0 +1,39 @@
|
||||
// since there is so much code in the component, we have moved it to a separate file
|
||||
export function videoCheck(url) {
|
||||
return url.startsWith('data:video/') || url.endsWith('.mp4') || url.endsWith('.webm') || url.endsWith('.ogg');
|
||||
}
|
||||
|
||||
export function offlineBackground() {
|
||||
const offlineImages = require('./offlineImages.json');
|
||||
|
||||
// Get all photographers from the keys in offlineImages.json
|
||||
const photographers = Object.keys(offlineImages);
|
||||
const photographer = photographers[Math.floor(Math.random() * photographers.length)];
|
||||
|
||||
const randomImage = offlineImages[photographer].photo[
|
||||
Math.floor(Math.random() * offlineImages[photographer].photo.length)
|
||||
];
|
||||
|
||||
const object = {
|
||||
url: `./offline-images/${randomImage}.webp`,
|
||||
photoInfo: {
|
||||
offline: true,
|
||||
credit: photographer
|
||||
}
|
||||
};
|
||||
|
||||
localStorage.setItem('currentBackground', JSON.stringify(object));
|
||||
|
||||
return object;
|
||||
};
|
||||
|
||||
export function gradientStyleBuilder({ type, angle, gradient }) {
|
||||
// Note: Append the gradient for additional browser support.
|
||||
const steps = gradient?.map((v) => `${v.colour} ${v.stop}%`);
|
||||
const grad = `background: ${type}-gradient(${type === 'linear' ? `${angle}deg,` : ''}${steps})`;
|
||||
|
||||
return {
|
||||
type: 'colour',
|
||||
style: `background:${gradient[0]?.colour};${grad}`
|
||||
}
|
||||
};
|
||||
@@ -1,18 +1,20 @@
|
||||
export default class Date {
|
||||
static nth(d) {
|
||||
if (d > 3 && d < 21) {
|
||||
return d + 'th';
|
||||
}
|
||||
export function nth(d) {
|
||||
if (d > 3 && d < 21) {
|
||||
return d + 'th';
|
||||
}
|
||||
|
||||
switch (d % 10) {
|
||||
case 1:
|
||||
return d + 'st';
|
||||
case 2:
|
||||
return d + 'nd';
|
||||
case 3:
|
||||
return d + 'rd';
|
||||
default:
|
||||
return d + 'th';
|
||||
}
|
||||
switch (d % 10) {
|
||||
case 1:
|
||||
return d + 'st';
|
||||
case 2:
|
||||
return d + 'nd';
|
||||
case 3:
|
||||
return d + 'rd';
|
||||
default:
|
||||
return d + 'th';
|
||||
}
|
||||
}
|
||||
|
||||
export function convertTimezone(date, tz) {
|
||||
return new Date((typeof date === 'string' ? new Date(date) : date).toLocaleString('en-US', { timeZone: tz }));
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ export default class EventBus {
|
||||
}));
|
||||
}
|
||||
|
||||
static remove(event, callback) {
|
||||
static off(event, callback) {
|
||||
document.removeEventListener(event, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// based on https://stackoverflow.com/a/47009962
|
||||
export default function Interval(callback, interval, name) {
|
||||
export default function interval(callback, interval, name) {
|
||||
const key = name + 'interval';
|
||||
const timeInMs = localStorage.getItem(key);
|
||||
|
||||
@@ -8,7 +8,7 @@ export default function Interval(callback, interval, name) {
|
||||
const executeCallback = () => {
|
||||
localStorage.setItem(key, Date.now());
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
if (timeInMs) {
|
||||
const delta = now - parseInt(timeInMs);
|
||||
|
||||
@@ -1,101 +1,99 @@
|
||||
import EventBus from './eventbus';
|
||||
|
||||
export default class MarketplaceFunctions {
|
||||
// based on https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links
|
||||
static urlParser(input) {
|
||||
const urlPattern = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_+.~#?&//=]*)/;
|
||||
return input.replace(urlPattern, '<a href="$&" target="_blank">$&</a>');
|
||||
}
|
||||
// based on https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links
|
||||
export function urlParser(input) {
|
||||
const urlPattern = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_+.~#?&//=]*)/;
|
||||
return input.replace(urlPattern, '<a href="$&" target="_blank">$&</a>');
|
||||
};
|
||||
|
||||
static uninstall(type, name) {
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
const oldSettings = JSON.parse(localStorage.getItem('backup_settings'));
|
||||
localStorage.clear();
|
||||
oldSettings.forEach((item) => {
|
||||
localStorage.setItem(item.name, item.value);
|
||||
});
|
||||
break;
|
||||
case 'quotes':
|
||||
localStorage.removeItem('quote_packs');
|
||||
localStorage.removeItem('quoteAPI');
|
||||
localStorage.setItem('quoteType', localStorage.getItem('oldQuoteType'));
|
||||
localStorage.removeItem('oldQuoteType');
|
||||
EventBus.dispatch('refresh', 'marketplacequoteuninstall');
|
||||
break;
|
||||
case 'photos':
|
||||
localStorage.removeItem('photo_packs');
|
||||
localStorage.setItem('backgroundType', localStorage.getItem('oldBackgroundType'));
|
||||
localStorage.removeItem('oldBackgroundType');
|
||||
EventBus.dispatch('refresh', 'marketplacebackgrounduninstall');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
let installed = JSON.parse(localStorage.getItem('installed'));
|
||||
for (let i = 0; i < installed.length; i++) {
|
||||
if (installed[i].name === name) {
|
||||
installed.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
}
|
||||
|
||||
static install(type, input, sideload) {
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
localStorage.removeItem('backup_settings');
|
||||
|
||||
let oldSettings = [];
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
oldSettings.push({
|
||||
name: key,
|
||||
value: localStorage.getItem(key)
|
||||
});
|
||||
});
|
||||
|
||||
localStorage.setItem('backup_settings', JSON.stringify(oldSettings));
|
||||
Object.keys(input.settings).forEach((key) => {
|
||||
localStorage.setItem(key, input.settings[key]);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'photos':
|
||||
localStorage.setItem('photo_packs', JSON.stringify(input.photos));
|
||||
localStorage.setItem('oldBackgroundType', localStorage.getItem('backgroundType'));
|
||||
localStorage.setItem('backgroundType', 'photo_pack');
|
||||
EventBus.dispatch('refresh', 'background');
|
||||
break;
|
||||
|
||||
case 'quotes':
|
||||
if (input.quote_api) {
|
||||
localStorage.setItem('quoteAPI', JSON.stringify(input.quote_api));
|
||||
}
|
||||
|
||||
localStorage.setItem('quote_packs', JSON.stringify(input.quotes));
|
||||
localStorage.setItem('oldQuoteType', localStorage.getItem('quoteType'));
|
||||
localStorage.setItem('quoteType', 'quote_pack');
|
||||
EventBus.dispatch('refresh', 'quote');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
|
||||
if (sideload) {
|
||||
installed.push({
|
||||
content: {
|
||||
updated: 'Unpublished',
|
||||
data: input
|
||||
}
|
||||
export function uninstall(type, name) {
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
const oldSettings = JSON.parse(localStorage.getItem('backup_settings'));
|
||||
localStorage.clear();
|
||||
oldSettings.forEach((item) => {
|
||||
localStorage.setItem(item.name, item.value);
|
||||
});
|
||||
} else {
|
||||
installed.push(input);
|
||||
}
|
||||
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
break;
|
||||
case 'quotes':
|
||||
localStorage.removeItem('quote_packs');
|
||||
localStorage.removeItem('quoteAPI');
|
||||
localStorage.setItem('quoteType', localStorage.getItem('oldQuoteType'));
|
||||
localStorage.removeItem('oldQuoteType');
|
||||
EventBus.dispatch('refresh', 'marketplacequoteuninstall');
|
||||
break;
|
||||
case 'photos':
|
||||
localStorage.removeItem('photo_packs');
|
||||
localStorage.setItem('backgroundType', localStorage.getItem('oldBackgroundType'));
|
||||
localStorage.removeItem('oldBackgroundType');
|
||||
EventBus.dispatch('refresh', 'marketplacebackgrounduninstall');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let installed = JSON.parse(localStorage.getItem('installed'));
|
||||
for (let i = 0; i < installed.length; i++) {
|
||||
if (installed[i].name === name) {
|
||||
installed.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
};
|
||||
|
||||
export function install(type, input, sideload) {
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
localStorage.removeItem('backup_settings');
|
||||
|
||||
let oldSettings = [];
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
oldSettings.push({
|
||||
name: key,
|
||||
value: localStorage.getItem(key)
|
||||
});
|
||||
});
|
||||
|
||||
localStorage.setItem('backup_settings', JSON.stringify(oldSettings));
|
||||
Object.keys(input.settings).forEach((key) => {
|
||||
localStorage.setItem(key, input.settings[key]);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'photos':
|
||||
localStorage.setItem('photo_packs', JSON.stringify(input.photos));
|
||||
localStorage.setItem('oldBackgroundType', localStorage.getItem('backgroundType'));
|
||||
localStorage.setItem('backgroundType', 'photo_pack');
|
||||
EventBus.dispatch('refresh', 'background');
|
||||
break;
|
||||
|
||||
case 'quotes':
|
||||
if (input.quote_api) {
|
||||
localStorage.setItem('quoteAPI', JSON.stringify(input.quote_api));
|
||||
}
|
||||
|
||||
localStorage.setItem('quote_packs', JSON.stringify(input.quotes));
|
||||
localStorage.setItem('oldQuoteType', localStorage.getItem('quoteType'));
|
||||
localStorage.setItem('quoteType', 'quote_pack');
|
||||
EventBus.dispatch('refresh', 'quote');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
|
||||
if (sideload) {
|
||||
installed.push({
|
||||
content: {
|
||||
updated: 'Unpublished',
|
||||
data: input
|
||||
}
|
||||
});
|
||||
} else {
|
||||
installed.push(input);
|
||||
}
|
||||
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
};
|
||||
|
||||
@@ -3,144 +3,142 @@ import experimentalInit from '../experimental';
|
||||
const defaultSettings = require('../../default_settings.json');
|
||||
const languages = require('../../languages.json');
|
||||
|
||||
export default class SettingsFunctions {
|
||||
static setDefaultSettings(reset) {
|
||||
localStorage.clear();
|
||||
defaultSettings.forEach((element) => localStorage.setItem(element.name, element.value));
|
||||
export function setDefaultSettings(reset) {
|
||||
localStorage.clear();
|
||||
defaultSettings.forEach((element) => localStorage.setItem(element.name, element.value));
|
||||
|
||||
// Languages
|
||||
const languageCodes = languages.map(({ value }) => value);
|
||||
const browserLanguage = (navigator.languages && navigator.languages.find((lang) => lang.replace('-', '_') && languageCodes.includes(lang))) || navigator.language.replace('-', '_');
|
||||
// Languages
|
||||
const languageCodes = languages.map(({ value }) => value);
|
||||
const browserLanguage = (navigator.languages && navigator.languages.find((lang) => lang.replace('-', '_') && languageCodes.includes(lang))) || navigator.language.replace('-', '_');
|
||||
|
||||
if (languageCodes.includes(browserLanguage)) {
|
||||
localStorage.setItem('language', browserLanguage);
|
||||
} else {
|
||||
localStorage.setItem('language', 'en_GB');
|
||||
}
|
||||
|
||||
localStorage.setItem('tabName', window.language.tabname);
|
||||
|
||||
if (reset) {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
}
|
||||
|
||||
// Finally we set this to true so it doesn't run the function on every load
|
||||
localStorage.setItem('firstRun', true);
|
||||
if (languageCodes.includes(browserLanguage)) {
|
||||
localStorage.setItem('language', browserLanguage);
|
||||
} else {
|
||||
localStorage.setItem('language', 'en_GB');
|
||||
}
|
||||
|
||||
static loadSettings(hotreload) {
|
||||
document.getElementById('widgets').style.zoom = localStorage.getItem('widgetzoom') + '%';
|
||||
localStorage.setItem('tabName', window.language.tabname);
|
||||
|
||||
const theme = localStorage.getItem('theme');
|
||||
switch (theme) {
|
||||
case 'dark':
|
||||
document.body.classList.add('dark');
|
||||
break;
|
||||
case 'auto':
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
document.body.classList.add('dark');
|
||||
} else {
|
||||
document.body.classList.remove('dark');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
document.body.classList.remove('dark');
|
||||
}
|
||||
|
||||
const tabName = localStorage.getItem('tabName') || window.language.tabname;
|
||||
document.title = tabName;
|
||||
|
||||
if (hotreload === true) {
|
||||
const custom = ['customcss', 'customjs', 'customfont'];
|
||||
custom.forEach((element) => {
|
||||
try {
|
||||
document.head.removeChild(document.getElementById(element));
|
||||
} catch (e) {
|
||||
// Disregard exception
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const css = localStorage.getItem('customcss');
|
||||
if (css) {
|
||||
document.head.insertAdjacentHTML('beforeend', '<style id="customcss">' + css + '</style>');
|
||||
}
|
||||
|
||||
const font = localStorage.getItem('font');
|
||||
if (font) {
|
||||
let url = '';
|
||||
if (localStorage.getItem('fontGoogle') === 'true') {
|
||||
url = `@import url('https://fonts.googleapis.com/css2?family=${font}&display=swap');`;
|
||||
}
|
||||
|
||||
document.head.insertAdjacentHTML('beforeend', `
|
||||
<style id='customfont'>
|
||||
${url}
|
||||
* {
|
||||
font-family: '${font}', 'Lexend Deca', 'Montserrat', sans-serif !important;
|
||||
font-weight: ${localStorage.getItem('fontweight')};
|
||||
font-style: ${localStorage.getItem('fontstyle')};
|
||||
}
|
||||
</style>
|
||||
`);
|
||||
}
|
||||
|
||||
// everything below this either doesn't support hot reload (custom js) or shouldn't run on a hot reload event
|
||||
if (hotreload === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
const js = localStorage.getItem('customjs');
|
||||
if (js) {
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(js);
|
||||
} catch (e) {
|
||||
console.error('Failed to run custom JS: ', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (localStorage.getItem('experimental') === 'true') {
|
||||
experimentalInit();
|
||||
}
|
||||
|
||||
// easter egg
|
||||
console.log(`
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-${new Date().getFullYear()} The Mue Authors ██
|
||||
██ GitHub: https://github.com/mue/mue ██
|
||||
██ ██
|
||||
██ Thank you for using Mue! ██
|
||||
██ Feedback: hello@muetab.com ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
`);
|
||||
if (reset) {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
}
|
||||
|
||||
// in a nutshell, this function saves all of the current settings, resets them, sets the defaults and then overrides
|
||||
// the new settings with the old saved messages where they exist
|
||||
static moveSettings() {
|
||||
if (Object.keys(localStorage).length === 0) {
|
||||
return this.setDefaultSettings();
|
||||
}
|
||||
|
||||
let settings = {};
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
|
||||
localStorage.clear();
|
||||
|
||||
this.setDefaultSettings();
|
||||
Object.keys(settings).forEach((key) => {
|
||||
localStorage.setItem(key, settings[key]);
|
||||
});
|
||||
}
|
||||
// Finally we set this to true so it doesn't run the function on every load
|
||||
localStorage.setItem('firstRun', true);
|
||||
}
|
||||
|
||||
export function loadSettings(hotreload) {
|
||||
document.getElementById('widgets').style.zoom = localStorage.getItem('widgetzoom') + '%';
|
||||
|
||||
switch (localStorage.getItem('theme')) {
|
||||
case 'dark':
|
||||
document.body.classList.add('dark');
|
||||
break;
|
||||
case 'auto':
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
document.body.classList.add('dark');
|
||||
} else {
|
||||
document.body.classList.remove('dark');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
document.body.classList.remove('dark');
|
||||
}
|
||||
|
||||
document.title = localStorage.getItem('tabName') || window.language.tabname;
|
||||
|
||||
if (hotreload === true) {
|
||||
// remove old custom stuff and add new
|
||||
const custom = ['customcss', 'customfont'];
|
||||
custom.forEach((element) => {
|
||||
try {
|
||||
document.head.removeChild(document.getElementById(element));
|
||||
} catch (e) {
|
||||
// Disregard exception if custom stuff doesn't exist
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const css = localStorage.getItem('customcss');
|
||||
if (css) {
|
||||
document.head.insertAdjacentHTML('beforeend', '<style id="customcss">' + css + '</style>');
|
||||
}
|
||||
|
||||
const font = localStorage.getItem('font');
|
||||
if (font) {
|
||||
let url = '';
|
||||
if (localStorage.getItem('fontGoogle') === 'true') {
|
||||
url = `@import url('https://fonts.googleapis.com/css2?family=${font}&display=swap');`;
|
||||
}
|
||||
|
||||
document.head.insertAdjacentHTML('beforeend', `
|
||||
<style id='customfont'>
|
||||
${url}
|
||||
* {
|
||||
font-family: '${font}', 'Lexend Deca', 'Montserrat', sans-serif !important;
|
||||
font-weight: ${localStorage.getItem('fontweight')};
|
||||
font-style: ${localStorage.getItem('fontstyle')};
|
||||
}
|
||||
</style>
|
||||
`);
|
||||
}
|
||||
|
||||
// everything below this either doesn't support hot reload (custom js) or shouldn't run on a hot reload event
|
||||
if (hotreload === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
const js = localStorage.getItem('customjs');
|
||||
if (js) {
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(js);
|
||||
} catch (e) {
|
||||
console.error('Failed to run custom JS: ', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (localStorage.getItem('experimental') === 'true') {
|
||||
experimentalInit();
|
||||
}
|
||||
|
||||
// easter egg
|
||||
console.log(`
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-${new Date().getFullYear()} The Mue Authors ██
|
||||
██ GitHub: https://github.com/mue/mue ██
|
||||
██ ██
|
||||
██ Thank you for using Mue! ██
|
||||
██ Feedback: hello@muetab.com ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
`);
|
||||
};
|
||||
|
||||
// in a nutshell, this function saves all of the current settings, resets them, sets the defaults and then overrides
|
||||
// the new settings with the old saved messages where they exist
|
||||
export function moveSettings() {
|
||||
const currentSettings = Object.keys(localStorage);
|
||||
if (currentSettings.length === 0) {
|
||||
return this.setDefaultSettings();
|
||||
}
|
||||
|
||||
const settings = {};
|
||||
currentSettings.forEach((key) => {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
|
||||
localStorage.clear();
|
||||
setDefaultSettings();
|
||||
|
||||
Object.keys(settings).forEach((key) => {
|
||||
localStorage.setItem(key, settings[key]);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
const saveFile = (data, filename = 'file') => {
|
||||
export function saveFile(data, filename = 'file') {
|
||||
if (typeof data === 'object') {
|
||||
data = JSON.stringify(data, undefined, 4);
|
||||
}
|
||||
|
||||
const blob = new Blob([data], { type: 'text/json' });
|
||||
let e = document.createEvent('MouseEvents');
|
||||
let a = document.createElement('a');
|
||||
|
||||
const event = document.createEvent('MouseEvents');
|
||||
const a = document.createElement('a');
|
||||
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.download = filename;
|
||||
a.dataset.downloadurl = ['text/json', a.download, a.href].join(':');
|
||||
|
||||
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
a.dispatchEvent(e);
|
||||
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
a.dispatchEvent(event);
|
||||
}
|
||||
|
||||
export function exportSettings() {
|
||||
const settings = {};
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
saveFile(settings, 'mue-settings.json');
|
||||
window.stats.postEvent('tab', 'Settings exported');
|
||||
};
|
||||
|
||||
export default class ModalSettingsFunctions {
|
||||
static exportSettings() {
|
||||
let settings = {};
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
saveFile(settings, 'mue-settings.json');
|
||||
window.stats.postEvent('tab', 'Settings exported');
|
||||
}
|
||||
export function importSettings(e) {
|
||||
const content = JSON.parse(e.target.result);
|
||||
|
||||
static importSettings(e) {
|
||||
const content = JSON.parse(e.target.result);
|
||||
Object.keys(content).forEach((key) => {
|
||||
localStorage.setItem(key, content[key]);
|
||||
});
|
||||
|
||||
Object.keys(content).forEach((key) => {
|
||||
localStorage.setItem(key, content[key]);
|
||||
});
|
||||
|
||||
toast(window.language.toasts.imported);
|
||||
window.stats.postEvent('tab', 'Settings imported');
|
||||
}
|
||||
}
|
||||
toast(window.language.toasts.imported);
|
||||
window.stats.postEvent('tab', 'Settings imported');
|
||||
};
|
||||
|
||||
@@ -1,34 +1,8 @@
|
||||
export default class Stats {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
this.url = window.constants.UMAMI_DOMAIN + '/api/collect';
|
||||
this.online = (localStorage.getItem('offlineMode') === 'false');
|
||||
}
|
||||
|
||||
async postEvent(type, name) {
|
||||
static async postEvent(type, name) {
|
||||
const value = name.toLowerCase().replaceAll(' ', '-');
|
||||
|
||||
if (this.online) {
|
||||
// umami
|
||||
await fetch(this.url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'event',
|
||||
payload: {
|
||||
website: this.id,
|
||||
url: '/',
|
||||
event_type: type,
|
||||
event_value: value
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// local
|
||||
let data = JSON.parse(localStorage.getItem('statsData'));
|
||||
const data = JSON.parse(localStorage.getItem('statsData'));
|
||||
// tl;dr this creates the objects if they don't exist
|
||||
// this really needs a cleanup at some point
|
||||
if (!data[type] || !data[type][value]) {
|
||||
@@ -45,29 +19,9 @@ export default class Stats {
|
||||
localStorage.setItem('statsData', JSON.stringify(data));
|
||||
}
|
||||
|
||||
async tabLoad() {
|
||||
if (this.online) {
|
||||
// umami
|
||||
await fetch(this.url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'pageview',
|
||||
payload: {
|
||||
website: this.id,
|
||||
url: '/',
|
||||
language: localStorage.getItem('language').replace('_', '-'),
|
||||
screen: `${window.screen.width}x${window.screen.height}`
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// local
|
||||
let data = JSON.parse(localStorage.getItem('statsData'));
|
||||
static async tabLoad() {
|
||||
const data = JSON.parse(localStorage.getItem('statsData'));
|
||||
data['tabs-opened'] = data['tabs-opened'] + 1 || 1;
|
||||
localStorage.setItem('statsData', JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,13 @@
|
||||
outline: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
color: grey !important;
|
||||
background: none;
|
||||
border: 2px solid grey !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dark %settingsButton {
|
||||
@@ -69,7 +76,7 @@
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--tab-active);
|
||||
background: var(--tab-active);
|
||||
}
|
||||
|
||||
svg {
|
||||
@@ -79,4 +86,4 @@
|
||||
span {
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,6 +290,20 @@
|
||||
},
|
||||
"experimental_warning": "Bitte beachten Sie, dass das Mue Team keinen Support leisten kann, wenn Sie den experimentellen Modus aktiviert haben. Bitte deaktivieren Sie ihn zuerst und schauen Sie, ob das Problem weiterhin auftritt, bevor Sie den Support kontaktieren."
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats",
|
||||
"warning": "You need to enable usage data in order to use this feature. This data is only stored locally.",
|
||||
"sections": {
|
||||
"tabs_opened": "Tabs opened",
|
||||
"backgrounds_favourited": "Backgrounds favourited",
|
||||
"backgrounds_downloaded": "Backgrounds downloaded",
|
||||
"quotes_favourited": "Quotes favourited",
|
||||
"quicklinks_added": "Quicklinks added",
|
||||
"settings_changed": "Settings changed",
|
||||
"addons_installed": "Add-ons installed"
|
||||
},
|
||||
"usage": "Usage Stats"
|
||||
},
|
||||
"experimental": {
|
||||
"title": "Experimentell",
|
||||
"warning": "Diese Einstellungen sind nicht vollständig getestet/implementiert und funktionieren möglicherweise nicht korrekt!",
|
||||
@@ -371,6 +385,30 @@
|
||||
"oldest": "Installed (Oldest)",
|
||||
"a_z": "Alphabetical (A-Z)",
|
||||
"z_a": "Alphabetical (Z-A)"
|
||||
},
|
||||
"create": {
|
||||
"title": "Create",
|
||||
"other_title": "Create Add-on",
|
||||
"metadata": {
|
||||
"name": "Name",
|
||||
"icon_url": "Icon URL",
|
||||
"screenshot_url": "Screenshot URL",
|
||||
"description": "Description"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Finish",
|
||||
"download": "Download Add-on"
|
||||
},
|
||||
"settings": {
|
||||
"current": "Import current setup",
|
||||
"json": "Upload JSON"
|
||||
},
|
||||
"photos": {
|
||||
"title": "Add Photos"
|
||||
},
|
||||
"quotes": {
|
||||
"title": "Add Quotes"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -396,7 +434,7 @@
|
||||
},
|
||||
"language": {
|
||||
"title": "Choose your language",
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our GitHub!"
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Select a theme",
|
||||
|
||||
@@ -290,6 +290,20 @@
|
||||
},
|
||||
"experimental_warning": "Please note that the Mue team cannot provide support if you have experimental mode on. Please disable it first and see if the issue continues to occur before contacting support."
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats",
|
||||
"warning": "You need to enable usage data in order to use this feature. This data is only stored locally.",
|
||||
"sections": {
|
||||
"tabs_opened": "Tabs opened",
|
||||
"backgrounds_favourited": "Backgrounds favourited",
|
||||
"backgrounds_downloaded": "Backgrounds downloaded",
|
||||
"quotes_favourited": "Quotes favourited",
|
||||
"quicklinks_added": "Quicklinks added",
|
||||
"settings_changed": "Settings changed",
|
||||
"addons_installed": "Add-ons installed"
|
||||
},
|
||||
"usage": "Usage Stats"
|
||||
},
|
||||
"experimental": {
|
||||
"title": "Experimental",
|
||||
"warning": "These settings have not been fully tested/implemented and may not work correctly!",
|
||||
@@ -371,6 +385,30 @@
|
||||
"oldest": "Installed (Oldest)",
|
||||
"a_z": "Alphabetical (A-Z)",
|
||||
"z_a": "Alphabetical (Z-A)"
|
||||
},
|
||||
"create": {
|
||||
"title": "Create",
|
||||
"other_title": "Create Add-on",
|
||||
"metadata": {
|
||||
"name": "Name",
|
||||
"icon_url": "Icon URL",
|
||||
"screenshot_url": "Screenshot URL",
|
||||
"description": "Description"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Finish",
|
||||
"download": "Download Add-on"
|
||||
},
|
||||
"settings": {
|
||||
"current": "Import current setup",
|
||||
"json": "Upload JSON"
|
||||
},
|
||||
"photos": {
|
||||
"title": "Add Photos"
|
||||
},
|
||||
"quotes": {
|
||||
"title": "Add Quotes"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -392,11 +430,11 @@
|
||||
"sections": {
|
||||
"intro": {
|
||||
"title": "Welcome to Mue Tab",
|
||||
"description": "Thank you for installing, we hope you enjoy your time with our extension."
|
||||
"description": "Thank you for installing Mue, we hope you enjoy your time with our extension."
|
||||
},
|
||||
"language": {
|
||||
"title": "Choose your language",
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our GitHub!"
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Select a theme",
|
||||
|
||||
@@ -290,6 +290,20 @@
|
||||
},
|
||||
"experimental_warning": "Please note that the Mue team cannot provide support if you have experimental mode on. Please disable it first and see if the issue continues to occur before contacting support."
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats",
|
||||
"warning": "You need to enable usage data in order to use this feature. This data is only stored locally.",
|
||||
"sections": {
|
||||
"tabs_opened": "Tabs opened",
|
||||
"backgrounds_favourited": "Backgrounds favourited",
|
||||
"backgrounds_downloaded": "Backgrounds downloaded",
|
||||
"quotes_favourited": "Quotes favourited",
|
||||
"quicklinks_added": "Quicklinks added",
|
||||
"settings_changed": "Settings changed",
|
||||
"addons_installed": "Add-ons installed"
|
||||
},
|
||||
"usage": "Usage Stats"
|
||||
},
|
||||
"experimental": {
|
||||
"title": "Experimental",
|
||||
"warning": "These settings have not been fully tested/implemented and may not work correctly!",
|
||||
@@ -371,6 +385,30 @@
|
||||
"oldest": "Installed (Oldest)",
|
||||
"a_z": "Alphabetical (A-Z)",
|
||||
"z_a": "Alphabetical (Z-A)"
|
||||
},
|
||||
"create": {
|
||||
"title": "Create",
|
||||
"other_title": "Create Add-on",
|
||||
"metadata": {
|
||||
"name": "Name",
|
||||
"icon_url": "Icon URL",
|
||||
"screenshot_url": "Screenshot URL",
|
||||
"description": "Description"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Finish",
|
||||
"download": "Download Add-on"
|
||||
},
|
||||
"settings": {
|
||||
"current": "Import current setup",
|
||||
"json": "Upload JSON"
|
||||
},
|
||||
"photos": {
|
||||
"title": "Add Photos"
|
||||
},
|
||||
"quotes": {
|
||||
"title": "Add Quotes"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -392,11 +430,11 @@
|
||||
"sections": {
|
||||
"intro": {
|
||||
"title": "Welcome to Mue Tab",
|
||||
"description": "Thank you for installing, we hope you enjoy your time with our extension."
|
||||
"description": "Thank you for installing Mue, we hope you enjoy your time with our extension."
|
||||
},
|
||||
"language": {
|
||||
"title": "Choose your language",
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our GitHub!"
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Select a theme",
|
||||
|
||||
@@ -179,12 +179,12 @@
|
||||
}
|
||||
},
|
||||
"interval": {
|
||||
"title": "Change every",
|
||||
"minute": "Minute",
|
||||
"half_hour": "Half hour",
|
||||
"hour": "Hour",
|
||||
"day": "Day",
|
||||
"month": "Month"
|
||||
"title": "Cambiar cada",
|
||||
"minute": "Minuto",
|
||||
"half_hour": "Media hora",
|
||||
"hour": "Hora",
|
||||
"day": "Día",
|
||||
"month": "Mes"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
@@ -285,11 +285,25 @@
|
||||
"custom_js": "JS personalizado",
|
||||
"tab_name": "Nombre de la pestaña",
|
||||
"timezone": {
|
||||
"title": "Time Zone",
|
||||
"automatic": "Automatic"
|
||||
"title": "Zona horaria",
|
||||
"automatic": "Automático"
|
||||
},
|
||||
"experimental_warning": "Por favor, ten en cuenta que el equipo de Mue no puede dar soporte si tienes el modo experimental activado. Por favor, desactívalo primero y comprueba si el problema sigue ocurriendo antes de contactar con el equipo de soporte."
|
||||
},
|
||||
"stats": {
|
||||
"title": "Estadísticas",
|
||||
"warning": "Tienes que activar las estadísticas de uso para poder utilizar esta función. This data is only stored locally.",
|
||||
"sections": {
|
||||
"tabs_opened": "Pestañas abiertas",
|
||||
"backgrounds_favourited": "Fondos favoritos",
|
||||
"backgrounds_downloaded": "Fondos descargados",
|
||||
"quotes_favourited": "Citas favoritas",
|
||||
"quicklinks_added": "Enlaces rápidos añadidos",
|
||||
"settings_changed": "Ajustes cambiados",
|
||||
"addons_installed": "Complementos instalados"
|
||||
},
|
||||
"usage": "Estadísticas de uso"
|
||||
},
|
||||
"experimental": {
|
||||
"title": "Experimental",
|
||||
"warning": "Estos ajustes no han sido totalmente probados/implementados y pueden no funcionar correctamente.",
|
||||
@@ -371,6 +385,30 @@
|
||||
"oldest": "Instalado (Antiguos)",
|
||||
"a_z": "Alfabético (A-Z)",
|
||||
"z_a": "Alfabético (Z-A)"
|
||||
},
|
||||
"create": {
|
||||
"title": "Crear",
|
||||
"other_title": "Crear complemento",
|
||||
"metadata": {
|
||||
"name": "Nombre",
|
||||
"icon_url": "URL del icono",
|
||||
"screenshot_url": "URL de la captura de pantalla",
|
||||
"description": "Descripción"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Acabar",
|
||||
"download": "Descargar complemento"
|
||||
},
|
||||
"settings": {
|
||||
"current": "Importar la configuración actual",
|
||||
"json": "Subir JSON"
|
||||
},
|
||||
"photos": {
|
||||
"title": "Añadir fotos"
|
||||
},
|
||||
"quotes": {
|
||||
"title": "Añadir citas"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -388,49 +426,49 @@
|
||||
"contact_support": "Contáctanos aquí"
|
||||
},
|
||||
"welcome": {
|
||||
"tip": "Quick Tip",
|
||||
"tip": "Truco",
|
||||
"sections": {
|
||||
"intro": {
|
||||
"title": "Bienvenido a Mue Tab",
|
||||
"description": "Gracias por instalar, esperamos que disfrute de su tiempo con nuestra extensión."
|
||||
"description": "Gracias por instalar Mue, esperamos que disfrute de su tiempo con nuestra extensión."
|
||||
},
|
||||
"language": {
|
||||
"title": "Choose your language",
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our GitHub!"
|
||||
"title": "Elige el idioma",
|
||||
"description": "Mue puede mostrarse en los idiomas que se indican debajo. ¡También puedes contribuir con traducciones en nuestro"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Select a theme",
|
||||
"description": "Mue is available in both light and dark theme, or this can be automatically set depending on your system theme.",
|
||||
"tip": "Using the Auto settings will use the theme on your computer. This setting will impact the modals and some of the widgets displayed on the screen, such as weather and notes."
|
||||
"title": "Selecciona un tema",
|
||||
"description": "Mue está disponible tanto en el tema claro como en el oscuro, también se puede configurar automáticamente en función del tema de su sistema.",
|
||||
"tip": "Si utiliza la configuración automática, utilizará el tema de su ordenador. Esta configuración afectará a los modales y a algunos de los widgets que aparecen en la pantalla, como el tiempo y las notas."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Import Settings",
|
||||
"description": "Installing Mue on a new device? Feel free to import your old settings!",
|
||||
"tip": "You can export your old settings by navigating to the Advanced tab in your old Mue setup. Then you need to click the export button which will download the JSON file. You can upload this file here to carry across your settings and preferences from your previous Mue installation."
|
||||
"title": "Importa los ajustes",
|
||||
"description": "¿Instalando Mue en un nuevo dispositivo? No dudes en importar tu antigua configuración.",
|
||||
"tip": "Puedes exportar tu antigua configuración navegando a la pestaña Avanzado en tu antigua configuración de Mue. Luego debes hacer clic en el botón de exportación que descargará el archivo JSON. Puedes subir este archivo aquí para mantener tus ajustes y preferencias de tu anterior instalación de Mue."
|
||||
},
|
||||
"privacy": {
|
||||
"title": "Privacy Options",
|
||||
"description": "Enable settings to further protect your privacy with Mue.",
|
||||
"offline_mode_description": "Enabling offline mode will disable all requests to any service. This will result in online backgrounds, online quotes, marketplace, weather, quick links, change log and some about tab information to be disabled.",
|
||||
"ddg_proxy_description": "You can make image requests go through DuckDuckGo if you wish. By default, API requests go through our open source servers and image requests go through the original server. Turning this off for quick links will get the icons from Google instead of DuckDuckGo. DuckDuckGo proxy is always enabled for the Marketplace.",
|
||||
"title": "Opciones de privacidad",
|
||||
"description": "Activa estos ajustes para proteger aún más tu privacidad con Mue.",
|
||||
"offline_mode_description": "Al activar el modo offline se deshabilitarán todas las peticiones a cualquier servicio. Esto hará que se desactiven los fondos en línea, las citas en línea, la tienda, el tiempo, los enlaces rápidos, el registro de cambios y alguna información de la pestaña Acerca de.",
|
||||
"ddg_proxy_description": "Puedes hacer que las solicitudes de imágenes pasen por DuckDuckGo si lo deseas. Por defecto, las solicitudes a la API van a tráves de nuestros servidores de código abierto y las solicitudes de imágenes van a través del servidor original. Si desactivas esta opción para los enlaces rápidos y los iconos de obtendrán de Google en lugar de DuckDuckGo. El proxy de DuckDuckGo está siempre activado para la tienda.",
|
||||
"links": {
|
||||
"title": "Links",
|
||||
"privacy_policy": "Privacy Policy",
|
||||
"source_code": "Source Code"
|
||||
"title": "Enlaces",
|
||||
"privacy_policy": "Política de privacidad",
|
||||
"source_code": "Código fuente"
|
||||
}
|
||||
},
|
||||
"final": {
|
||||
"title": "Final step",
|
||||
"description": "Your Mue Tab experience is about to begin.",
|
||||
"changes": "Changes",
|
||||
"changes_description": "To change settings later click on the settings icon in the top right corner of your tab.",
|
||||
"imported": "Imported",
|
||||
"settings": "settings"
|
||||
"title": "Último paso",
|
||||
"description": "Tu experiencia con Mue Tab está a punto de comenzar",
|
||||
"changes": "Cambios",
|
||||
"changes_description": "Para cambiar la configuración más tarde, haga clic en el icono de configuración en la esquina superior derecha de su pestaña.",
|
||||
"imported": "Importados",
|
||||
"settings": "ajustes"
|
||||
}
|
||||
},
|
||||
"buttons": {
|
||||
"next": "Next",
|
||||
"previous": "Previous",
|
||||
"next": "Siguiente",
|
||||
"previous": "Anterior",
|
||||
"close": "Cerrar"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -290,6 +290,20 @@
|
||||
},
|
||||
"experimental_warning": "Veuillez noter que l'équipe Mue ne peut pas fournir d'assistance si vous avez activé le mode expérimental. Veuillez d'abord le désactiver et voir si le problème persiste avant de contacter le support."
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats",
|
||||
"warning": "You need to enable usage data in order to use this feature. This data is only stored locally.",
|
||||
"sections": {
|
||||
"tabs_opened": "Tabs opened",
|
||||
"backgrounds_favourited": "Backgrounds favourited",
|
||||
"backgrounds_downloaded": "Backgrounds downloaded",
|
||||
"quotes_favourited": "Quotes favourited",
|
||||
"quicklinks_added": "Quicklinks added",
|
||||
"settings_changed": "Settings changed",
|
||||
"addons_installed": "Add-ons installed"
|
||||
},
|
||||
"usage": "Usage Stats"
|
||||
},
|
||||
"experimental": {
|
||||
"title": "Expérimental",
|
||||
"warning": "These settings have not been fully tested/implemented and may not work correctly!",
|
||||
@@ -371,6 +385,30 @@
|
||||
"oldest": "Installed (Oldest)",
|
||||
"a_z": "Alphabetical (A-Z)",
|
||||
"z_a": "Alphabetical (Z-A)"
|
||||
},
|
||||
"create": {
|
||||
"title": "Create",
|
||||
"other_title": "Create Add-on",
|
||||
"metadata": {
|
||||
"name": "Name",
|
||||
"icon_url": "Icon URL",
|
||||
"screenshot_url": "Screenshot URL",
|
||||
"description": "Description"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Finish",
|
||||
"download": "Download Add-on"
|
||||
},
|
||||
"settings": {
|
||||
"current": "Import current setup",
|
||||
"json": "Upload JSON"
|
||||
},
|
||||
"photos": {
|
||||
"title": "Add Photos"
|
||||
},
|
||||
"quotes": {
|
||||
"title": "Add Quotes"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -392,11 +430,11 @@
|
||||
"sections": {
|
||||
"intro": {
|
||||
"title": "Bienvenue en Mue Tab",
|
||||
"description": "Merci d'avoir installé, nous espérons que vous apprécierez votre temps avec notre extension."
|
||||
"description": "Merci d'avoir installé Mue, nous espérons que vous apprécierez votre temps avec notre extension."
|
||||
},
|
||||
"language": {
|
||||
"title": "Choose your language",
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our GitHub!"
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Select a theme",
|
||||
|
||||
@@ -290,6 +290,20 @@
|
||||
},
|
||||
"experimental_warning": "Please note that the Mue team cannot provide support if you have experimental mode on. Please disable it first and see if the issue continues to occur before contacting support."
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats",
|
||||
"warning": "You need to enable usage data in order to use this feature. This data is only stored locally.",
|
||||
"sections": {
|
||||
"tabs_opened": "Tabs opened",
|
||||
"backgrounds_favourited": "Backgrounds favourited",
|
||||
"backgrounds_downloaded": "Backgrounds downloaded",
|
||||
"quotes_favourited": "Quotes favourited",
|
||||
"quicklinks_added": "Quicklinks added",
|
||||
"settings_changed": "Settings changed",
|
||||
"addons_installed": "Add-ons installed"
|
||||
},
|
||||
"usage": "Usage Stats"
|
||||
},
|
||||
"experimental": {
|
||||
"title": "Experimenteel",
|
||||
"warning": "These settings have not been fully tested/implemented and may not work correctly!",
|
||||
@@ -371,6 +385,30 @@
|
||||
"oldest": "Installed (Oldest)",
|
||||
"a_z": "Alphabetical (A-Z)",
|
||||
"z_a": "Alphabetical (Z-A)"
|
||||
},
|
||||
"create": {
|
||||
"title": "Create",
|
||||
"other_title": "Create Add-on",
|
||||
"metadata": {
|
||||
"name": "Name",
|
||||
"icon_url": "Icon URL",
|
||||
"screenshot_url": "Screenshot URL",
|
||||
"description": "Description"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Finish",
|
||||
"download": "Download Add-on"
|
||||
},
|
||||
"settings": {
|
||||
"current": "Import current setup",
|
||||
"json": "Upload JSON"
|
||||
},
|
||||
"photos": {
|
||||
"title": "Add Photos"
|
||||
},
|
||||
"quotes": {
|
||||
"title": "Add Quotes"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -392,11 +430,11 @@
|
||||
"sections": {
|
||||
"intro": {
|
||||
"title": "Welcome to Mue Tab",
|
||||
"description": "Thank you for installing, we hope you enjoy your time with our extension."
|
||||
"description": "Thank you for installing Mue, we hope you enjoy your time with our extension."
|
||||
},
|
||||
"language": {
|
||||
"title": "Choose your language",
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our GitHub!"
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Select a theme",
|
||||
|
||||
@@ -290,6 +290,20 @@
|
||||
},
|
||||
"experimental_warning": "Please note that the Mue team cannot provide support if you have experimental mode on. Please disable it first and see if the issue continues to occur before contacting support."
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats",
|
||||
"warning": "You need to enable usage data in order to use this feature. This data is only stored locally.",
|
||||
"sections": {
|
||||
"tabs_opened": "Tabs opened",
|
||||
"backgrounds_favourited": "Backgrounds favourited",
|
||||
"backgrounds_downloaded": "Backgrounds downloaded",
|
||||
"quotes_favourited": "Quotes favourited",
|
||||
"quicklinks_added": "Quicklinks added",
|
||||
"settings_changed": "Settings changed",
|
||||
"addons_installed": "Add-ons installed"
|
||||
},
|
||||
"usage": "Usage Stats"
|
||||
},
|
||||
"experimental": {
|
||||
"title": "Eksperimental",
|
||||
"warning": "These settings have not been fully tested/implemented and may not work correctly!",
|
||||
@@ -371,6 +385,30 @@
|
||||
"oldest": "Installed (Oldest)",
|
||||
"a_z": "Alphabetical (A-Z)",
|
||||
"z_a": "Alphabetical (Z-A)"
|
||||
},
|
||||
"create": {
|
||||
"title": "Create",
|
||||
"other_title": "Create Add-on",
|
||||
"metadata": {
|
||||
"name": "Name",
|
||||
"icon_url": "Icon URL",
|
||||
"screenshot_url": "Screenshot URL",
|
||||
"description": "Description"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Finish",
|
||||
"download": "Download Add-on"
|
||||
},
|
||||
"settings": {
|
||||
"current": "Import current setup",
|
||||
"json": "Upload JSON"
|
||||
},
|
||||
"photos": {
|
||||
"title": "Add Photos"
|
||||
},
|
||||
"quotes": {
|
||||
"title": "Add Quotes"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -392,11 +430,11 @@
|
||||
"sections": {
|
||||
"intro": {
|
||||
"title": "Welcome to Mue Tab",
|
||||
"description": "Thank you for installing, we hope you enjoy your time with our extension."
|
||||
"description": "Thank you for installing Mue, we hope you enjoy your time with our extension."
|
||||
},
|
||||
"language": {
|
||||
"title": "Choose your language",
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our GitHub!"
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Select a theme",
|
||||
|
||||
@@ -290,6 +290,20 @@
|
||||
},
|
||||
"experimental_warning": "Please note that the Mue team cannot provide support if you have experimental mode on. Please disable it first and see if the issue continues to occur before contacting support."
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats",
|
||||
"warning": "You need to enable usage data in order to use this feature. This data is only stored locally.",
|
||||
"sections": {
|
||||
"tabs_opened": "Tabs opened",
|
||||
"backgrounds_favourited": "Backgrounds favourited",
|
||||
"backgrounds_downloaded": "Backgrounds downloaded",
|
||||
"quotes_favourited": "Quotes favourited",
|
||||
"quicklinks_added": "Quicklinks added",
|
||||
"settings_changed": "Settings changed",
|
||||
"addons_installed": "Add-ons installed"
|
||||
},
|
||||
"usage": "Usage Stats"
|
||||
},
|
||||
"experimental": {
|
||||
"title": "Экспериментальные настройки",
|
||||
"warning": "These settings have not been fully tested/implemented and may not work correctly!",
|
||||
@@ -371,6 +385,30 @@
|
||||
"oldest": "Installed (Oldest)",
|
||||
"a_z": "Alphabetical (A-Z)",
|
||||
"z_a": "Alphabetical (Z-A)"
|
||||
},
|
||||
"create": {
|
||||
"title": "Create",
|
||||
"other_title": "Create Add-on",
|
||||
"metadata": {
|
||||
"name": "Name",
|
||||
"icon_url": "Icon URL",
|
||||
"screenshot_url": "Screenshot URL",
|
||||
"description": "Description"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Finish",
|
||||
"download": "Download Add-on"
|
||||
},
|
||||
"settings": {
|
||||
"current": "Import current setup",
|
||||
"json": "Upload JSON"
|
||||
},
|
||||
"photos": {
|
||||
"title": "Add Photos"
|
||||
},
|
||||
"quotes": {
|
||||
"title": "Add Quotes"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -393,11 +431,11 @@
|
||||
"sections": {
|
||||
"intro": {
|
||||
"title": "Welcome to Mue Tab",
|
||||
"description": "Thank you for installing, we hope you enjoy your time with our extension."
|
||||
"description": "Thank you for installing Mue, we hope you enjoy your time with our extension."
|
||||
},
|
||||
"language": {
|
||||
"title": "Choose your language",
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our GitHub!"
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Select a theme",
|
||||
|
||||
@@ -290,6 +290,20 @@
|
||||
},
|
||||
"experimental_warning": "请注意 Mue 团队无法提供实验性功能的支持。若您遇到问题,请先关闭实验性功能,再寻求帮助。"
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats",
|
||||
"warning": "You need to enable usage data in order to use this feature. This data is only stored locally.",
|
||||
"sections": {
|
||||
"tabs_opened": "Tabs opened",
|
||||
"backgrounds_favourited": "Backgrounds favourited",
|
||||
"backgrounds_downloaded": "Backgrounds downloaded",
|
||||
"quotes_favourited": "Quotes favourited",
|
||||
"quicklinks_added": "Quicklinks added",
|
||||
"settings_changed": "Settings changed",
|
||||
"addons_installed": "Add-ons installed"
|
||||
},
|
||||
"usage": "Usage Stats"
|
||||
},
|
||||
"experimental": {
|
||||
"title": "实验性功能",
|
||||
"warning": "以下设置仍未完成编写或测试,可能无法正常运作!",
|
||||
@@ -371,6 +385,30 @@
|
||||
"oldest": "Installed (Oldest)",
|
||||
"a_z": "Alphabetical (A-Z)",
|
||||
"z_a": "Alphabetical (Z-A)"
|
||||
},
|
||||
"create": {
|
||||
"title": "Create",
|
||||
"other_title": "Create Add-on",
|
||||
"metadata": {
|
||||
"name": "Name",
|
||||
"icon_url": "Icon URL",
|
||||
"screenshot_url": "Screenshot URL",
|
||||
"description": "Description"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Finish",
|
||||
"download": "Download Add-on"
|
||||
},
|
||||
"settings": {
|
||||
"current": "Import current setup",
|
||||
"json": "Upload JSON"
|
||||
},
|
||||
"photos": {
|
||||
"title": "Add Photos"
|
||||
},
|
||||
"quotes": {
|
||||
"title": "Add Quotes"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -396,7 +434,7 @@
|
||||
},
|
||||
"language": {
|
||||
"title": "Choose your language",
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our GitHub!"
|
||||
"description": "Mue can be displayed the languages listed below. You can also add new translations on our"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Select a theme",
|
||||
|
||||
Reference in New Issue
Block a user