mirror of
https://github.com/mue/mue.git
synced 2026-06-13 12:08:42 +02:00
Compare commits
176 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a170981772 | ||
|
|
14d3589551 | ||
|
|
c736dcf57a | ||
|
|
623b54eca0 | ||
|
|
1dc0389a96 | ||
|
|
3c7be2d64d | ||
|
|
5703abb685 | ||
|
|
8f08b4d09d | ||
|
|
441d440c74 | ||
|
|
fe5f15be24 | ||
|
|
bd941896aa | ||
|
|
d9563ce343 | ||
|
|
af2edeb737 | ||
|
|
f8ee23eb40 | ||
|
|
234d489a29 | ||
|
|
7881e47590 | ||
|
|
40397ac33c | ||
|
|
9da0cb611e | ||
|
|
7c7994c63f | ||
|
|
42f89b3fc3 | ||
|
|
da6c1bba63 | ||
|
|
f96399c9df | ||
|
|
d17948f744 | ||
|
|
cf39845892 | ||
|
|
a3fb4a6522 | ||
|
|
f4e88cef2e | ||
|
|
e786ad654c | ||
|
|
a356c8fe61 | ||
|
|
c627599590 | ||
| 4a6f93e701 | |||
|
|
8aca9ae31c | ||
|
|
8966336b93 | ||
|
|
41750ff0e8 | ||
|
|
62066fef05 | ||
|
|
eb0ffc3cc2 | ||
|
|
3bc1409f4f | ||
|
|
c15f9d20f8 | ||
|
|
7f5b826683 | ||
|
|
cb2c5e3117 | ||
|
|
2824e7c995 | ||
|
|
8888cb2144 | ||
|
|
1c80d37bda | ||
|
|
1b4b37d4b1 | ||
|
|
8820343628 | ||
|
|
00f30c306e | ||
|
|
ed964c1354 | ||
|
|
bc8ef0d9f2 | ||
|
|
14121fb975 | ||
|
|
bd8d9fb8e4 | ||
|
|
2a6505799b | ||
|
|
33ef113f6d | ||
|
|
a5dc2c98a0 | ||
|
|
c9854d5362 | ||
|
|
9a6119e861 | ||
|
|
9ac410d989 | ||
|
|
80a7b6a56c | ||
|
|
883f025fbb | ||
|
|
d1b23dc07b | ||
|
|
364fb4b0d7 | ||
|
|
cef4026b97 | ||
|
|
5702c45187 | ||
|
|
e7dc9f04d1 | ||
|
|
bc9f68e0c1 | ||
|
|
d2d7083d3d | ||
|
|
43ad2c31f8 | ||
|
|
fa485c35c3 | ||
|
|
231966546f | ||
|
|
95c6216b0c | ||
|
|
52745a267f | ||
|
|
cbbc911cc3 | ||
|
|
e78611cb05 | ||
|
|
2049359a96 | ||
|
|
96f26042d2 | ||
|
|
f7ba4c0ed9 | ||
|
|
4ad9d4810e | ||
|
|
94e9c5d65e | ||
|
|
e3edc3d89e | ||
|
|
8b4d1d18f1 | ||
|
|
0fd50e949c | ||
|
|
42b4aa1d62 | ||
|
|
39fbd9336f | ||
|
|
93ba1cd9bf | ||
|
|
a946d07c9c | ||
|
|
3f3ee2dd82 | ||
|
|
4953298b9f | ||
|
|
f931abf3fb | ||
|
|
b97b925978 | ||
|
|
1c65447c55 | ||
|
|
66a3629b51 | ||
|
|
7e3fc8085e | ||
|
|
6cd934acdc | ||
|
|
a7e7743028 | ||
|
|
27668172ec | ||
|
|
b0eeff1bf8 | ||
|
|
941c168486 | ||
|
|
aceba529ba | ||
|
|
2d7db1558d | ||
|
|
102078744e | ||
|
|
6bb461b8a2 | ||
|
|
2984e162cc | ||
|
|
b86e73f2fc | ||
|
|
22ef3ad139 | ||
|
|
26ae76accc | ||
|
|
da2c70f897 | ||
|
|
c63a13a4c2 | ||
|
|
5baf067ff1 | ||
|
|
7f71321e67 | ||
|
|
3cc8031c84 | ||
|
|
b5c3ca201e | ||
|
|
604834c4d7 | ||
|
|
d368270553 | ||
|
|
8cbe3b909b | ||
|
|
b0399b6314 | ||
|
|
a00037747b | ||
|
|
5af8dfac11 | ||
|
|
21f8b63b80 | ||
|
|
67ab7c8674 | ||
|
|
6b23e56bd6 | ||
|
|
8b25175b9c | ||
|
|
06038a201e | ||
|
|
4db47d9fec | ||
|
|
fcff3e44a6 | ||
|
|
abe70617d8 | ||
|
|
feb7e1d93c | ||
|
|
de4ffbcb0a | ||
|
|
e5f8cf3e66 | ||
|
|
10f27d24b0 | ||
|
|
4a4e4604e8 | ||
|
|
3ed2171a34 | ||
|
|
d33a879281 | ||
|
|
d91b939135 | ||
|
|
daee291aa9 | ||
|
|
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 |
@@ -12,7 +12,7 @@ Mue is a fast, open and free-to-use browser extension that gives a new, fresh an
|
||||
<br>
|
||||
|
||||
## Table of contents
|
||||
* [Screenshot](#screenshot)
|
||||
* [Screenshots](#screenshot)
|
||||
* [Features](#features)
|
||||
* [Planned Features](#planned-features)
|
||||
* [Installation](#installation)
|
||||
@@ -37,13 +37,13 @@ Mue is a fast, open and free-to-use browser extension that gives a new, fresh an
|
||||
* Supports multiple browsers
|
||||
* Actively developed and open source
|
||||
* Automatically updating [API](https://github.com/mue/api) with new photos, quotes and offline mode
|
||||
* Widgets such as searchbar, weather, quick links, clock, date, quote, greeting
|
||||
* Widgets such as search bar, weather, quick links, clock, date, quote, greeting
|
||||
* Settings - enable/disable various features and customise parts of Mue
|
||||
* Navbar with copy button, favourite background, notes feature etc
|
||||
* [Marketplace](https://github.com/mue/marketplace) - download custom photo packs, quote packs and preset settings made by the community
|
||||
|
||||
### Planned Features
|
||||
Please see our [roadmap](https://github.com/mue/mue/projects)
|
||||
Please see our [roadmap](https://trello.com/b/w7zhS7Hi/mue-50).
|
||||
|
||||
## Installation
|
||||
*A demo of the tab can be found [here](https://demo.muetab.com), and the latest GitHub commit build [here](https://mue.vercel.app)*
|
||||
@@ -74,7 +74,7 @@ Please see the [documentation](https://docs.muetab.com/translations).
|
||||
|
||||
## Credits
|
||||
### Developers
|
||||
[David Ralph](https://github.com/davidjcralph) - Lead development, photographer <br/>
|
||||
[David Ralph](https://github.com/davidcralph) - Lead development, photographer <br/>
|
||||
[Alex Sparkes](https://github.com/alexsparkes) - Name, lead design, photographer <br/>
|
||||
[Isaac Saunders](https://github.com/eartharoid) - QA, development, photographer <br/>
|
||||
[Wessel Tip](https://github.com/Wessel) - Development <br/>
|
||||
|
||||
@@ -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": "6.0.3",
|
||||
"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": "6.0.3",
|
||||
"homepage_url": "https://muetab.com",
|
||||
"browser_action": {
|
||||
"default_icon": "icons/128x128.png"
|
||||
|
||||
58
package.json
58
package.json
@@ -9,53 +9,55 @@
|
||||
"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": "6.0.3",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.4.0",
|
||||
"@eartharoid/i18n": "^1.0.2",
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@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",
|
||||
"@mui/icons-material": "5.2.0",
|
||||
"@mui/material": "5.2.1",
|
||||
"react": "17.0.2",
|
||||
"react-clock": "3.0.0",
|
||||
"react-color-gradient-picker": "0.1.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-modal": "3.14.3",
|
||||
"react-hot-keys": "2.7.1",
|
||||
"react-modal": "3.14.4",
|
||||
"react-sortable-hoc": "2.0.0",
|
||||
"react-toastify": "7.0.4",
|
||||
"react-toastify": "8.1.0",
|
||||
"weather-icons-react": "1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.8",
|
||||
"@babel/eslint-parser": "^7.14.9",
|
||||
"@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/preset-react": "^7.14.5",
|
||||
"@eartharoid/deep-merge": "^0.0.1",
|
||||
"babel-loader": "^8.2.2",
|
||||
"@babel/core": "^7.16.0",
|
||||
"@babel/eslint-parser": "^7.16.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.16.0",
|
||||
"@babel/plugin-transform-react-constant-elements": "^7.16.0",
|
||||
"@babel/plugin-transform-react-inline-elements": "^7.16.0",
|
||||
"@babel/plugin-transform-runtime": "^7.16.0",
|
||||
"@babel/preset-env": "^7.16.0",
|
||||
"@babel/preset-react": "^7.16.0",
|
||||
"@eartharoid/deep-merge": "^0.0.2",
|
||||
"babel-loader": "^8.2.3",
|
||||
"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",
|
||||
"copy-webpack-plugin": "9.1.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"eslint": "^8.2.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",
|
||||
"sass-loader": "^12.1.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"mini-css-extract-plugin": "^2.4.4",
|
||||
"sass": "^1.43.4",
|
||||
"sass-loader": "^12.3.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.63.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-dev-server": "^4.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack serve",
|
||||
"updatetranslations": "cd scripts && node updatetranslations.js",
|
||||
"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": {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// tl;dr this function merges the translation file with the english file in order to add untranslated strings
|
||||
const fs = require('fs');
|
||||
const merge = require('@eartharoid/deep-merge');
|
||||
|
||||
@@ -8,5 +9,6 @@ fs.readdirSync('../src/translations').forEach((file) => {
|
||||
|
||||
const newdata = merge(require('../src/translations/en_GB.json'), require('../src/translations/' + file));
|
||||
fs.writeFileSync('../src/translations/' + file, JSON.stringify(newdata, null, 2));
|
||||
// add new line
|
||||
fs.appendFileSync('../src/translations/' + file, '\n');
|
||||
});
|
||||
|
||||
29
src/App.jsx
29
src/App.jsx
@@ -1,33 +1,34 @@
|
||||
import React from 'react';
|
||||
|
||||
import Background from './components/widgets/background/Background';
|
||||
import Widgets from './components/widgets/Widgets';
|
||||
import Modals from './components/modals/Modals';
|
||||
|
||||
import EventBus from './modules/helpers/eventbus';
|
||||
import SettingsFunctions from './modules/helpers/settings';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
|
||||
export default class App extends React.PureComponent {
|
||||
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';
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
window.stats.tabLoad();
|
||||
variables.stats.tabLoad();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -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
|
||||
});
|
||||
@@ -32,9 +35,28 @@ export default class Autocomplete extends React.PureComponent {
|
||||
input: e.target.innerText
|
||||
});
|
||||
|
||||
this.props.onClick(e);
|
||||
this.props.onClick({
|
||||
preventDefault: () => e.preventDefault(),
|
||||
target: {
|
||||
value: e.target.innerText
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'search') {
|
||||
this.setState({
|
||||
autocompleteDisabled: (localStorage.getItem('autocomplete') !== 'true')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnount() {
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
let autocomplete = null;
|
||||
|
||||
@@ -42,26 +64,18 @@ export default class Autocomplete extends React.PureComponent {
|
||||
if (this.state.filtered.length > 0 && this.state.input.length > 0) {
|
||||
autocomplete = (
|
||||
<ul className='suggestions'>
|
||||
{this.state.filtered.map((suggestion) => {
|
||||
return (
|
||||
<li key={suggestion} onClick={this.onClick}>
|
||||
{suggestion}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
{this.state.filtered.map((suggestion) => (
|
||||
<li key={suggestion} onClick={this.onClick}>
|
||||
{suggestion}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type='text'
|
||||
onChange={this.onChange}
|
||||
value={this.state.input}
|
||||
placeholder={this.props.placeholder || ''}
|
||||
autoComplete='off'
|
||||
id={this.props.id || ''} />
|
||||
<input type='text' onChange={this.onChange} value={this.state.input} placeholder={this.props.placeholder || ''} autoComplete='off' id={this.props.id || ''} />
|
||||
{autocomplete}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -47,4 +47,4 @@
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
src/components/helpers/preview/Preview.jsx
Normal file
15
src/components/helpers/preview/Preview.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import variables from 'modules/variables';
|
||||
|
||||
import './preview.scss';
|
||||
|
||||
export default function Preview(props) {
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
return (
|
||||
<div className='preview-mode'>
|
||||
<h1>{getMessage('modals.main.settings.reminder.title')}</h1>
|
||||
<p>{getMessage('modals.welcome.preview.description')}</p>
|
||||
<button className='pinNote' onClick={() => props.setup()}>{getMessage('modals.welcome.preview.continue')}</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
17
src/components/helpers/preview/preview.scss
Normal file
17
src/components/helpers/preview/preview.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
.preview-mode {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
padding: 15px;
|
||||
color: var(--modal-text);
|
||||
background: var(--background);
|
||||
max-width: 300px;
|
||||
border-radius: .7em;
|
||||
z-index: 999;
|
||||
text-align: left;
|
||||
cursor: default;
|
||||
|
||||
h1 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import './tooltip.scss';
|
||||
|
||||
export default function Tooltip(props) {
|
||||
export default function Tooltip({ children, title }) {
|
||||
return (
|
||||
<div className='tooltip'>
|
||||
{props.children}
|
||||
<span className='tooltipTitle'>{props.title}</span>
|
||||
{children}
|
||||
<span className='tooltipTitle'>{title}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
display: inline-block;
|
||||
|
||||
.tooltipTitle {
|
||||
width: 60px;
|
||||
min-width: 60px;
|
||||
background-color: rgba(255, 255, 255, 0.89);
|
||||
color: #000000;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
font-size: 0.6rem;
|
||||
border-radius: 6px;
|
||||
@@ -20,7 +20,7 @@
|
||||
cursor: initial;
|
||||
user-select: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.8s;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -33,5 +33,5 @@
|
||||
|
||||
.dark .tooltipTitle {
|
||||
background-color: rgba(0, 0, 0, 0.79);
|
||||
color: #ffffff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { ErrorOutline } from '@mui/icons-material';
|
||||
|
||||
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 = {
|
||||
error: false
|
||||
};
|
||||
this.language = window.language.modals.main.error_boundary;
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
console.log(error);
|
||||
window.stats.postEvent('modal', 'Error occurred');
|
||||
variables.stats.postEvent('modal', 'Error occurred');
|
||||
return {
|
||||
error: true
|
||||
};
|
||||
@@ -24,10 +23,10 @@ export default class ErrorBoundary extends React.PureComponent {
|
||||
return (
|
||||
<div className='emptyitems'>
|
||||
<div className='emptyMessage'>
|
||||
<ErrorOutlineIcon/>
|
||||
<h1>{this.language.title}</h1>
|
||||
<p>{this.language.message}</p>
|
||||
<button className='refresh' onClick={() => window.location.reload()}>{this.language.refresh}</button>
|
||||
<ErrorOutline/>
|
||||
<h1>{variables.language.getMessage(variables.languagecode, 'modals.main.error_boundary.title')}</h1>
|
||||
<p>{variables.language.getMessage(variables.languagecode, 'modals.main.error_boundary.message')}</p>
|
||||
<button className='refresh' onClick={() => window.location.reload()}>{variables.language.getMessage(variables.languagecode, 'modals.main.error_boundary.refresh')}</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../modules/helpers/eventbus';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent, Suspense, lazy } from 'react';
|
||||
import Modal from 'react-modal';
|
||||
//import Hotkeys from 'react-hot-keys';
|
||||
|
||||
import Main from './main/Main';
|
||||
import Navbar from '../widgets/navbar/Navbar';
|
||||
import Preview from '../helpers/preview/Preview';
|
||||
|
||||
import Modal from 'react-modal';
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
|
||||
// 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 = {
|
||||
mainModal: false,
|
||||
updateModal: false,
|
||||
welcomeModal: false,
|
||||
feedbackModal: false
|
||||
feedbackModal: false,
|
||||
preview: false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,7 +31,15 @@ export default class Modals extends React.PureComponent {
|
||||
this.setState({
|
||||
welcomeModal: true
|
||||
});
|
||||
window.stats.postEvent('modal', 'Opened welcome');
|
||||
variables.stats.postEvent('modal', 'Opened welcome');
|
||||
}
|
||||
|
||||
if (window.location.search === '?nointro=true') {
|
||||
if (localStorage.getItem('showWelcome') === 'true') {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
EventBus.dispatch('refresh', 'widgets');
|
||||
EventBus.dispatch('refresh', 'backgroundwelcome');
|
||||
}
|
||||
}
|
||||
|
||||
// hide refresh reminder once the user has refreshed the page
|
||||
@@ -41,35 +51,45 @@ export default class Modals extends React.PureComponent {
|
||||
this.setState({
|
||||
welcomeModal: false
|
||||
});
|
||||
EventBus.dispatch('refresh', 'widgetsWelcomeDone');
|
||||
EventBus.dispatch('refresh', 'widgets');
|
||||
EventBus.dispatch('refresh', 'backgroundwelcome');
|
||||
}
|
||||
|
||||
previewWelcome() {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
localStorage.setItem('welcomePreview', true);
|
||||
this.setState({
|
||||
welcomeModal: false,
|
||||
preview: true
|
||||
});
|
||||
EventBus.dispatch('refresh', 'widgetsWelcome');
|
||||
}
|
||||
|
||||
toggleModal(type, action) {
|
||||
this.setState({
|
||||
[type]: action
|
||||
});
|
||||
|
||||
if (action !== false) {
|
||||
window.stats.postEvent('modal', `Opened ${type.replace('Modal', '')}`);
|
||||
variables.stats.postEvent('modal', `Opened ${type.replace('Modal', '')}`);
|
||||
}
|
||||
}
|
||||
|
||||
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()}/>
|
||||
<Welcome modalClose={() => this.closeWelcome()} modalSkip={() => this.previewWelcome()}/>
|
||||
</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>
|
||||
{this.state.preview ? <Preview setup={() => window.location.reload()}/> : null}
|
||||
{/*variables.keybinds.toggleModal && variables.keybinds.toggleModal !== '' ? <Hotkeys keyName={variables.keybinds.toggleModal} onKeyDown={() => this.toggleModal('mainModal', (this.state.mainModal === true ? false : true))}/> : null*/}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import './feedback.scss';
|
||||
|
||||
export default class FeedbackModal extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
questionone: 5,
|
||||
questionthree: 5,
|
||||
questiontwoerror: '',
|
||||
questionfourerror: '',
|
||||
formsubmit: ''
|
||||
};
|
||||
this.language = window.language.modals.feedback;
|
||||
}
|
||||
|
||||
async submitForm () {
|
||||
let questiontwoerror, questionfourerror;
|
||||
|
||||
if (document.getElementById('questiontwo').value.length <= 0) {
|
||||
questiontwoerror = this.language.not_filled;
|
||||
}
|
||||
|
||||
if (document.getElementById('questionfour').value.length <= 0) {
|
||||
questionfourerror = this.language.not_filled;
|
||||
}
|
||||
|
||||
if (questiontwoerror || questionfourerror) {
|
||||
this.setState({
|
||||
questiontwoerror: questiontwoerror,
|
||||
questionfourerror: questionfourerror
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
questiontwoerror: '',
|
||||
questionfourerror: ''
|
||||
});
|
||||
|
||||
await fetch(window.constants.FEEDBACK_FORM, {
|
||||
'method': 'POST'
|
||||
});
|
||||
|
||||
this.setState({
|
||||
formsubmit: this.language.success
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.props.modalClose();
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='feedback'>
|
||||
<h1>{this.language.title}</h1>
|
||||
<span className='closeModal' onClick={this.props.modalClose}>×</span>
|
||||
<>
|
||||
<input type='hidden' name='version' value={window.constants.VERSION} />
|
||||
<>
|
||||
<label>{this.language.question_one}</label>
|
||||
<br/><br/>
|
||||
<label className='values'>0</label>
|
||||
<input className='range' type='range' min='0' max='10' name='question1' value={this.state.questionone} onChange={(e) => this.setState({ questionone: e.target.value })}/>
|
||||
<label className='values'>10 ({this.state.questionone})</label>
|
||||
</>
|
||||
<br/><br/>
|
||||
<>
|
||||
<label>{this.language.question_two}</label>
|
||||
<textarea name='question2' id='questiontwo'/>
|
||||
<p className='feedbackerror'>{this.state.questiontwoerror}</p>
|
||||
</>
|
||||
<>
|
||||
<label>{this.language.question_three}</label>
|
||||
<br/><br/>
|
||||
<label className='values'>0</label>
|
||||
<input className='range' type='range' min='0' max='10' name='question3' value={this.state.questionthree} onChange={(e) => this.setState({ questionthree: e.target.value })}/>
|
||||
<label className='values'>10 ({this.state.questionthree})</label>
|
||||
</>
|
||||
<br/><br/>
|
||||
<>
|
||||
<label>{this.language.question_four}</label>
|
||||
<textarea name='question4' id='questionfour'/>
|
||||
<p className='feedbackerror'>{this.state.questionfourerror}</p>
|
||||
</>
|
||||
{this.state.formsubmit}
|
||||
<button onClick={() => this.submitForm()}>{this.language.submit}</button>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
@import '../main/scss/index.scss';
|
||||
|
||||
#feedbackmodal {
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 400px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.feedback {
|
||||
width: 400px;
|
||||
padding: 5px;
|
||||
|
||||
h1,
|
||||
.closeModal {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 6em;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 50%;
|
||||
border-radius: 48px;
|
||||
outline: none;
|
||||
border: none;
|
||||
padding: 15px 20px;
|
||||
font-size: 1.5em;
|
||||
background: linear-gradient(90deg, #ffb032 0%, #dd3b67 100%);
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
width: 100%;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
label.values {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 80%;
|
||||
background-color: var(--sidebar);
|
||||
}
|
||||
|
||||
.feedbackerror {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
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>
|
||||
<div label={window.language.modals.main.loading}>
|
||||
<div label={variables.language.getMessage(variables.languagecode, 'modals.main.loading')}>
|
||||
<div className='emptyitems'>
|
||||
<div className='emptyMessage'>
|
||||
<h1>{window.language.modals.main.loading}</h1>
|
||||
<h1>{variables.language.getMessage(variables.languagecode, 'modals.main.loading')}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -22,29 +23,34 @@ const renderLoader = () => (
|
||||
</Tabs>
|
||||
);
|
||||
|
||||
export default function MainModal(props) {
|
||||
const language = window.language.modals.main.navbar;
|
||||
export default function MainModal({ modalClose }) {
|
||||
const display = (localStorage.getItem('showReminder') === 'true') ? 'block' : 'none';
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className='closeModal' onClick={props.modalClose}>×</span>
|
||||
<span className='closeModal' onClick={modalClose}>×</span>
|
||||
<Tabs navbar={true}>
|
||||
<div label={language.settings} name='settings'>
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<div label={variables.language.getMessage(variables.languagecode, 'modals.main.navbar.settings')} name='settings'>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Settings/>
|
||||
</React.Suspense>
|
||||
</Suspense>
|
||||
</div>
|
||||
<div label={language.addons} name='addons'>
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<div label={variables.language.getMessage(variables.languagecode, 'modals.main.navbar.addons')} name='addons'>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Addons/>
|
||||
</React.Suspense>
|
||||
</Suspense>
|
||||
</div>
|
||||
<div label={language.marketplace} name='marketplace'>
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<div label={variables.language.getMessage(variables.languagecode, 'modals.main.navbar.marketplace')} name='marketplace'>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Marketplace/>
|
||||
</React.Suspense>
|
||||
</Suspense>
|
||||
</div>
|
||||
</Tabs>
|
||||
<div className='reminder-info' style={{ display }}>
|
||||
<h1>{variables.language.getMessage(variables.languagecode, 'modals.main.settings.reminder.title')}</h1>
|
||||
<p>{variables.language.getMessage(variables.languagecode, 'modals.main.settings.reminder.message')}</p>
|
||||
<button className='pinNote' onClick={() => window.location.reload()}>{variables.language.getMessage(variables.languagecode, 'modals.main.error_boundary.refresh')}</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,33 @@
|
||||
import React from 'react';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent, Fragment } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ArrowBack } from '@mui/icons-material';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import { install, uninstall } from 'modules/helpers/marketplace';
|
||||
|
||||
import Lightbox from './Lightbox';
|
||||
|
||||
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
||||
|
||||
export default class Item extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
export default class Item extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showLightbox: false
|
||||
showLightbox: false,
|
||||
showUpdateButton: (this.props.addonInstalled === true && this.props.addonInstalledVersion !== this.props.data.version)
|
||||
};
|
||||
}
|
||||
|
||||
updateAddon() {
|
||||
uninstall(this.props.data.type, this.props.data.display_name);
|
||||
install(this.props.data.type, this.props.data);
|
||||
toast(variables.language.getMessage(variables.languagecode, 'toasts.updated'));
|
||||
this.setState({
|
||||
showUpdateButton: false
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const language = window.language.modals.main.marketplace.product;
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
if (!this.props.data.display_name) {
|
||||
return null;
|
||||
@@ -26,35 +38,48 @@ export default class Item extends React.PureComponent {
|
||||
warningHTML = (
|
||||
<div className='productInformation'>
|
||||
<ul>
|
||||
<li className='header'>{language.quote_warning.title}</li>
|
||||
<li id='updated'>{language.quote_warning.description}</li>
|
||||
<li className='header'>{getMessage('modals.main.marketplace.product.quote_warning.title')}</li>
|
||||
<li id='updated'>{getMessage('modals.main.marketplace.product.quote_warning.description')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// prevent console error
|
||||
let iconsrc = window.constants.DDG_IMAGE_PROXY + this.props.data.icon;
|
||||
let iconsrc = variables.constants.DDG_IMAGE_PROXY + this.props.data.icon;
|
||||
if (!this.props.data.icon) {
|
||||
iconsrc = null;
|
||||
}
|
||||
|
||||
let updateButton;
|
||||
if (this.state.showUpdateButton) {
|
||||
updateButton = (
|
||||
<Fragment key='update'>
|
||||
<br/><br/>
|
||||
<button className='removeFromMue' onClick={() => this.updateAddon()}>
|
||||
{getMessage('modals.main.addons.product.buttons.update_addon')}
|
||||
</button>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
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}
|
||||
{updateButton}
|
||||
<br/><br/>
|
||||
{iconsrc ? <img alt='product' draggable='false' src={iconsrc} onClick={() => this.setState({ showLightbox: true })}/> : null}
|
||||
<div className='side'>
|
||||
<div className='productInformation'>
|
||||
<ul>
|
||||
<li className='header'>{language.version}</li>
|
||||
<li>{this.props.data.version}</li>
|
||||
<li className='header'>{getMessage('modals.main.marketplace.product.version')}</li>
|
||||
{updateButton ? <li>{this.props.data.version} (Installed: {this.props.data.addonInstalledVersion})</li> : <li>{this.props.data.version}</li>}
|
||||
<br/>
|
||||
<li className='header'>{language.author}</li>
|
||||
<li className='header'>{getMessage('modals.main.marketplace.product.author')}</li>
|
||||
<li>{this.props.data.author}</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -65,7 +90,7 @@ export default class Item extends React.PureComponent {
|
||||
<br/><br/>
|
||||
</div>
|
||||
<div className='informationContainer'>
|
||||
<h1 className='overview'>{language.overview}</h1>
|
||||
<h1 className='overview'>{getMessage('modals.main.marketplace.product.overview')}</h1>
|
||||
<p className='description' dangerouslySetInnerHTML={{ __html: this.props.data.description }}></p>
|
||||
</div>
|
||||
<Modal closeTimeoutMS={100} onRequestClose={() => this.setState({ showLightbox: false })} isOpen={this.state.showLightbox} className='Modal lightboxmodal' overlayClassName='Overlay resetoverlay' ariaHideApp={false}>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
export default function Items(props) {
|
||||
import variables from 'modules/variables';
|
||||
|
||||
export default function Items({ items, toggleFunction }) {
|
||||
return (
|
||||
<div className='items'>
|
||||
{props.items.map((item) => (
|
||||
<div className='item' onClick={() => props.toggleFunction(item.name)} key={item.name}>
|
||||
<img alt='icon' draggable='false' src={window.constants.DDG_IMAGE_PROXY + item.icon_url} />
|
||||
{items.map((item) => (
|
||||
<div className='item' onClick={() => toggleFunction(item.name)} key={item.name}>
|
||||
<img alt='icon' draggable='false' src={variables.constants.DDG_IMAGE_PROXY + item.icon_url} />
|
||||
<div className='details'>
|
||||
<h4>{item.display_name || item.name}</h4>
|
||||
<p>{item.author}</p>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
export default function Lightbox(props) {
|
||||
window.stats.postEvent('modal', 'Opened lightbox');
|
||||
import variables from 'modules/variables';
|
||||
|
||||
export default function Lightbox({ modalClose, img }) {
|
||||
variables.stats.postEvent('modal', 'Opened lightbox');
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className='closeModal' onClick={props.modalClose}>×</span>
|
||||
<img src={props.img} className='lightboximg' draggable={false} alt='Item screenshot'/>
|
||||
<span className='closeModal' onClick={modalClose}>×</span>
|
||||
<img src={img} className='lightboximg' draggable={false} alt='Item screenshot'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import variables from 'modules/variables';
|
||||
import { Close } from '@mui/icons-material';
|
||||
|
||||
export default function SideloadFailedModal({ modalClose, reason }) {
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>{getMessage('modals.main.error_boundary.title')}</h1>
|
||||
<span>{getMessage('modals.main.addons.sideload.failed')}</span>
|
||||
<br/><br/>
|
||||
<span>{reason}</span>
|
||||
<div className='resetfooter'>
|
||||
<button className='round import' style={{ marginLeft: '-30px' }} onClick={modalClose}>
|
||||
<Close/>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { LocalMall } from '@mui/icons-material';
|
||||
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 PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
export default class Added extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -18,9 +20,8 @@ export default class Added extends React.PureComponent {
|
||||
button: ''
|
||||
};
|
||||
this.buttons = {
|
||||
uninstall: <button className='removeFromMue' onClick={() => this.uninstall()}>{window.language.modals.main.marketplace.product.buttons.remove}</button>,
|
||||
uninstall: <button className='removeFromMue' onClick={() => this.uninstall()}>{this.getMessage('modals.main.marketplace.product.buttons.remove')}</button>,
|
||||
};
|
||||
this.language = window.language.modals.main.addons;
|
||||
}
|
||||
|
||||
toggle(type, data) {
|
||||
@@ -34,7 +35,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,
|
||||
@@ -42,7 +43,7 @@ export default class Added extends React.PureComponent {
|
||||
},
|
||||
button: this.buttons.uninstall
|
||||
});
|
||||
window.stats.postEvent('marketplace', 'Item viewed');
|
||||
variables.stats.postEvent('marketplace', 'Item viewed');
|
||||
} else {
|
||||
this.setState({
|
||||
item: {}
|
||||
@@ -51,16 +52,16 @@ 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);
|
||||
toast(this.getMessage('toasts.uninstalled'));
|
||||
|
||||
this.setState({
|
||||
button: '',
|
||||
installed: JSON.parse(localStorage.getItem('installed'))
|
||||
});
|
||||
|
||||
window.stats.postEvent('marketplace', 'Uninstall');
|
||||
variables.stats.postEvent('marketplace', 'Uninstall');
|
||||
}
|
||||
|
||||
sortAddons(value, sendEvent) {
|
||||
@@ -87,7 +88,25 @@ export default class Added extends React.PureComponent {
|
||||
});
|
||||
|
||||
if (sendEvent) {
|
||||
window.stats.postEvent('marketplace', 'Sort');
|
||||
variables.stats.postEvent('marketplace', 'Sort');
|
||||
}
|
||||
}
|
||||
|
||||
updateCheck() {
|
||||
let updates = 0;
|
||||
this.state.installed.forEach(async (item) => {
|
||||
const data = await (await fetch(variables.constants.MARKETPLACE_URL + '/item/' + item.name)).json();
|
||||
if (data.version !== item.version) {
|
||||
updates++;
|
||||
}
|
||||
});
|
||||
|
||||
if (updates > 0) {
|
||||
toast(this.getMessage('modals.main.addons.updates_available', {
|
||||
amount: updates
|
||||
}));
|
||||
} else {
|
||||
toast(this.getMessage('modals.main.addons.no_updates'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,9 +119,9 @@ export default class Added extends React.PureComponent {
|
||||
return (
|
||||
<div className='emptyitems'>
|
||||
<div className='emptyMessage'>
|
||||
<LocalMallIcon/>
|
||||
<h1>{this.language.empty.title}</h1>
|
||||
<p className='description'>{this.language.empty.description}</p>
|
||||
<LocalMall/>
|
||||
<h1>{this.getMessage('modals.main.addons.empty.title')}</h1>
|
||||
<p className='description'>{this.getMessage('modals.main.addons.empty.description')}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -114,12 +133,13 @@ export default class Added extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dropdown label={this.language.sort.title} name='sortAddons' onChange={(value) => this.sortAddons(value)}>
|
||||
<option value='newest'>{this.language.sort.newest}</option>
|
||||
<option value='oldest'>{this.language.sort.oldest}</option>
|
||||
<option value='a-z'>{this.language.sort.a_z}</option>
|
||||
<option value='z-a'>{this.language.sort.z_a}</option>
|
||||
<Dropdown label={this.getMessage('modals.main.addons.sort.title')} name='sortAddons' onChange={(value) => this.sortAddons(value)}>
|
||||
<option value='newest'>{this.getMessage('modals.main.addons.sort.newest')}</option>
|
||||
<option value='oldest'>{this.getMessage('modals.main.addons.sort.oldest')}</option>
|
||||
<option value='a-z'>{this.getMessage('modals.main.addons.sort.a_z')}</option>
|
||||
<option value='z-a'>{this.getMessage('modals.main.addons.sort.z_a')}</option>
|
||||
</Dropdown>
|
||||
<button className='addToMue sideload updateCheck' onClick={() => this.updateCheck()}>{this.getMessage('modals.main.addons.check_updates')}</button>
|
||||
<br/>
|
||||
<Items items={this.state.installed} toggleFunction={(input) => this.toggle('item', input)} />
|
||||
</>
|
||||
|
||||
336
src/components/modals/main/marketplace/sections/Create.jsx
Normal file
336
src/components/modals/main/marketplace/sections/Create.jsx
Normal file
@@ -0,0 +1,336 @@
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import {
|
||||
SettingsRounded as Settings,
|
||||
PhotoOutlined as Photos,
|
||||
FormatQuoteOutlined as Quotes,
|
||||
Upload as ImportIcon,
|
||||
Download as ExportIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { TextField } from '@mui/material';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { saveFile } from 'modules/helpers/settings/modals';
|
||||
|
||||
import FileUpload from '../../settings/FileUpload';
|
||||
import Dropdown from '../../settings/Dropdown';
|
||||
|
||||
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' || key === 'backup_settings' || key === 'showReminder'
|
||||
|| key === 'experimental' || key === 'debugtimeout' || key === 'quotelanguage') {
|
||||
return;
|
||||
}
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
|
||||
this.setState({
|
||||
addonData: settings,
|
||||
settingsClasses: {
|
||||
current: input ? 'toggle lightTheme active' : 'toggle lightTheme',
|
||||
json: input ? 'toggle lightTheme active' : 'toggle lightTheme'
|
||||
}
|
||||
});
|
||||
|
||||
toast(variables.language.getMessage(variables.languagecode, 'toasts.imported'));
|
||||
}
|
||||
|
||||
updateQuotePackType(type) {
|
||||
if (type === 'quotePack') {
|
||||
this.setState({
|
||||
addonMetadata: {
|
||||
type,
|
||||
name: this.state.addonMetadata.name,
|
||||
description: this.state.addonMetadata.description,
|
||||
version: this.state.addonMetadata.version,
|
||||
author: this.state.addonMetadata.author,
|
||||
icon_url: this.state.addonMetadata.icon_url,
|
||||
screenshot_url: this.state.addonMetadata.screenshot_url,
|
||||
quotes: []
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
addonMetadata: {
|
||||
type,
|
||||
name: this.state.addonMetadata.name,
|
||||
description: this.state.addonMetadata.description,
|
||||
version: this.state.addonMetadata.version,
|
||||
author: this.state.addonMetadata.author,
|
||||
icon_url: this.state.addonMetadata.icon_url,
|
||||
screenshot_url: this.state.addonMetadata.screenshot_url
|
||||
},
|
||||
addonData: {
|
||||
url: '',
|
||||
name: '',
|
||||
author: ''
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateQuotePackAPI(type, data) {
|
||||
this.setState({
|
||||
addonData: {
|
||||
url: (type === 'url') ? data : this.state.addonData.url || '',
|
||||
name: (type === 'name') ? data : this.state.addonData.name || '',
|
||||
author: (type === 'author') ? data : this.state.addonData.author || '',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
importQuotes() {
|
||||
this.setState({
|
||||
addonData: JSON.parse(localStorage.getItem('customQuote')) || []
|
||||
});
|
||||
|
||||
toast(variables.language.getMessage(variables.languagecode, 'toasts.imported'));
|
||||
}
|
||||
|
||||
importPhotos() {
|
||||
let data = [];
|
||||
try {
|
||||
const current = JSON.parse(localStorage.getItem('customBackground')) || [];
|
||||
data = current.map((item) => {
|
||||
return {
|
||||
photographer: '???',
|
||||
location: '???',
|
||||
url: {
|
||||
default: item
|
||||
}
|
||||
}
|
||||
});
|
||||
toast(variables.language.getMessage(variables.languagecode, 'toasts.imported'));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
toast(variables.language.getMessage(variables.languagecode, 'toasts.error'));
|
||||
}
|
||||
|
||||
this.setState({
|
||||
addonData: data
|
||||
});
|
||||
}
|
||||
|
||||
downloadAddon() {
|
||||
saveFile({
|
||||
name: this.state.addonMetadata.name,
|
||||
description: this.state.addonMetadata.description,
|
||||
type: (this.state.addonMetadata.type === 'quote_api') ? 'quotes' : 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 getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
const chooseType = (
|
||||
<>
|
||||
<h3>{getMessage('modals.main.settings.sections.time.type')}</h3>
|
||||
<div className='themesToggleArea'>
|
||||
<div className='options'>
|
||||
<div className='toggle lightTheme' onClick={() => this.changeTab(2, 'photos')}>
|
||||
<Photos/>
|
||||
<span>{getMessage('modals.main.marketplace.photo_packs')}</span>
|
||||
</div>
|
||||
<div className='toggle lightTheme' onClick={() => this.changeTab(2, 'quotes')}>
|
||||
<Quotes/>
|
||||
<span>{getMessage('modals.main.marketplace.quote_packs')}</span>
|
||||
</div>
|
||||
<div className='toggle lightTheme' onClick={() => this.changeTab(2, 'settings')}>
|
||||
<Settings/>
|
||||
<span>{getMessage('modals.main.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>{getMessage('modals.main.marketplace.product.information')}</h3>
|
||||
<TextField label={getMessage('modals.main.addons.create.metadata.name')} varient='outlined' InputLabelProps={{ shrink: true }} value={this.state.addonMetadata.name} onInput={(e) => setMetadata(e.target.value, 'name')}/>
|
||||
<TextField label={getMessage('modals.main.marketplace.product.version')} varient='outlined' InputLabelProps={{ shrink: true }} value={this.state.addonMetadata.version} onInput={(e) => setMetadata(e.target.value, 'version')}/>
|
||||
<TextField label={getMessage('modals.main.marketplace.product.author')} varient='outlined' InputLabelProps={{ shrink: true }} value={this.state.addonMetadata.author} onInput={(e) => setMetadata(e.target.value, 'author')}/>
|
||||
<TextField label={getMessage('modals.main.addons.create.metadata.icon_url')} varient='outlined' InputLabelProps={{ shrink: true }} value={this.state.addonMetadata.icon_url} onInput={(e) => setMetadata(e.target.value, 'icon_url')}/>
|
||||
<TextField label={getMessage('modals.main.addons.create.metadata.screenshot_url')} varient='outlined' InputLabelProps={{ shrink: true }} value={this.state.addonMetadata.screenshot_url} onInput={(e) => setMetadata(e.target.value, 'screenshot_url')}/>
|
||||
<TextField label={getMessage('modals.main.addons.create.metadata.description')} varient='outlined' InputLabelProps={{ shrink: true }} multiline spellCheck={false} rows={4} InputLabelProps={{ shrink: true }} value={this.state.addonMetadata.description} onInput={(e) => setMetadata(e.target.value, 'description')}/>
|
||||
<br/>
|
||||
<button onClick={() => this.changeTab(1)} className='uploadbg' style={{ marginRight: '10px' }}>{getMessage('modals.welcome.buttons.previous')}</button>
|
||||
<button onClick={() => this.changeTab(this.state.addonMetadata.type)} className='uploadbg' disabled={nextDescriptionDisabled}>{getMessage('modals.welcome.buttons.next')}</button>
|
||||
</>
|
||||
);
|
||||
|
||||
// settings
|
||||
const nextSettingsDisabled = (this.state.addonData === '') ? true : false;
|
||||
const importSettings = (
|
||||
<>
|
||||
<h3>{getMessage('modals.welcome.sections.settings.title')}</h3>
|
||||
<div className='themesToggleArea' >
|
||||
<div className='options' style={{ maxWidth: '512px' }}>
|
||||
<div className={this.state.settingsClasses.current} onClick={() => this.importSettings()}>
|
||||
<ExportIcon/>
|
||||
<span>{getMessage('modals.main.addons.create.settings.current')}</span>
|
||||
</div>
|
||||
<div className={this.state.settingsClasses.json} onClick={() => document.getElementById('file-input').click()}>
|
||||
<ImportIcon/>
|
||||
<span>{getMessage('modals.main.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={{ margin: '10px' }}>{getMessage('modals.welcome.buttons.previous')}</button>
|
||||
<button onClick={() => this.changeTab(3)} className='uploadbg' style={{ margin: '10px' }} disabled={nextSettingsDisabled}>{getMessage('modals.welcome.buttons.next')}</button>
|
||||
</>
|
||||
);
|
||||
|
||||
// quotes
|
||||
const nextQuotesDisabled = ((this.state.addonMetadata.type === 'quote_api' && this.state.addonData.url !== '' && this.state.addonData.name !== '' && this.state.addonData.author !== '')
|
||||
|| (this.state.addonMetadata.type === 'quotes' && this.state.addonData.quotes !== '')) ? false : true;
|
||||
const addQuotes = (
|
||||
<>
|
||||
<h3>{getMessage('modals.main.addons.create.quotes.title')}</h3>
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.time.type')} noSetting onChange={(e) => this.updateQuotePackType(e)}>
|
||||
<option value='quotes'>{getMessage('modals.main.addons.create.quotes.local.title')}</option>
|
||||
<option value='quote_api'>{getMessage('modals.main.addons.create.quotes.api.title')}</option>
|
||||
</Dropdown>
|
||||
{this.state.addonMetadata.type === 'quote_api' ? <>
|
||||
<TextField label={getMessage('modals.main.addons.create.quotes.api.url')} varient='outlined' InputLabelProps={{ shrink: true }} value={this.state.addonData.url} onInput={(e) => this.updateQuotePack(e.target.value, 'url')}/>
|
||||
<TextField label={getMessage('modals.main.addons.create.quotes.api.name')} varient='outlined' InputLabelProps={{ shrink: true }} value={this.state.addonData.name} onInput={(e) => this.updateQuotePack(e.target.value, 'name')}/>
|
||||
<TextField label={getMessage('modals.main.addons.create.quotes.api.author')} varient='outlined' InputLabelProps={{ shrink: true }} value={this.state.addonData.author} onInput={(e) => this.updateQuotePack(e.target.value, 'author')}/>
|
||||
<br/><br/>
|
||||
</> : <>
|
||||
<div className='themesToggleArea'>
|
||||
<div className='options'>
|
||||
<div onClick={() => this.importQuotes()} className='toggle lightTheme' style={{ width: '60%', margin: '10px 0 10px 0' }}>
|
||||
<ExportIcon/>
|
||||
<span>{getMessage('modals.main.addons.create.settings.current')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
</>}
|
||||
<button onClick={() => this.changeTab(2)} className='uploadbg'>{getMessage('modals.welcome.buttons.previous')}</button>
|
||||
<button onClick={() => this.changeTab(3)} className='uploadbg' style={{ margin: '10px' }} disabled={nextQuotesDisabled}>{getMessage('modals.welcome.buttons.next')}</button>
|
||||
</>
|
||||
);
|
||||
|
||||
// photos
|
||||
const nextPhotosDisabled = (this.state.addonData.photos !== '' && this.state.addonData.photos !== []) ? false : true;
|
||||
const addPhotos = (
|
||||
<>
|
||||
<h3>{getMessage('modals.main.addons.create.photos.title')}</h3>
|
||||
<div className='themesToggleArea'>
|
||||
<div className='options'>
|
||||
<div onClick={() => this.importPhotos()} className='toggle lightTheme' style={{ width: '60%', margin: '10px 0 10px 0' }}>
|
||||
<ExportIcon/>
|
||||
<span>{getMessage('modals.main.addons.create.settings.current')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<button onClick={() => this.changeTab(2)} className='uploadbg'>{getMessage('modals.welcome.buttons.previous')}</button>
|
||||
<button onClick={() => this.changeTab(3)} className='uploadbg' style={{ margin: '10px' }} disabled={nextPhotosDisabled}>{getMessage('modals.welcome.buttons.next')}</button>
|
||||
</>
|
||||
);
|
||||
|
||||
const downloadAddon = (
|
||||
<>
|
||||
<div className='themesToggleArea'>
|
||||
<div className='options'>
|
||||
<div onClick={() => this.downloadAddon()} className='toggle lightTheme' style={{ width: '60%', margin: '10px 0 10px 0' }}>
|
||||
<ExportIcon/>
|
||||
<span>{getMessage('modals.main.addons.create.finish.download')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<button onClick={() => this.changeTab((this.state.addonMetadata.type === 'quote_api') ? 'quotes' : this.state.addonMetadata.type)} className='uploadbg' style={{ marginRight: '10px' }}>{getMessage('modals.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>{getMessage('modals.main.addons.create.other_title')}</h2>
|
||||
{tabContent}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
import React from 'react';
|
||||
|
||||
import WifiOffIcon from '@material-ui/icons/WifiOff';
|
||||
import LocalMallIcon from '@material-ui/icons/LocalMall';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { WifiOff, LocalMall } from '@mui/icons-material';
|
||||
|
||||
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 PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
export default class Marketplace extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -22,10 +22,9 @@ export default class Marketplace extends React.PureComponent {
|
||||
item: {}
|
||||
};
|
||||
this.buttons = {
|
||||
uninstall: <button className='removeFromMue' onClick={() => this.manage('uninstall')}>{window.language.modals.main.marketplace.product.buttons.remove}</button>,
|
||||
install: <button className='addToMue' onClick={() => this.manage('install')}>{window.language.modals.main.marketplace.product.buttons.addtomue}</button>
|
||||
uninstall: <button className='removeFromMue' onClick={() => this.manage('uninstall')}>{this.getMessage('modals.main.marketplace.product.buttons.remove')}</button>,
|
||||
install: <button className='addToMue' onClick={() => this.manage('install')}>{this.getMessage('modals.main.marketplace.product.buttons.addtomue')}</button>
|
||||
};
|
||||
this.language = window.language.modals.main.marketplace;
|
||||
this.controller = new AbortController();
|
||||
}
|
||||
|
||||
@@ -34,10 +33,10 @@ export default class Marketplace extends React.PureComponent {
|
||||
let info;
|
||||
// get item info
|
||||
try {
|
||||
info = await (await fetch(`${window.constants.MARKETPLACE_URL}/item/${this.props.type}/${data}`, { signal: this.controller.signal })).json();
|
||||
info = await (await fetch(`${variables.constants.MARKETPLACE_URL}/item/${this.props.type}/${data}`, { signal: this.controller.signal })).json();
|
||||
} catch (e) {
|
||||
if (this.controller.signal.aborted === false) {
|
||||
return toast(window.language.toasts.error);
|
||||
return toast(this.getMessage('toasts.error'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,11 +46,20 @@ export default class Marketplace extends React.PureComponent {
|
||||
|
||||
// check if already installed
|
||||
let button = this.buttons.install;
|
||||
let addonInstalled = false;
|
||||
let addonInstalledVersion;
|
||||
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
|
||||
if (installed.some((item) => item.name === info.data.name)) {
|
||||
button = this.buttons.uninstall;
|
||||
addonInstalled = true;
|
||||
for (let i = 0; i < installed.length; i++) {
|
||||
if (installed[i].name === info.data.name) {
|
||||
addonInstalledVersion = installed[i].version;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@@ -59,16 +67,18 @@ 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,
|
||||
data: info.data
|
||||
data: info.data,
|
||||
addonInstalled,
|
||||
addonInstalledVersion
|
||||
},
|
||||
button: button
|
||||
});
|
||||
|
||||
window.stats.postEvent('marketplace-item', `${this.state.item.display_name} viewed`);
|
||||
variables.stats.postEvent('marketplace-item', `${this.state.item.display_name} viewed`);
|
||||
} else {
|
||||
this.setState({
|
||||
item: {}
|
||||
@@ -77,8 +87,8 @@ export default class Marketplace extends React.PureComponent {
|
||||
}
|
||||
|
||||
async getItems() {
|
||||
const { data } = await (await fetch(window.constants.MARKETPLACE_URL + '/items/' + this.props.type, { signal: this.controller.signal })).json();
|
||||
const featured = await (await fetch(window.constants.MARKETPLACE_URL + '/featured', { signal: this.controller.signal })).json();
|
||||
const { data } = await (await fetch(variables.constants.MARKETPLACE_URL + '/items/' + this.props.type, { signal: this.controller.signal })).json();
|
||||
const featured = await (await fetch(variables.constants.MARKETPLACE_URL + '/featured', { signal: this.controller.signal })).json();
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
return;
|
||||
@@ -96,18 +106,18 @@ 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']);
|
||||
toast(this.getMessage('toasts.' + type + 'ed'));
|
||||
this.setState({
|
||||
button: (type === 'install') ? this.buttons.uninstall : this.buttons.install
|
||||
});
|
||||
|
||||
window.stats.postEvent('marketplace-item', `${this.state.item.display_name} ${(type === 'install' ? 'installed': 'uninstalled')}`);
|
||||
window.stats.postEvent('marketplace', (type === 'install' ? 'Install': 'Uninstall'));
|
||||
variables.stats.postEvent('marketplace-item', `${this.state.item.display_name} ${(type === 'install' ? 'installed': 'uninstalled')}`);
|
||||
variables.stats.postEvent('marketplace', (type === 'install' ? 'Install': 'Uninstall'));
|
||||
}
|
||||
|
||||
sortMarketplace(value, sendEvent) {
|
||||
@@ -134,7 +144,7 @@ export default class Marketplace extends React.PureComponent {
|
||||
});
|
||||
|
||||
if (sendEvent) {
|
||||
window.stats.postEvent('marketplace', 'Sort');
|
||||
variables.stats.postEvent('marketplace', 'Sort');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,11 +172,24 @@ export default class Marketplace extends React.PureComponent {
|
||||
);
|
||||
};
|
||||
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return errorMessage(<>
|
||||
<WifiOff/>
|
||||
<h1>{this.getMessage('modals.main.marketplace.offline.title')}</h1>
|
||||
<p className='description'>{this.getMessage('modals.main.marketplace.offline.description')}</p>
|
||||
</>);
|
||||
}
|
||||
|
||||
if (this.state.done === false) {
|
||||
return errorMessage(<h1>{this.getMessage('modals.main.loading')}</h1>);
|
||||
}
|
||||
|
||||
const featured = () => {
|
||||
const openFeatured = () => {
|
||||
window.stats.postEvent('marketplace', 'Featured clicked');
|
||||
variables.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>
|
||||
@@ -176,44 +199,31 @@ export default class Marketplace extends React.PureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return errorMessage(<>
|
||||
<WifiOffIcon/>
|
||||
<h1>{this.language.offline.title}</h1>
|
||||
<p className='description'>{this.language.offline.description}</p>
|
||||
</>);
|
||||
}
|
||||
|
||||
if (this.state.done === false) {
|
||||
return errorMessage(<h1>{window.language.modals.main.loading}</h1>);
|
||||
}
|
||||
|
||||
if (this.state.items.length === 0) {
|
||||
return (
|
||||
<>
|
||||
{featured()}
|
||||
{errorMessage(<>
|
||||
<LocalMallIcon/>
|
||||
<h1>{window.language.modals.main.addons.empty.title}</h1>
|
||||
<p className='description'>{this.language.no_items}</p>
|
||||
<LocalMall/>
|
||||
<h1>{this.getMessage('modals.main.addons.empty.title')}</h1>
|
||||
<p className='description'>{this.getMessage('modals.main.marketplace.no_items')}</p>
|
||||
</>)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.item.display_name) {
|
||||
return <Item data={this.state.item} button={this.state.button} toggleFunction={() => this.toggle()}/>;
|
||||
return <Item data={this.state.item} button={this.state.button} toggleFunction={() => this.toggle()} addonInstalled={this.state.item.addonInstalled} addonInstalledVersion={this.state.item.addonInstalledVersion}/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{featured()}
|
||||
<br/>
|
||||
<Dropdown label={window.language.modals.main.addons.sort.title} name='sortMarketplace' onChange={(value) => this.sortMarketplace(value)}>
|
||||
<option value='a-z'>{window.language.modals.main.addons.sort.a_z}</option>
|
||||
<option value='z-a'>{window.language.modals.main.addons.sort.z_a}</option>
|
||||
<Dropdown label={this.getMessage('modals.main.addons.sort.title')} name='sortMarketplace' onChange={(value) => this.sortMarketplace(value)}>
|
||||
<option value='a-z'>{this.getMessage('modals.main.addons.sort.a_z')}</option>
|
||||
<option value='z-a'>{this.getMessage('modals.main.addons.sort.z_a')}</option>
|
||||
</Dropdown>
|
||||
<br/>
|
||||
<Items items={this.state.items} toggleFunction={(input) => this.toggle('item', input)} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,26 +1,67 @@
|
||||
import LocalMallIcon from '@material-ui/icons/LocalMall';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { LocalMall } from '@mui/icons-material';
|
||||
import { toast } from 'react-toastify';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import SideloadFailedModal from '../SideloadFailedModal';
|
||||
|
||||
import FileUpload from '../../settings/FileUpload';
|
||||
|
||||
import MarketplaceFunctions from '../../../../../modules/helpers/marketplace';
|
||||
import { install } from 'modules/helpers/marketplace';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
export default class Sideload extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
export default function Sideload() {
|
||||
const install = (input) => {
|
||||
MarketplaceFunctions.install(input.type, input);
|
||||
toast(window.language.toasts.installed);
|
||||
window.stats.postEvent('marketplace', 'Sideload');
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showFailed: false,
|
||||
failedReason: ''
|
||||
}
|
||||
}
|
||||
|
||||
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/>
|
||||
<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>
|
||||
installAddon(input) {
|
||||
let failedReason = '';
|
||||
if (!input.name) {
|
||||
failedReason = this.getMessage('modals.main.addons.sideload.errors.no_name');
|
||||
} else if (!input.author) {
|
||||
failedReason = this.getMessage('modals.main.addons.sideload.errors.no_author');
|
||||
} else if (!input.type) {
|
||||
failedReason = this.getMessage('modals.main.addons.sideload.errors.no_type');
|
||||
} else if (!input.version) {
|
||||
failedReason = this.getMessage('modals.main.addons.sideload.errors.no_version');
|
||||
} else if (input.type === 'photos' && (!input.photos || !input.photos.length || !input.photos[0].url || !input.photos[0].url.default || !input.photos[0].photographer || !input.photos[0].location)) {
|
||||
failedReason = this.getMessage('modals.main.addons.sideload.errors.invalid_photos');
|
||||
} else if (input.type === 'quotes' && (!input.quotes || !input.quotes.length || !input.quotes[0].quote || !input.quotes[0].author)) {
|
||||
failedReason = this.getMessage('modals.main.addons.sideload.errors.invalid_quotes');
|
||||
}
|
||||
|
||||
if (failedReason !== '') {
|
||||
return this.setState({
|
||||
failedReason,
|
||||
showFailed: true
|
||||
});
|
||||
}
|
||||
|
||||
install(input.type, input);
|
||||
toast(this.getMessage('toasts.installed'));
|
||||
variables.stats.postEvent('marketplace', 'Sideload');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='emptyitems'>
|
||||
<div className='emptyMessage'>
|
||||
<FileUpload id='file-input' type='settings' accept='application/json' loadFunction={(e) => this.installAddon(JSON.parse(e.target.result))} />
|
||||
<LocalMall/>
|
||||
<h1>{this.getMessage('modals.main.addons.sideload.title')}</h1>
|
||||
<button className='addToMue sideload' onClick={() => document.getElementById('file-input').click()}>{this.getMessage('modals.main.settings.sections.background.source.upload')}</button>
|
||||
</div>
|
||||
<Modal closeTimeoutMS={100} onRequestClose={() => this.setState({ showFailed: false })} isOpen={this.state.showFailed} className='Modal resetmodal mainModal sideloadModal' overlayClassName='Overlay resetoverlay' ariaHideApp={false}>
|
||||
<SideloadFailedModal modalClose={() => this.setState({ showFailed: false })} reason={this.state.failedReason}/>
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
@import '../../../../scss/variables';
|
||||
|
||||
@import 'modules/sidebar';
|
||||
@import 'modules/navbar';
|
||||
@import 'modules/tab-content';
|
||||
@import 'modules/links';
|
||||
@import 'modules/scrollbars';
|
||||
|
||||
@import 'settings/main';
|
||||
@import 'settings/buttons';
|
||||
@import 'settings/dropdown';
|
||||
@import 'settings/daypicker';
|
||||
|
||||
@import 'marketplace/main';
|
||||
@import 'marketplace/buttons';
|
||||
|
||||
.Modal {
|
||||
color: var(--modal-text);
|
||||
@@ -27,36 +30,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.mainModal {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.resetLink {
|
||||
color: var(--modal-link);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 1.2rem;
|
||||
color: var(--modal-link);
|
||||
vertical-align: text-bottom;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.modalLink {
|
||||
color: var(--modal-link);
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.closeModal {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
@@ -89,18 +62,15 @@
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
/* modal transition */
|
||||
.ReactModal__Content--after-open {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
@@ -118,43 +88,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
ul.sidebar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
background: var(--sidebar);
|
||||
border-radius: 12px 0 0 12px;
|
||||
text-align: left;
|
||||
font-size: 24px;
|
||||
min-height: 100vh;
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 3px;
|
||||
background: rgba(196, 196, 196, 0.74);
|
||||
width: 75%;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
font-size: 24px;
|
||||
padding: 5px 30px 5px 30px;
|
||||
cursor: pointer;
|
||||
margin-top: 2px;
|
||||
/* main modal */
|
||||
.mainModal {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
#modal {
|
||||
@@ -165,238 +101,16 @@ li {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 80%;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1300px) {
|
||||
@media (max-width: 1700px) {
|
||||
#modal {
|
||||
width: 90% !important;
|
||||
width: 80% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1310px) {
|
||||
#modal {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-list-active {
|
||||
background: var(--tab-active);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
li.tab-list-item {
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
ul.sidebar {
|
||||
h1 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-list-item {
|
||||
&:hover {
|
||||
background: var(--tab-active);
|
||||
}
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
position: absolute;
|
||||
|
||||
h3 {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 2300px) {
|
||||
.tab-content {
|
||||
left: 350px;
|
||||
top: 7%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1920px) {
|
||||
.tab-content {
|
||||
left: 120px;
|
||||
top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1920px) {
|
||||
.tab-content {
|
||||
left: 350px;
|
||||
top: 7%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1400px), (min-width: 1400px) {
|
||||
.tab-content {
|
||||
left: 350px;
|
||||
top: 75px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.tab-content {
|
||||
left: 125px;
|
||||
top: 75px;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-item {
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
display: inline-flex;
|
||||
|
||||
&:hover {
|
||||
color: rgb(165, 165, 165);
|
||||
background: none;
|
||||
}
|
||||
|
||||
span,
|
||||
svg {
|
||||
font-size: 1.1em !important;
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 1.2em !important;
|
||||
}
|
||||
}
|
||||
|
||||
@supports (-webkit-hyphens: none) {
|
||||
.navbar-item {
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.modalNavbar {
|
||||
position: absolute;
|
||||
left: 20rem;
|
||||
top: 1rem;
|
||||
justify-content: center;
|
||||
|
||||
svg {
|
||||
margin-right: 0.5rem;
|
||||
padding: 3px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.modalNavbar {
|
||||
left: 6rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1650px) {
|
||||
li.navbar-item {
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1200px) {
|
||||
ul.sidebar {
|
||||
width: 310px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-item-active {
|
||||
background: map-get($theme-colours, 'gradient');
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
|
||||
svg {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: map-get($theme-colours, 'gradient');
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-top-right-radius: map-get($modal, 'border-radius');
|
||||
border-bottom-right-radius: map-get($modal, 'border-radius');
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #636e72;
|
||||
border-top-right-radius: map-get($modal, 'border-radius');
|
||||
border-bottom-right-radius: map-get($modal, 'border-radius');
|
||||
}
|
||||
|
||||
.abouticon {
|
||||
width: 96px;
|
||||
height: auto;
|
||||
border-radius: 50%;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.resetmodal {
|
||||
min-height: 300px !important;
|
||||
max-width: 300px !important;
|
||||
margin: auto;
|
||||
font-size: 1rem;
|
||||
|
||||
h4 {
|
||||
cursor: initial;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.resetfooter {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
width: 300px;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
|
||||
button.reset {
|
||||
margin-right: 43px;
|
||||
}
|
||||
}
|
||||
|
||||
.resetoverlay {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.aboutIcon {
|
||||
color: var(--modal-text) !important;
|
||||
padding-right: 10px;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.aboutLink {
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.aboutLogo {
|
||||
height: 100px;
|
||||
width: auto;
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
.MuiFormControl-root {
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
|
||||
/* fixes for font size on extension */
|
||||
label,
|
||||
p,
|
||||
span.modalLink {
|
||||
@@ -404,7 +118,8 @@ span.modalLink {
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@@ -415,6 +130,12 @@ h5 {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.checkbox svg {
|
||||
fill: var(--modal-text) !important;
|
||||
}
|
||||
.tab-content {
|
||||
hr {
|
||||
height: 5px;
|
||||
background: rgba(196, 196, 196, 0.74);
|
||||
outline: none;
|
||||
border: none;
|
||||
margin: 50px 0 30px 0;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,11 @@
|
||||
#item a {
|
||||
color: var(--modal-link);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.emptyitems {
|
||||
width: 25vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 90px;
|
||||
}
|
||||
@import 'modules/item';
|
||||
@import 'modules/buttons';
|
||||
@import 'modules/featured';
|
||||
@import 'modules/lightbox';
|
||||
|
||||
.items {
|
||||
display: inline-grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
margin-top: 15px;
|
||||
|
||||
.item {
|
||||
@@ -25,11 +14,9 @@
|
||||
height: 80px;
|
||||
width: 260px;
|
||||
background: var(--sidebar);
|
||||
transition: 0.5s;
|
||||
cursor: pointer;
|
||||
margin-right: 20px;
|
||||
margin-top: 20px;
|
||||
box-shadow: 0 0 6px rgb(0 0 0 / 30%);
|
||||
|
||||
img {
|
||||
height: 80px;
|
||||
@@ -66,67 +53,34 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
background: var(--tab-active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1680px) {
|
||||
.side {
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
.sidebr {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
p.author {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
#item {
|
||||
h1 {
|
||||
font-size: 40px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.side {
|
||||
float: right;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
#item>h1,
|
||||
#item>.MuiSvgIcon-root {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.description {
|
||||
margin-top: 0px;
|
||||
max-width: 800px;
|
||||
.emptyitems {
|
||||
width: 25vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 90px;
|
||||
}
|
||||
|
||||
.emptyMessage {
|
||||
@@ -148,95 +102,28 @@ p.description {
|
||||
}
|
||||
}
|
||||
|
||||
.backArrow {
|
||||
cursor: pointer;
|
||||
width: 2rem !important;
|
||||
height: 2rem !important;
|
||||
|
||||
&:hover {
|
||||
color: grey;
|
||||
}
|
||||
p.author {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.informationContainer {
|
||||
margin-top: 150px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.productInformation {
|
||||
padding: 10px;
|
||||
background: var(--sidebar);
|
||||
width: 350px;
|
||||
border-radius: 12px;
|
||||
|
||||
h4 {
|
||||
cursor: initial !important;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-left: -4px;
|
||||
list-style: none;
|
||||
font-size: 16px;
|
||||
cursor: initial !important;
|
||||
|
||||
&.header {
|
||||
text-transform: uppercase;
|
||||
color: #787878;
|
||||
margin-left: -5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#item>img, .updateimage, .updatechangelog>p>img {
|
||||
#item>img,
|
||||
.updateimage,
|
||||
.updatechangelog>p>img {
|
||||
border-radius: 12px;
|
||||
height: 200px;
|
||||
width: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.featured {
|
||||
margin-top: 40px;
|
||||
border-radius: 15px;
|
||||
padding: 50px;
|
||||
color: #fff;
|
||||
box-shadow: 0 0 10px rgb(0 0 0 / 30%);
|
||||
width: 85%;
|
||||
|
||||
button {
|
||||
float: left;
|
||||
margin-top: -7px;
|
||||
border: 2px solid map-get($theme-colours, 'main');
|
||||
color: map-get($theme-colours, 'main');
|
||||
|
||||
&:hover {
|
||||
background: map-get($theme-colours, 'main');
|
||||
color: #2d3436;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: -20px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
/* sideload failed modal */
|
||||
.sideloadModal {
|
||||
min-width: 250px;
|
||||
max-width: 250px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.lightboxmodal {
|
||||
margin: auto;
|
||||
max-width: 60%;
|
||||
background: none !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
img {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.closeModal {
|
||||
color: #fff;
|
||||
text-shadow: 0 0 20px rgb(0 0 0 / 30%);
|
||||
@media (max-height: 1080px) {
|
||||
.dropdownsortAddons {
|
||||
margin-top: 40px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.overview {
|
||||
font-size: 30px !important;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
.addToMue {
|
||||
@extend %storebutton;
|
||||
|
||||
float: right;
|
||||
margin-top: -10px;
|
||||
}
|
||||
@@ -60,3 +61,7 @@ button.round {
|
||||
vertical-align: middle;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.updateCheck {
|
||||
margin-top: 15px;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
.featured {
|
||||
margin-top: 40px;
|
||||
border-radius: 15px;
|
||||
padding: 50px;
|
||||
color: #fff;
|
||||
box-shadow: 0 0 10px rgb(0 0 0 / 30%);
|
||||
width: 85%;
|
||||
|
||||
button {
|
||||
float: left;
|
||||
margin-top: -7px;
|
||||
border: 2px solid map-get($theme-colours, 'main');
|
||||
color: map-get($theme-colours, 'main');
|
||||
|
||||
&:hover {
|
||||
background: map-get($theme-colours, 'main');
|
||||
color: #2d3436;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: -20px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#item {
|
||||
h1 {
|
||||
font-size: 40px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
float: left;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--modal-link);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.side {
|
||||
float: right;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
#item>h1,
|
||||
#item>.MuiSvgIcon-root {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.description {
|
||||
margin-top: 0px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.backArrow {
|
||||
cursor: pointer;
|
||||
width: 2rem !important;
|
||||
height: 2rem !important;
|
||||
|
||||
&:hover {
|
||||
color: grey;
|
||||
}
|
||||
}
|
||||
|
||||
.informationContainer {
|
||||
margin-top: 150px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.productInformation {
|
||||
padding: 10px;
|
||||
background: var(--sidebar);
|
||||
width: 350px;
|
||||
border-radius: 12px;
|
||||
min-height: 180px;
|
||||
|
||||
h4 {
|
||||
cursor: initial !important;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-left: -4px;
|
||||
list-style: none;
|
||||
font-size: 16px;
|
||||
cursor: initial !important;
|
||||
|
||||
&.header {
|
||||
text-transform: uppercase;
|
||||
color: #787878;
|
||||
margin-left: -5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.side {
|
||||
margin-top: 222px;
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
.overview {
|
||||
margin-top: -160px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.overview {
|
||||
font-size: 30px !important;
|
||||
margin-top: 33px;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
.lightboxmodal {
|
||||
margin: auto;
|
||||
max-width: 60%;
|
||||
background: none !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
img {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.closeModal {
|
||||
color: #fff;
|
||||
text-shadow: 0 0 20px rgb(0 0 0 / 30%);
|
||||
}
|
||||
}
|
||||
25
src/components/modals/main/scss/modules/_links.scss
Normal file
25
src/components/modals/main/scss/modules/_links.scss
Normal file
@@ -0,0 +1,25 @@
|
||||
.resetLink {
|
||||
color: var(--modal-link);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 1.2rem;
|
||||
color: var(--modal-link);
|
||||
vertical-align: text-bottom;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.modalLink {
|
||||
color: var(--modal-link);
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
72
src/components/modals/main/scss/modules/_navbar.scss
Normal file
72
src/components/modals/main/scss/modules/_navbar.scss
Normal file
@@ -0,0 +1,72 @@
|
||||
.navbar-item {
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: var(--photo-info);
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
background: var(--tab-active);
|
||||
}
|
||||
|
||||
color: var(--modal-text)
|
||||
}
|
||||
|
||||
span,
|
||||
svg {
|
||||
font-size: 1.1em !important;
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 1.2em !important;
|
||||
width: 60px;
|
||||
padding: 5px;
|
||||
border-radius: 20px;
|
||||
color: var(--photo-info);
|
||||
}
|
||||
}
|
||||
|
||||
/* safari fix */
|
||||
@supports (-webkit-hyphens: none) {
|
||||
.navbar-item {
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.modalNavbar {
|
||||
position: absolute;
|
||||
left: 20rem;
|
||||
top: 1rem;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
|
||||
svg {
|
||||
margin-right: 0.5rem;
|
||||
padding: 3px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.modalNavbar {
|
||||
left: 6rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
li.navbar-item {
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-item-active {
|
||||
color: var(--modal-text);
|
||||
|
||||
svg {
|
||||
background: var(--sidebar);
|
||||
}
|
||||
}
|
||||
12
src/components/modals/main/scss/modules/_scrollbars.scss
Normal file
12
src/components/modals/main/scss/modules/_scrollbars.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-top-right-radius: map-get($modal, 'border-radius');
|
||||
border-bottom-right-radius: map-get($modal, 'border-radius');
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #636e72;
|
||||
border-top-right-radius: map-get($modal, 'border-radius');
|
||||
border-bottom-right-radius: map-get($modal, 'border-radius');
|
||||
}
|
||||
87
src/components/modals/main/scss/modules/_sidebar.scss
Normal file
87
src/components/modals/main/scss/modules/_sidebar.scss
Normal file
@@ -0,0 +1,87 @@
|
||||
ul.sidebar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
background: var(--sidebar);
|
||||
border-radius: 12px 0 0 12px;
|
||||
text-align: left;
|
||||
font-size: 24px;
|
||||
min-height: 110vh;
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 3px;
|
||||
background: rgba(196, 196, 196, 0.74);
|
||||
width: 75%;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1200px) {
|
||||
ul.sidebar {
|
||||
width: 310px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
font-size: 24px;
|
||||
padding: 5px 30px 5px 30px;
|
||||
cursor: pointer;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.tab-list-active {
|
||||
background: var(--tab-active);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
li.tab-list-item {
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
ul.sidebar {
|
||||
h1 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-list-item {
|
||||
&:hover {
|
||||
background: var(--tab-active);
|
||||
}
|
||||
}
|
||||
43
src/components/modals/main/scss/modules/_tab-content.scss
Normal file
43
src/components/modals/main/scss/modules/_tab-content.scss
Normal file
@@ -0,0 +1,43 @@
|
||||
.tab-content {
|
||||
position: absolute;
|
||||
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 2300px) {
|
||||
.tab-content {
|
||||
left: 350px;
|
||||
top: 7%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1920px) {
|
||||
.tab-content {
|
||||
left: 120px;
|
||||
top: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1920px) {
|
||||
.tab-content {
|
||||
left: 350px;
|
||||
top: 9%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1400px),
|
||||
(min-width: 1400px) {
|
||||
.tab-content {
|
||||
left: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.tab-content {
|
||||
left: 125px;
|
||||
top: 90px;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.add {
|
||||
.add,
|
||||
.close {
|
||||
@extend %settingsButton;
|
||||
|
||||
background-color: map-get($button-colours, 'other');
|
||||
@@ -37,30 +38,39 @@
|
||||
}
|
||||
|
||||
.close {
|
||||
@extend %settingsButton;
|
||||
|
||||
padding: 10px 50px 10px 50px;
|
||||
background-color: map-get($button-colours, 'other');
|
||||
border: 2px solid map-get($button-colours, 'other');
|
||||
|
||||
&:hover {
|
||||
color: map-get($button-colours, 'other');
|
||||
border: 2px solid map-get($button-colours, 'other');
|
||||
}
|
||||
}
|
||||
|
||||
.export,
|
||||
.uploadbg,
|
||||
.import {
|
||||
@extend %settingsButton;
|
||||
|
||||
background-color: map-get($button-colours, 'other');
|
||||
color: map-get($theme-colours, 'primary');
|
||||
border: 2px solid map-get($button-colours, 'other');
|
||||
width: 440px;
|
||||
height: 60px;
|
||||
background-color: var(--sidebar);
|
||||
border: none;
|
||||
outline: none;
|
||||
color: var(--modal-text);
|
||||
border-radius: 12px;
|
||||
margin-right: 25px;
|
||||
width: 220px;
|
||||
cursor: pointer;
|
||||
border-radius: 24px;
|
||||
border: 3px solid var(--tab-active);
|
||||
font-size: 1rem;
|
||||
|
||||
&:hover {
|
||||
color: map-get($button-colours, 'other');
|
||||
border: 2px solid map-get($button-colours, 'other');
|
||||
background-color: var(--tab-active);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
background: none;
|
||||
border: 1px solid var(--tab-active);
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
border: 1px solid var(--tab-active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,32 +79,39 @@
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.MuiIconButton-label > svg.MuiSvgIcon-root {
|
||||
color: var(--modal-text) !important;
|
||||
.round-small {
|
||||
height: 10px !important;
|
||||
width: 10px !important;
|
||||
}
|
||||
|
||||
.sortableitem {
|
||||
background: var(--sidebar) !important;
|
||||
padding: 10px 80px;
|
||||
padding-left: 10px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.325rem;
|
||||
color: var(--modal-text) !important;
|
||||
cursor: move;
|
||||
width: 150px;
|
||||
z-index: 999 !important;
|
||||
.data-buttons-row {
|
||||
width: 350px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
svg {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
button {
|
||||
background: var(--sidebar);
|
||||
text-align: center;
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
border: 3px solid var(--tab-active);
|
||||
height: 40px;
|
||||
font-size: 1rem;
|
||||
margin: 0 10px 10px 0;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
align-items: center;
|
||||
color: var(--modal-text);
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.15);
|
||||
transition: 0.3s;
|
||||
&:hover {
|
||||
background: var(--tab-active);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.MuiTouchRipple-root {
|
||||
background: transparent;
|
||||
.customvideoicon {
|
||||
position: absolute;
|
||||
margin-bottom: 45px;
|
||||
font-size: 3em !important;
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
.DayPickerInput,
|
||||
.input-container {
|
||||
input {
|
||||
width: 200px;
|
||||
color: var(--modal-text) !important;
|
||||
background: var(--sidebar);
|
||||
border: none;
|
||||
padding: 10px 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.DayPicker-Day--selected {
|
||||
background-color: #ff4757 !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.DayPicker-Months,
|
||||
.DayPickerInput-Overlay {
|
||||
background-color: var(--background) !important;
|
||||
color: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
.DayPicker:not(.DayPicker--interactionDisabled) .DayPicker-Day:not(.DayPicker-Day--disabled):not(.DayPicker-Day--selected):not(.DayPicker-Day--outside):hover {
|
||||
color: black !important;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
select {
|
||||
margin-left: 10px;
|
||||
width: 120px;
|
||||
color: var(--modal-text);
|
||||
background: var(--sidebar);
|
||||
border: none;
|
||||
padding: 10px 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
// firefox dropdown
|
||||
@supports (-moz-appearance: none) {
|
||||
select {
|
||||
-moz-appearance: none !important;
|
||||
background: url("data:image/svg+xml;utf8,<svg fill='black' 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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
option {
|
||||
font: -moz-pull-down-menu !important;
|
||||
}
|
||||
}
|
||||
|
||||
// safari dropdown
|
||||
@supports (-webkit-hyphens: none) {
|
||||
select {
|
||||
-webkit-appearance: none !important;
|
||||
background: url("data:image/svg+xml;utf8,<svg fill='black' 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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
input {
|
||||
&[type=text] {
|
||||
width: 200px;
|
||||
color: var(--modal-text);
|
||||
background: var(--sidebar);
|
||||
border: none;
|
||||
padding: 10px 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
@import 'modules/resetmodal';
|
||||
@import 'modules/material-ui';
|
||||
@import 'modules/reminder';
|
||||
|
||||
@import 'modules/tabs/about';
|
||||
@import 'modules/tabs/changelog';
|
||||
@import 'modules/tabs/order';
|
||||
|
||||
input {
|
||||
/* colour picker */
|
||||
&[type=color] {
|
||||
border-radius: 100%;
|
||||
height: 30px;
|
||||
@@ -17,17 +17,18 @@ input {
|
||||
-webkit-appearance: none;
|
||||
vertical-align: middle;
|
||||
background: none;
|
||||
|
||||
|
||||
&::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
&::-webkit-color-swatch {
|
||||
border: none;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* firefox fixes for colour picker (using "," didn't work) */
|
||||
&[type=color]::-moz-color-swatch {
|
||||
border-radius: 100%;
|
||||
height: 30px;
|
||||
@@ -37,27 +38,35 @@ input {
|
||||
-moz-appearance: none;
|
||||
vertical-align: middle;
|
||||
background: none;
|
||||
|
||||
|
||||
&::-moz-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
&::-moz-color-swatch {
|
||||
border: none;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* date picker */
|
||||
&[type=date] {
|
||||
width: 200px;
|
||||
width: 280px;
|
||||
color: var(--modal-text);
|
||||
background: var(--sidebar);
|
||||
border: none;
|
||||
padding: 10px 10px;
|
||||
border-radius: 5px;
|
||||
background: var(--background);
|
||||
border: solid var(--modal-text) 1px;
|
||||
padding: 15px 20px;
|
||||
border-radius: 4px;
|
||||
display: flex !important;
|
||||
cursor: pointer;
|
||||
|
||||
&::-webkit-calendar-picker-indicator {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* dark theme date picker fix */
|
||||
.dark {
|
||||
::-webkit-calendar-picker-indicator {
|
||||
filter: invert(1);
|
||||
@@ -68,171 +77,10 @@ h4 {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 0px;
|
||||
margin: 0;
|
||||
.keybind-table {
|
||||
text-align: left;
|
||||
|
||||
>label {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.range {
|
||||
-webkit-appearance: none;
|
||||
width: 200px;
|
||||
height: 12px;
|
||||
border-radius: 12px;
|
||||
outline: none;
|
||||
background: var(--sidebar);
|
||||
box-shadow: 0 0 100px rgba(0, 0, 0, 0.3);
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 12px;
|
||||
background: var(--modal-text);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 12px;
|
||||
border: 0;
|
||||
background: var(--modal-text);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.newFeature {
|
||||
color: #ff4757;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.settingsTextarea {
|
||||
font-family: Consolas !important;
|
||||
padding: 15px;
|
||||
border-radius: 15px;
|
||||
background-color: var(--sidebar) !important;
|
||||
border: none;
|
||||
margin-left: 0;
|
||||
width: 400px;
|
||||
height: 200px;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
.MuiCheckbox-colorPrimary.Mui-checked,
|
||||
.MuiSwitch-colorPrimary.Mui-checked,
|
||||
.MuIconButton-colorPrimary.Mui-checked,
|
||||
.MuiSwitch-thumb,
|
||||
.MuiRadio-colorSecondary.Mui-checked,
|
||||
.PrivateSwitchBase-input-4,
|
||||
.MuiRadio-root,
|
||||
.aboutLink,
|
||||
legend {
|
||||
color: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
.MuiFormControlLabel-labelPlacementStart {
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
|
||||
.MuiSwitch-colorPrimary.Mui-checked+.MuiSwitch-track {
|
||||
background: darkgray !important;
|
||||
}
|
||||
|
||||
.reminder-info {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
padding: 15px;
|
||||
color: var(--modal-text);
|
||||
background: var(--sidebar);
|
||||
max-width: 300px;
|
||||
border-radius: 0.7em;
|
||||
|
||||
h1 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-title {
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
font-size: 1.17rem;
|
||||
}
|
||||
|
||||
.radio-title-small {
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.sortableitem {
|
||||
color: var(--modal-text) !important;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.updatechangelog {
|
||||
max-width: 75%;
|
||||
|
||||
li {
|
||||
cursor: initial;
|
||||
font-size: 1rem;
|
||||
list-style-type:disc;
|
||||
padding: 0;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--modal-link);
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.changelogtab {
|
||||
h1 {
|
||||
max-width: 85%;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
.sliderText {
|
||||
color: var(--modal-text);
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
.resetArea {
|
||||
h2 {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
h2, span, svg {
|
||||
display: inline;
|
||||
}
|
||||
svg {
|
||||
vertical-align: sub;
|
||||
font-size: 1.4rem;
|
||||
th {
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/* these are overrides for the material ui default styles */
|
||||
.MuiCheckbox-colorPrimary.Mui-checked,
|
||||
.MuiSwitch-colorPrimary.Mui-checked,
|
||||
.MuIconButton-colorPrimary.Mui-checked,
|
||||
.MuiSwitch-thumb,
|
||||
.MuiRadio-colorSecondary.Mui-checked,
|
||||
.PrivateSwitchBase-input-4,
|
||||
.MuiRadio-root,
|
||||
.aboutLink,
|
||||
.MuiSlider-colorPrimary,
|
||||
legend {
|
||||
color: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
.MuiFormControlLabel-labelPlacementStart {
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
|
||||
.MuiSwitch-colorPrimary.Mui-checked+.MuiSwitch-track {
|
||||
background: darkgray !important;
|
||||
}
|
||||
|
||||
.MuiIconButton-label>svg.MuiSvgIcon-root {
|
||||
color: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
.MuiTouchRipple-root {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.MuiFormControl-root {
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
|
||||
.checkbox svg {
|
||||
fill: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
.radio-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.17rem;
|
||||
}
|
||||
|
||||
.radio-title-small {
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.MuiSlider-root {
|
||||
margin-bottom: 30px !important;
|
||||
}
|
||||
|
||||
.MuiOutlinedInput-notchedOutline {
|
||||
border-color: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
.MuiFormLabel-root-MuiInputLabel-root {
|
||||
color: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
.MuiInputLabel-root,
|
||||
.MuiSlider-markLabel,
|
||||
.MuiInputLabel-root,
|
||||
.MuiSelect-icon,
|
||||
.MuiSelect-select,
|
||||
.Mui-focused,
|
||||
legend,
|
||||
.MuiOutlinedInput-input {
|
||||
color: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
.MuiMenu-list {
|
||||
background-color: var(--background) !important;
|
||||
color: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
.Mui-selected {
|
||||
background-color: var(--tab-active) !important;
|
||||
}
|
||||
|
||||
.MuiTextField-root,
|
||||
.MuiFormControl-root,
|
||||
.MuiSlider-root {
|
||||
width: 300px !important;
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.Mui-disabled {
|
||||
color: #818181 !important;
|
||||
cursor: not-allowed;
|
||||
|
||||
.checkbox svg {
|
||||
fill: #818181 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.MuiPaper-root {
|
||||
background-color: var(--background) !important;
|
||||
}
|
||||
|
||||
.MuiSlider-valueLabel {
|
||||
background-color: var(--tab-active) !important;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
.reminder-info {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
padding: 15px;
|
||||
color: var(--modal-text);
|
||||
background: var(--sidebar);
|
||||
max-width: 300px;
|
||||
border-radius: 0.7em;
|
||||
|
||||
h1 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
.resetmodal {
|
||||
min-height: 300px !important;
|
||||
max-width: 300px !important;
|
||||
margin: auto;
|
||||
font-size: 1rem;
|
||||
|
||||
h4 {
|
||||
cursor: initial;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.resetfooter {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
width: 300px;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
|
||||
button.reset {
|
||||
margin-right: 43px;
|
||||
}
|
||||
}
|
||||
|
||||
.resetoverlay {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
.aboutIcon {
|
||||
color: var(--modal-text) !important;
|
||||
padding-right: 10px;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.aboutLink {
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.aboutLogo {
|
||||
height: 100px;
|
||||
width: auto;
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
.abouticon {
|
||||
width: 96px;
|
||||
height: auto;
|
||||
border-radius: 50%;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.contacth3 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.8em !important;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
.updatechangelog {
|
||||
max-width: 75%;
|
||||
|
||||
li {
|
||||
cursor: initial;
|
||||
font-size: 1rem;
|
||||
list-style-type: disc;
|
||||
padding: 0;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--modal-link);
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.changelogtab {
|
||||
h1 {
|
||||
max-width: 85%;
|
||||
font-size: 2rem;
|
||||
margin-bottom: -10px !important;
|
||||
}
|
||||
|
||||
h5 {
|
||||
line-height: 0px !important;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 95%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
.sortableitem {
|
||||
background: var(--sidebar) !important;
|
||||
padding: 10px 80px;
|
||||
padding-left: 10px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.325rem;
|
||||
color: var(--modal-text) !important;
|
||||
cursor: move;
|
||||
width: 150px;
|
||||
z-index: 999 !important;
|
||||
|
||||
svg {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--tab-active) !important;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 0px;
|
||||
margin: 0;
|
||||
|
||||
>label {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.images-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 500px;
|
||||
|
||||
div {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-color: var(--sidebar);
|
||||
border: 20px solid var(--sidebar);
|
||||
border-radius: 20px;
|
||||
margin: 0 20px 20px 0;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
|
||||
svg {
|
||||
font-size: 1.9em;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,3 +31,8 @@ div.color-preview-area > div > div:nth-child(5) {
|
||||
.gradient-type-item.active::after {
|
||||
border: 2px solid var(--modal-text) !important;
|
||||
}
|
||||
|
||||
.text-input, .number-input {
|
||||
background-color: var(--sidebar) !important;
|
||||
color: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { Checkbox as CheckboxUI, FormControlLabel } from '@mui/material';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
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 = {
|
||||
@@ -21,7 +20,11 @@ export default class Checkbox extends React.PureComponent {
|
||||
checked: value
|
||||
});
|
||||
|
||||
window.stats.postEvent('setting', `${this.props.name} ${(this.state.checked === true) ? 'enabled' : 'disabled'}`);
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(value);
|
||||
}
|
||||
|
||||
variables.stats.postEvent('setting', `${this.props.name} ${(this.state.checked === true) ? 'enabled' : 'disabled'}`);
|
||||
|
||||
if (this.props.element) {
|
||||
if (!document.querySelector(this.props.element)) {
|
||||
@@ -34,19 +37,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}
|
||||
control={<CheckboxUI name={this.props.name} color='primary' className='checkbox' checked={this.state.checked} onChange={this.handleChange} disabled={this.props.disabled || false} />}
|
||||
label={this.props.text}
|
||||
/>
|
||||
<br/>
|
||||
</>
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent, createRef } from 'react';
|
||||
import { InputLabel, MenuItem, FormControl, Select } from '@mui/material';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
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 = {
|
||||
value: localStorage.getItem(this.props.name) || '',
|
||||
value: localStorage.getItem(this.props.name) || this.props.children[0].props.value,
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
return this.props.label ? <label>{this.props.label}</label> : null;
|
||||
this.dropdown = createRef();
|
||||
}
|
||||
|
||||
onChange = (e) => {
|
||||
const { value } = e.target;
|
||||
|
||||
if (value === window.language.modals.main.loading) {
|
||||
if (value === variables.language.getMessage(variables.languagecode, 'modals.main.loading')) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`);
|
||||
variables.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`);
|
||||
|
||||
this.setState({
|
||||
value: value,
|
||||
title: e.target[e.target.selectedIndex].text
|
||||
value
|
||||
});
|
||||
|
||||
localStorage.setItem(this.props.name, value);
|
||||
if (!this.props.noSetting) {
|
||||
localStorage.setItem(this.props.name, value);
|
||||
}
|
||||
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(value);
|
||||
@@ -45,22 +45,19 @@ export default class Dropdown extends React.PureComponent {
|
||||
EventBus.dispatch('refresh', this.props.category);
|
||||
}
|
||||
|
||||
// todo: find a better way to do this
|
||||
componentDidMount() {
|
||||
const element = document.getElementById(this.props.name);
|
||||
this.setState({
|
||||
title: element[element.selectedIndex].text
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const id = 'dropdown' + this.props.name;
|
||||
const label = this.props.label || '';
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.getLabel()}
|
||||
<select id={this.props.name} value={this.state.value} onChange={this.onChange} style={{ width: `${(8*this.state.title.length) + 50}px` }}>
|
||||
{this.props.children}
|
||||
</select>
|
||||
</>
|
||||
<FormControl fullWidth className={id}>
|
||||
<InputLabel id={id}>{label}</InputLabel>
|
||||
<Select labelId={id} id={this.props.name} value={this.state.value} label={label} onChange={this.onChange} ref={this.dropdown} key={id}>
|
||||
{this.props.manual ? this.props.children : this.props.children.map((e, index) => {
|
||||
return e ? <MenuItem key={index} value={e.props ? e.props.value : ''}>{e.props ? e.props.children : ''}</MenuItem> : null
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export default class FileUpload extends React.PureComponent {
|
||||
export default class FileUpload extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
componentDidMount() {
|
||||
document.getElementById(this.props.id).onchange = (e) => {
|
||||
const reader = new FileReader();
|
||||
@@ -13,7 +15,7 @@ export default class FileUpload extends React.PureComponent {
|
||||
} else {
|
||||
// background upload
|
||||
if (file.size > 2000000) {
|
||||
return toast(window.language.modals.main.file_upload_error);
|
||||
return toast(this.getMessage('modals.main.file_upload_error'));
|
||||
}
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
|
||||
24
src/components/modals/main/settings/Header.jsx
Normal file
24
src/components/modals/main/settings/Header.jsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import variables from 'modules/variables';
|
||||
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Slider from './Slider';
|
||||
import Switch from './Switch';
|
||||
|
||||
import { values } from 'modules/helpers/settings/modals';
|
||||
|
||||
export default class Header extends PureComponent {
|
||||
render() {
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{this.props.title}</h2>
|
||||
<Switch name={this.props.setting} text={getMessage('modals.main.settings.enabled')} category={this.props.category} element={this.props.element || null} />
|
||||
{this.props.zoomSetting ?
|
||||
<><Slider title={getMessage('modals.main.settings.sections.appearance.accessibility.widget_zoom')} name={this.props.zoomSetting} min='10' max='400' default='100' display='%' marks={values('zoom')} category={this.props.zoomCategory || this.props.category}/></>
|
||||
: <br/>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
24
src/components/modals/main/settings/KeybindInput.jsx
Normal file
24
src/components/modals/main/settings/KeybindInput.jsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import variables from 'modules/variables';
|
||||
import { Cancel } from '@mui/icons-material';
|
||||
import { TextField } from '@mui/material';
|
||||
|
||||
export default function KeybindInput(props) {
|
||||
const value = props.state[props.setting];
|
||||
|
||||
const getButton = () => {
|
||||
if (!value) {
|
||||
return <button className='cleanButton' style={{ visibility: 'hidden' }} onClick={() => props.action('reset', props.setting)}><Cancel/></button>;;
|
||||
} else if (value === variables.language.getMessage(variables.languagecode, 'modals.main.settings.sections.keybinds.recording')) {
|
||||
return <button className='cleanButton' onClick={() => props.action('cancel', props.setting)}><Cancel/></button>;
|
||||
} else {
|
||||
return <button className='cleanButton' onClick={() => props.action('reset', props.setting)}><Cancel/></button>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextField label={props.name} onClick={() => props.action('listen', props.setting)} value={value || variables.language.getMessage(variables.languagecode, 'modals.main.settings.sections.keybinds.click_to_record')} readOnly spellCheck={false} varient='outlined' InputLabelProps={{ shrink: true }} />
|
||||
{getButton()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { Radio as RadioUI, RadioGroup, FormControlLabel, FormControl, FormLabel } from '@mui/material';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
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,13 +19,24 @@ export default class Radio extends React.PureComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.name === 'language') {
|
||||
// old tab name
|
||||
if (localStorage.getItem('tabName') === variables.language.getMessage(variables.languagecode, 'tabname')) {
|
||||
localStorage.setItem('tabName', require(`translations/${value.replace('-', '_')}.json`).tabname);
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem(this.props.name, value);
|
||||
|
||||
this.setState({
|
||||
value: value
|
||||
value
|
||||
});
|
||||
|
||||
window.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`);
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(value);
|
||||
}
|
||||
|
||||
variables.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`);
|
||||
|
||||
if (this.props.element) {
|
||||
if (!document.querySelector(this.props.element)) {
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
import DeleteIcon from '@material-ui/icons/Delete';
|
||||
|
||||
import SettingsFunctions from '../../../../modules/helpers/settings';
|
||||
|
||||
export default function ResetModal(props) {
|
||||
const language = window.language.modals.main.settings.sections.advanced.reset_modal;
|
||||
import variables from 'modules/variables';
|
||||
import { Close, Delete } from '@mui/icons-material';
|
||||
import { setDefaultSettings } from 'modules/helpers/settings';
|
||||
|
||||
export default function ResetModal({ modalClose }) {
|
||||
const reset = () => {
|
||||
window.stats.postEvent('setting', 'Reset');
|
||||
SettingsFunctions.setDefaultSettings('reset');
|
||||
variables.stats.postEvent('setting', 'Reset');
|
||||
setDefaultSettings('reset');
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 style={{ textAlign: 'center' }}>{language.title}</h1>
|
||||
<span>{language.question}</span>
|
||||
<h1 style={{ textAlign: 'center' }}>{variables.language.getMessage(variables.languagecode, 'modals.main.settings.sections.advanced.reset_modal.title')}</h1>
|
||||
<span>{variables.language.getMessage(variables.languagecode, 'modals.main.settings.sections.advanced.reset_modal.question')}</span>
|
||||
<br/><br/>
|
||||
<span>{language.information}</span>
|
||||
<span>{variables.language.getMessage(variables.languagecode, 'modals.main.settings.sections.advanced.reset_modal.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/>
|
||||
<button className='round add' style={{ marginLeft: '5px' }} onClick={modalClose}>
|
||||
<Close/>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
// todo: find a better method to do width of number input
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Slider } from '@mui/material';
|
||||
|
||||
export default class Slider extends React.PureComponent {
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
|
||||
export default class SliderComponent extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: localStorage.getItem(this.props.name) || this.props.default,
|
||||
numberWidth: localStorage.getItem(this.props.name) ? ((localStorage.getItem(this.props.name).length + 1) * ((this.props.toast === true) ? 7.75 : 7)) : 32
|
||||
value: localStorage.getItem(this.props.name) || this.props.default
|
||||
};
|
||||
this.language = window.language.modals.main.settings;
|
||||
this.widthCalculation = (this.props.toast === true) ? 7.75 : 7;
|
||||
}
|
||||
|
||||
handleChange = (e, text) => {
|
||||
let { value } = e.target;
|
||||
value = Number(value);
|
||||
|
||||
if (text) {
|
||||
if (value === '') {
|
||||
@@ -26,19 +24,18 @@ export default class Slider extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
if (Number(value) > this.props.max) {
|
||||
if (value > this.props.max) {
|
||||
value = this.props.max;
|
||||
}
|
||||
|
||||
if (Number(value) < this.props.min) {
|
||||
if (value < this.props.min) {
|
||||
value = this.props.min;
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem(this.props.name, value);
|
||||
this.setState({
|
||||
value: value,
|
||||
numberWidth: ((value.length + 1) * this.widthCalculation)
|
||||
value
|
||||
});
|
||||
|
||||
if (this.props.element) {
|
||||
@@ -57,16 +54,24 @@ export default class Slider extends React.PureComponent {
|
||||
value: this.props.default || ''
|
||||
}
|
||||
});
|
||||
toast(window.language.toasts.reset);
|
||||
toast(variables.language.getMessage(variables.languagecode, 'toasts.reset'));
|
||||
}
|
||||
|
||||
render() {
|
||||
const text = <input className='sliderText' type='number' min={this.props.min} max={this.props.max} onChange={(e) => this.handleChange(e, 'text')} value={this.state.value} style={{ width: this.state.numberWidth }}/>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>{this.props.title} ({text}{this.props.display}) <span className='modalLink' onClick={this.resetItem}>{this.language.buttons.reset}</span></p>
|
||||
<input className='range' type='range' min={this.props.min} max={this.props.max} step={this.props.step || 1} value={this.state.value} onChange={this.handleChange} />
|
||||
<p>{this.props.title}<span className='modalLink' onClick={this.resetItem}>{variables.language.getMessage(variables.languagecode, 'modals.main.settings.buttons.reset')}</span></p>
|
||||
<Slider
|
||||
value={Number(this.state.value)}
|
||||
onChange={this.handleChange}
|
||||
valueLabelDisplay='auto'
|
||||
default={Number(this.props.default)}
|
||||
min={Number(this.props.min)}
|
||||
max={Number(this.props.max)}
|
||||
step={Number(this.props.step) || 1}
|
||||
getAriaValueText={(value) => `${value}`}
|
||||
marks={this.props.marks || []}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { Switch as SwitchUI, FormControlLabel } from '@mui/material';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
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 = {
|
||||
@@ -21,7 +20,7 @@ export default class Switch extends React.PureComponent {
|
||||
checked: value
|
||||
});
|
||||
|
||||
window.stats.postEvent('setting', `${this.props.name} ${(this.state.checked === true) ? 'enabled' : 'disabled'}`);
|
||||
variables.stats.postEvent('setting', `${this.props.name} ${(this.state.checked === true) ? 'enabled' : 'disabled'}`);
|
||||
|
||||
if (this.props.element) {
|
||||
if (!document.querySelector(this.props.element)) {
|
||||
@@ -34,22 +33,13 @@ 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,16 +1,16 @@
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../../modules/helpers/eventbus';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { TextField } from '@mui/material';
|
||||
|
||||
export default class Text extends React.PureComponent {
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
|
||||
export default class Text extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: localStorage.getItem(this.props.name) || ''
|
||||
};
|
||||
this.language = window.language.modals.main.settings;
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
@@ -23,7 +23,7 @@ export default class Text extends React.PureComponent {
|
||||
|
||||
localStorage.setItem(this.props.name, value);
|
||||
this.setState({
|
||||
value: value
|
||||
value
|
||||
});
|
||||
|
||||
if (this.props.element) {
|
||||
@@ -42,17 +42,17 @@ export default class Text extends React.PureComponent {
|
||||
value: this.props.default || ''
|
||||
}
|
||||
});
|
||||
toast(window.language.toasts.reset);
|
||||
toast(variables.language.getMessage(variables.languagecode, 'toasts.reset'));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<p>{this.props.title} <span className='modalLink' onClick={this.resetItem}>{this.language.buttons.reset}</span></p>
|
||||
{(this.props.textarea === true) ?
|
||||
<textarea className='settingsTextarea' spellCheck={false} value={this.state.value} onChange={this.handleChange}/>
|
||||
: <input type='text' value={this.state.value} onChange={this.handleChange}/>
|
||||
{(this.props.textarea === true) ?
|
||||
<TextField label={this.props.title} value={this.state.value} onChange={this.handleChange} varient='outlined' multiline spellCheck={false} minRows={4} maxRows={10} InputLabelProps={{ shrink: true }} />
|
||||
: <TextField label={this.props.title} value={this.state.value} onChange={this.handleChange} varient='outlined' InputLabelProps={{ shrink: true }} />
|
||||
}
|
||||
<span className='modalLink' onClick={this.resetItem}>{variables.language.getMessage(variables.languagecode, 'modals.main.settings.buttons.reset')}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { Email, Twitter, Chat, Instagram, Facebook } from '@mui/icons-material';
|
||||
|
||||
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';
|
||||
import Tooltip from 'components/helpers/tooltip/Tooltip';
|
||||
|
||||
const other_contributors = require('../../../../../modules/other_contributors.json');
|
||||
const other_contributors = require('modules/other_contributors.json');
|
||||
|
||||
export default class About extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
export default class About extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
contributors: [],
|
||||
sponsors: [],
|
||||
other_contributors: [],
|
||||
photographers: window.language.modals.main.loading,
|
||||
update: window.language.modals.main.settings.sections.about.version.checking_update,
|
||||
loading: window.language.modals.main.loading
|
||||
photographers: this.getMessage('modals.main.loading'),
|
||||
update: this.getMessage('modals.main.settings.sections.about.version.checking_update'),
|
||||
loading: this.getMessage('modals.main.loading')
|
||||
};
|
||||
this.language = window.language.modals.main.settings.sections.about;
|
||||
this.controller = new AbortController();
|
||||
}
|
||||
|
||||
@@ -28,40 +26,42 @@ export default class About extends React.PureComponent {
|
||||
let contributors, sponsors, photographers, versionData;
|
||||
|
||||
try {
|
||||
versionData = await (await fetch(window.constants.GITHUB_URL + '/repos/' + window.constants.ORG_NAME + '/' + window.constants.REPO_NAME + '/releases', { signal: this.controller.signal })).json();
|
||||
|
||||
contributors = await (await fetch(window.constants.GITHUB_URL + '/repos/'+ window.constants.ORG_NAME + '/' + window.constants.REPO_NAME + '/contributors', { signal: this.controller.signal })).json();
|
||||
sponsors = (await (await fetch(window.constants.SPONSORS_URL + '/list', { signal: this.controller.signal })).json()).sponsors;
|
||||
|
||||
photographers = await (await fetch(window.constants.API_URL + '/images/photographers', { signal: this.controller.signal })).json();
|
||||
versionData = await (await fetch(variables.constants.GITHUB_URL + '/repos/' + variables.constants.ORG_NAME + '/' + variables.constants.REPO_NAME + '/releases', { signal: this.controller.signal })).json();
|
||||
contributors = await (await fetch(variables.constants.GITHUB_URL + '/repos/'+ variables.constants.ORG_NAME + '/' + variables.constants.REPO_NAME + '/contributors', { signal: this.controller.signal })).json();
|
||||
sponsors = (await (await fetch(variables.constants.SPONSORS_URL + '/list', { signal: this.controller.signal })).json()).sponsors;
|
||||
photographers = await (await fetch(variables.constants.API_URL + '/images/photographers', { signal: this.controller.signal })).json();
|
||||
} catch (e) {
|
||||
if (this.controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.setState({
|
||||
update: this.language.version.error.title,
|
||||
loading: this.language.version.error.description
|
||||
update: this.getMessage('modals.main.settings.sections.about.version.error.title'),
|
||||
loading: this.getMessage('modals.main.settings.sections.about.version.error.description')
|
||||
});
|
||||
}
|
||||
|
||||
if (sponsors.length === 0) {
|
||||
sponsors = [{ handle: 'empty' }];
|
||||
}
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newVersion = versionData[0].tag_name;
|
||||
|
||||
let updateMsg = this.language.version.no_update;
|
||||
if (Number(window.constants.VERSION.replaceAll('.', '')) < Number(newVersion.replaceAll('.', ''))) {
|
||||
updateMsg = `${this.language.version.update_available}: ${newVersion}`;
|
||||
let update = this.getMessage('modals.main.settings.sections.about.version.no_update');
|
||||
if (Number(variables.constants.VERSION.replaceAll('.', '')) < Number(newVersion.replaceAll('.', ''))) {
|
||||
update = `${this.getMessage('modals.main.settings.sections.about.version.update_available')}: ${newVersion}`;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
// exclude bots
|
||||
contributors: contributors.filter((contributor) => !contributor.login.includes('bot')),
|
||||
sponsors: sponsors,
|
||||
update: updateMsg,
|
||||
other_contributors: other_contributors,
|
||||
sponsors,
|
||||
update,
|
||||
other_contributors,
|
||||
photographers: photographers.sort().join(', '),
|
||||
loading: null
|
||||
});
|
||||
@@ -70,8 +70,8 @@ export default class About extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
this.setState({
|
||||
update: this.language.version.offline_mode,
|
||||
loading: window.language.modals.main.marketplace.offline.description
|
||||
update: this.getMessage('modals.main.settings.sections.about.version.checking_update'),
|
||||
loading: this.getMessage('modals.main.marketplace.offline.description')
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -87,57 +87,62 @@ export default class About extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<h2>{this.language.title}</h2>
|
||||
<h2>{this.getMessage('modals.main.settings.sections.about.title')}</h2>
|
||||
<img draggable='false' className='aboutLogo' src='./././icons/logo_horizontal.png' alt='Logo'></img>
|
||||
<p>{this.language.copyright} {window.constants.COPYRIGHT_YEAR}-{new Date().getFullYear()} <a href={'https://github.com/' + window.constants.ORG_NAME + '/' + window.constants.REPO_NAME + '/graphs/contributors'} className='aboutLink' target='_blank' rel='noopener noreferrer'>{window.constants.COPYRIGHT_NAME}</a> ({window.constants.COPYRIGHT_LICENSE})</p>
|
||||
<p>{this.language.version.title} {window.constants.VERSION} ({this.state.update})</p>
|
||||
<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>
|
||||
<p>{this.getMessage('modals.main.settings.sections.about.copyright')} {variables.constants.COPYRIGHT_YEAR}-{new Date().getFullYear()} <a href={'https://github.com/' + variables.constants.ORG_NAME + '/' + variables.constants.REPO_NAME + '/graphs/contributors'} className='aboutLink' target='_blank' rel='noopener noreferrer'>{variables.constants.COPYRIGHT_NAME}</a> ({variables.constants.COPYRIGHT_LICENSE})</p>
|
||||
<p>{this.getMessage('modals.main.settings.sections.about.version.title')} {variables.constants.VERSION} ({this.state.update})</p>
|
||||
<a href={variables.constants.PRIVACY_URL} className='aboutLink' target='_blank' rel='noopener noreferrer' style={{ fontSize: '1rem' }}>{this.getMessage('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>
|
||||
<h3 className='contacth3'>{this.getMessage('modals.main.settings.sections.about.contact_us')}</h3>
|
||||
<a href={'mailto:' + variables.constants.EMAIL} className='aboutIcon' target='_blank' rel='noopener noreferrer'><Email/></a>
|
||||
<a href={'https://twitter.com/' + variables.constants.TWITTER_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><Twitter/></a>
|
||||
<a href={'https://instagram.com/' + variables.constants.INSTAGRAM_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><Instagram/></a>
|
||||
<a href={'https://facebook.com/' + variables.constants.FACEBOOK_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><Facebook/></a>
|
||||
<a href={'https://discord.gg/' + variables.constants.DISCORD_SERVER} className='aboutIcon' target='_blank' rel='noopener noreferrer'><Chat/></a>
|
||||
|
||||
<h3>{this.language.support_mue}</h3>
|
||||
<h3>{this.getMessage('modals.main.settings.sections.about.support_mue')}</h3>
|
||||
<p>
|
||||
<a href={'https://github.com/sponsors/' + window.constants.DONATE_USERNAME} className='aboutLink' target='_blank' rel='noopener noreferrer'>GitHub Sponsors</a>
|
||||
• <a href={'https://ko-fi.com/' + window.constants.DONATE_USERNAME} className='aboutLink' target='_blank' rel='noopener noreferrer'>Ko-Fi</a>
|
||||
• <a href={'https://patreon.com/' + window.constants.DONATE_USERNAME} className='aboutLink' target='_blank' rel='noopener noreferrer'>Patreon</a>
|
||||
<a href={'https://github.com/sponsors/' + variables.constants.DONATE_USERNAME} className='aboutLink' target='_blank' rel='noopener noreferrer'>GitHub Sponsors</a>
|
||||
• <a href={'https://ko-fi.com/' + variables.constants.DONATE_USERNAME} className='aboutLink' target='_blank' rel='noopener noreferrer'>Ko-Fi</a>
|
||||
• <a href={'https://patreon.com/' + variables.constants.DONATE_USERNAME} className='aboutLink' target='_blank' rel='noopener noreferrer'>Patreon</a>
|
||||
</p>
|
||||
|
||||
<h3>{this.language.resources_used.title}</h3>
|
||||
<h3>{this.getMessage('modals.main.settings.sections.about.resources_used.title')}</h3>
|
||||
<p>
|
||||
<a href='https://www.pexels.com' className='aboutLink' target='_blank' rel='noopener noreferrer'>Pexels</a>
|
||||
, <a href='https://unsplash.com' className='aboutLink' target='_blank' rel='noopener noreferrer'>Unsplash</a> ({this.language.resources_used.bg_images})
|
||||
, <a href='https://unsplash.com' className='aboutLink' target='_blank' rel='noopener noreferrer'>Unsplash</a> ({this.getMessage('modals.main.settings.sections.about.resources_used.bg_images')})
|
||||
</p>
|
||||
<p><a href='https://fonts.google.com/icons?selected=Material+Icons' className='aboutLink' target='_blank' rel='noopener noreferrer'>Google Fonts</a> ({this.language.resources_used.pin_icon})</p>
|
||||
<p><a href='https://undraw.co' className='aboutLink' target='_blank' rel='noopener noreferrer'>Undraw</a> ({this.language.resources_used.welcome_img})</p>
|
||||
<p><a href='https://undraw.co' className='aboutLink' target='_blank' rel='noopener noreferrer'>Undraw</a> ({this.getMessage('modals.main.settings.sections.about.resources_used.welcome_img')})</p>
|
||||
|
||||
<h3>{this.language.contributors}</h3>
|
||||
<h3>{this.getMessage('modals.main.settings.sections.about.contributors')}</h3>
|
||||
<p>{this.state.loading}</p>
|
||||
{this.state.contributors.map((item) => (
|
||||
<Tooltip title={item.login} key={item.login}>
|
||||
<a href={'https://github.com/' + item.login} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={item.avatar_url + '&size=128'} alt={item.login}/></a>
|
||||
{this.state.contributors.map(({ login, id }) => (
|
||||
<Tooltip title={login} key={login}>
|
||||
<a href={'https://github.com/' + login} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={'https://avatars.githubusercontent.com/u/' + id + '?s=128'} alt={login}></img></a>
|
||||
</Tooltip>
|
||||
))}
|
||||
{ // for those who contributed without opening a pull request
|
||||
this.state.other_contributors.map((item) => (
|
||||
<Tooltip title={item.login} key={item.login}>
|
||||
<a href={'https://github.com/' + item.login} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={item.avatar_url + '&size=128'} alt={item.login}/></a>
|
||||
this.state.other_contributors.map(({ login, avatar_url }) => (
|
||||
<Tooltip title={login} key={login}>
|
||||
<a href={'https://github.com/' + login} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={avatar_url + '&s=128'} alt={login}></img></a>
|
||||
</Tooltip>
|
||||
))}
|
||||
|
||||
<h3>{this.language.supporters}</h3>
|
||||
<h3>{this.getMessage('modals.main.settings.sections.about.supporters')}</h3>
|
||||
<p>{this.state.loading}</p>
|
||||
{this.state.sponsors.map((item) => (
|
||||
<Tooltip title={item.handle} key={item.handle}>
|
||||
<a href={'https://github.com/' + item.handle} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={item.avatar + '&size=128'} alt={item.handle}></img></a>
|
||||
</Tooltip>
|
||||
))}
|
||||
{this.state.sponsors.map(({ handle, avatar }) => {
|
||||
if (handle === 'empty') {
|
||||
return <p>{this.getMessage('modals.main.settings.sections.about.no_supporters')}</p>;
|
||||
}
|
||||
|
||||
<h3>{this.language.photographers}</h3>
|
||||
return (
|
||||
<Tooltip title={handle} key={handle}>
|
||||
<a href={'https://github.com/' + handle} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={avatar.split('?')[0] + '?s=128'} alt={handle}></img></a>
|
||||
</Tooltip>
|
||||
)
|
||||
})}
|
||||
|
||||
<h3>{this.getMessage('modals.main.settings.sections.about.photographers')}</h3>
|
||||
<p>{this.state.photographers}</p>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import Modal from 'react-modal';
|
||||
import { MenuItem } from '@mui/material';
|
||||
import { Upload as ImportIcon, Download as ExportIcon, RestartAlt as ResetIcon } from '@mui/icons-material';
|
||||
|
||||
import { exportSettings, importSettings } from 'modules/helpers/settings/modals';
|
||||
|
||||
import Checkbox from '../Checkbox';
|
||||
import FileUpload from '../FileUpload';
|
||||
@@ -7,49 +13,62 @@ import Switch from '../Switch';
|
||||
import ResetModal from '../ResetModal';
|
||||
import Dropdown from '../Dropdown';
|
||||
|
||||
import SettingsFunctions from '../../../../../modules/helpers/settings/modals';
|
||||
const time_zones = require('components/widgets/time/timezones.json');
|
||||
|
||||
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 = {
|
||||
resetModal: false
|
||||
};
|
||||
this.language = window.language.modals.main.settings;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { advanced } = this.language.sections;
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{advanced.title}</h2>
|
||||
<Checkbox name='offlineMode' text={advanced.offline_mode} element='.other' />
|
||||
<Dropdown name='timezone' label={advanced.timezone.title} category='timezone'>
|
||||
<option value='auto'>{advanced.timezone.automatic}</option>
|
||||
<h2>{getMessage('modals.main.settings.sections.advanced.title')}</h2>
|
||||
<Checkbox name='offlineMode' text={getMessage('modals.main.settings.sections.advanced.offline_mode')} element='.other' />
|
||||
<Dropdown name='timezone' label={getMessage('modals.main.settings.sections.advanced.timezone.title')} category='timezone' manual={true}>
|
||||
<MenuItem value='auto'>{getMessage('modals.main.settings.sections.advanced.timezone.automatic')}</MenuItem>
|
||||
{time_zones.map((timezone) => (
|
||||
<option value={timezone} key={timezone}>{timezone}</option>
|
||||
<MenuItem value={timezone} key={timezone}>{timezone}</MenuItem>
|
||||
))}
|
||||
</Dropdown>
|
||||
|
||||
<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='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)}/>
|
||||
{localStorage.getItem('welcomePreview') !== 'true' ?
|
||||
<>
|
||||
<h3>{getMessage('modals.main.settings.sections.advanced.data')}</h3>
|
||||
<br/>
|
||||
<div className='data-buttons-row'>
|
||||
<button onClick={() => this.setState({ resetModal: true })}>
|
||||
{getMessage('modals.main.settings.buttons.reset')}
|
||||
<ResetIcon/>
|
||||
</button>
|
||||
<button onClick={() => exportSettings()}>
|
||||
{getMessage('modals.main.settings.buttons.export')}
|
||||
<ExportIcon/>
|
||||
</button>
|
||||
<button onClick={() => document.getElementById('file-input').click()}>
|
||||
{getMessage('modals.main.settings.buttons.import')}
|
||||
<ImportIcon/>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
: null}
|
||||
<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'/>
|
||||
<Text title={advanced.custom_js} name='customjs' textarea={true} category='other' element='other'/>
|
||||
<Text title={advanced.custom_css} name='customcss' textarea={true} category='other'/>
|
||||
<h3>{getMessage('modals.main.settings.sections.advanced.customisation')}</h3>
|
||||
<Text title={getMessage('modals.main.settings.sections.advanced.tab_name')} name='tabName' default={getMessage('tabname')} category='other'/>
|
||||
{window.location.href.startsWith('http://') || window.location.href.startsWith('https://') ?
|
||||
<Text title={getMessage('modals.main.settings.sections.advanced.custom_js')} name='customjs' textarea={true} category='other' element='other'/>
|
||||
: null}
|
||||
<Text title={getMessage('modals.main.settings.sections.advanced.custom_css')} name='customcss' textarea={true} category='other'/>
|
||||
|
||||
<h3>{this.language.sections.experimental.title}</h3>
|
||||
<p style={{ maxWidth: '75%' }}>{advanced.experimental_warning}</p>
|
||||
<Switch name='experimental' text={this.language.enabled} element='.other'/>
|
||||
<h3>{getMessage('modals.main.settings.sections.experimental.title')}</h3>
|
||||
<p style={{ maxWidth: '75%' }}>{getMessage('modals.main.settings.sections.advanced.experimental_warning')}</p>
|
||||
<Switch name='experimental' text={getMessage('modals.main.settings.enabled')} element='.other'/>
|
||||
|
||||
<Modal closeTimeoutMS={100} onRequestClose={() => this.setState({ resetModal: false })} isOpen={this.state.resetModal} className='Modal resetmodal mainModal' overlayClassName='Overlay resetoverlay' ariaHideApp={false}>
|
||||
<ResetModal modalClose={() => this.setState({ resetModal: false })} />
|
||||
|
||||
@@ -1,63 +1,62 @@
|
||||
import variables from 'modules/variables';
|
||||
|
||||
import Checkbox from '../Checkbox';
|
||||
import Dropdown from '../Dropdown';
|
||||
import Radio from '../Radio';
|
||||
import Slider from '../Slider';
|
||||
import Text from '../Text';
|
||||
|
||||
import { values } from 'modules/helpers/settings/modals';
|
||||
|
||||
export default function AppearanceSettings() {
|
||||
const { appearance } = window.language.modals.main.settings.sections;
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
const themeOptions = [
|
||||
{
|
||||
name: appearance.theme.auto,
|
||||
name: getMessage('modals.main.settings.sections.appearance.theme.auto'),
|
||||
value: 'auto'
|
||||
},
|
||||
{
|
||||
name: appearance.theme.light,
|
||||
name: getMessage('modals.main.settings.sections.appearance.theme.light'),
|
||||
value: 'light'
|
||||
},
|
||||
{
|
||||
name: appearance.theme.dark,
|
||||
name: getMessage('modals.main.settings.sections.appearance.theme.dark'),
|
||||
value: 'dark'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{appearance.title}</h2>
|
||||
<Radio name='theme' title={appearance.theme.title} options={themeOptions} category='other' />
|
||||
<h2>{getMessage('modals.main.settings.sections.appearance.title')}</h2>
|
||||
<Radio name='theme' title={getMessage('modals.main.settings.sections.appearance.theme.title')} options={themeOptions} category='other' />
|
||||
|
||||
<h3>{appearance.navbar.title}</h3>
|
||||
<Checkbox name='notesEnabled' text={appearance.navbar.notes} category='navbar' />
|
||||
<Checkbox name='refresh' text={appearance.navbar.refresh} category='navbar' />
|
||||
|
||||
<h3>{appearance.font.title}</h3>
|
||||
<Text title={appearance.font.custom} name='font' upperCaseFirst={true} category='other' />
|
||||
<h3>{getMessage('modals.main.settings.sections.appearance.font.title')}</h3>
|
||||
<Text title={getMessage('modals.main.settings.sections.appearance.font.custom')} name='font' upperCaseFirst={true} category='other' />
|
||||
<br/>
|
||||
<Checkbox name='fontGoogle' text={appearance.font.google} category='other' />
|
||||
<Dropdown label={appearance.font.weight.title} name='fontweight' category='other'>
|
||||
<Checkbox name='fontGoogle' text={getMessage('modals.main.settings.sections.appearance.font.google')} category='other' />
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.appearance.font.weight.title')} name='fontweight' category='other'>
|
||||
{/* names are taken from https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight */}
|
||||
<option value='100'>{appearance.font.weight.thin}</option>
|
||||
<option value='200'>{appearance.font.weight.extra_light}</option>
|
||||
<option value='300'>{appearance.font.weight.light}</option>
|
||||
<option value='400'>{appearance.font.weight.normal}</option>
|
||||
<option value='500'>{appearance.font.weight.medium}</option>
|
||||
<option value='600'>{appearance.font.weight.semi_bold}</option>
|
||||
<option value='700'>{appearance.font.weight.bold}</option>
|
||||
<option value='800'>{appearance.font.weight.extra_bold}</option>
|
||||
<option value='100'>{getMessage('modals.main.settings.sections.appearance.font.weight.thin')}</option>
|
||||
<option value='200'>{getMessage('modals.main.settings.sections.appearance.font.weight.extra_light')}</option>
|
||||
<option value='300'>{getMessage('modals.main.settings.sections.appearance.font.weight.light')}</option>
|
||||
<option value='400'>{getMessage('modals.main.settings.sections.appearance.font.weight.normal')}</option>
|
||||
<option value='500'>{getMessage('modals.main.settings.sections.appearance.font.weight.medium')}</option>
|
||||
<option value='600'>{getMessage('modals.main.settings.sections.appearance.font.weight.semi_bold')}</option>
|
||||
<option value='700'>{getMessage('modals.main.settings.sections.appearance.font.weight.bold')}</option>
|
||||
<option value='800'>{getMessage('modals.main.settings.sections.appearance.font.weight.extra_bold')}</option>
|
||||
</Dropdown>
|
||||
<br/><br/>
|
||||
<Dropdown label={appearance.font.style.title} name='fontstyle' category='other'>
|
||||
<option value='normal'>{appearance.font.style.normal}</option>
|
||||
<option value='italic'>{appearance.font.style.italic}</option>
|
||||
<option value='oblique'>{appearance.font.style.oblique}</option>
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.appearance.font.style.title')} name='fontstyle' category='other'>
|
||||
<option value='normal'>{getMessage('modals.main.settings.sections.appearance.font.style.normal')}</option>
|
||||
<option value='italic'>{getMessage('modals.main.settings.sections.appearance.font.style.italic')}</option>
|
||||
<option value='oblique'>{getMessage('modals.main.settings.sections.appearance.font.style.oblique')}</option>
|
||||
</Dropdown>
|
||||
|
||||
<h3>{appearance.accessibility.title}</h3>
|
||||
{(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}
|
||||
<Slider title={appearance.accessibility.toast_duration} name='toastDisplayTime' default='2500' step='100' min='500' max='5000' toast={true} display={' ' + appearance.accessibility.milliseconds} />
|
||||
<h3>{getMessage('modals.main.settings.sections.appearance.accessibility.title')}</h3>
|
||||
<Checkbox text={getMessage('modals.main.settings.sections.appearance.accessibility.text_shadow')} name='textBorder' category='other'/>
|
||||
<Checkbox text={getMessage('modals.main.settings.sections.appearance.accessibility.animations')} name='animations' category='other'/>
|
||||
<Slider title={getMessage('modals.main.settings.sections.appearance.accessibility.toast_duration')} name='toastDisplayTime' default='2500' step='100' min='500' max='5000' marks={values('toast')} toast={true}
|
||||
display={' ' + getMessage('modals.main.settings.sections.appearance.accessibility.milliseconds')} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent, createRef } from 'react';
|
||||
import { WifiOff } from '@mui/icons-material';
|
||||
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 = {
|
||||
@@ -14,19 +13,20 @@ export default class Changelog extends React.PureComponent {
|
||||
showLightbox: false,
|
||||
lightboxImg: null
|
||||
};
|
||||
this.language = window.language.modals.update;
|
||||
this.offlineMode = (localStorage.getItem('offlineMode') === 'true');
|
||||
this.controller = new AbortController();
|
||||
this.changelog = createRef();
|
||||
}
|
||||
|
||||
async getUpdate() {
|
||||
const data = await (await fetch(window.constants.BLOG_POST + '/index.json', { signal: this.controller.signal })).json();
|
||||
const data = await (await fetch(variables.constants.BLOG_POST + '/index.json', { signal: this.controller.signal })).json();
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
let date = new Date(data.date.split(' ')[0]);
|
||||
date = date.toLocaleDateString(window.languagecode.replace('_', '-'), {
|
||||
date = date.toLocaleDateString(variables.languagecode.replace('_', '-'), {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
@@ -34,16 +34,17 @@ export default class Changelog extends React.PureComponent {
|
||||
|
||||
this.setState({
|
||||
title: data.title,
|
||||
date: date,
|
||||
date,
|
||||
image: data.featured_image || null,
|
||||
author: 'By ' + data.authors.join(', '),
|
||||
author: variables.language.getMessage(variables.languagecode, 'modals.main.settings.sections.changelog.by', {
|
||||
author: data.authors.join(', ')
|
||||
}),
|
||||
html: data.html
|
||||
});
|
||||
|
||||
// lightbox etc
|
||||
const content = document.querySelector('.tab-content');
|
||||
const images = content.getElementsByTagName('img');
|
||||
const links = content.getElementsByTagName('a');
|
||||
const images = this.changelog.current.getElementsByTagName('img');
|
||||
const links = this.changelog.current.getElementsByTagName('a');
|
||||
|
||||
for (const img of images) {
|
||||
img.draggable = false;
|
||||
@@ -55,6 +56,7 @@ export default class Changelog extends React.PureComponent {
|
||||
};
|
||||
}
|
||||
|
||||
// open in new tab
|
||||
for (let link = 0; link < links.length; link++) {
|
||||
links[link].target = '_blank';
|
||||
links[link].rel = 'noopener noreferrer';
|
||||
@@ -62,7 +64,7 @@ export default class Changelog extends React.PureComponent {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
if (navigator.onLine === false || this.offlineMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,6 +77,8 @@ export default class Changelog extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
const errorMessage = (msg) => {
|
||||
return (
|
||||
<div className='emptyitems'>
|
||||
@@ -85,25 +89,23 @@ export default class Changelog extends React.PureComponent {
|
||||
);
|
||||
};
|
||||
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
const language = window.language.modals.main.marketplace;
|
||||
|
||||
if (navigator.onLine === false || this.offlineMode) {
|
||||
return errorMessage(<>
|
||||
<WifiOffIcon/>
|
||||
<h1>{language.offline.title}</h1>
|
||||
<p className='description'>{language.offline.description}</p>
|
||||
<WifiOff/>
|
||||
<h1>{getMessage('modals.main.marketplace.offline.title')}</h1>
|
||||
<p className='description'>{getMessage('modals.main.marketplace.offline.description')}</p>
|
||||
</>);
|
||||
}
|
||||
|
||||
if (!this.state.title) {
|
||||
return errorMessage(<h1>{window.language.modals.main.loading}</h1>);
|
||||
return errorMessage(<h1>{getMessage('modals.main.loading')}</h1>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='changelogtab'>
|
||||
<h1 style={{ marginBottom: '-10px' }}>{this.state.title}</h1>
|
||||
<h5 style={{ lineHeight: '0px' }}>{this.state.author} • {this.state.date}</h5>
|
||||
{this.state.image ? <img draggable='false' src={this.state.image} alt={window.language.modals.update.title} className='updateimage'/> : null}
|
||||
<div className='changelogtab' ref={this.changelog}>
|
||||
<h1>{this.state.title}</h1>
|
||||
<h5>{this.state.author} • {this.state.date}</h5>
|
||||
{this.state.image ? <img draggable='false' src={this.state.image} alt={this.state.title} className='updateimage'/> : null}
|
||||
<div className='updatechangelog' dangerouslySetInnerHTML={{ __html: this.state.html }}/>
|
||||
<Modal closeTimeoutMS={100} onRequestClose={() => this.setState({ showLightbox: false })} isOpen={this.state.showLightbox} className='Modal lightboxmodal' overlayClassName='Overlay resetoverlay' ariaHideApp={false}>
|
||||
<Lightbox modalClose={() => this.setState({ showLightbox: false })} img={this.state.lightboxImg}/>
|
||||
|
||||
66
src/components/modals/main/settings/sections/Date.jsx
Normal file
66
src/components/modals/main/settings/sections/Date.jsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Header from '../Header';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Dropdown from '../Dropdown';
|
||||
|
||||
export default class DateSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
dateType: localStorage.getItem('dateType') || 'long'
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
let dateSettings;
|
||||
|
||||
const longSettings = (
|
||||
<>
|
||||
<Checkbox name='dayofweek' text={getMessage('modals.main.settings.sections.date.day_of_week')} category='date' />
|
||||
<Checkbox name='datenth' text={getMessage('modals.main.settings.sections.date.datenth')} category='date' />
|
||||
</>
|
||||
);
|
||||
|
||||
const shortSettings = (
|
||||
<>
|
||||
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.date.short_format')} name='dateFormat' category='date'>
|
||||
<option value='DMY'>DMY</option>
|
||||
<option value='MDY'>MDY</option>
|
||||
<option value='YMD'>YMD</option>
|
||||
</Dropdown>
|
||||
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.date.short_separator.title')} name='shortFormat' category='date'>
|
||||
<option value='dash'>{getMessage('modals.main.settings.sections.date.short_separator.dash')}</option>
|
||||
<option value='dots'>{getMessage('modals.main.settings.sections.date.short_separator.dots')}</option>
|
||||
<option value='gaps'>{getMessage('modals.main.settings.sections.date.short_separator.gaps')}</option>
|
||||
<option value='slashes'>{getMessage('modals.main.settings.sections.date.short_separator.slashes')}</option>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
|
||||
switch (this.state.dateType) {
|
||||
case 'short': dateSettings = shortSettings; break;
|
||||
case 'long': dateSettings = longSettings; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header title={getMessage('modals.main.settings.sections.date.title')} setting='date' category='date' element='.date' zoomSetting='zoomDate'/>
|
||||
<Checkbox name='weeknumber' text={getMessage('modals.main.settings.sections.date.week_number')} category='date'/>
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.time.type')} name='dateType' onChange={(value) => this.setState({ dateType: value })} category='date'>
|
||||
<option value='long'>{getMessage('modals.main.settings.sections.date.type.long')}</option>
|
||||
<option value='short'>{getMessage('modals.main.settings.sections.date.type.short')}</option>
|
||||
</Dropdown>
|
||||
|
||||
<Checkbox name='datezero' text={getMessage('modals.main.settings.sections.time.digital.zero')} category='date'/>
|
||||
{dateSettings}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,29 @@
|
||||
import variables from 'modules/variables';
|
||||
import { useState } from 'react';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Slider from '../Slider';
|
||||
import EventBus from '../../../../../modules/helpers/eventbus';
|
||||
import { TextField } from '@mui/material';
|
||||
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
import { values } from 'modules/helpers/settings/modals';
|
||||
|
||||
export default function ExperimentalSettings() {
|
||||
const { experimental } = window.language.modals.main.settings.sections;
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
const [eventType, setEventType] = useState();
|
||||
const [eventName, setEventName] = useState();
|
||||
|
||||
return (
|
||||
<>
|
||||
<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>
|
||||
<h2>{getMessage('modals.main.settings.sections.experimental.title')}</h2>
|
||||
<p>{getMessage('modals.main.settings.sections.experimental.warning')}</p>
|
||||
<h3>{getMessage('modals.main.settings.sections.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' />
|
||||
<br/>
|
||||
<Slider title='Debug timeout' name='debugtimeout' min='0' max='5000' default='0' step='100' marks={values('experimental')} element='.other' />
|
||||
<p>Send Event</p>
|
||||
Type <input type='text' id='eventType'/>
|
||||
<br/><br/>
|
||||
Name <input type='text' id='eventName'/>
|
||||
<br/><br/>
|
||||
<button className='uploadbg' onClick={() => EventBus.dispatch(document.getElementById('eventType').value, document.getElementById('eventName').value)}>Send</button>
|
||||
<TextField label={'Type'} value={eventType} onChange={(e) => setEventType(e.target.value)} spellCheck={false} varient='outlined' InputLabelProps={{ shrink: true }} />
|
||||
<TextField label={'Name'} value={eventName} onChange={(e) => setEventName(e.target.value)} spellCheck={false} varient='outlined' InputLabelProps={{ shrink: true }} />
|
||||
<br/>
|
||||
<button className='uploadbg' onClick={() => EventBus.dispatch(eventType, eventName)}>Send</button>
|
||||
<br/><br/>
|
||||
<button className='reset' style={{ marginLeft: '0px' }} onClick={() => localStorage.clear()}>Clear LocalStorage</button>
|
||||
</>
|
||||
|
||||
@@ -1,44 +1,43 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Header from '../Header';
|
||||
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 = {
|
||||
birthday: new Date(localStorage.getItem('birthday')) || new Date()
|
||||
};
|
||||
this.language = window.language.modals.main.settings;
|
||||
}
|
||||
|
||||
changeDate = (e) => {
|
||||
localStorage.setItem('birthday', e.target.value);
|
||||
localStorage.setItem('birthday', e.target.value || new Date());
|
||||
|
||||
this.setState({
|
||||
birthday: new Date(e.target.value)
|
||||
birthday: e.target.value ? new Date(e.target.value) : new Date()
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { greeting } = this.language.sections;
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
return (
|
||||
<>
|
||||
<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' />
|
||||
<Header title={getMessage('modals.main.settings.sections.greeting.title')} setting='greeting' category='greeting' element='.greeting' zoomSetting='zoomGreeting'/>
|
||||
<Checkbox name='events' text={getMessage('modals.main.settings.sections.greeting.events')} category='greeting'/>
|
||||
<Checkbox name='defaultGreetingMessage' text={getMessage('modals.main.settings.sections.greeting.default')} category='greeting'/>
|
||||
<Text title={getMessage('modals.main.settings.sections.greeting.name')} name='greetingName' 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'/>
|
||||
<p>{greeting.birthday_date}</p>
|
||||
<input type='date' onChange={this.changeDate} value={this.state.birthday.toISOString().substr(0, 10)}/>
|
||||
<h3>{getMessage('modals.main.settings.sections.greeting.birthday')}</h3>
|
||||
<Switch name='birthdayenabled' text={getMessage('modals.main.settings.enabled')} category='greeting'/>
|
||||
<br/>
|
||||
<Checkbox name='birthdayage' text={getMessage('modals.main.settings.sections.greeting.birthday_age')} category='greeting'/>
|
||||
<p>{getMessage('modals.main.settings.sections.greeting.birthday_date')}</p>
|
||||
<input type='date' onChange={this.changeDate} value={this.state.birthday.toISOString().substr(0, 10)} required/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
148
src/components/modals/main/settings/sections/Keybinds.jsx
Normal file
148
src/components/modals/main/settings/sections/Keybinds.jsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Header from '../Header';
|
||||
import KeybindInput from '../KeybindInput';
|
||||
|
||||
export default class KeybindSettings extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
keybinds: JSON.parse(localStorage.getItem('keybinds')) || {},
|
||||
cancelled: false
|
||||
};
|
||||
}
|
||||
|
||||
showReminder() {
|
||||
document.querySelector('.reminder-info').style.display = 'none';
|
||||
return localStorage.setItem('showReminder', false);
|
||||
}
|
||||
|
||||
listen(type) {
|
||||
const currentKeybinds = this.state.keybinds;
|
||||
currentKeybinds[type] = this.getMessage('modals.main.settings.sections.keybinds.recording');
|
||||
this.setState({
|
||||
keybinds: currentKeybinds,
|
||||
cancelled: false
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
let keys = '';
|
||||
let previouskey = '';
|
||||
this.keydown = document.addEventListener('keydown', (event) => {
|
||||
if (event.key === previouskey && this.state.cancelled === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (keys === '') {
|
||||
keys = event.key;
|
||||
} else {
|
||||
keys = `${keys}+${event.key}`;
|
||||
}
|
||||
|
||||
previouskey = event.key
|
||||
});
|
||||
|
||||
this.keyup = document.addEventListener('keyup', () => {
|
||||
if (this.state.cancelled === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.removeEventListener('keydown', this.keydown);
|
||||
const keybinds = this.state.keybinds;
|
||||
keybinds[type] = keys.split('+').slice(0, 4).join('+');
|
||||
localStorage.setItem('keybinds', JSON.stringify(keybinds));
|
||||
this.setState({
|
||||
keybinds: JSON.parse(localStorage.getItem('keybinds')) || {}
|
||||
});
|
||||
});
|
||||
|
||||
document.removeEventListener('keyup', this.keyup);
|
||||
|
||||
this.showReminder();
|
||||
}
|
||||
|
||||
cancel(type) {
|
||||
document.removeEventListener('keydown', this.keydown);
|
||||
document.removeEventListener('keyup', this.keyup);
|
||||
|
||||
const currentKeybinds = this.state.keybinds;
|
||||
delete currentKeybinds[type];
|
||||
|
||||
this.setState({
|
||||
keybinds: currentKeybinds,
|
||||
cancelled: true
|
||||
});
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
reset(type) {
|
||||
const keybinds = this.state.keybinds;
|
||||
keybinds[type] = '';
|
||||
localStorage.setItem('keybinds', JSON.stringify(keybinds));
|
||||
|
||||
this.setState({
|
||||
keybinds: JSON.parse(localStorage.getItem('keybinds')) || {},
|
||||
cancelled: true
|
||||
});
|
||||
|
||||
this.showReminder();
|
||||
}
|
||||
|
||||
action(action, e) {
|
||||
switch (action) {
|
||||
case 'listen':
|
||||
this.listen(e);
|
||||
break;
|
||||
case 'cancel':
|
||||
this.cancel(e);
|
||||
break;
|
||||
case 'reset':
|
||||
this.reset(e);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Header title={this.getMessage('modals.main.settings.sections.keybinds.title')} setting='keybindsEnabled' element='.other' />
|
||||
<table className='keybind-table'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.background.favourite')} state={this.state.keybinds} setting='favouriteBackground' action={(type, e) => this.action(type, e)}/></th>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.background.maximise')} state={this.state.keybinds} setting='maximiseBackground' action={(type, e) => this.action(type, e)}/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.background.download')} state={this.state.keybinds} setting='downloadBackground' action={(type, e) => this.action(type, e)}/></th>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.background.show_info')} state={this.state.keybinds} setting='showBackgroundInformation' action={(type, e) => this.action(type, e)}/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.background.show_info')} state={this.state.keybinds} setting='showBackgroundInformation' action={(type, e) => this.action(type, e)}/></th>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.quote.favourite')} state={this.state.keybinds} setting='favouriteQuote' action={(type, e) => this.action(type, e)}/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.quote.copy')} state={this.state.keybinds} setting='copyQuote' action={(type, e) => this.action(type, e)}/></th>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.quote.tweet')} state={this.state.keybinds} setting='tweetQuote' action={(type, e) => this.action(type, e)}/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.notes.pin')} state={this.state.keybinds} setting='pinNotes' action={(type, e) => this.action(type, e)}/></th>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.notes.copy')} state={this.state.keybinds} setting='copyNotes' action={(type, e) => this.action(type, e)}/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.search')} state={this.state.keybinds} setting='focusSearch' action={(type, e) => this.action(type, e)}/></th>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.quicklinks')} state={this.state.keybinds} setting='toggleQuicklinks' action={(type, e) => this.action(type, e)}/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><KeybindInput name={this.getMessage('modals.main.settings.sections.keybinds.modal')} state={this.state.keybinds} setting='toggleModal' action={(type, e) => this.action(type, e)}/></th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Radio from '../Radio';
|
||||
|
||||
const languages = require('../../../../../modules/languages.json');
|
||||
const languages = require('modules/languages.json');
|
||||
|
||||
export default class LanguageSettings extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
export default class BackgroundSettings extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
quoteLanguages: [{
|
||||
name: window.language.modals.main.loading,
|
||||
name: this.getMessage('modals.main.loading'),
|
||||
value: 'loading'
|
||||
}]
|
||||
};
|
||||
@@ -17,22 +20,22 @@ export default class BackgroundSettings extends React.PureComponent {
|
||||
}
|
||||
|
||||
async getQuoteLanguages() {
|
||||
const data = await (await fetch(window.constants.API_URL + '/quotes/languages', { signal: this.controller.signal })).json();
|
||||
const data = await (await fetch(variables.constants.API_URL + '/quotes/languages', { signal: this.controller.signal })).json();
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
let array = [];
|
||||
const quoteLanguages = [];
|
||||
data.forEach((item) => {
|
||||
array.push({
|
||||
quoteLanguages.push({
|
||||
name: item,
|
||||
value: item
|
||||
});
|
||||
});
|
||||
|
||||
this.setState({
|
||||
quoteLanguages: array
|
||||
quoteLanguages
|
||||
});
|
||||
}
|
||||
|
||||
@@ -40,7 +43,7 @@ export default class BackgroundSettings extends React.PureComponent {
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return this.setState({
|
||||
quoteLanguages: [{
|
||||
name: window.language.modals.main.marketplace.offline.description,
|
||||
name: this.getMessage('modals.main.marketplace.offline.description'),
|
||||
value: 'loading'
|
||||
}]
|
||||
});
|
||||
@@ -55,13 +58,11 @@ export default class BackgroundSettings extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const language = window.language.modals.main.settings.sections.language;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{language.title}</h2>
|
||||
<Radio name='language' options={languages} element='.language' />
|
||||
<h3>{language.quote}</h3>
|
||||
<h2>{this.getMessage('modals.main.settings.sections.language.title')}</h2>
|
||||
<Radio name='language' options={languages} element='.other' />
|
||||
<h3>{this.getMessage('modals.main.settings.sections.language.quote')}</h3>
|
||||
<Radio name='quotelanguage' options={this.state.quoteLanguages} category='quote' />
|
||||
</>
|
||||
);
|
||||
|
||||
81
src/components/modals/main/settings/sections/Message.jsx
Normal file
81
src/components/modals/main/settings/sections/Message.jsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { Cancel, Add } from '@mui/icons-material';
|
||||
import { toast } from 'react-toastify';
|
||||
import { TextField } from '@mui/material';
|
||||
|
||||
import Header from '../Header';
|
||||
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
|
||||
export default class Message extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
messages: JSON.parse(localStorage.getItem('messages')) || [''],
|
||||
};
|
||||
}
|
||||
|
||||
reset = () => {
|
||||
localStorage.setItem('messages', '[""]');
|
||||
this.setState({
|
||||
messages: ['']
|
||||
});
|
||||
toast(this.getMessage(this.languagecode, 'toasts.reset'));
|
||||
EventBus.dispatch('refresh', 'message');
|
||||
}
|
||||
|
||||
modifyMessage(type, index) {
|
||||
const messages = this.state.messages;
|
||||
if (type === 'add') {
|
||||
messages.push('');
|
||||
} else {
|
||||
messages.splice(index, 1);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
messages
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
localStorage.setItem('messages', JSON.stringify(messages));
|
||||
}
|
||||
|
||||
message(e, text, index) {
|
||||
const result = (text === true) ? e.target.value : e.target.result;
|
||||
|
||||
const messages = this.state.messages;
|
||||
messages[index] = result;
|
||||
this.setState({
|
||||
messages
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
localStorage.setItem('messages', JSON.stringify(messages));
|
||||
document.querySelector('.reminder-info').style.display = 'block';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Header title={this.getMessage('modals.main.settings.sections.message.title')} setting='message' category='message' element='.message' zoomSetting='zoomMessage'/>
|
||||
<p>{this.getMessage('modals.main.settings.sections.message.text')}</p>
|
||||
<div className='data-buttons-row'>
|
||||
<button onClick={() => this.modifyMessage('add')}>{this.getMessage('modals.main.settings.sections.message.add')} <Add/></button>
|
||||
</div>
|
||||
{this.state.messages.map((_url, index) => (
|
||||
<div style={{ display: 'flex' }} key={index}>
|
||||
<TextField value={this.state.messages[index]} onChange={(e) => this.message(e, true, index)} varient='outlined' />
|
||||
{this.state.messages.length > 1 ? <button className='cleanButton' onClick={() => this.modifyMessage('remove', index)}>
|
||||
<Cancel/>
|
||||
</button> : null}
|
||||
</div>
|
||||
))}
|
||||
<br/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
34
src/components/modals/main/settings/sections/Navbar.jsx
Normal file
34
src/components/modals/main/settings/sections/Navbar.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import variables from 'modules/variables';
|
||||
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Checkbox from '../Checkbox';
|
||||
import Dropdown from '../Dropdown';
|
||||
import Slider from '../Slider';
|
||||
|
||||
import { values } from 'modules/helpers/settings/modals';
|
||||
|
||||
export default class Navbar extends PureComponent {
|
||||
render() {
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{getMessage('modals.main.settings.sections.appearance.navbar.title')}</h2>
|
||||
<Slider title={getMessage('modals.main.settings.sections.appearance.accessibility.widget_zoom')} name='zoomNavbar' min='10' max='400' default='100' display='%' marks={values('zoom')} category='navbar' />
|
||||
<Checkbox name='navbarHover' text={getMessage('modals.main.settings.sections.appearance.navbar.hover')} category='navbar'/>
|
||||
<Checkbox name='notesEnabled' text={getMessage('modals.main.settings.sections.appearance.navbar.notes')} category='navbar' />
|
||||
<Checkbox name='view' text={getMessage('modals.main.settings.sections.background.buttons.view')} category='navbar' />
|
||||
<Checkbox name='favouriteEnabled' text={getMessage('modals.main.settings.sections.background.buttons.favourite')} category='navbar' />
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.appearance.navbar.refresh')} name='refresh' category='navbar'>
|
||||
<option value='false'>{getMessage('modals.main.settings.sections.appearance.navbar.refresh_options.none')}</option>
|
||||
<option value='background'>{getMessage('modals.main.settings.sections.background.title')}</option>
|
||||
<option value='quote'>{getMessage('modals.main.settings.sections.quote.title')}</option>
|
||||
<option value='quotebackground'>{getMessage('modals.main.settings.sections.quote.title')} + {getMessage('modals.main.settings.sections.background.title')}</option>
|
||||
{/* before it was just a checkbox */}
|
||||
<option value='true'>{getMessage('modals.main.settings.sections.appearance.navbar.refresh_options.page')}</option>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,66 +1,46 @@
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../../../modules/helpers/eventbus';
|
||||
|
||||
import DragHandleIcon from '@material-ui/icons/DragIndicator';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { DragIndicator } from '@mui/icons-material';
|
||||
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 getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
const widget_name = {
|
||||
greeting: settings.greeting.title,
|
||||
time: settings.time.title,
|
||||
quicklinks: settings.quicklinks.title,
|
||||
quote: settings.quote.title,
|
||||
date: settings.time.date.title
|
||||
greeting: getMessage('modals.main.settings.sections.greeting.title'),
|
||||
time: getMessage('modals.main.settings.sections.time.title'),
|
||||
quicklinks: getMessage('modals.main.settings.sections.quicklinks.title'),
|
||||
quote: getMessage('modals.main.settings.sections.quote.title'),
|
||||
date: getMessage('modals.main.settings.sections.date.title'),
|
||||
message: getMessage('modals.main.settings.sections.message.title')
|
||||
};
|
||||
|
||||
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>
|
||||
));
|
||||
|
||||
const SortableContainer = sortableContainer(({ children }) => {
|
||||
return <ul className='sortablecontainer'>{children}</ul>;
|
||||
});
|
||||
const SortableContainer = sortableContainer(({ children }) => (
|
||||
<ul className='sortablecontainer'>{children}</ul>
|
||||
));
|
||||
|
||||
export default class OrderSettings extends React.PureComponent {
|
||||
export default class OrderSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
items: JSON.parse(localStorage.getItem('order'))
|
||||
};
|
||||
this.language = window.language.modals.main.settings;
|
||||
}
|
||||
|
||||
// based on https://stackoverflow.com/a/48301905
|
||||
arrayMove(array, oldIndex, newIndex) {
|
||||
if (oldIndex === newIndex) {
|
||||
return array;
|
||||
}
|
||||
const result = Array.from(array);
|
||||
const [removed] = result.splice(oldIndex, 1);
|
||||
result.splice(newIndex, 0, removed);
|
||||
|
||||
const newArray = [...array];
|
||||
const target = newArray[oldIndex];
|
||||
const inc = newIndex < oldIndex ? -1 : 1;
|
||||
|
||||
for (let i = oldIndex; i !== newIndex; i += inc) {
|
||||
newArray[i] = newArray[i + inc];
|
||||
}
|
||||
|
||||
newArray[newIndex] = target;
|
||||
return newArray;
|
||||
return result;
|
||||
}
|
||||
|
||||
onSortEnd = ({ oldIndex, newIndex }) => {
|
||||
@@ -70,30 +50,45 @@ export default class OrderSettings extends React.PureComponent {
|
||||
}
|
||||
|
||||
reset = () => {
|
||||
localStorage.setItem('order', JSON.stringify(['greeting', 'time', 'quicklinks', 'quote', 'date']));
|
||||
localStorage.setItem('order', JSON.stringify(['greeting', 'time', 'quicklinks', 'quote', 'date', 'message']));
|
||||
|
||||
this.setState({
|
||||
items: JSON.parse(localStorage.getItem('order'))
|
||||
});
|
||||
|
||||
toast(window.language.toasts.reset);
|
||||
toast(getMessage('toats.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');
|
||||
variables.stats.postEvent('setting', 'Widget order');
|
||||
EventBus.dispatch('refresh', 'widgets');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<h2>{this.language.sections.order.title}</h2>
|
||||
<span className='modalLink' onClick={this.reset}>{this.language.buttons.reset}</span>
|
||||
<h2>{getMessage('modals.main.settings.sections.order.title')}</h2>
|
||||
<span className='modalLink' onClick={this.reset}>{getMessage('modals.main.settings.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>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import Switch from '../Switch';
|
||||
import variables from 'modules/variables';
|
||||
import { useState } from 'react';
|
||||
|
||||
import Header from '../Header';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Slider from '../Slider';
|
||||
|
||||
export default function QuickLinks() {
|
||||
const language = window.language.modals.main.settings.sections.quicklinks;
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
const [textOnly, setTextOnly] = useState(localStorage.getItem('quicklinksText') === 'true');
|
||||
|
||||
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' />
|
||||
<Header title={getMessage('modals.main.settings.sections.quicklinks.title')} setting='quicklinksenabled' category='quicklinks' element='.quicklinks-container' zoomSetting='zoomQuicklinks'/>
|
||||
<Checkbox name='quicklinksText' text={getMessage('modals.main.settings.sections.quicklinks.text_only')} category='quicklinks' onChange={(value) => setTextOnly(value)}/>
|
||||
<Checkbox name='quicklinksddgProxy' text={getMessage('modals.main.settings.sections.background.ddg_image_proxy')} category='quicklinks' disabled={textOnly}/>
|
||||
<Checkbox name='quicklinksnewtab' text={getMessage('modals.main.settings.sections.quicklinks.open_new')} category='quicklinks'/>
|
||||
<Checkbox name='quicklinkstooltip' text={getMessage('modals.main.settings.sections.quicklinks.tooltip')} category='quicklinks' disabled={textOnly}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,49 +1,118 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { Cancel, Add } from '@mui/icons-material';
|
||||
import { TextField } from '@mui/material';
|
||||
|
||||
import Header from '../Header';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Text from '../Text';
|
||||
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 {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
quoteType: localStorage.getItem('quoteType') || 'api',
|
||||
customQuote: this.getCustom()
|
||||
};
|
||||
}
|
||||
|
||||
marketplaceType = () => {
|
||||
if (localStorage.getItem('quote_packs')) {
|
||||
return <option value='quote_pack'>{window.language.modals.main.navbar.marketplace}</option>;
|
||||
return <option value='quote_pack'>{this.getMessage('modals.main.navbar.marketplace')}</option>;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { quote, background } = window.language.modals.main.settings.sections;
|
||||
resetCustom = () => {
|
||||
localStorage.setItem('customQuote', '[{"quote": "", "author": ""}]');
|
||||
this.setState({
|
||||
customQuote: [{
|
||||
quote: '',
|
||||
author: ''
|
||||
}]
|
||||
});
|
||||
toast(this.getMessage('toasts.reset'));
|
||||
EventBus.dispatch('refresh', 'background');
|
||||
}
|
||||
|
||||
customQuote(e, text, index, type) {
|
||||
const result = (text === true) ? e.target.value : e.target.result;
|
||||
|
||||
const customQuote = this.state.customQuote;
|
||||
customQuote[index][type] = result;
|
||||
this.setState({
|
||||
customQuote
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
localStorage.setItem('customQuote', JSON.stringify(customQuote));
|
||||
document.querySelector('.reminder-info').style.display = 'block';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
|
||||
modifyCustomQuote(type, index) {
|
||||
const customQuote = this.state.customQuote;
|
||||
if (type === 'add') {
|
||||
customQuote.push({
|
||||
quote: '',
|
||||
author: ''
|
||||
});
|
||||
} else {
|
||||
customQuote.splice(index, 1);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
customQuote
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
localStorage.setItem('customQuote', JSON.stringify(customQuote));
|
||||
}
|
||||
|
||||
getCustom() {
|
||||
let data = JSON.parse(localStorage.getItem('customQuote'));
|
||||
if (data === null) {
|
||||
data = [{
|
||||
quote: localStorage.getItem('customQuote') || '',
|
||||
author: localStorage.getItem('customQuoteAuthor') || ''
|
||||
}];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
render() {
|
||||
let customSettings;
|
||||
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'/>
|
||||
<p>{this.getMessage('modals.main.settings.sections.quote.custom')} <span className='modalLink' onClick={this.resetCustom}>{this.getMessage('modals.main.settings.buttons.reset')}</span></p>
|
||||
<div className='data-buttons-row'>
|
||||
<button onClick={() => this.modifyCustomQuote('add')}>{this.getMessage('modals.main.settings.sections.quote.add')} <Add/></button>
|
||||
</div>
|
||||
{this.state.customQuote.map((_url, index) => (
|
||||
<div style={{ display: 'flex' }} key={index}>
|
||||
<TextField value={this.state.customQuote[index].quote} placeholder='Quote' onChange={(e) => this.customQuote(e, true, index, 'quote')} varient='outlined' style={{ marginRight: '10px' }} />
|
||||
<TextField value={this.state.customQuote[index].author} placeholder='Author' onChange={(e) => this.customQuote(e, true, index, 'author')} varient='outlined' />
|
||||
{this.state.customQuote.length > 1 ? <button className='cleanButton' onClick={() => this.modifyCustomQuote('remove', index)} style={{ marginBottom: '-14px' }}>
|
||||
<Cancel/>
|
||||
</button> : null}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
// api
|
||||
customSettings = (
|
||||
<>
|
||||
<br/><br/>
|
||||
<Dropdown label={background.interval.title} name='quotechange'>
|
||||
<option value='refresh'>{window.language.tabname}</option>
|
||||
<option value='60000'>{background.interval.minute}</option>
|
||||
<option value='1800000'>{background.interval.half_hour}</option>
|
||||
<option value='3600000'>{background.interval.hour}</option>
|
||||
<option value='86400000'>{background.interval.day}</option>
|
||||
<option value='604800000'>{window.language.widgets.date.week}</option>
|
||||
<option value='2628000000'>{background.interval.month}</option>
|
||||
<Dropdown label={this.getMessage('modals.main.settings.sections.background.interval.title')} name='quotechange'>
|
||||
<option value='refresh'>{this.getMessage('tabname')}</option>
|
||||
<option value='60000'>{this.getMessage('modals.main.settings.sections.background.interval.minute')}</option>
|
||||
<option value='1800000'>{this.getMessage('modals.main.settings.sections.background.interval.half_hour')}</option>
|
||||
<option value='3600000'>{this.getMessage('modals.main.settings.sections.background.interval.hour')}</option>
|
||||
<option value='86400000'>{this.getMessage('modals.main.settings.sections.background.interval.day')}</option>
|
||||
<option value='604800000'>{this.getMessage('widgets.date.week')}</option>
|
||||
<option value='2628000000'>{this.getMessage('modals.main.settings.sections.background.interval.month')}</option>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
@@ -51,22 +120,20 @@ export default class QuoteSettings extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{quote.title}</h2>
|
||||
<Switch name='quote' text={window.language.modals.main.settings.enabled} category='quote' element='.quotediv' />
|
||||
<Checkbox name='authorLink' text={quote.author_link} element='.other' />
|
||||
<Dropdown label={window.language.modals.main.settings.sections.background.type.title} name='quoteType' onChange={(value) => this.setState({ quoteType: value })} category='quote'>
|
||||
<Header title={this.getMessage('modals.main.settings.sections.quote.title')} setting='quote' category='quote' element='.quotediv' zoomSetting='zoomQuote'/>
|
||||
<Checkbox name='authorLink' text={this.getMessage('modals.main.settings.sections.quote.author_link')} element='.other' />
|
||||
<Dropdown label={this.getMessage('modals.main.settings.sections.background.type.title')} name='quoteType' onChange={(value) => this.setState({ quoteType: value })} category='quote'>
|
||||
{this.marketplaceType()}
|
||||
<option value='api'>{window.language.modals.main.settings.sections.background.type.api}</option>
|
||||
<option value='custom'>{quote.custom}</option>
|
||||
<option value='api'>{this.getMessage('modals.main.settings.sections.background.type.api')}</option>
|
||||
<option value='custom'>{this.getMessage('modals.main.settings.sections.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' />
|
||||
|
||||
<h3>{quote.buttons.title}</h3>
|
||||
<Checkbox name='copyButton' text={quote.buttons.copy} category='quote'/>
|
||||
<Checkbox name='tweetButton' text={quote.buttons.tweet} category='quote'/>
|
||||
<Checkbox name='favouriteQuoteEnabled' text={quote.buttons.favourite} category='quote'/>
|
||||
<h3>{this.getMessage('modals.main.settings.sections.quote.buttons.title')}</h3>
|
||||
<Checkbox name='copyButton' text={this.getMessage('modals.main.settings.sections.quote.buttons.copy')} category='quote'/>
|
||||
<Checkbox name='tweetButton' text={this.getMessage('modals.main.settings.sections.quote.buttons.tweet')} category='quote'/>
|
||||
<Checkbox name='favouriteQuoteEnabled' text={this.getMessage('modals.main.settings.sections.quote.buttons.favourite')} category='quote'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { MenuItem, TextField } from '@mui/material';
|
||||
|
||||
import Header from '../Header';
|
||||
import Dropdown from '../Dropdown';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Switch from '../Switch';
|
||||
import Radio from '../Radio';
|
||||
|
||||
import EventBus from '../../../../../modules/helpers/eventbus';
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
const searchEngines = require('components/widgets/search/search_engines.json');
|
||||
const autocompleteProviders = require('components/widgets/search/autocomplete_providers.json');
|
||||
|
||||
const searchEngines = require('../../../../widgets/search/search_engines.json');
|
||||
const autocompleteProviders = require('../../../../widgets/search/autocomplete_providers.json');
|
||||
export default class SearchSettings extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
export default class SearchSettings extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -28,7 +31,7 @@ export default class SearchSettings extends React.PureComponent {
|
||||
customValue: ''
|
||||
});
|
||||
|
||||
toast(window.language.toasts.reset);
|
||||
toast(this.getMessage('toasts.reset'));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -68,30 +71,26 @@ export default class SearchSettings extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const language = window.language.modals.main.settings;
|
||||
const { search } = language.sections;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{search.title}</h2>
|
||||
<Switch name='searchBar' text={language.enabled} category='widgets' />
|
||||
<Header title={this.getMessage('modals.main.settings.sections.search.title')} setting='searchBar' category='widgets'/>
|
||||
{/* not supported on firefox */}
|
||||
{(navigator.userAgent.includes('Chrome') && typeof InstallTrigger === 'undefined') ?
|
||||
<Checkbox name='voiceSearch' text={search.voice_search} category='search'/>
|
||||
<Checkbox name='voiceSearch' text={this.getMessage('modals.main.settings.sections.search.voice_search')} category='search'/>
|
||||
: null}
|
||||
<Dropdown label={search.search_engine} name='searchEngine' onChange={(value) => this.setSearchEngine(value)}>
|
||||
<Checkbox name='searchDropdown' text={this.getMessage('modals.main.settings.sections.search.dropdown')} category='search' element='.other'/>
|
||||
<Dropdown label={this.getMessage('modals.main.settings.sections.search.search_engine')} name='searchEngine' onChange={(value) => this.setSearchEngine(value)} manual={true}>
|
||||
{searchEngines.map((engine) => (
|
||||
<option key={engine.name} value={engine.settingsName}>{engine.name}</option>
|
||||
<MenuItem key={engine.name} value={engine.settingsName}>{engine.name}</MenuItem>
|
||||
))}
|
||||
<option value='custom'>{search.custom.split(' ')[0]}</option>
|
||||
<MenuItem value='custom'>{this.getMessage('modals.main.settings.sections.search.custom').split(' ')[0]}</MenuItem>
|
||||
</Dropdown>
|
||||
<ul style={{ display: this.state.customDisplay }}>
|
||||
<br/>
|
||||
<p style={{ marginTop: '0px' }}>{search.custom} <span className='modalLink' onClick={() => this.resetSearch()}>{language.buttons.reset}</span></p>
|
||||
<input type='text' value={this.state.customValue} onInput={(e) => this.setState({ customValue: e.target.value })}></input>
|
||||
<p style={{ marginTop: '0px' }}><span className='modalLink' onClick={() => this.resetSearch()}>{this.getMessage('modals.main.settings.buttons.reset')}</span></p>
|
||||
<TextField label={this.getMessage('modals.main.settings.sections.search.custom')} value={this.state.customValue} onInput={(e) => this.setState({ customValue: e.target.value })} varient='outlined' InputLabelProps={{ shrink: true }} />
|
||||
</ul>
|
||||
<br/>
|
||||
<Checkbox name='autocomplete' text={search.autocomplete} category='search' element='.other'/>
|
||||
<Radio title={search.autocomplete_provider} options={autocompleteProviders} name='autocompleteProvider' category='search'/>
|
||||
<Checkbox name='autocomplete' text={this.getMessage('modals.main.settings.sections.search.autocomplete')} category='search' />
|
||||
<Radio title={this.getMessage('modals.main.settings.sections.search.autocomplete_provider')} options={autocompleteProviders} name='autocompleteProvider' category='search'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
61
src/components/modals/main/settings/sections/Stats.jsx
Normal file
61
src/components/modals/main/settings/sections/Stats.jsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import variables from 'modules/variables';
|
||||
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')) || {}
|
||||
};
|
||||
}
|
||||
|
||||
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() {
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
if (localStorage.getItem('stats') === 'false') {
|
||||
return (
|
||||
<>
|
||||
<h2>{getMessage('modals.main.settings.reminder.title')}</h2>
|
||||
<p>{getMessage('modals.main.settings.sections.stats.warning')}</p>
|
||||
<Switch name='stats' text={getMessage('modals.main.settings.sections.stats.usage')} category='stats'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{getMessage('modals.main.settings.sections.stats.title')}</h2>
|
||||
<Switch name='stats' text={getMessage('modals.main.settings.sections.stats.usage')} category='stats'/>
|
||||
<p>{getMessage('modals.main.settings.sections.stats.sections.tabs_opened')}: {this.state.stats['tabs-opened'] || 0}</p>
|
||||
<p>{getMessage('modals.main.settings.sections.stats.sections.backgrounds_favourited')}: {this.state.stats.feature ? this.state.stats.feature['background-favourite'] || 0 : 0}</p>
|
||||
<p>{getMessage('modals.main.settings.sections.stats.sections.backgrounds_downloaded')}: {this.state.stats.feature ? this.state.stats.feature['background-download'] || 0 : 0}</p>
|
||||
<p>{getMessage('modals.main.settings.sections.stats.sections.quotes_favourited')}: {this.state.stats.feature ? this.state.stats.feature['quoted-favourite'] || 0 : 0}</p>
|
||||
<p>{getMessage('modals.main.settings.sections.stats.sections.quicklinks_added')}: {this.state.stats.feature ? this.state.stats.feature['quicklink-add'] || 0 : 0}</p>
|
||||
<p>{getMessage('modals.main.settings.sections.stats.sections.settings_changed')}: {this.state.stats.setting ? Object.keys(this.state.stats.setting).length : 0}</p>
|
||||
<p>{getMessage('modals.main.settings.sections.stats.sections.addons_installed')}: {this.state.stats.marketplace ? this.state.stats.marketplace['install'] : 0}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +1,52 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Header from '../Header';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Dropdown from '../Dropdown';
|
||||
import Switch from '../Switch';
|
||||
import Radio from '../Radio';
|
||||
import Slider from '../Slider';
|
||||
|
||||
export default class TimeSettings extends React.PureComponent {
|
||||
export default class TimeSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
timeType: localStorage.getItem('timeType') || 'digital',
|
||||
dateType: localStorage.getItem('dateType') || 'long'
|
||||
timeType: localStorage.getItem('timeType') || 'digital'
|
||||
};
|
||||
this.language = window.language.modals.main.settings;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { time } = this.language.sections;
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
let timeSettings;
|
||||
|
||||
const digitalOptions = [
|
||||
{
|
||||
name: time.digital.twentyfourhour,
|
||||
name: getMessage('modals.main.settings.sections.time.digital.twentyfourhour'),
|
||||
value: 'twentyfourhour'
|
||||
},
|
||||
{
|
||||
name: time.digital.twelvehour,
|
||||
name: getMessage('modals.main.settings.sections.time.digital.twelvehour'),
|
||||
value: 'twelvehour'
|
||||
}
|
||||
];
|
||||
|
||||
const digitalSettings = (
|
||||
<>
|
||||
<h3>{time.digital.title}</h3>
|
||||
<Radio title={time.format} name='timeformat' options={digitalOptions} smallTitle={true} category='clock' element='.clock-container' />
|
||||
<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' />
|
||||
<h3>{getMessage('modals.main.settings.sections.time.digital.title')}</h3>
|
||||
<Radio title={getMessage('modals.main.settings.sections.time.format')} name='timeformat' options={digitalOptions} smallTitle={true} category='clock' />
|
||||
<Checkbox name='seconds' text={getMessage('modals.main.settings.sections.time.digital.seconds')} category='clock' />
|
||||
<Checkbox name='zero' text={getMessage('modals.main.settings.sections.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' />
|
||||
<h3>{getMessage('modals.main.settings.sections.time.analogue.title')}</h3>
|
||||
<Checkbox name='secondHand' text={getMessage('modals.main.settings.sections.time.analogue.second_hand')} category='clock' />
|
||||
<Checkbox name='minuteHand' text={getMessage('modals.main.settings.sections.time.analogue.minute_hand')} category='clock' />
|
||||
<Checkbox name='hourHand' text={getMessage('modals.main.settings.sections.time.analogue.hour_hand')} category='clock' />
|
||||
<Checkbox name='hourMarks' text={getMessage('modals.main.settings.sections.time.analogue.hour_marks')} category='clock' />
|
||||
<Checkbox name='minuteMarks' text={getMessage('modals.main.settings.sections.time.analogue.minute_marks')} category='clock' />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -59,64 +56,15 @@ export default class TimeSettings extends React.PureComponent {
|
||||
default: timeSettings = null; break;
|
||||
}
|
||||
|
||||
let dateSettings;
|
||||
|
||||
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' />
|
||||
</>
|
||||
);
|
||||
|
||||
const shortSettings = (
|
||||
<>
|
||||
<br/>
|
||||
<Dropdown label={time.date.short_format} name='dateFormat' category='date' element='.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>
|
||||
<option value='dash'>{time.date.short_separator.dash}</option>
|
||||
<option value='gaps'>{time.date.short_separator.gaps}</option>
|
||||
<option value='slashes'>{time.date.short_separator.slashes}</option>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
|
||||
switch (this.state.dateType) {
|
||||
case 'short': dateSettings = shortSettings; break;
|
||||
case 'long': dateSettings = longSettings; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<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'>
|
||||
<option value='digital'>{time.digital.title}</option>
|
||||
<option value='analogue'>{time.analogue.title}</option>
|
||||
<option value='percentageComplete'>{time.percentage_complete}</option>
|
||||
<Header title={getMessage('modals.main.settings.sections.time.title')} setting='time' category='clock' element='.clock-container' zoomSetting='zoomClock'/>
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.time.type')} name='timeType' onChange={(value) => this.setState({ timeType: value })} category='clock'>
|
||||
<option value='digital'>{getMessage('modals.main.settings.sections.time.digital.title')}</option>
|
||||
<option value='analogue'>{getMessage('modals.main.settings.sections.time.analogue.title')}</option>
|
||||
<option value='percentageComplete'>{getMessage('modals.main.settings.sections.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' />
|
||||
: 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'>
|
||||
<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'/>
|
||||
{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' />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,41 +1,45 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import Switch from '../Switch';
|
||||
import Header from '../Header';
|
||||
import Radio from '../Radio';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Slider from '../Slider';
|
||||
import { TextField } from '@mui/material';
|
||||
|
||||
export default class TimeSettings extends React.PureComponent {
|
||||
export default class TimeSettings extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
location: localStorage.getItem('location') || 'London'
|
||||
location: localStorage.getItem('location') || '',
|
||||
windSpeed: (localStorage.getItem('windspeed') !== 'true')
|
||||
};
|
||||
this.language = window.language.modals.main.settings;
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
localStorage.setItem('location', this.state.location);
|
||||
}
|
||||
|
||||
showReminder() {
|
||||
document.querySelector('.reminder-info').style.display = 'block';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
|
||||
changeLocation(e) {
|
||||
this.setState({
|
||||
location: e.target.value
|
||||
});
|
||||
|
||||
document.querySelector('.reminder-info').style.display = 'block';
|
||||
localStorage.setItem('showReminder', true);
|
||||
this.showReminder();
|
||||
}
|
||||
|
||||
getAuto() {
|
||||
navigator.geolocation.getCurrentPosition(async (position) => {
|
||||
const data = await (await fetch(`${window.constants.PROXY_URL}/weather/autolocation?lat=${position.coords.latitude}&lon=${position.coords.longitude}`)).json();
|
||||
const data = await (await fetch(`${variables.constants.PROXY_URL}/weather/autolocation?lat=${position.coords.latitude}&lon=${position.coords.longitude}`)).json();
|
||||
this.setState({
|
||||
location: data[0].name
|
||||
});
|
||||
|
||||
document.querySelector('.reminder-info').style.display = 'block';
|
||||
localStorage.setItem('showReminder', true);
|
||||
this.showReminder();
|
||||
}, (error) => {
|
||||
// firefox requires this 2nd function
|
||||
console.log(error);
|
||||
@@ -45,46 +49,41 @@ export default class TimeSettings extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const language = window.language.modals.main.settings.sections.weather;
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
const tempFormat = [
|
||||
{
|
||||
name: language.temp_format.celsius + ' (°C)',
|
||||
name: getMessage('modals.main.settings.sections.weather.temp_format.celsius') + ' (°C)',
|
||||
value: 'celsius'
|
||||
},
|
||||
{
|
||||
name: language.temp_format.fahrenheit + ' (°F)',
|
||||
name: getMessage('modals.main.settings.sections.weather.temp_format.fahrenheit') + ' (°F)',
|
||||
value: 'fahrenheit'
|
||||
},
|
||||
{
|
||||
name: language.temp_format.kelvin + ' (K)',
|
||||
name: getMessage('modals.main.settings.sections.weather.temp_format.kelvin') + ' (K)',
|
||||
value: 'kelvin'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{language.title}</h2>
|
||||
<Switch name='weatherEnabled' text={this.language.enabled} category='widgets'/>
|
||||
<ul>
|
||||
<p>{language.location} <span className='modalLink' onClick={() => this.getAuto()}>{language.auto}</span></p>
|
||||
<input type='text' value={this.state.location} onChange={(e) => this.changeLocation(e)}></input>
|
||||
</ul>
|
||||
<br/>
|
||||
<Radio name='tempformat' title={language.temp_format.title} options={tempFormat} category='weather'/>
|
||||
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomWeather' min='10' max='400' default='100' display='%' category='weather'/>
|
||||
<Header title={getMessage('modals.main.settings.sections.weather.title')} setting='weatherEnabled' category='widgets' zoomSetting='zoomWeather' zoomCategory='weather'/>
|
||||
<TextField label={getMessage('modals.main.settings.sections.weather.location')} value={this.state.location} onChange={(e) => this.changeLocation(e)} placeholder='London' varient='outlined' InputLabelProps={{ shrink: true }} />
|
||||
<span className='modalLink' onClick={() => this.getAuto()}>{getMessage('modals.main.settings.sections.weather.auto')}</span>
|
||||
<Radio name='tempformat' title={getMessage('modals.main.settings.sections.weather.temp_format.title')} options={tempFormat} category='weather'/>
|
||||
|
||||
<h3>{language.extra_info.title}</h3>
|
||||
<Checkbox name='showlocation' text={language.extra_info.show_location} category='weather'/>
|
||||
<Checkbox name='weatherdescription' text={language.extra_info.show_description} category='weather'/>
|
||||
<Checkbox name='cloudiness' text={language.extra_info.cloudiness} category='weather'/>
|
||||
<Checkbox name='humidity' text={language.extra_info.humidity} category='weather'/>
|
||||
<Checkbox name='visibility' text={language.extra_info.visibility} category='weather'/>
|
||||
<Checkbox name='windspeed' text={language.extra_info.wind_speed} category='weather'/>
|
||||
<Checkbox name='windDirection' text={language.extra_info.wind_direction} category='weather'/>
|
||||
<Checkbox name='mintemp' text={language.extra_info.min_temp} category='weather'/>
|
||||
<Checkbox name='maxtemp' text={language.extra_info.max_temp} category='weather'/>
|
||||
<Checkbox name='atmosphericpressure' text={language.extra_info.atmospheric_pressure} category='weather'/>
|
||||
<h3>{getMessage('modals.main.settings.sections.weather.extra_info.title')}</h3>
|
||||
<Checkbox name='showlocation' text={getMessage('modals.main.settings.sections.weather.extra_info.show_location')} category='weather'/>
|
||||
<Checkbox name='weatherdescription' text={getMessage('modals.main.settings.sections.weather.extra_info.show_description')} category='weather'/>
|
||||
<Checkbox name='cloudiness' text={getMessage('modals.main.settings.sections.weather.extra_info.cloudiness')} category='weather'/>
|
||||
<Checkbox name='humidity' text={getMessage('modals.main.settings.sections.weather.extra_info.humidity')} category='weather'/>
|
||||
<Checkbox name='visibility' text={getMessage('modals.main.settings.sections.weather.extra_info.visibility')} category='weather'/>
|
||||
<Checkbox name='windspeed' text={getMessage('modals.main.settings.sections.weather.extra_info.wind_speed')} category='weather' onChange={() => this.setState({ windSpeed: (localStorage.getItem('windspeed') !== 'true') })}/>
|
||||
<Checkbox name='windDirection' text={getMessage('modals.main.settings.sections.weather.extra_info.wind_direction')} category='weather' disabled={this.state.windSpeed}/>
|
||||
<Checkbox name='mintemp' text={getMessage('modals.main.settings.sections.weather.extra_info.min_temp')} category='weather'/>
|
||||
<Checkbox name='maxtemp' text={getMessage('modals.main.settings.sections.weather.extra_info.max_temp')} category='weather'/>
|
||||
<Checkbox name='atmosphericpressure' text={getMessage('modals.main.settings.sections.weather.extra_info.atmospheric_pressure')} category='weather'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,71 +1,35 @@
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../../../../modules/helpers/eventbus';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { MenuItem } from '@mui/material';
|
||||
|
||||
import Header from '../../Header';
|
||||
import Checkbox from '../../Checkbox';
|
||||
import Dropdown from '../../Dropdown';
|
||||
import FileUpload from '../../FileUpload';
|
||||
import Slider from '../../Slider';
|
||||
import Switch from '../../Switch';
|
||||
import Radio from '../../Radio';
|
||||
|
||||
import ColourSettings from './Colour';
|
||||
import CustomSettings from './Custom';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
import { values } from 'modules/helpers/settings/modals';
|
||||
|
||||
export default class BackgroundSettings extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
export default class BackgroundSettings extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
customBackground: localStorage.getItem('customBackground') || '',
|
||||
backgroundType: localStorage.getItem('backgroundType') || 'api',
|
||||
backgroundCategories: [window.language.modals.main.loading]
|
||||
backgroundFilter: localStorage.getItem('backgroundFilter') || 'none',
|
||||
backgroundCategories: [this.getMessage('modals.main.loading')],
|
||||
backgroundAPI: localStorage.getItem('backgroundAPI') || 'mue',
|
||||
marketplaceEnabled: localStorage.getItem('photo_packs')
|
||||
};
|
||||
this.language = window.language.modals.main.settings;
|
||||
this.controller = new AbortController();
|
||||
}
|
||||
|
||||
resetCustom = () => {
|
||||
localStorage.setItem('customBackground', '');
|
||||
this.setState({
|
||||
customBackground: ''
|
||||
});
|
||||
toast(window.language.toasts.reset);
|
||||
EventBus.dispatch('refresh', 'background');
|
||||
}
|
||||
|
||||
customBackground(e, text) {
|
||||
const result = (text === true) ? e.target.value : e.target.result;
|
||||
localStorage.setItem('customBackground', result);
|
||||
this.setState({
|
||||
customBackground: result
|
||||
});
|
||||
EventBus.dispatch('refresh', 'background');
|
||||
}
|
||||
|
||||
videoCustomSettings = () => {
|
||||
const customBackground = this.state.customBackground;
|
||||
|
||||
if (customBackground.startsWith('data:video/') || customBackground.endsWith('.mp4') || customBackground.endsWith('.webm') || customBackground.endsWith('.ogg')) {
|
||||
return (
|
||||
<>
|
||||
<Checkbox name='backgroundVideoLoop' text={this.language.sections.background.source.loop_video}/>
|
||||
<Checkbox name='backgroundVideoMute' text={this.language.sections.background.source.mute_video}/>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
marketplaceType = () => {
|
||||
if (localStorage.getItem('photo_packs')) {
|
||||
return <option value='photo_pack'>{window.language.modals.main.navbar.marketplace}</option>;
|
||||
}
|
||||
}
|
||||
|
||||
async getBackgroundCategories() {
|
||||
const data = await (await fetch(window.constants.API_URL + '/images/categories', { signal: this.controller.signal })).json();
|
||||
const data = await (await fetch(variables.constants.API_URL + '/images/categories', { signal: this.controller.signal })).json();
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
return;
|
||||
@@ -79,7 +43,7 @@ export default class BackgroundSettings extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return this.setState({
|
||||
backgroundCategories: [window.language.modals.update.offline.title]
|
||||
backgroundCategories: [this.getMessage('modals.update.offline.title')]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -92,7 +56,7 @@ export default class BackgroundSettings extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { background } = this.language.sections;
|
||||
const { getMessage } = this;
|
||||
|
||||
let backgroundSettings;
|
||||
|
||||
@@ -111,94 +75,104 @@ export default class BackgroundSettings extends React.PureComponent {
|
||||
}
|
||||
];
|
||||
|
||||
const APISettings = (
|
||||
<>
|
||||
<br/>
|
||||
<Radio title={background.source.api} options={apiOptions} name='backgroundAPI' category='background' element='#backgroundImage'/>
|
||||
<br/>
|
||||
<Dropdown label={background.category} name='apiCategory'>
|
||||
{this.state.backgroundCategories.map((category) => (
|
||||
<option value={category} key={category}>{category.charAt(0).toUpperCase() + category.slice(1)}</option>
|
||||
))}
|
||||
</Dropdown>
|
||||
<br/><br/>
|
||||
<Dropdown label={background.source.quality.title} name='apiQuality' category='background' 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>
|
||||
<option value='datasaver'>{background.source.quality.datasaver}</option>
|
||||
</Dropdown>
|
||||
<br/><br/>
|
||||
<Dropdown label={background.interval.title} name='backgroundchange'>
|
||||
<option value='refresh'>{window.language.tabname}</option>
|
||||
<option value='60000'>{background.interval.minute}</option>
|
||||
<option value='1800000'>{background.interval.half_hour}</option>
|
||||
<option value='3600000'>{background.interval.hour}</option>
|
||||
<option value='86400000'>{background.interval.day}</option>
|
||||
<option value='604800000'>{window.language.widgets.date.week}</option>
|
||||
<option value='2628000000'>{background.interval.month}</option>
|
||||
</Dropdown>
|
||||
</>
|
||||
const interval = (
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.background.interval.title')} name='backgroundchange'>
|
||||
<option value='refresh'>{getMessage('tabname')}</option>
|
||||
<option value='60000'>{getMessage('modals.main.settings.sections.background.interval.minute')}</option>
|
||||
<option value='1800000'>{getMessage('modals.main.settings.sections.background.interval.half_hour')}</option>
|
||||
<option value='3600000'>{getMessage('modals.main.settings.sections.background.interval.hour')}</option>
|
||||
<option value='86400000'>{getMessage('modals.main.settings.sections.background.interval.day')}</option>
|
||||
<option value='604800000'>{getMessage('widgets.date.week')}</option>
|
||||
<option value='2628000000'>{getMessage('modals.main.settings.sections.background.interval.month')}</option>
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
const customSettings = (
|
||||
const APISettings = (
|
||||
<>
|
||||
<ul>
|
||||
<p>{background.source.custom_background} <span className='modalLink' onClick={this.resetCustom}>{this.language.buttons.reset}</span></p>
|
||||
<input type='text' value={this.state.customBackground} onChange={(e) => this.customBackground(e, true)}></input>
|
||||
<span className='modalLink' onClick={() => document.getElementById('bg-input').click()}>{background.source.upload}</span>
|
||||
<FileUpload id='bg-input' accept='image/jpeg, image/png, image/webp, image/webm, image/gif, video/mp4, video/webm, video/ogg' loadFunction={(e) => this.customBackground(e)} />
|
||||
</ul>
|
||||
{this.videoCustomSettings()}
|
||||
<Radio title={getMessage('modals.main.settings.sections.background.source.api')} options={apiOptions} name='backgroundAPI' category='background' element='#backgroundImage' onChange={(e) => this.setState({ backgroundAPI: e })}/>
|
||||
{this.state.backgroundCategories[0] === getMessage('modals.main.loading') ?
|
||||
<>
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.background.category')} name='apiCategory'>
|
||||
<MenuItem value='loading' key='loading'>{getMessage('modals.main.loading')}</MenuItem>
|
||||
<MenuItem value='loading' key='loading'>{getMessage('modals.main.loading')}</MenuItem>
|
||||
</Dropdown>
|
||||
</> :
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.background.category')} name='apiCategory' >
|
||||
{this.state.backgroundCategories.map((category) => (
|
||||
<MenuItem value={category} key={category}>{category.charAt(0).toUpperCase() + category.slice(1)}</MenuItem>
|
||||
))}
|
||||
</Dropdown>
|
||||
}
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.background.source.quality.title')} name='apiQuality' element='.other'>
|
||||
<option value='original'>{getMessage('modals.main.settings.sections.background.source.quality.original')}</option>
|
||||
<option value='high'>{getMessage('modals.main.settings.sections.background.source.quality.high')}</option>
|
||||
<option value='normal'>{getMessage('modals.main.settings.sections.background.source.quality.normal')}</option>
|
||||
<option value='datasaver'>{getMessage('modals.main.settings.sections.background.source.quality.datasaver')}</option>
|
||||
</Dropdown>
|
||||
{interval}
|
||||
</>
|
||||
);
|
||||
|
||||
switch (this.state.backgroundType) {
|
||||
case 'custom': backgroundSettings = customSettings; break;
|
||||
case 'custom': backgroundSettings = <CustomSettings interval={interval}/>; break;
|
||||
case 'colour': backgroundSettings = <ColourSettings/>; break;
|
||||
case 'random_colour': backgroundSettings = <></>; break;
|
||||
case 'random_gradient': backgroundSettings = <></>; break;
|
||||
default: backgroundSettings = APISettings; break;
|
||||
}
|
||||
|
||||
if (localStorage.getItem('photo_packs') && this.state.backgroundType !== 'custom' && this.state.backgroundType !== 'colour' && this.state.backgroundType !== 'api') {
|
||||
backgroundSettings = null;
|
||||
}
|
||||
|
||||
const usingImage = this.state.backgroundType !== 'colour' && this.state.backgroundType !== 'random_colour' && this.state.backgroundType !== 'random_gradient';
|
||||
|
||||
return (
|
||||
<>
|
||||
<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' />
|
||||
<Header title={getMessage('modals.main.settings.sections.background.title')} setting='background' category='background' element='#backgroundImage'/>
|
||||
<Checkbox name='ddgProxy' text={getMessage('modals.main.settings.sections.background.ddg_image_proxy')} element='.other' disabled={!usingImage} />
|
||||
<Checkbox name='bgtransition' text={getMessage('modals.main.settings.sections.background.transition')} element='.other' disabled={!usingImage} />
|
||||
<Checkbox name='photoInformation' text={getMessage('modals.main.settings.sections.background.photo_information')} element='.other' disabled={this.state.backgroundType !== 'api' && this.state.backgroundType !== 'marketplace'} />
|
||||
<Checkbox name='photoMap' text={getMessage('modals.main.settings.sections.background.show_map')} element='.other' disabled={this.state.backgroundAPI !== 'unsplash'}/>
|
||||
|
||||
<h3>{background.source.title}</h3>
|
||||
<Dropdown label={background.type.title} name='backgroundType' onChange={(value) => this.setState({ backgroundType: value })} category='background'>
|
||||
{this.marketplaceType()}
|
||||
<option value='api'>{background.type.api}</option>
|
||||
<option value='custom'>{background.type.custom_image}</option>
|
||||
<option value='colour'>{background.type.custom_colour}</option>
|
||||
<h3>{getMessage('modals.main.settings.sections.background.source.title')}</h3>
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.background.type.title')} name='backgroundType' onChange={(value) => this.setState({ backgroundType: value })} category='background'>
|
||||
{this.state.marketplaceEnabled ? <option value='photo_pack'>{this.getMessage('modals.main.navbar.marketplace')}</option> : null}
|
||||
<option value='api'>{getMessage('modals.main.settings.sections.background.type.api')}</option>
|
||||
<option value='custom'>{getMessage('modals.main.settings.sections.background.type.custom_image')}</option>
|
||||
<option value='colour'>{getMessage('modals.main.settings.sections.background.type.custom_colour')}</option>
|
||||
<option value='random_colour'>{getMessage('modals.main.settings.sections.background.type.random_colour')}</option>
|
||||
<option value='random_gradient'>{getMessage('modals.main.settings.sections.background.type.random_gradient')}</option>
|
||||
</Dropdown>
|
||||
<br/>
|
||||
|
||||
|
||||
{backgroundSettings}
|
||||
|
||||
<h3>{background.buttons.title}</h3>
|
||||
<Checkbox name='view' text={background.buttons.view} category='navbar' />
|
||||
<Checkbox name='favouriteEnabled' text={background.buttons.favourite} category='navbar' />
|
||||
<Checkbox name='downloadbtn' text={background.buttons.download} element='.other' />
|
||||
|
||||
<h3>{background.effects.title}</h3>
|
||||
<Slider title={background.effects.blur} name='blur' min='0' max='100' default='0' display='%' category='background' element='#backgroundImage' />
|
||||
<Slider title={background.effects.brightness} name='brightness' min='0' max='100' default='90' display='%' category='background' element='#backgroundImage' />
|
||||
<br/><br/>
|
||||
<Dropdown label={background.effects.filters.title} name='backgroundFilter' category='background' element='#backgroundImage'>
|
||||
<option value='grayscale'>{background.effects.filters.grayscale}</option>
|
||||
<option value='sepia'>{background.effects.filters.sepia}</option>
|
||||
<option value='invert'>{background.effects.filters.invert}</option>
|
||||
<option value='saturate'>{background.effects.filters.saturate}</option>
|
||||
<option value='contrast'>{background.effects.filters.contrast}</option>
|
||||
</Dropdown>
|
||||
<Slider title={background.effects.filters.amount} name='backgroundFilterAmount' min='0' max='100' default='0' display='%' category='background' element='#backgroundImage' />
|
||||
{this.state.backgroundType === 'api' && APISettings && this.state.backgroundAPI === 'mue' ?
|
||||
<>
|
||||
<h3>{getMessage('modals.main.settings.sections.background.buttons.title')}</h3>
|
||||
<Checkbox name='downloadbtn' text={getMessage('modals.main.settings.sections.background.buttons.download')} element='.other' />
|
||||
</>
|
||||
: null}
|
||||
|
||||
{this.state.backgroundType === 'api' || this.state.backgroundType === 'custom' || this.state.marketplaceEnabled ?
|
||||
<>
|
||||
<h3>{getMessage('modals.main.settings.sections.background.effects.title')}</h3>
|
||||
<Slider title={getMessage('modals.main.settings.sections.background.effects.blur')} name='blur' min='0' max='100' default='0' display='%' marks={values('background')} category='background' element='#backgroundImage' />
|
||||
<Slider title={getMessage('modals.main.settings.sections.background.effects.brightness')} name='brightness' min='0' max='100' default='90' display='%' marks={values('background')} category='background' element='#backgroundImage' />
|
||||
<br/>
|
||||
<Dropdown label={getMessage('modals.main.settings.sections.background.effects.filters.title')} name='backgroundFilter' onChange={(value) => this.setState({ backgroundFilter: value })} category='background' element='#backgroundImage'>
|
||||
<option value='none'>{getMessage('modals.main.settings.sections.appearance.navbar.refresh_options.none')}</option>
|
||||
<option value='grayscale'>{getMessage('modals.main.settings.sections.background.effects.filters.grayscale')}</option>
|
||||
<option value='sepia'>{getMessage('modals.main.settings.sections.background.effects.filters.sepia')}</option>
|
||||
<option value='invert'>{getMessage('modals.main.settings.sections.background.effects.filters.invert')}</option>
|
||||
<option value='saturate'>{getMessage('modals.main.settings.sections.background.effects.filters.saturate')}</option>
|
||||
<option value='contrast'>{getMessage('modals.main.settings.sections.background.effects.filters.contrast')}</option>
|
||||
</Dropdown>
|
||||
{this.state.backgroundFilter !== 'none' ?
|
||||
<Slider title={getMessage('modals.main.settings.sections.background.effects.filters.amount')} name='backgroundFilterAmount' min='0' max='100' default='0' display='%' marks={values('background')} category='background' element='#backgroundImage' />
|
||||
: null}
|
||||
</>
|
||||
: null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import React from 'react';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent, Fragment } from 'react';
|
||||
import { ColorPicker } from 'react-color-gradient-picker';
|
||||
|
||||
import hexToRgb from '../../../../../../modules/helpers/background/hexToRgb';
|
||||
import rgbToHex from '../../../../../../modules/helpers/background/rgbToHex';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import hexToRgb from 'modules/helpers/background/hexToRgb';
|
||||
import rgbToHex from 'modules/helpers/background/rgbToHex';
|
||||
|
||||
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;
|
||||
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
gradientSettings: this.DefaultGradientSettings
|
||||
};
|
||||
this.language = window.language.modals.main.settings;
|
||||
}
|
||||
|
||||
resetColour() {
|
||||
@@ -27,7 +27,7 @@ export default class ColourSettings extends React.PureComponent {
|
||||
this.setState({
|
||||
gradientSettings: this.DefaultGradientSettings
|
||||
});
|
||||
toast(window.language.toasts.reset);
|
||||
toast(this.getMessage('toasts.reset'));
|
||||
}
|
||||
|
||||
initialiseColourPickerState(gradientSettings) {
|
||||
@@ -69,7 +69,7 @@ export default class ColourSettings extends React.PureComponent {
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
localStorage.setItem('customBackgroundColour', document.getElementById('customBackgroundHex').value);
|
||||
localStorage.setItem('customBackgroundColour', this.currentGradientSettings());
|
||||
}
|
||||
|
||||
onGradientChange = (event, index) => {
|
||||
@@ -85,11 +85,7 @@ export default class ColourSettings extends React.PureComponent {
|
||||
return newState;
|
||||
});
|
||||
|
||||
const reminderInfo = document.querySelector('.reminder-info');
|
||||
if (reminderInfo.style.display !== 'block') {
|
||||
reminderInfo.style.display = 'block';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
this.showReminder();
|
||||
}
|
||||
|
||||
addColour = () => {
|
||||
@@ -104,18 +100,18 @@ export default class ColourSettings extends React.PureComponent {
|
||||
return newState;
|
||||
});
|
||||
|
||||
window.stats.postEvent('setting', 'Changed backgroundtype from colour to gradient');
|
||||
variables.stats.postEvent('setting', 'Changed backgroundtype from colour to gradient');
|
||||
}
|
||||
|
||||
currentGradientSettings = () => {
|
||||
if (typeof this.state.gradientSettings === 'object' && this.state.gradientSettings.gradient.every(g => g.colour !== this.language.sections.background.source.disabled)) {
|
||||
if (typeof this.state.gradientSettings === 'object' && this.state.gradientSettings.gradient.every(g => g.colour !== this.getMessage('modals.main.settings.sections.background.source.disabled'))) {
|
||||
const clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));
|
||||
return JSON.stringify({
|
||||
...this.state.gradientSettings,
|
||||
gradient: [...this.state.gradientSettings.gradient.map(g => { return { ...g, stop: clampNumber(+g.stop, 0, 100) } })].sort((a, b) => (a.stop > b.stop) ? 1 : -1)
|
||||
});
|
||||
}
|
||||
return this.language.sections.background.source.disabled;
|
||||
return this.getMessage('modals.main.settings.sections.background.source.disabled');
|
||||
}
|
||||
|
||||
onColourPickerChange = (attrs, name) => {
|
||||
@@ -135,16 +131,18 @@ export default class ColourSettings extends React.PureComponent {
|
||||
}
|
||||
});
|
||||
|
||||
this.showReminder();
|
||||
};
|
||||
|
||||
showReminder() {
|
||||
const reminderInfo = document.querySelector('.reminder-info');
|
||||
if (reminderInfo.style.display !== 'block') {
|
||||
reminderInfo.style.display = 'block';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { background } = this.language.sections;
|
||||
|
||||
let colourSettings = null;
|
||||
if (typeof this.state.gradientSettings === 'object') {
|
||||
const gradientHasMoreThanOneColour = this.state.gradientSettings.gradient.length > 1;
|
||||
@@ -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>
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -177,15 +175,14 @@ export default class ColourSettings extends React.PureComponent {
|
||||
colourSettings = (
|
||||
<>
|
||||
{gradientInputs}
|
||||
{!gradientHasMoreThanOneColour ? (<><br/><br/><button type='button' className='add' onClick={this.addColour}>{background.source.add_colour}</button></>) : null}
|
||||
{!gradientHasMoreThanOneColour ? (<><br/><br/><button type='button' className='add' onClick={this.addColour}>{this.getMessage('modals.main.settings.sections.background.source.add_colour')}</button></>) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>{background.source.custom_colour} <span className='modalLink' onClick={() => this.resetColour()}>{this.language.buttons.reset}</span></p>
|
||||
<input id='customBackgroundHex' type='hidden' value={this.currentGradientSettings()} />
|
||||
<p>{this.getMessage('modals.main.settings.sections.background.source.custom_colour')} <span className='modalLink' onClick={() => this.resetColour()}>{this.getMessage('modals.main.settings.buttons.reset')}</span></p>
|
||||
{colourSettings}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Cancel, AddLink, AddPhotoAlternate, PersonalVideo } from '@mui/icons-material';
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
|
||||
import Checkbox from '../../Checkbox';
|
||||
import FileUpload from '../../FileUpload';
|
||||
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import CustomURLModal from './CustomURLModal';
|
||||
|
||||
export default class CustomSettings extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
customBackground: this.getCustom(),
|
||||
customURLModal: false
|
||||
};
|
||||
}
|
||||
|
||||
resetCustom = () => {
|
||||
localStorage.setItem('customBackground', '[]');
|
||||
this.setState({
|
||||
customBackground: []
|
||||
});
|
||||
toast(this.getMessage('toasts.reset'));
|
||||
EventBus.dispatch('refresh', 'background');
|
||||
}
|
||||
|
||||
customBackground(e, text, index) {
|
||||
const result = (text === true) ? e.target.value : e.target.result;
|
||||
|
||||
const customBackground = this.state.customBackground;
|
||||
customBackground[index] = result;
|
||||
this.setState({
|
||||
customBackground
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
localStorage.setItem('customBackground', JSON.stringify(customBackground));
|
||||
document.querySelector('.reminder-info').style.display = 'block';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
|
||||
modifyCustomBackground(type, index) {
|
||||
const customBackground = this.state.customBackground;
|
||||
if (type === 'add') {
|
||||
customBackground.push('');
|
||||
} else {
|
||||
customBackground.splice(index, 1);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
customBackground
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
localStorage.setItem('customBackground', JSON.stringify(customBackground));
|
||||
document.querySelector('.reminder-info').style.display = 'block';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
|
||||
videoCheck(url) {
|
||||
return url.startsWith('data:video/') || url.endsWith('.mp4') || url.endsWith('.webm') || url.endsWith('.ogg');
|
||||
}
|
||||
|
||||
videoCustomSettings = () => {
|
||||
const hasVideo = this.state.customBackground.filter(bg => this.videoCheck(bg));
|
||||
|
||||
if (hasVideo.length > 0) {
|
||||
return (
|
||||
<>
|
||||
<Checkbox name='backgroundVideoLoop' text={this.getMessage('modals.main.settings.sections.background.source.loop_video')}/>
|
||||
<Checkbox name='backgroundVideoMute' text={this.getMessage('modals.main.settings.sections.background.source.mute_video')}/>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getCustom() {
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(localStorage.getItem('customBackground'));
|
||||
} catch (e) {
|
||||
data = [localStorage.getItem('customBackground')];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
uploadCustomBackground() {
|
||||
document.getElementById('bg-input').setAttribute('index', this.state.customBackground.length);
|
||||
document.getElementById('bg-input').click();
|
||||
// to fix loadFunction
|
||||
this.setState({
|
||||
currentBackgroundIndex: this.state.customBackground.length
|
||||
});
|
||||
}
|
||||
|
||||
addCustomURL(e) {
|
||||
this.setState({
|
||||
customURLModal: false,
|
||||
currentBackgroundIndex: this.state.customBackground.length
|
||||
});
|
||||
this.customBackground({ target: { value: e }}, true, this.state.customBackground.length);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ul>
|
||||
<p>{this.getMessage('modals.main.settings.sections.background.source.custom_background')} <span className='modalLink' onClick={this.resetCustom}>{this.getMessage('modals.main.settings.buttons.reset')}</span></p>
|
||||
<div className='data-buttons-row'>
|
||||
<button onClick={() => this.uploadCustomBackground()}>{this.getMessage('modals.main.settings.sections.background.source.add_background')} <AddPhotoAlternate/></button>
|
||||
<button onClick={() => this.setState({ customURLModal: true })}>{this.getMessage('modals.main.settings.sections.background.source.add_url')} <AddLink/></button>
|
||||
</div>
|
||||
<div className='images-row'>
|
||||
{this.state.customBackground.map((url, index) => (
|
||||
<div style={{ backgroundImage: `url(${!this.videoCheck(url) ? this.state.customBackground[index] : ''})` }} key={index}>
|
||||
{this.videoCheck(url) ? <PersonalVideo className='customvideoicon'/> : null}
|
||||
{this.state.customBackground.length > 0 ? <button className='cleanButton' onClick={() => this.modifyCustomBackground('remove', index)}>
|
||||
<Cancel/>
|
||||
</button> : null}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<FileUpload id='bg-input' accept='image/jpeg, image/png, image/webp, image/webm, image/gif, video/mp4, video/webm, video/ogg' loadFunction={(e) => this.customBackground(e, false, this.state.currentBackgroundIndex)} />
|
||||
{this.props.interval}
|
||||
{this.videoCustomSettings()}
|
||||
<Modal closeTimeoutMS={100} onRequestClose={() => this.setState({ customURLModal: false })} isOpen={this.state.customURLModal} className='Modal resetmodal mainModal' overlayClassName='Overlay resetoverlay' ariaHideApp={false}>
|
||||
<CustomURLModal modalClose={(e) => this.addCustomURL(e)} modalCloseOnly={() => this.setState({ customURLModal: false })} />
|
||||
</Modal>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import variables from 'modules/variables';
|
||||
import { useState } from 'react';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { TextField } from '@mui/material';
|
||||
|
||||
export default function CustomURLModal({ modalClose, modalCloseOnly }) {
|
||||
const [url, setURL] = useState();
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className='closeModal' onClick={modalCloseOnly}>×</span>
|
||||
<h1>{variables.language.getMessage(variables.languagecode, 'modals.main.settings.sections.background.source.add_url')}</h1>
|
||||
<TextField value={url} onChange={(e) => setURL(e.target.value)} varient='outlined'/>
|
||||
<div className='resetfooter'>
|
||||
<button className='round import' style={{ marginLeft: '5px' }} onClick={() => modalClose(url)}>
|
||||
<Add/>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
import Added from '../marketplace/sections/Added';
|
||||
import Sideload from '../marketplace/sections/Sideload';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import Tabs from './backend/Tabs';
|
||||
|
||||
import Added from '../marketplace/sections/Added';
|
||||
import Sideload from '../marketplace/sections/Sideload';
|
||||
import Create from '../marketplace/sections/Create';
|
||||
|
||||
export default function Addons() {
|
||||
const addons = window.language.modals.main.addons;
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
return (
|
||||
<Tabs>
|
||||
<div label={addons.added} name='added'><Added/></div>
|
||||
<div label={addons.sideload} name='sideload'><Sideload/></div>
|
||||
<div label={getMessage('modals.main.addons.added')} name='added'><Added/></div>
|
||||
<div label={getMessage('modals.main.addons.sideload.title')} name='sideload'><Sideload/></div>
|
||||
<div label={getMessage('modals.main.addons.create.title')} name='create'><Create/></div>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import MarketplaceTab from '../marketplace/sections/Marketplace';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import Tabs from './backend/Tabs';
|
||||
|
||||
import MarketplaceTab from '../marketplace/sections/Marketplace';
|
||||
|
||||
export default function Marketplace() {
|
||||
const marketplace = window.language.modals.main.marketplace;
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
return (
|
||||
<Tabs>
|
||||
<div label={marketplace.photo_packs} name='photo_packs'><MarketplaceTab type='photo_packs'/></div>
|
||||
<div label={marketplace.quote_packs} name='quote_packs'><MarketplaceTab type='quote_packs'/></div>
|
||||
<div label={marketplace.preset_settings} name='preset_settings'><MarketplaceTab type='preset_settings'/></div>
|
||||
<div label={getMessage('modals.main.marketplace.photo_packs')} name='photo_packs'><MarketplaceTab type='photo_packs'/></div>
|
||||
<div label={getMessage('modals.main.marketplace.quote_packs')} name='quote_packs'><MarketplaceTab type='quote_packs'/></div>
|
||||
<div label={getMessage('modals.main.marketplace.preset_settings')} name='preset_settings'><MarketplaceTab type='preset_settings'/></div>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,51 +1,52 @@
|
||||
import About from '../settings/sections/About';
|
||||
import Language from '../settings/sections/Language';
|
||||
import Search from '../settings/sections/Search';
|
||||
import Greeting from '../settings/sections/Greeting';
|
||||
import Time from '../settings/sections/Time';
|
||||
import Quote from '../settings/sections/Quote';
|
||||
import Appearance from '../settings/sections/Appearance';
|
||||
import Background from '../settings/sections/background/Background';
|
||||
import Advanced from '../settings/sections/Advanced';
|
||||
import Changelog from '../settings/sections/Changelog';
|
||||
import Order from '../settings/sections/Order';
|
||||
import Experimental from '../settings/sections/Experimental';
|
||||
import QuickLinks from '../settings/sections/QuickLinks';
|
||||
import Weather from '../settings/sections/Weather';
|
||||
|
||||
import variables from 'modules/variables';
|
||||
import Tabs from './backend/Tabs';
|
||||
|
||||
export default function Settings() {
|
||||
const { reminder, sections } = window.language.modals.main.settings;
|
||||
import Navbar from '../settings/sections/Navbar';
|
||||
import Greeting from '../settings/sections/Greeting';
|
||||
import Time from '../settings/sections/Time';
|
||||
import QuickLinks from '../settings/sections/QuickLinks';
|
||||
import Quote from '../settings/sections/Quote';
|
||||
import Date from '../settings/sections/Date';
|
||||
import Message from '../settings/sections/Message';
|
||||
import Background from '../settings/sections/background/Background';
|
||||
import Search from '../settings/sections/Search';
|
||||
import Weather from '../settings/sections/Weather';
|
||||
import Order from '../settings/sections/Order';
|
||||
import Appearance from '../settings/sections/Appearance';
|
||||
import Language from '../settings/sections/Language';
|
||||
import Advanced from '../settings/sections/Advanced';
|
||||
//import Keybinds from '../settings/sections/Keybinds';
|
||||
import Stats from '../settings/sections/Stats';
|
||||
import Experimental from '../settings/sections/Experimental';
|
||||
import Changelog from '../settings/sections/Changelog';
|
||||
import About from '../settings/sections/About';
|
||||
|
||||
let display = 'none';
|
||||
if (localStorage.getItem('showReminder') === 'true') {
|
||||
display = 'block';
|
||||
}
|
||||
export default function Settings() {
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tabs>
|
||||
<div label={sections.time.title} name='time'><Time/></div>
|
||||
<div label={sections.quote.title} name='quote'><Quote/></div>
|
||||
<div label={sections.greeting.title} name='greeting'><Greeting/></div>
|
||||
<div label={sections.background.title} name='background'><Background/></div>
|
||||
<div label={sections.search.title} name='search'><Search/></div>
|
||||
<div label={sections.quicklinks.title} name='quicklinks'><QuickLinks/></div>
|
||||
<div label={sections.weather.title} name='weather'><Weather/></div>
|
||||
<div label={sections.appearance.title} name='appearance'><Appearance/></div>
|
||||
<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.experimental.title} name='experimental'><Experimental/></div>
|
||||
<div label={sections.changelog} name='changelog'><Changelog/></div>
|
||||
<div label={sections.about.title} name='about'><About/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.appearance.navbar.title')} name='navbar'><Navbar/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.greeting.title')} name='greeting'><Greeting/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.time.title')} name='time'><Time/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.quicklinks.title')} name='quicklinks'><QuickLinks/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.quote.title')} name='quote'><Quote/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.date.title')} name='date'><Date/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.message.title')} name='message'><Message/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.background.title')} name='background'><Background/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.search.title')} name='search'><Search/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.weather.title')} name='weather'><Weather/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.order.title')} name='order'><Order/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.appearance.title')} name='appearance'><Appearance/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.language.title')} name='language'><Language/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.advanced.title')} name='advanced'><Advanced/></div>
|
||||
{/*<div label={getMessage('modals.main.settings.sections.keybinds.title')} name='keybinds'><Keybinds/></div>*/}
|
||||
<div label={getMessage('modals.main.settings.sections.stats.title')} name='stats'><Stats/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.experimental.title')} name='experimental'><Experimental/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.changelog.title')} name='changelog'><Changelog/></div>
|
||||
<div label={getMessage('modals.main.settings.sections.about.title')} name='about'><About/></div>
|
||||
</Tabs>
|
||||
<div className='reminder-info' style={{ display: display }}>
|
||||
<h1>{reminder.title}</h1>
|
||||
<p>{reminder.message}</p>
|
||||
<button className='pinNote' onClick={() => window.location.reload()}>{window.language.modals.main.error_boundary.refresh}</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,82 +1,89 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { memo } from 'react';
|
||||
import {
|
||||
SettingsRounded as Settings,
|
||||
Widgets as Addons,
|
||||
ShoppingBasket as Marketplace,
|
||||
|
||||
MenuOutlined as Navbar,
|
||||
EmojiPeopleOutlined as Greeting,
|
||||
AccessAlarm as Time,
|
||||
FormatQuoteOutlined as Quote,
|
||||
Link as QuickLinks,
|
||||
DateRangeOutlined as Date,
|
||||
SmsOutlined as Message,
|
||||
PhotoOutlined as Background,
|
||||
Search,
|
||||
CloudOutlined as Weather,
|
||||
List as Order,
|
||||
FormatPaintOutlined as Appearance,
|
||||
Translate as Language,
|
||||
SettingsOutlined as Advanced,
|
||||
BugReportOutlined as Experimental,
|
||||
//KeyboardAltOutlined as Keybinds,
|
||||
AssessmentOutlined as Stats,
|
||||
NewReleasesOutlined as Changelog,
|
||||
InfoOutlined as About,
|
||||
|
||||
// Navbar
|
||||
import Settings from '@material-ui/icons/SettingsRounded';
|
||||
import Addons from '@material-ui/icons/Widgets';
|
||||
import Marketplace from '@material-ui/icons/ShoppingBasket';
|
||||
Code as Sideload,
|
||||
AddCircleOutline as Added,
|
||||
CreateNewFolderOutlined as Create
|
||||
} from '@mui/icons-material';
|
||||
|
||||
// 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';
|
||||
function Tab({ label, currentTab, onClick, navbarTab }) {
|
||||
const getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
// Addons
|
||||
import Sideload from '@material-ui/icons/Code';
|
||||
import Added from '@material-ui/icons/AddCircleOutline';
|
||||
|
||||
function Tab(props) {
|
||||
let className = 'tab-list-item';
|
||||
if (props.currentTab === props.label) {
|
||||
if (currentTab === label) {
|
||||
className += ' tab-list-active';
|
||||
}
|
||||
|
||||
if (props.navbar === true) {
|
||||
if (navbarTab === true) {
|
||||
className = 'navbar-item';
|
||||
if (props.currentTab === props.label) {
|
||||
if (currentTab === label) {
|
||||
className += ' navbar-item-active';
|
||||
}
|
||||
}
|
||||
|
||||
const settings = window.language.modals.main.settings.sections;
|
||||
const { navbar, marketplace, addons } = window.language.modals.main;
|
||||
|
||||
let icon, divider;
|
||||
switch (props.label) {
|
||||
// Navbar
|
||||
case navbar.settings: icon = <Settings/>; break;
|
||||
case navbar.addons: icon = <Addons/>; break;
|
||||
case navbar.marketplace: icon = <Marketplace/>; break;
|
||||
switch (label) {
|
||||
case getMessage('modals.main.navbar.settings'): icon = <Settings/>; break;
|
||||
case getMessage('modals.main.navbar.addons'): icon = <Addons/>; break;
|
||||
case getMessage('modals.main.navbar.marketplace'): icon = <Marketplace/>; break;
|
||||
|
||||
// Settings
|
||||
case settings.time.title: icon = <Time/>; break;
|
||||
case settings.greeting.title: icon = <Greeting/>; break;
|
||||
case settings.quote.title: icon = <Quote/>; break;
|
||||
case settings.background.title: icon = <Background/>; break;
|
||||
case settings.search.title: icon = <Search/>; break;
|
||||
case settings.weather.title: icon = <Weather/>; break;
|
||||
case settings.quicklinks.title: icon = <QuickLinks/>; break;
|
||||
case settings.appearance.title: icon = <Appearance/>; break;
|
||||
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.experimental.title: icon = <Experimental/>; divider = true; break;
|
||||
case settings.changelog: icon = <Changelog/>; break;
|
||||
case settings.about.title: icon = <About/>; break;
|
||||
case getMessage('modals.main.settings.sections.appearance.navbar.title'): icon = <Navbar/>; break;
|
||||
case getMessage('modals.main.settings.sections.greeting.title'): icon = <Greeting/>; break;
|
||||
case getMessage('modals.main.settings.sections.time.title'): icon = <Time/>; break;
|
||||
case getMessage('modals.main.settings.sections.quicklinks.title'): icon = <QuickLinks/>; break;
|
||||
case getMessage('modals.main.settings.sections.quote.title'): icon = <Quote/>; break;
|
||||
case getMessage('modals.main.settings.sections.date.title'): icon = <Date/>; break;
|
||||
case getMessage('modals.main.settings.sections.message.title'): icon = <Message/>; break;
|
||||
case getMessage('modals.main.settings.sections.background.title'): icon = <Background/>; break;
|
||||
case getMessage('modals.main.settings.sections.search.title'): icon = <Search/>; break;
|
||||
case getMessage('modals.main.settings.sections.weather.title'): icon = <Weather/>; divider = true; break;
|
||||
case getMessage('modals.main.settings.sections.order.title'): icon = <Order/>; break;
|
||||
|
||||
// Addons
|
||||
case addons.added: icon = <Added/>; break;
|
||||
case addons.sideload: icon = <Sideload/>; break;
|
||||
case getMessage('modals.main.settings.sections.appearance.title'): icon = <Appearance/>; break;
|
||||
case getMessage('modals.main.settings.sections.language.title'): icon = <Language/>; divider = true; break;
|
||||
case getMessage('modals.main.settings.sections.advanced.title'): icon = <Advanced/>; break;
|
||||
//case getMessage('modals.main.settings.sections.keybinds.title'): icon = <Keybinds/>; break;
|
||||
case getMessage('modals.main.settings.sections.stats.title'): icon = <Stats/>; break;
|
||||
case getMessage('modals.main.settings.sections.experimental.title'): icon = <Experimental/>; divider = true; break;
|
||||
case getMessage('modals.main.settings.sections.changelog.title'): icon = <Changelog/>; break;
|
||||
case getMessage('modals.main.settings.sections.about.title'): icon = <About/>; break;
|
||||
|
||||
// Marketplace
|
||||
case marketplace.photo_packs: icon = <Background/>; break;
|
||||
case marketplace.quote_packs: icon = <Quote/>; break;
|
||||
case marketplace.preset_settings: icon = <Advanced/>; break;
|
||||
case getMessage('modals.main.addons.added'): icon = <Added/>; break;
|
||||
case getMessage('modals.main.addons.sideload.title'): icon = <Sideload/>; break;
|
||||
case getMessage('modals.main.addons.create.title'): icon = <Create/>; break;
|
||||
|
||||
case getMessage('modals.main.marketplace.photo_packs'): icon = <Background/>; break;
|
||||
case getMessage('modals.main.marketplace.quote_packs'): icon = <Quote/>; break;
|
||||
case getMessage('modals.main.marketplace.preset_settings'): icon = <Advanced/>; break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (props.label === settings.experimental.title) {
|
||||
if (label === getMessage('modals.main.settings.sections.experimental.title')) {
|
||||
if (localStorage.getItem('experimental') === 'false') {
|
||||
return <hr/>;
|
||||
}
|
||||
@@ -84,12 +91,12 @@ function Tab(props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<li className={className} onClick={() => props.onClick(props.label)}>
|
||||
{icon} <span>{props.label}</span>
|
||||
<li className={className} onClick={() => onClick(label)}>
|
||||
{icon} <span>{label}</span>
|
||||
</li>
|
||||
{(divider === true) ? <hr/> : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Tab);
|
||||
export default memo(Tab);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
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 +16,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}`);
|
||||
variables.stats.postEvent('tab', `Opened ${name}`);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@@ -27,7 +28,7 @@ export default class Tabs extends React.PureComponent {
|
||||
render() {
|
||||
let className = 'sidebar';
|
||||
let tabClass = 'tab-content';
|
||||
let optionsText = (<h1>{window.language.modals.main.title}</h1>);
|
||||
let optionsText = (<h1>{variables.language.getMessage(variables.languagecode, 'modals.main.title')}</h1>);
|
||||
|
||||
if (this.props.navbar) {
|
||||
className = 'modalNavbar';
|
||||
@@ -45,7 +46,7 @@ export default class Tabs extends React.PureComponent {
|
||||
key={index}
|
||||
label={tab.props.label}
|
||||
onClick={(nextTab) => this.onClick(nextTab, tab.props.name)}
|
||||
navbar={this.props.navbar || false}
|
||||
navbarTab={this.props.navbar || false}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
export default function ProgressBar(props) {
|
||||
export default function ProgressBar({ count, currentTab, switchTab }) {
|
||||
return (
|
||||
<div className='progressbar'>
|
||||
{props.count.map((num) => {
|
||||
{count.map((num) => {
|
||||
let className = 'step';
|
||||
|
||||
const index = props.count.indexOf(num);
|
||||
if (index === props.currentTab) {
|
||||
const index = count.indexOf(num);
|
||||
if (index === currentTab) {
|
||||
className = 'step active';
|
||||
}
|
||||
|
||||
return <div className={className} key={index} onClick={() => props.switchTab(index)}></div>;
|
||||
return <div className={className} key={index} onClick={() => switchTab(index)}></div>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
|
||||
import WelcomeSections from './WelcomeSections';
|
||||
import ProgressBar from './ProgressBar';
|
||||
|
||||
import './welcome.scss';
|
||||
|
||||
export default class WelcomeModal extends React.PureComponent {
|
||||
export default class WelcomeModal extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
image: './././icons/undraw_celebration.svg',
|
||||
currentTab: 0,
|
||||
finalTab: 4,
|
||||
buttonText: window.language.modals.welcome.buttons.next
|
||||
buttonText: this.getMessage('modals.welcome.buttons.next')
|
||||
};
|
||||
this.language = window.language.modals.welcome;
|
||||
this.images = [
|
||||
'./././icons/undraw_celebration.svg',
|
||||
'./././icons/undraw_around_the_world_modified.svg',
|
||||
@@ -35,18 +37,18 @@ export default class WelcomeModal extends React.PureComponent {
|
||||
return this.setState({
|
||||
currentTab: this.state.currentTab - 1,
|
||||
image: this.images[this.state.currentTab - 1],
|
||||
buttonText: this.language.buttons.next
|
||||
buttonText: this.getMessage('modals.welcome.buttons.next')
|
||||
});
|
||||
}
|
||||
|
||||
if (this.state.buttonText === this.language.buttons.close) {
|
||||
if (this.state.buttonText === this.getMessage('modals.main.addons.create.finish.title')) {
|
||||
return this.props.modalClose();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
currentTab: this.state.currentTab + 1,
|
||||
image: this.images[this.state.currentTab + 1],
|
||||
buttonText: (this.state.currentTab !== this.state.finalTab) ? this.language.buttons.next : this.language.buttons.close
|
||||
buttonText: (this.state.currentTab !== this.state.finalTab) ? this.getMessage('modals.welcome.buttons.next') : this.getMessage('modals.main.addons.create.finish.title')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,7 +57,7 @@ export default class WelcomeModal extends React.PureComponent {
|
||||
this.setState({
|
||||
currentTab: tab,
|
||||
image: this.images[tab],
|
||||
buttonText: (tab !== this.state.finalTab + 1) ? this.language.buttons.next : this.language.buttons.close
|
||||
buttonText: (tab !== this.state.finalTab + 1) ? this.getMessage('modals.welcome.buttons.next') : this.getMessage('modals.main.addons.create.finish.title')
|
||||
});
|
||||
|
||||
localStorage.setItem('bgtransition', true);
|
||||
@@ -68,7 +70,7 @@ export default class WelcomeModal extends React.PureComponent {
|
||||
this.setState({
|
||||
currentTab: Number(welcomeTab),
|
||||
image: this.images[Number(welcomeTab)],
|
||||
buttonText: (Number(welcomeTab) !== this.state.finalTab + 1) ? this.language.buttons.next : this.language.buttons.close
|
||||
buttonText: (Number(welcomeTab) !== this.state.finalTab + 1) ? this.getMessage('modals.welcome.buttons.next') : this.getMessage('modals.main.addons.create.finish.title')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -81,11 +83,15 @@ export default class WelcomeModal extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='welcomeContent'>
|
||||
<section>
|
||||
<img className='showcaseimg' alt='celebration' draggable={false} src={this.state.image} />
|
||||
<img className='showcaseimg' alt='sidebar icon' draggable={false} src={this.state.image} />
|
||||
<ProgressBar count={this.images} currentTab={this.state.currentTab} switchTab={(tab) => this.switchTab(tab)}/>
|
||||
</section>
|
||||
<section>
|
||||
@@ -93,7 +99,8 @@ export default class WelcomeModal extends React.PureComponent {
|
||||
<WelcomeSections currentTab={this.state.currentTab} switchTab={(tab) => this.switchTab(tab)}/>
|
||||
</div>
|
||||
<div className='buttons'>
|
||||
{(this.state.currentTab !== 0) ? <button className='close' style={{ marginRight: '20px' }} onClick={() => this.changeTab(true)}>{this.language.buttons.previous}</button> : null}
|
||||
{(this.state.currentTab === 0) ? <button className='close' style={{ marginRight: '20px' }} onClick={() => this.props.modalSkip()}>{this.getMessage('modals.welcome.buttons.preview')}</button> : null}
|
||||
{(this.state.currentTab !== 0) ? <button className='close' style={{ marginRight: '20px' }} onClick={() => this.changeTab(true)}>{this.getMessage('modals.welcome.buttons.previous')}</button> : null}
|
||||
<button className='close' onClick={() => this.changeTab()}>{this.state.buttonText}</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { CloudUpload, AutoAwesome, LightMode, DarkMode } from '@mui/icons-material';
|
||||
|
||||
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 { loadSettings } from 'modules/helpers/settings';
|
||||
import { importSettings } from 'modules/helpers/settings/modals';
|
||||
|
||||
import SettingsFunctions from '../../../modules/helpers/settings';
|
||||
import SettingsFunctionsModal from '../../../modules/helpers/settings/modals';
|
||||
const languages = require('modules/languages.json');
|
||||
const default_settings = require('modules/default_settings.json');
|
||||
|
||||
const languages = require('../../../modules/languages.json');
|
||||
const default_settings = require('../../../modules/default_settings.json');
|
||||
export default class WelcomeSections extends PureComponent {
|
||||
getMessage = (text) => variables.language.getMessage(variables.languagecode, text);
|
||||
|
||||
export default class WelcomeSections extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@@ -40,7 +39,7 @@ export default class WelcomeSections extends React.PureComponent {
|
||||
});
|
||||
|
||||
localStorage.setItem('theme', type);
|
||||
SettingsFunctions.loadSettings(true);
|
||||
loadSettings(true);
|
||||
}
|
||||
|
||||
getSetting(name) {
|
||||
@@ -49,11 +48,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;
|
||||
}
|
||||
@@ -92,9 +92,16 @@ export default class WelcomeSections extends React.PureComponent {
|
||||
this.timeout = setTimeout(this.changeWelcomeImg, 3 * 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
// cancel welcome image timer if not on welcome tab
|
||||
componentDidUpdate() {
|
||||
if (this.props.currentTab !== 0) {
|
||||
if (this.props.currentTab !== 0) {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
@@ -107,13 +114,12 @@ export default class WelcomeSections extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const language = window.language.modals.welcome;
|
||||
let tabContent;
|
||||
|
||||
const intro = (
|
||||
<>
|
||||
<h1>{language.sections.intro.title}</h1>
|
||||
<p>{language.sections.intro.description}</p>
|
||||
<h1>{this.getMessage('modals.welcome.sections.intro.title')}</h1>
|
||||
<p>{this.getMessage('modals.welcome.sections.intro.description')}</p>
|
||||
<h3 className='quicktip'>#shareyourmue</h3>
|
||||
<div className='examples'>
|
||||
<img src={this.welcomeImages[this.state.welcomeImage]} alt='Example Mue setup' draggable={false}/>
|
||||
@@ -123,81 +129,78 @@ export default class WelcomeSections extends React.PureComponent {
|
||||
|
||||
const chooseLanguage = (
|
||||
<>
|
||||
<h1>{language.sections.language.title}</h1>
|
||||
<p>{language.sections.language.description}</p>
|
||||
<h1>{this.getMessage('modals.welcome.sections.language.title')}</h1>
|
||||
<p>{this.getMessage('modals.welcome.sections.language.description')} <a href={variables.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 languageSettings = window.language.modals.main.settings.sections.language;
|
||||
|
||||
const theme = (
|
||||
<>
|
||||
<h1>{language.sections.theme.title}</h1>
|
||||
<p>{language.sections.theme.description}</p>
|
||||
<h1>{this.getMessage('modals.welcome.sections.theme.title')}</h1>
|
||||
<p>{this.getMessage('modals.welcome.sections.theme.description')}</p>
|
||||
<div className='themesToggleArea'>
|
||||
<div className={this.state.autoClass} onClick={() => this.changeTheme('auto')}>
|
||||
<AutoIcon/>
|
||||
<span>{appearance.theme.auto}</span>
|
||||
<AutoAwesome/>
|
||||
<span>{this.getMessage('modals.main.settings.sections.appearance.theme.auto')}</span>
|
||||
</div>
|
||||
<div className='options'>
|
||||
<div className={this.state.lightClass} onClick={() => this.changeTheme('light')}>
|
||||
<LightModeIcon/>
|
||||
<span>{appearance.theme.light}</span>
|
||||
<LightMode/>
|
||||
<span>{this.getMessage('modals.main.settings.sections.appearance.theme.light')}</span>
|
||||
</div>
|
||||
<div className={this.state.darkClass} onClick={() => this.changeTheme('dark')}>
|
||||
<DarkModeIcon/>
|
||||
<span>{appearance.theme.dark}</span>
|
||||
<DarkMode/>
|
||||
<span>{this.getMessage('modals.main.settings.sections.appearance.theme.dark')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className='quicktip'>{language.tip}</h3>
|
||||
<p>{language.sections.theme.tip}</p>
|
||||
<h3 className='quicktip'>{this.getMessage('modals.welcome.tip')}</h3>
|
||||
<p>{this.getMessage('modals.welcome.sections.theme.tip')}</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const settings = (
|
||||
<>
|
||||
<h1>{language.sections.settings.title}</h1>
|
||||
<p>{language.sections.settings.description}</p>
|
||||
<h1>{this.getMessage('modals.welcome.sections.settings.title')}</h1>
|
||||
<p>{this.getMessage('modals.welcome.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>
|
||||
<span>{this.getMessage('modals.main.settings.buttons.import')}</span>
|
||||
</button>
|
||||
<FileUpload id='file-input' accept='application/json' type='settings' loadFunction={(e) => this.importSettings(e)}/>
|
||||
<h3 className='quicktip'>{language.tip}</h3>
|
||||
<p>{language.sections.settings.tip}</p>
|
||||
<h3 className='quicktip'>{this.getMessage('modals.welcome.tip')}</h3>
|
||||
<p>{this.getMessage('modals.welcome.sections.settings.tip')}</p>
|
||||
</>
|
||||
);
|
||||
|
||||
const privacy = (
|
||||
<>
|
||||
<h1>{language.sections.privacy.title}</h1>
|
||||
<p>{language.sections.privacy.description}</p>
|
||||
<Checkbox name='offlineMode' text={advanced.offline_mode} element='.other' />
|
||||
<p>{language.sections.privacy.offline_mode_description}</p>
|
||||
<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>
|
||||
<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>
|
||||
<h1>{this.getMessage('modals.welcome.sections.privacy.title')}</h1>
|
||||
<p>{this.getMessage('modals.welcome.sections.privacy.description')}</p>
|
||||
<Checkbox name='offlineMode' text={this.getMessage('modals.main.settings.sections.advanced.offline_mode')} element='.other' />
|
||||
<p>{this.getMessage('modals.welcome.sections.privacy.offline_mode_description')}</p>
|
||||
<Checkbox name='quicklinksddgProxy' text={this.getMessage('modals.main.settings.sections.background.ddg_image_proxy') + ' (' + this.getMessage('modals.main.settings.sections.quicklinks.title') + ')'}/>
|
||||
<Checkbox name='ddgProxy' text={this.getMessage('modals.main.settings.sections.background.ddg_image_proxy') + ' (' +this.getMessage('modals.main.settings.sections.background.title') + ')'}/>
|
||||
<p>{this.getMessage('modals.welcome.sections.privacy.ddg_proxy_description')}</p>
|
||||
<h3 className='quicktip'>{this.getMessage('modals.welcome.sections.privacy.links.title')}</h3>
|
||||
<a className='privacy' href={variables.constants.PRIVACY_URL} target='_blank' rel='noopener noreferrer'>{this.getMessage('modals.welcome.sections.privacy.links.privacy_policy')}</a>
|
||||
<br/><br/>
|
||||
<a className='privacy' href={'https://github.com/' + window.constants.ORG_NAME} target='_blank' rel='noopener noreferrer'>{language.sections.privacy.links.source_code}</a>
|
||||
<a className='privacy' href={'https://github.com/' + variables.constants.ORG_NAME} target='_blank' rel='noopener noreferrer'>{this.getMessage('modals.welcome.sections.privacy.links.source_code')}</a>
|
||||
</>
|
||||
);
|
||||
|
||||
const final = (
|
||||
<>
|
||||
<h1>{language.sections.final.title}</h1>
|
||||
<p>{language.sections.final.description}</p>
|
||||
<h3 className='quicktip'>{language.sections.final.changes}</h3>
|
||||
<p>{language.sections.final.changes_description}</p>
|
||||
<h1>{this.getMessage('modals.welcome.sections.final.title')}</h1>
|
||||
<p>{this.getMessage('modals.welcome.sections.final.description')}</p>
|
||||
<h3 className='quicktip'>{this.getMessage('modals.welcome.sections.final.changes')}</h3>
|
||||
<p>{this.getMessage('modals.welcome.sections.final.changes_description')}</p>
|
||||
<div className='themesToggleArea'>
|
||||
<div className='toggle' onClick={() => this.props.switchTab(1)}><span>{languageSettings.title}: {languages.find((i) => i.value === localStorage.getItem('language')).name}</span></div>
|
||||
<div className='toggle' onClick={() => this.props.switchTab(3)}><span>{appearance.theme.title}: {this.getSetting('theme')}</span></div>
|
||||
{(this.state.importedSettings.length !== 0) ? <div className='toggle' onClick={() => this.props.switchTab(2)}>{language.sections.final.imported} {this.state.importedSettings.length} {language.sections.final.settings}</div> : null}
|
||||
<div className='toggle' onClick={() => this.props.switchTab(1)}><span>{this.getMessage('modals.main.settings.sections.language.title')}: {languages.find((i) => i.value === localStorage.getItem('language')).name}</span></div>
|
||||
<div className='toggle' onClick={() => this.props.switchTab(3)}><span>{this.getMessage('modals.main.settings.sections.appearance.theme.title')}: {this.getSetting('theme')}</span></div>
|
||||
{(this.state.importedSettings.length !== 0) ? <div className='toggle' onClick={() => this.props.switchTab(2)}>{this.getMessage('modals.main.settings.sections.final.imported', { amount: this.state.importedSettings.length })} {this.state.importedSettings.length}</div> : null}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
background: #8395a7;
|
||||
height: 4px;
|
||||
margin: 10px;
|
||||
transition: .2s ease;
|
||||
transition: 0.2s ease;
|
||||
cursor: pointer;
|
||||
border-radius: 15px;
|
||||
}
|
||||
@@ -77,27 +77,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.themesToggleArea {
|
||||
.active {
|
||||
background: var(--tab-active) !important;
|
||||
background: var(--tab-active);
|
||||
}
|
||||
|
||||
.toggle {
|
||||
background: var(--sidebar);
|
||||
text-align: center;
|
||||
border-radius: 40px;
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
margin: 10px;
|
||||
border: 3px solid var(--tab-active);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--tab-active);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.auto {
|
||||
@@ -123,6 +134,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
.upload {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 20px;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 50px;
|
||||
background: var(--sidebar);
|
||||
color: var(--modal-text);
|
||||
cursor: pointer;
|
||||
border: 3px solid var(--tab-active);
|
||||
|
||||
&:hover {
|
||||
background: var(--tab-active);
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
a.privacy {
|
||||
text-decoration: none;
|
||||
color: var(--modal-text);
|
||||
@@ -153,18 +189,22 @@ a.privacy {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1440px) {
|
||||
.buttons {
|
||||
position: relative !important;
|
||||
bottom: 0rem !important;
|
||||
@media (max-width: 1820px) and (min-width: 1200px) {
|
||||
.welcomemodal {
|
||||
width: 85%;
|
||||
height: 90%;
|
||||
}
|
||||
|
||||
.examples img {
|
||||
width: 15rem !important;
|
||||
section {
|
||||
height: 90vh !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1600px) {
|
||||
@media (max-width: 1300px) {
|
||||
.welcomemodal {
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
|
||||
.examples img {
|
||||
width: 20rem !important;
|
||||
}
|
||||
@@ -172,5 +212,32 @@ a.privacy {
|
||||
.buttons {
|
||||
position: relative !important;
|
||||
bottom: 1rem !important;
|
||||
right: -1rem !important;
|
||||
}
|
||||
|
||||
section {
|
||||
height: 100vh !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1190px) {
|
||||
.welcomemodal {
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.welcomemodal {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.examples img {
|
||||
width: 15rem !important;
|
||||
}
|
||||
|
||||
section {
|
||||
height: 140vh !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
@@ -8,15 +6,20 @@ import Quote from './quote/Quote';
|
||||
import Search from './search/Search';
|
||||
import QuickLinks from './quicklinks/QuickLinks';
|
||||
import Date from './time/Date';
|
||||
import Message from './message/Message';
|
||||
|
||||
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 = {
|
||||
order: JSON.parse(localStorage.getItem('order'))
|
||||
order: JSON.parse(localStorage.getItem('order')),
|
||||
welcome: localStorage.getItem('showWelcome')
|
||||
};
|
||||
// widgets we can re-order
|
||||
this.widgets = {
|
||||
@@ -24,7 +27,8 @@ 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,
|
||||
message: this.enabled('message') ? <Message/> : null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,12 +43,29 @@ export default class Widgets extends React.PureComponent {
|
||||
order: JSON.parse(localStorage.getItem('order'))
|
||||
});
|
||||
}
|
||||
|
||||
if (data === 'widgetsWelcome') {
|
||||
this.setState({
|
||||
welcome: localStorage.getItem('showWelcome')
|
||||
});
|
||||
localStorage.setItem('showWelcome', true);
|
||||
window.onbeforeunload = () => {
|
||||
localStorage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (data === 'widgetsWelcomeDone') {
|
||||
this.setState({
|
||||
welcome: localStorage.getItem('showWelcome')
|
||||
});
|
||||
window.onbeforeunload = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
// don't show when welcome is there
|
||||
if (localStorage.getItem('showWelcome') !== 'false') {
|
||||
if (this.state.welcome !== 'false') {
|
||||
return <div id='widgets'></div>;
|
||||
}
|
||||
|
||||
@@ -53,20 +74,20 @@ 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
|
||||
elements = [<Greeting/>, <Clock/>, <QuickLinks/>, <Quote/>, <Date/>];
|
||||
elements = [<Greeting/>, <Clock/>, <QuickLinks/>, <Quote/>, <Date/>, <Message/>];
|
||||
}
|
||||
|
||||
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,16 @@
|
||||
// todo: rewrite this mess
|
||||
import React from 'react';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
import Interval from '../../../modules/helpers/interval';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import PhotoInformation from './PhotoInformation';
|
||||
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
import Interval from 'modules/helpers/interval';
|
||||
import { videoCheck, offlineBackground, getGradient, randomColourStyleBuilder } 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 = {
|
||||
@@ -22,66 +24,23 @@ export default class Background extends React.PureComponent {
|
||||
photoURL: ''
|
||||
}
|
||||
};
|
||||
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');
|
||||
|
||||
if (this.state.url !== '') {
|
||||
const url = (localStorage.getItem('ddgProxy') === 'true' && this.state.photoInfo.offline !== true && !this.state.url.startsWith('data:')) ? window.constants.DDG_IMAGE_PROXY + this.state.url : this.state.url;
|
||||
let url = this.state.url;
|
||||
if (localStorage.getItem('ddgProxy') === 'true' && this.state.photoInfo.offline !== true && !this.state.url.startsWith('data:')) {
|
||||
url = variables.constants.DDG_IMAGE_PROXY + this.state.url;
|
||||
}
|
||||
|
||||
const photoInformation = document.querySelector('.photoInformation');
|
||||
|
||||
// just set the background
|
||||
if (localStorage.getItem('bgtransition') === 'false') {
|
||||
if (photoInformation) {
|
||||
photoInformation.style.display = 'block';
|
||||
}
|
||||
backgroundImage.style.background = null;
|
||||
return backgroundImage.style.background = `url(${url})`;
|
||||
photoInformation?.[photoInformation.style.display = 'block'];
|
||||
return backgroundImage.style.background = `url(${url})`;
|
||||
}
|
||||
|
||||
// firstly we set the background as hidden and make sure there is no background set currently
|
||||
@@ -89,9 +48,7 @@ export default class Background extends React.PureComponent {
|
||||
backgroundImage.style.background = null;
|
||||
|
||||
// same with photo information if not using custom background
|
||||
if (photoInformation) {
|
||||
photoInformation.classList.add('backgroundPreload');
|
||||
}
|
||||
photoInformation?.classList.add('backgroundPreload');
|
||||
|
||||
// preloader for background transition, required so it loads in nice
|
||||
const preloader = document.createElement('img');
|
||||
@@ -124,41 +81,53 @@ export default class Background extends React.PureComponent {
|
||||
offline = true;
|
||||
}
|
||||
|
||||
switch (localStorage.getItem('backgroundType')) {
|
||||
const setFavourited = ({ type, url, credit, location, camera }) => {
|
||||
console.log(type)
|
||||
if (type === 'random_colour' || type === 'random_gradient') {
|
||||
return this.setState({
|
||||
type: 'colour',
|
||||
style: `background:${url}`
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
url,
|
||||
photoInfo: {
|
||||
credit,
|
||||
location,
|
||||
camera
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const favourited = JSON.parse(localStorage.getItem('favourite'));
|
||||
if (favourited) {
|
||||
return setFavourited(favourited);
|
||||
}
|
||||
|
||||
const type = localStorage.getItem('backgroundType');
|
||||
switch (type) {
|
||||
case 'api':
|
||||
if (offline) {
|
||||
return this.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 this.setState(offlineBackground());
|
||||
}
|
||||
|
||||
// API background
|
||||
const backgroundAPI = localStorage.getItem('backgroundAPI');
|
||||
const apiCategory = localStorage.getItem('apiCategory');
|
||||
const apiQuality = localStorage.getItem('apiQuality');
|
||||
const photoMap = (localStorage.getItem('photoMap') === 'true');
|
||||
|
||||
let requestURL, data;
|
||||
switch (backgroundAPI) {
|
||||
case 'unsplash':
|
||||
requestURL = `${window.constants.PROXY_URL}/images/unsplash?quality=${apiQuality}`;
|
||||
requestURL = `${variables.constants.PROXY_URL}/images/unsplash?quality=${apiQuality}&map=${photoMap}`;
|
||||
break;
|
||||
case 'pexels':
|
||||
requestURL = `${window.constants.PROXY_URL}/images/pexels?quality=${apiQuality}`;
|
||||
requestURL = `${variables.constants.PROXY_URL}/images/pexels?quality=${apiQuality}`;
|
||||
break;
|
||||
// Defaults to Mue
|
||||
default:
|
||||
requestURL = `${window.constants.API_URL}/images/random?category=${apiCategory}&quality=${apiQuality}`;
|
||||
requestURL = `${variables.constants.API_URL}/images/random?category=${apiCategory}&quality=${apiQuality}`;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -166,18 +135,12 @@ 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;
|
||||
|
||||
if (backgroundAPI === 'unsplash') {
|
||||
credit = data.photographer + ` ${this.language.unsplash}`;
|
||||
photoURL = data.photo_page;
|
||||
photographerURL = data.photographer_page;
|
||||
} else if (backgroundAPI === 'pexels') {
|
||||
credit = data.photographer + ` ${this.language.pexels}`;
|
||||
if (backgroundAPI === 'unsplash' || backgroundAPI === 'pexels') {
|
||||
photoURL = data.photo_page;
|
||||
photographerURL = data.photographer_page;
|
||||
}
|
||||
@@ -188,62 +151,78 @@ export default class Background extends React.PureComponent {
|
||||
currentAPI: backgroundAPI,
|
||||
photoInfo: {
|
||||
hidden: false,
|
||||
credit: credit,
|
||||
credit: data.photographer,
|
||||
location: data.location,
|
||||
camera: data.camera,
|
||||
url: data.file,
|
||||
photographerURL: photographerURL,
|
||||
photoURL: photoURL
|
||||
photographerURL,
|
||||
photoURL,
|
||||
latitude: data.latitude || null,
|
||||
longitude: data.longitude || null,
|
||||
// location map token from mapbox
|
||||
maptoken: data.maptoken || null
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.setState(object);
|
||||
|
||||
localStorage.setItem('currentBackground', JSON.stringify(object));
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'colour':
|
||||
const customBackgroundColour = localStorage.getItem('customBackgroundColour') || {'angle':'180','gradient':[{'colour':'#ffb032','stop':0}],'type':'linear'};
|
||||
|
||||
let gradientSettings = '';
|
||||
try {
|
||||
gradientSettings = JSON.parse(customBackgroundColour);
|
||||
} catch (e) {
|
||||
const hexColorRegex = /#[0-9a-fA-F]{6}/s;
|
||||
if (hexColorRegex.exec(customBackgroundColour)) {
|
||||
// Colour use to be simply a hex colour or a NULL value before it was a JSON object. This automatically upgrades the hex colour value to the new standard. (NULL would not trigger an exception)
|
||||
gradientSettings = { 'type': 'linear', 'angle': '180', 'gradient': [{ 'colour': customBackgroundColour, 'stop': 0 }] };
|
||||
localStorage.setItem('customBackgroundColour', JSON.stringify(gradientSettings));
|
||||
}
|
||||
const gradient = getGradient();
|
||||
if (gradient) {
|
||||
this.setState(gradient);
|
||||
}
|
||||
break;
|
||||
|
||||
if (typeof gradientSettings === 'object' && gradientSettings !== null) {
|
||||
return this.gradientStyleBuilder(gradientSettings);
|
||||
}
|
||||
break;
|
||||
case 'random_colour':
|
||||
case 'random_gradient':
|
||||
this.setState(randomColourStyleBuilder(type));
|
||||
break;
|
||||
|
||||
case 'custom':
|
||||
const customBackground = localStorage.getItem('customBackground');
|
||||
let customBackground;
|
||||
try {
|
||||
customBackground = JSON.parse(localStorage.getItem('customBackground'));
|
||||
} catch (e) {
|
||||
// move to new format
|
||||
customBackground = [localStorage.getItem('customBackground')];
|
||||
localStorage.setItem('customBackground', JSON.stringify(customBackground));
|
||||
}
|
||||
|
||||
// pick random
|
||||
customBackground = customBackground[Math.floor(Math.random() * customBackground.length)];
|
||||
|
||||
// allow users to use offline images
|
||||
if (offline && !customBackground.startsWith('data:')) {
|
||||
return this.offlineBackground();
|
||||
return this.setState(offlineBackground());
|
||||
}
|
||||
|
||||
if (customBackground !== '' && customBackground !== 'undefined') {
|
||||
this.setState({
|
||||
if (customBackground !== '' && customBackground !== 'undefined' && customBackground !== [''] && customBackground !== undefined) {
|
||||
const object = {
|
||||
url: customBackground,
|
||||
type: 'custom',
|
||||
video: this.videoCheck(customBackground),
|
||||
video: videoCheck(customBackground),
|
||||
photoInfo: {
|
||||
hidden: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.setState(object);
|
||||
|
||||
localStorage.setItem('currentBackground', JSON.stringify(object));
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
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'));
|
||||
@@ -259,8 +238,8 @@ export default class Background extends React.PureComponent {
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -302,7 +281,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';
|
||||
@@ -325,24 +304,23 @@ export default class Background extends React.PureComponent {
|
||||
if (backgroundType !== this.state.type || (this.state.type === 'api' && localStorage.getItem('backgroundAPI') !== this.state.currentAPI) || (this.state.type === 'custom' && localStorage.getItem('customBackground') !== this.state.url)) {
|
||||
return refresh();
|
||||
}
|
||||
} else {
|
||||
if (backgroundType !== this.state.type) {
|
||||
return refresh();
|
||||
}
|
||||
} else if (backgroundType !== this.state.type) {
|
||||
return refresh();
|
||||
}
|
||||
|
||||
// background effects so we don't get another image again
|
||||
const backgroundFilter = localStorage.getItem('backgroundFilter');
|
||||
const backgroundFilterSetting = localStorage.getItem('backgroundFilter');
|
||||
const backgroundFilter = backgroundFilterSetting && backgroundFilterSetting !== 'none';
|
||||
|
||||
if (this.state.video === true) {
|
||||
document.getElementById('backgroundVideo').style.webkitFilter = `blur(${localStorage.getItem('blur')}px) brightness(${localStorage.getItem('brightness')}%) ${backgroundFilter ? backgroundFilter + '(' + localStorage.getItem('backgroundFilterAmount') + '%)' : ''}`;
|
||||
document.getElementById('backgroundVideo').style.webkitFilter = `blur(${localStorage.getItem('blur')}px) brightness(${localStorage.getItem('brightness')}%) ${backgroundFilter ? backgroundFilterSetting + '(' + localStorage.getItem('backgroundFilterAmount') + '%)' : ''}`;
|
||||
} else {
|
||||
element.style.webkitFilter = `blur(${localStorage.getItem('blur')}px) brightness(${localStorage.getItem('brightness')}%) ${backgroundFilter ? backgroundFilter + '(' + localStorage.getItem('backgroundFilterAmount') + '%)' : ''}`;
|
||||
element.style.webkitFilter = `blur(${localStorage.getItem('blur')}px) brightness(${localStorage.getItem('brightness')}%) ${backgroundFilter ? backgroundFilterSetting + '(' + localStorage.getItem('backgroundFilterAmount') + '%)' : ''}`;
|
||||
}
|
||||
}
|
||||
|
||||
// uninstall photo pack reverts your background to what you had previously
|
||||
if (data === 'marketplacebackgrounduninstall' || data === 'backgroundwelcome') {
|
||||
if (data === 'marketplacebackgrounduninstall' || data === 'backgroundwelcome' || data === 'backgroundrefresh') {
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
@@ -352,34 +330,41 @@ 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');
|
||||
if (interval && interval !== 'refresh') {
|
||||
const type = localStorage.getItem('backgroundType');
|
||||
|
||||
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 (type === 'api' || type === 'custom') {
|
||||
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'));
|
||||
if (current.type !== type) {
|
||||
this.getBackground();
|
||||
}
|
||||
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 +380,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 +402,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,14 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { Star, StarBorder } from '@mui/icons-material';
|
||||
//import Hotkeys from 'react-hot-keys';
|
||||
|
||||
import Tooltip from '../../helpers/tooltip/Tooltip';
|
||||
import Tooltip from 'components/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() {
|
||||
@@ -24,34 +24,65 @@ export default class Favourite extends React.PureComponent {
|
||||
this.setState({
|
||||
favourited: this.buttons.unfavourited
|
||||
});
|
||||
window.stats.postEvent('feature', 'Background favourite');
|
||||
variables.stats.postEvent('feature', 'Background favourite');
|
||||
} else {
|
||||
const url = document.getElementById('backgroundImage').style.backgroundImage.replace('url("', '').replace('")', '');
|
||||
const type = localStorage.getItem('backgroundType');
|
||||
switch (type) {
|
||||
case 'colour':
|
||||
return;
|
||||
case 'random_colour':
|
||||
case 'random_gradient':
|
||||
localStorage.setItem('favourite', JSON.stringify({
|
||||
type: localStorage.getItem('backgroundType'),
|
||||
url: document.getElementById('backgroundImage').style.background
|
||||
}));
|
||||
break;
|
||||
default:
|
||||
const url = document.getElementById('backgroundImage').style.backgroundImage.replace('url("', '').replace('")', '').replace(variables.constants.DDG_IMAGE_PROXY, '');
|
||||
|
||||
if (!url) {
|
||||
return;
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'custom') {
|
||||
localStorage.setItem('favourite', JSON.stringify({
|
||||
type,
|
||||
url
|
||||
}));
|
||||
} else {
|
||||
// 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({
|
||||
type,
|
||||
url,
|
||||
credit: document.getElementById('credit').textContent || '',
|
||||
location: location ? location.innerText : 'N/A',
|
||||
camera: camera ? camera.innerText : 'N/A',
|
||||
resolution: document.getElementById('infoResolution').textContent || '',
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem('favourite', JSON.stringify({
|
||||
url: url,
|
||||
credit: document.getElementById('credit').textContent,
|
||||
location: document.getElementById('infoLocation').textContent,
|
||||
camera: document.getElementById('infoCamera').textContent
|
||||
}));
|
||||
|
||||
this.setState({
|
||||
favourited: this.buttons.favourited
|
||||
});
|
||||
window.stats.postEvent('feature', 'Background unfavourite');
|
||||
variables.stats.postEvent('feature', 'Background unfavourite');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const backgroundType = localStorage.getItem('backgroundType');
|
||||
if (backgroundType === 'colour' || backgroundType === 'custom') {
|
||||
if (backgroundType === 'colour') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Tooltip title={window.language.modals.main.settings.sections.background.buttons.favourite}>{this.state.favourited}</Tooltip>;
|
||||
return (
|
||||
<Tooltip title={variables.language.getMessage(variables.languagecode, 'modals.main.settings.sections.background.buttons.favourite')}>
|
||||
{this.state.favourited}
|
||||
{/*variables.keybinds.favouriteBackground && variables.keybinds.favouriteBackground !== '' ? <Hotkeys keyName={variables.keybinds.favouriteBackground} onKeyDown={() => this.favourite()} /> : null*/}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { Fullscreen } from '@mui/icons-material';
|
||||
//import Hotkeys from 'react-hot-keys';
|
||||
|
||||
import Tooltip from '../../helpers/tooltip/Tooltip';
|
||||
import Tooltip from 'components/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 = {
|
||||
@@ -15,7 +16,7 @@ export default class Maximise extends React.PureComponent {
|
||||
setAttribute(blur, brightness, filter) {
|
||||
// don't attempt to modify the background if it isn't an image
|
||||
const backgroundType = localStorage.getItem('backgroundType');
|
||||
if (backgroundType === 'colour') {
|
||||
if (backgroundType === 'colour' || backgroundType === 'random_colour' || backgroundType === 'random_gradient') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,21 +44,22 @@ export default class Maximise extends React.PureComponent {
|
||||
});
|
||||
|
||||
this.setAttribute(0, 100);
|
||||
window.stats.postEvent('feature', 'Background maximise');
|
||||
variables.stats.postEvent('feature', 'Background maximise');
|
||||
} else {
|
||||
this.setState({
|
||||
hidden: false
|
||||
});
|
||||
|
||||
this.setAttribute(localStorage.getItem('blur'), localStorage.getItem('brightness'), true);
|
||||
window.stats.postEvent('feature', 'Background unmaximise');
|
||||
variables.stats.postEvent('feature', 'Background unmaximise');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Tooltip title={window.language.modals.main.settings.sections.background.buttons.view}>
|
||||
<FullscreenIcon onClick={this.maximise} className='topicons' />
|
||||
<Tooltip title={variables.language.getMessage(variables.languagecode, 'modals.main.settings.sections.background.buttons.view')}>
|
||||
<Fullscreen onClick={this.maximise} className='topicons' />
|
||||
{/*variables.keybinds.maximiseBackground && variables.keybinds.maximiseBackground !== '' ? <Hotkeys keyName={variables.keybinds.maximiseBackground} onKeyDown={this.maximise} /> : null*/}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,88 +1,157 @@
|
||||
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 variables from 'modules/variables';
|
||||
import { useState, Fragment } from 'react';
|
||||
import { Info, LocationOn, PhotoCamera, Crop as Resolution, Person as Photographer, GetApp as Download } from '@mui/icons-material';
|
||||
//import Hotkeys from 'react-hot-keys';
|
||||
|
||||
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);
|
||||
window.stats.postEvent('feature', 'Background download');
|
||||
variables.stats.postEvent('feature', 'Background download');
|
||||
};
|
||||
|
||||
export default function PhotoInformation(props) {
|
||||
const [width, setWidth] = React.useState(0);
|
||||
const [height, setHeight] = React.useState(0);
|
||||
export default function PhotoInformation({ info, url, api }) {
|
||||
const [width, setWidth] = useState(0);
|
||||
const [height, setHeight] = useState(0);
|
||||
const [usePhotoMap, setPhotoMap] = useState(false);
|
||||
|
||||
const language = window.language.widgets.background;
|
||||
|
||||
if (props.info.hidden === true || !props.info.credit) {
|
||||
if (info.hidden === true || !info.credit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// remove unsplash and pexels text
|
||||
const photographer = props.info.credit.split(` ${language.unsplash}`)[0].split(` ${language.pexels}`);
|
||||
const unsplash = variables.language.getMessage(variables.languagecode, 'widgets.background.unsplash');
|
||||
const pexels = variables.language.getMessage(variables.languagecode, 'widgets.background.pexels');
|
||||
const photographer = info.credit.split(` ${unsplash}`)[0].split(` ${pexels}`);
|
||||
|
||||
let credit = props.info.credit;
|
||||
let photo = language.credit;
|
||||
let credit = info.credit;
|
||||
let photo = variables.language.getMessage(variables.languagecode, 'widgets.background.credit');
|
||||
|
||||
// unsplash and pexels credit
|
||||
if (props.info.photographerURL && props.info.photographerURL !== '' && !props.info.offline && props.api) {
|
||||
if (props.api === 'unsplash') {
|
||||
photo = <a href={props.info.photoURL + '?utm_source=mue'} target='_blank' rel='noopener noreferrer'>{language.credit}</a>;
|
||||
credit = <><a href={props.info.photographerURL} target='_blank' rel='noopener noreferrer'>{photographer}</a> <a href='https://unsplash.com?utm_source=mue' target='_blank' rel='noopener noreferrer'>{language.unsplash}</a></>;
|
||||
if (info.photographerURL && info.photographerURL !== '' && !info.offline && api) {
|
||||
if (api === 'unsplash') {
|
||||
photo = <a href={info.photoURL + '?utm_source=mue'} target='_blank' rel='noopener noreferrer'>{photo}</a>;
|
||||
credit = <><a href={info.photographerURL} target='_blank' rel='noopener noreferrer'>{info.credit}</a> <a href='https://unsplash.com?utm_source=mue' target='_blank' rel='noopener noreferrer'>{unsplash}</a></>;
|
||||
} else {
|
||||
photo = <a href={props.info.photoURL} target='_blank' rel='noopener noreferrer'>{language.credit}</a>;
|
||||
credit = <><a href={props.info.photographerURL} target='_blank' rel='noopener noreferrer'>{photographer}</a> <a href='https://pexels.com' target='_blank' rel='noopener noreferrer'>{language.pexels}</a></>;
|
||||
photo = <a href={info.photoURL} target='_blank' rel='noopener noreferrer'>{photo}</a>;
|
||||
credit = <><a href={info.photographerURL} target='_blank' rel='noopener noreferrer'>{info.credit}</a> <a href='https://pexels.com' target='_blank' rel='noopener noreferrer'>{pexels}</a></>;
|
||||
}
|
||||
}
|
||||
|
||||
const ddgProxy = (localStorage.getItem('ddgProxy') === 'true');
|
||||
|
||||
// get resolution
|
||||
const img = new Image();
|
||||
img.onload = (event) => {
|
||||
setWidth(event.target.width);
|
||||
setHeight(event.target.height);
|
||||
};
|
||||
img.src = (ddgProxy && !info.offline && !url.startsWith('data:')) ? variables.constants.DDG_IMAGE_PROXY + url : 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'>{info.location || 'N/A'}</span>
|
||||
<span id='infoCamera'>{info.camera || 'N/A'}</span>
|
||||
<span id='infoResolution'>{width}x{height}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
img.src = (localStorage.getItem('ddgProxy') === 'true' && !props.info.offline && !props.url.startsWith('data:')) ? window.constants.DDG_IMAGE_PROXY + props.url : props.url;
|
||||
|
||||
const downloadEnabled = (localStorage.getItem('downloadbtn') === 'true') && !info.offline && !info.photographerURL && api;
|
||||
const downloadBackground = () => {
|
||||
if (downloadEnabled) {
|
||||
downloadImage(info);
|
||||
}
|
||||
};
|
||||
|
||||
const showBackgroundInformation = () => {
|
||||
const element = document.querySelector('.infoCard');
|
||||
if (element) {
|
||||
if (element.style.display === 'none' || element.style.display === '') {
|
||||
element.style.display = 'block';
|
||||
} else {
|
||||
element.style.display = 'none';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const photoMap = () => {
|
||||
if (localStorage.getItem('photoMap') !== 'true' || !info.latitude || !info.longitude || usePhotoMap === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const zoom = 12;
|
||||
const tile = `${variables.constants.MAPBOX_URL}/styles/v1/mapbox/streets-v11/static/pin-s+555555(${info.longitude},${info.latitude})/${info.longitude},${info.latitude},${zoom},0/300x100?access_token=${info.maptoken}`;
|
||||
|
||||
return (
|
||||
<Fragment key='photomap'>
|
||||
<a href={`${variables.constants.OPENSTREETMAP_URL}/?mlat=${info.latitude}&mlon=${info.longitude}`} target='_blank' rel='noopener noreferrer'>
|
||||
<img className='locationMap' src={tile} alt='location' draggable={false}/>
|
||||
</a>
|
||||
<br/>
|
||||
<span className='mapCopyright'>
|
||||
<a href='https://www.mapbox.com/about/maps/' target='_blank' rel='noopener noreferrer'> © Mapbox</a>, <a href='https://www.openstreetmap.org/about/' target='_blank' rel='noopener noreferrer'>© OpenStreetMap</a>. <a href='https://www.mapbox.com/map-feedback/' target='_blank' rel='noopener noreferrer'>Improve this map</a>.
|
||||
</span>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
// only request map image if the user looks at the photo information
|
||||
// this is to reduce requests to the api
|
||||
try {
|
||||
document.getElementsByClassName('photoInformation')[0].onmouseover = () => {
|
||||
setPhotoMap(true);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
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>{variables.language.getMessage(variables.languagecode, 'widgets.background.information')}</h1>
|
||||
<hr/>
|
||||
{photoMap()}
|
||||
{/* fix console error by using fragment and key */}
|
||||
{info.location && info.location !== 'N/A' ? <Fragment key='location'>
|
||||
<LocationOn/>
|
||||
<span id='infoLocation'>{info.location}</span>
|
||||
</Fragment> : null}
|
||||
{info.camera && info.camera !== 'N/A' ? <Fragment key='camera'>
|
||||
<PhotoCamera/>
|
||||
<span id='infoCamera'>{info.camera}</span>
|
||||
</Fragment> : null}
|
||||
<Resolution/>
|
||||
<span id='infoResolution'>{width}x{height}</span>
|
||||
<Photographer/>
|
||||
<span>{photographer}</span>
|
||||
{downloadEnabled ?
|
||||
<>
|
||||
<Download/>
|
||||
<span className='download' onClick={() => downloadImage(info)}>{variables.language.getMessage(variables.languagecode, 'widgets.background.download')}</span>
|
||||
</>
|
||||
: null}
|
||||
</div>
|
||||
{/*variables.keybinds.downloadBackground && variables.keybinds.downloadBackground !== '' ? <Hotkeys keyName={variables.keybinds.downloadBackground} onKeyDown={() => downloadBackground()} /> : null*/}
|
||||
{/*variables.keybinds.showBackgroundInformation && variables.keybinds.showBackgroundInformation !== '' ? <Hotkeys keyName={variables.keybinds.showBackgroundInformation} onKeyDown={() => showBackgroundInformation()} /> : null*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,11 +55,28 @@
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.locationMap {
|
||||
height: 100px;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.mapboxLogo {
|
||||
height: 20px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
span,
|
||||
svg {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
span {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
h1,
|
||||
.MuiSvgIcon-root {
|
||||
user-select: none;
|
||||
@@ -82,7 +99,7 @@
|
||||
border: none;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:hover,
|
||||
span {
|
||||
display: block !important;
|
||||
}
|
||||
@@ -95,4 +112,8 @@
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.mapCopyright {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import React from 'react';
|
||||
import variables from 'modules/variables';
|
||||
import { PureComponent, createRef } from 'react';
|
||||
|
||||
import { utcToZonedTime } from 'date-fns-tz';
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import dtf from '../../../modules/helpers/date';
|
||||
import { nth, convertTimezone } from 'modules/helpers/date';
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
|
||||
import './greeting.scss';
|
||||
|
||||
export default class Greeting extends React.PureComponent {
|
||||
export default class Greeting extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
greeting: ''
|
||||
};
|
||||
this.timer = undefined;
|
||||
this.language = window.language.widgets.greeting;
|
||||
this.greeting = createRef();
|
||||
}
|
||||
|
||||
doEvents(time, message) {
|
||||
@@ -28,13 +27,13 @@ export default class Greeting extends React.PureComponent {
|
||||
|
||||
// If it's December 25th, set the greeting string to "Merry Christmas"
|
||||
if (month === 11 && date === 25) {
|
||||
message = this.language.christmas;
|
||||
message = variables.language.getMessage(variables.languagecode, 'widgets.greeting.christmas');
|
||||
// If the date is January 1st, set the greeting string to "Happy new year"
|
||||
} else if (month === 0 && date === 1) {
|
||||
message = this.language.newyear;
|
||||
message = variables.language.getMessage(variables.languagecode, 'widgets.greeting.newyear');
|
||||
// If it's October 31st, set the greeting string to "Happy Halloween"
|
||||
} else if (month === 9 && date === 31) {
|
||||
message = this.language.halloween;
|
||||
message = variables.language.getMessage(variables.languagecode, 'widgets.greeting.halloween');
|
||||
}
|
||||
|
||||
return message;
|
||||
@@ -50,28 +49,28 @@ 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();
|
||||
|
||||
// Set the default greeting string to "Good evening"
|
||||
let message = this.language.evening;
|
||||
let message = variables.language.getMessage(variables.languagecode, 'widgets.greeting.evening');
|
||||
// If it's before 12am, set the greeting string to "Good morning"
|
||||
if (hour < 12) {
|
||||
message = this.language.morning;
|
||||
message = variables.language.getMessage(variables.languagecode, 'widgets.greeting.morning');
|
||||
// If it's before 6pm, set the greeting string to "Good afternoon"
|
||||
} else if (hour < 18) {
|
||||
message = this.language.afternoon;
|
||||
message = variables.language.getMessage(variables.languagecode, 'widgets.greeting.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 +83,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 = variables.language.getMessage(variables.languagecode, 'widgets.greeting.birthday').split(' ');
|
||||
message = `${text[0]} ${nth(this.calculateAge(birth))} ${text[1]}`;
|
||||
} else {
|
||||
message = variables.language.getMessage(variables.languagecode, 'widgets.greeting.birthday');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,34 +115,34 @@ export default class Greeting extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'greeting' || data === 'timezone') {
|
||||
const element = document.querySelector('.greeting');
|
||||
|
||||
if (localStorage.getItem('greeting') === 'false') {
|
||||
return element.style.display = 'none';
|
||||
return this.greeting.current.style.display = 'none';
|
||||
}
|
||||
|
||||
this.timer = null;
|
||||
this.getGreeting(0);
|
||||
|
||||
element.style.display = 'block';
|
||||
element.style.fontSize = `${1.6 * Number((localStorage.getItem('zoomGreeting') || 100) / 100)}em`;
|
||||
this.greeting.current.style.display = 'block';
|
||||
this.greeting.current.style.fontSize = `${1.6 * Number((localStorage.getItem('zoomGreeting') || 100) / 100)}em`;
|
||||
}
|
||||
});
|
||||
|
||||
// this comment can apply to all widget zoom features apart from the general one in the Accessibility section
|
||||
// in a nutshell: 1.6 is the current font size and we do "localstorage || 100" so we don't have to try that 4.0 -> 5.0 thing again
|
||||
document.querySelector('.greeting').style.fontSize = `${1.6 * Number((localStorage.getItem('zoomGreeting') || 100) / 100)}em`;
|
||||
this.greeting.current.style.fontSize = `${1.6 * Number((localStorage.getItem('zoomGreeting') || 100) / 100)}em`;
|
||||
|
||||
this.getGreeting(0);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.remove('refresh');
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
return <h1 className='greeting'>
|
||||
{this.state.greeting}
|
||||
</h1>;
|
||||
return (
|
||||
<h1 className='greeting' ref={this.greeting}>
|
||||
{this.state.greeting}
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,6 @@
|
||||
font-size: 1.6em;
|
||||
cursor: initial;
|
||||
user-select: none;
|
||||
|
||||
--shadow-shift: 0.2rem;
|
||||
}
|
||||
|
||||
42
src/components/widgets/message/Message.jsx
Normal file
42
src/components/widgets/message/Message.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { PureComponent, createRef } from 'react';
|
||||
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
|
||||
import './message.scss';
|
||||
|
||||
export default class Message extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
messageText: ''
|
||||
};
|
||||
this.message = createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'message') {
|
||||
if (localStorage.getItem('message') === 'false') {
|
||||
return this.message.current.style.display = 'none';
|
||||
}
|
||||
|
||||
this.message.current.style.display = 'block';
|
||||
this.message.current.style.fontSize = `${1.6 * Number((localStorage.getItem('zoomMessage') || 100) / 100)}em`;
|
||||
}
|
||||
});
|
||||
|
||||
this.message.current.style.fontSize = `${1.6 * Number((localStorage.getItem('zoomMessage') || 100) / 100)}em`;
|
||||
const messages = JSON.parse(localStorage.getItem('messages')) || [];
|
||||
this.setState({
|
||||
messageText: messages[Math.floor(Math.random() * messages.length)]
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<h2 className='message' ref={this.message}>
|
||||
{this.state.messageText}
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user