Compare commits
238 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df26dff9cc | ||
|
|
bb9b4cd3c7 | ||
|
|
d3b7088653 | ||
|
|
a5c966aa72 | ||
|
|
94a2608462 | ||
|
|
862b9d9a39 | ||
|
|
00961b85c8 | ||
|
|
1b48d59b4c | ||
|
|
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 | ||
|
|
6cdfb9db3e | ||
|
|
1ce6cb9419 | ||
|
|
141da4ba36 | ||
|
|
9db7b210bc | ||
|
|
090ffe05f4 | ||
|
|
66f6d0f7ed | ||
|
|
285145fdcb | ||
|
|
37df7fb08c | ||
|
|
5a91fa24e9 | ||
|
|
bdd277d876 | ||
|
|
d7be6e351d | ||
|
|
c9f2af6b96 | ||
|
|
b426041598 | ||
|
|
2d7e138b2f | ||
|
|
332e6c33ac | ||
|
|
99993720f2 | ||
|
|
59a888c861 | ||
|
|
6cccad0e17 | ||
|
|
8ffd0e04cb | ||
|
|
3d6884fe9a | ||
|
|
2a1e26d0c4 | ||
|
|
b842bd935e | ||
|
|
3a47089a00 | ||
|
|
e1f43dc343 | ||
|
|
75b3c23a38 | ||
|
|
7a271190d5 | ||
|
|
c725ad4cc3 | ||
|
|
c07d7ecbb0 | ||
|
|
99ffc82de0 | ||
|
|
7840b48727 | ||
|
|
401baa6653 | ||
|
|
70fc204e70 | ||
|
|
a01d778f65 | ||
|
|
ff3e2caf49 | ||
|
|
95614a383f | ||
|
|
44fc24951f | ||
|
|
19bc802f09 | ||
|
|
ba9f2e01c8 | ||
|
|
8ece0a7eb0 | ||
|
|
d91490874c | ||
|
|
6a3a367cda | ||
|
|
84e6532a80 | ||
|
|
cf36ced2a7 | ||
|
|
c6b65f943a | ||
|
|
6aa1c6b0ea | ||
|
|
2dcaa5270d | ||
|
|
1d44b2792e | ||
|
|
d0577ded59 | ||
|
|
ae5a07c756 | ||
|
|
5d3418a8af | ||
|
|
50353c9e49 | ||
|
|
ea850fae56 | ||
|
|
ba11ac171e | ||
|
|
faace9012a |
@@ -5,4 +5,4 @@ indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
insert_final_newline = true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
extends: 'react-app',
|
||||
parser: '@babel/eslint-parser'
|
||||
extends: 'react-app',
|
||||
parser: '@babel/eslint-parser'
|
||||
};
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Request things to be added to mue
|
||||
title: "[Feature Request]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description**
|
||||
A clear and concise description of what you want adding to Mue. If it's related to a problem, mention so.
|
||||
|
||||
**Expected behaviour**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Design**
|
||||
Images or Figma prototypes of what your idea would be like.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the feature request here.
|
||||
1
.gitignore
vendored
@@ -9,3 +9,4 @@ yarn-error.log
|
||||
.eslintcache
|
||||
stats.json
|
||||
yarn.lock
|
||||
*.zip
|
||||
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018-2021 The Mue Authors
|
||||
Copyright (c) 2018-2022 The Mue Authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
43
README.md
@@ -1,4 +1,4 @@
|
||||
<img src="https://raw.githubusercontent.com/mue/branding/main/logo/logo_round.png" align="left" width="180px" height="180px"/>
|
||||
<img src="assets/logo.png" align="left" width="180px" height="180px"/>
|
||||
<img align="left" width="0" height="192px" hspace="10"/>
|
||||
|
||||
> <a href="https://muetab.com/">Mue</a>
|
||||
@@ -7,19 +7,19 @@
|
||||
<br>
|
||||
[](https://microsoftedge.microsoft.com/addons/detail/aepnglgjfokepefimhbnibfjekidhmja) [](https://addons.mozilla.org/firefox/addon/mue) [](https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid)
|
||||
|
||||
Mue is a fast, open and free-to-use browser extension that gives a new, fresh and customisable tab page to modern browsers
|
||||
Mue is a fast, open and free-to-use browser extension that gives a new, fresh and customisable tab page to modern browsers.
|
||||
|
||||
<br>
|
||||
|
||||
## Table of contents
|
||||
* [Screenshot](#screenshot)
|
||||
* [Screenshots](#screenshot)
|
||||
* [Features](#features)
|
||||
* [Planned Features](#planned-features)
|
||||
* [Installation](#installation)
|
||||
* [Chrome](#chrome)
|
||||
* [Firefox](#firefox)
|
||||
* [Edge Chromium](#edge-chromium)
|
||||
* [Naver](#naver)
|
||||
* [Whale](#whale)
|
||||
* [Other](#other)
|
||||
* [Contributing](#development)
|
||||
* [Translations](#translations)
|
||||
@@ -36,17 +36,17 @@ Mue is a fast, open and free-to-use browser extension that gives a new, fresh an
|
||||
* Fast and free
|
||||
* Supports multiple browsers
|
||||
* Actively developed and open source
|
||||
* Automatically updating API (no tracking) with new photos, quotes and offline mode
|
||||
* Widgets such as searchbar, weather, quick links, clock, date, quote, greeting
|
||||
* Automatically updating [API](https://github.com/mue/api) with new photos, quotes and offline mode
|
||||
* 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 - download custom photo packs made by the community
|
||||
* [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)*
|
||||
*A demo of the tab can be found [here](https://demo.muetab.com), and the latest GitHub commit build [here](https://mue.vercel.app)*
|
||||
### Chrome
|
||||
[](https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid)
|
||||
<br>
|
||||
@@ -60,37 +60,36 @@ Please see our [roadmap](https://github.com/mue/mue/projects)
|
||||
### Edge (Chromium)
|
||||
[Microsoft Edge Addons](https://microsoftedge.microsoft.com/addons/detail/aepnglgjfokepefimhbnibfjekidhmja)
|
||||
|
||||
### Naver
|
||||
### Whale
|
||||
[Whale Store](https://store.whale.naver.com/detail/ecllekeilcmicbfkkiknfdddbogibbnc)
|
||||
|
||||
### Other
|
||||
Please note that we have dropped support for Opera as of Mue 5.0
|
||||
|
||||
[GitHub Releases](https://github.com/mue/mue/releases)
|
||||
|
||||
## Development
|
||||
This section has moved to the [documentation](https://docs.muetab.com/development#mue-tab).
|
||||
Please see the [documentation](https://docs.muetab.com/development#mue-tab).
|
||||
|
||||
### Translations
|
||||
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/>
|
||||
### Translators
|
||||
[Wessel Tip](https://github.com/Wessel), [Heimen Stoffels](https://github.com/Vistaus) - Dutch<br/>
|
||||
[Alex Sparkes](https://github.com/alexsparkes), [Maxime](https://github.com/exiam) - French<br/>
|
||||
[Anders](https://github.com/FuryingFox) - Norwegian<br/>
|
||||
[Pronin Egor](https://github.com/MrZillaGold) - Russian<br/>
|
||||
[Vicente](https://github.com/Vicente015) - Spanish<br/>
|
||||
[Austin Huang](https://github.com/austinhuang0131) - Chinese (Simplified)<br/>
|
||||
[FreeFun](https://github.com/xXFreeFunXx) - German<br/>
|
||||
[Wessel Tip](https://github.com/Wessel), [Heimen Stoffels](https://github.com/Vistaus) - Dutch <br/>
|
||||
[Alex Sparkes](https://github.com/alexsparkes), [Maxime](https://github.com/exiam) - French <br/>
|
||||
[Anders](https://github.com/FuryingFox) - Norwegian <br/>
|
||||
[Pronin Egor](https://github.com/MrZillaGold) - Russian <br/>
|
||||
[Vicente](https://github.com/Vicente015) - Spanish <br/>
|
||||
[Austin Huang](https://github.com/austinhuang0131) - Chinese (Simplified) <br/>
|
||||
[FreeFun](https://github.com/xXFreeFunXx) - German <br/>
|
||||
### Contributors
|
||||
Many thanks to the photographers [here](https://api.muetab.com/images/photographers) for letting us use their wonderful photographs.
|
||||
|
||||
And finally, a big thank you to all the other [contributors](https://github.com/mue/mue/graphs/contributors)!
|
||||
### Resources
|
||||
[Pexels](https://pexels.com), [Unsplash](https://unsplash.com) - Stock photos used for offline mode
|
||||
[Pexels](https://pexels.com), [Unsplash](https://unsplash.com) - Stock photos used for offline mode <br/>
|
||||
[Undraw](https://undraw.co) - Welcome modal images
|
||||
|
||||
BIN
assets/logo.png
Normal file
|
After Width: | Height: | Size: 222 KiB |
@@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
presets: ['@babel/preset-env', ['@babel/preset-react', {
|
||||
'runtime': 'automatic'
|
||||
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']
|
||||
};
|
||||
|
||||
8
manifest/_locales/de/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/en/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/en_GB/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/en_US/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/es/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/fr/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/nl/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/no/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/ru/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/zh_CN/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
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
@@ -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')
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"offline_enabled": true,
|
||||
"name": "Mue",
|
||||
"description": "Fast, open and free-to-use new tab page for modern browsers.",
|
||||
"version": "5.1.1",
|
||||
"default_locale": "en",
|
||||
"name": "__MSG_name__",
|
||||
"description": "__MSG_description__",
|
||||
"version": "6.0.4",
|
||||
"homepage_url": "https://muetab.com",
|
||||
"browser_action": {
|
||||
"default_icon": "icons/128x128.png"
|
||||
@@ -16,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.1.1",
|
||||
"version": "6.0.4",
|
||||
"homepage_url": "https://muetab.com",
|
||||
"browser_action": {
|
||||
"default_icon": "icons/128x128.png"
|
||||
|
||||
66
package.json
@@ -9,52 +9,56 @@
|
||||
"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.1.1",
|
||||
"version": "6.0.4",
|
||||
"dependencies": {
|
||||
"@fontsource/lexend-deca": "^4.4.5",
|
||||
"@fontsource/montserrat": "^4.4.5",
|
||||
"@material-ui/core": "4.11.4",
|
||||
"@material-ui/icons": "4.11.2",
|
||||
"fetch-jsonp": "^1.1.3",
|
||||
"@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",
|
||||
"@mui/icons-material": "5.2.5",
|
||||
"@mui/material": "5.2.6",
|
||||
"react": "17.0.2",
|
||||
"react-clock": "3.0.0",
|
||||
"react-color-gradient-picker": "0.1.2",
|
||||
"react-day-picker": "7.4.10",
|
||||
"react-device-detect": "1.17.0",
|
||||
"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.6",
|
||||
"@babel/eslint-parser": "^7.14.5",
|
||||
"@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.5",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"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.0",
|
||||
"css-loader": "^5.2.6",
|
||||
"eslint": "^7.28.0",
|
||||
"eslint-config-react-app": "^6.0.0",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"mini-css-extract-plugin": "^1.6.0",
|
||||
"sass": "^1.35.1",
|
||||
"sass-loader": "^7.3.1",
|
||||
"copy-webpack-plugin": "9.1.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"eslint": "^8.2.0",
|
||||
"eslint-config-react-app": "^7.0.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.39.1",
|
||||
"webpack-cli": "^4.7.2",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
"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",
|
||||
"firefox": "cp manifest/firefox.json build/manifest.json"
|
||||
"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": {
|
||||
"production": [
|
||||
|
||||
1
public/icons/undraw_add_files_modified.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="783" height="702" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path d="m400.51 1.53-25.446 6.562L61.56 88.94 36.113 95.5a48.18 48.18 0 0 0-34.582 58.618l110.341 427.877a48.183 48.183 0 0 0 22.157 29.413 48.183 48.183 0 0 0 36.46 5.17l.066-.017 364.265-93.936.066-.017a48.18 48.18 0 0 0 34.583-58.618L459.128 36.113A48.18 48.18 0 0 0 400.51 1.53z" fill="#F2F2F2"/><path d="m403.969 14.945-30.139 7.773-304.119 78.426-30.139 7.772a34.312 34.312 0 0 0-24.627 41.743l110.341 427.878a34.307 34.307 0 0 0 41.743 24.627l.065-.017L531.36 509.21l.066-.017a34.308 34.308 0 0 0 24.627-41.743L445.712 39.573a34.31 34.31 0 0 0-41.743-24.628z" fill="#fff"/><path d="m381.212 153.503-184.273 47.521a8.016 8.016 0 0 1-9.761-5.759 8.014 8.014 0 0 1 5.759-9.762l184.273-47.52a8.012 8.012 0 0 1 9.754 5.761 8.01 8.01 0 0 1-5.752 9.759zM419.977 171.439l-216.284 55.775a8.01 8.01 0 0 1-9.761-5.759 8.011 8.011 0 0 1 5.759-9.761l216.284-55.775a8.011 8.011 0 0 1 9.761 5.759 8.012 8.012 0 0 1-5.759 9.761zM411.48 270.877l-184.273 47.52a8.014 8.014 0 0 1-4.003-15.52l184.273-47.521a8.014 8.014 0 0 1 4.003 15.521zM450.245 288.813l-216.284 55.775a8.014 8.014 0 0 1-4.003-15.521l216.284-55.775a8.015 8.015 0 0 1 4.003 15.521zM441.748 388.25l-184.273 47.521a8.017 8.017 0 0 1-9.756-5.761 8.011 8.011 0 0 1 5.753-9.76l184.273-47.52a8.014 8.014 0 0 1 4.003 15.52zM480.513 406.186l-216.284 55.775a8 8 0 0 1-6.077-.854 8.019 8.019 0 0 1-3.866-8.027 8.01 8.01 0 0 1 3.121-5.284 8.006 8.006 0 0 1 2.82-1.355l216.284-55.775a8.011 8.011 0 0 1 9.761 5.759 8.012 8.012 0 0 1-5.759 9.761z" fill="#F2F2F2"/><path d="m165.481 249.749-65.212 16.817a3.847 3.847 0 0 1-4.68-2.762l-14.97-58.048a3.845 3.845 0 0 1 2.761-4.681l65.213-16.817a3.85 3.85 0 0 1 4.68 2.762l14.97 58.048a3.846 3.846 0 0 1-2.762 4.681zM195.749 367.122l-65.212 16.817a3.845 3.845 0 0 1-4.681-2.761l-14.97-58.048a3.854 3.854 0 0 1 .413-2.912 3.853 3.853 0 0 1 2.349-1.769l65.212-16.817a3.849 3.849 0 0 1 4.681 2.761l14.969 58.049a3.847 3.847 0 0 1-2.761 4.68zM226.019 484.496l-65.213 16.817a3.846 3.846 0 0 1-4.681-2.762l-14.969-58.048a3.846 3.846 0 0 1 2.761-4.681l65.213-16.817a3.85 3.85 0 0 1 4.681 2.762l14.969 58.048a3.846 3.846 0 0 1-2.761 4.681zM654.658 109.992H278.34a48.179 48.179 0 0 0-48.125 48.125v441.876a48.176 48.176 0 0 0 48.125 48.125h376.318a48.184 48.184 0 0 0 48.125-48.125V158.117a48.179 48.179 0 0 0-48.125-48.125z" fill="#E6E6E6"/><path d="M654.658 123.846H278.339a34.309 34.309 0 0 0-34.271 34.271v441.876a34.312 34.312 0 0 0 34.271 34.27h376.319a34.309 34.309 0 0 0 34.271-34.27V158.117a34.309 34.309 0 0 0-34.271-34.271z" fill="#fff"/><path d="M694.194 701.88c48.519 0 87.85-39.332 87.85-87.85 0-48.519-39.331-87.851-87.85-87.851-48.518 0-87.85 39.332-87.85 87.851 0 48.518 39.332 87.85 87.85 87.85z" fill="#5352ED"/><path d="M598.022 366.656H407.72a8.018 8.018 0 0 1-8.023-8.015 8.021 8.021 0 0 1 2.351-5.67 8.005 8.005 0 0 1 5.672-2.344h190.302a8.016 8.016 0 0 1 0 16.029zM631.08 393.703H407.72a8.017 8.017 0 0 1-7.412-4.945 8.021 8.021 0 0 1 0-6.138 8.008 8.008 0 0 1 4.343-4.338 8.017 8.017 0 0 1 3.069-.607h223.36a8.013 8.013 0 1 1 0 16.028zM598.022 487.869H407.72a8.017 8.017 0 0 1-7.412-4.945 8.021 8.021 0 0 1 0-6.138 8.008 8.008 0 0 1 4.343-4.338 8.017 8.017 0 0 1 3.069-.607h190.302a8.013 8.013 0 1 1 0 16.028zM631.08 514.917H407.72a8.018 8.018 0 0 1-8.023-8.014 8.014 8.014 0 0 1 8.023-8.015h223.36a8.027 8.027 0 0 1 5.673 2.344 8.02 8.02 0 0 1 0 11.341 8.016 8.016 0 0 1-5.673 2.344zM365.093 405.982h-67.346a3.847 3.847 0 0 1-3.843-3.843v-59.947a3.847 3.847 0 0 1 3.843-3.843h67.346a3.847 3.847 0 0 1 3.843 3.843v59.947a3.85 3.85 0 0 1-3.843 3.843zM365.093 527.195h-67.346a3.847 3.847 0 0 1-3.843-3.843v-59.947a3.847 3.847 0 0 1 3.843-3.843h67.346a3.847 3.847 0 0 1 3.843 3.843v59.947a3.847 3.847 0 0 1-3.843 3.843z" fill="#E6E6E6"/><path d="M598.234 231.721H457.932a8.015 8.015 0 1 1 0-16.028h140.302a8.015 8.015 0 1 1 0 16.028zM631.292 258.769h-173.36a8.009 8.009 0 0 1-7.404-4.948 8.005 8.005 0 0 1 0-6.133 8.011 8.011 0 0 1 7.404-4.948h173.36a8.016 8.016 0 0 1 5.667 13.681 8.016 8.016 0 0 1-5.667 2.348z" fill="#CCC"/><path d="M426.882 291.547H297.536a3.845 3.845 0 0 1-3.843-3.843V186.757a3.847 3.847 0 0 1 3.843-3.843h129.346a3.847 3.847 0 0 1 3.843 3.843v100.947a3.843 3.843 0 0 1-1.127 2.716 3.843 3.843 0 0 1-2.716 1.127z" fill="#5352ED"/><path d="M700.5 648v-57a6.5 6.5 0 1 0-13 0v57a6.5 6.5 0 1 0 13 0z" fill="#fff" stroke="#fff" stroke-width="5"/><path d="m728.202 621.069.609-.571a6.5 6.5 0 0 0 .33-9.154l-30.362-32.861a6.5 6.5 0 0 0-9.548-.001l-30.371 32.862a6.5 6.5 0 0 0 .329 9.154l.61.571a6.498 6.498 0 0 0 9.219-.332l23.885-25.853a1.5 1.5 0 0 1 2.204 0l23.875 25.852a6.5 6.5 0 0 0 9.22.333z" fill="#fff" stroke="#fff" stroke-width="5"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h782.044v701.88H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
1
public/icons/undraw_around_the_world_modified.svg
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
1
public/icons/undraw_by_the_road.svg
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
1
public/icons/undraw_dark_mode.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="769.001" height="771.636"><path d="M769.001 0H310.286a400.77 400.77 0 0 0-5.285 65c0 219.81 178.191 398 398 398a400.7 400.7 0 0 0 66-5.45z" fill="#2f2e41"/><path d="M574.85 248.809a57.4 57.4 0 0 1-20.163-3.64 3 3 0 1 1 2.106-5.617 52.393 52.393 0 0 0 51.263-8.531c-22.038.234-43.595-10.18-54.32-26.893-6.894-10.743-9.148-23.384-6.52-36.558a65.225 65.225 0 0 1 12.09-26.277 52.051 52.051 0 0 0-36.024 54.65 3 3 0 1 1-5.971.592 58.044 58.044 0 0 1 49.792-63.138 3 3 0 0 1 2.55 5.076C554.31 154.06 545 179.402 558.787 200.888c10.878 16.95 34.283 26.644 56.928 23.58a3 3 0 0 1 2.642 4.968 58.387 58.387 0 0 1-43.507 19.373z" fill="#6c63ff"/><path d="M527.001 250a22 22 0 1 1 22-22 22.025 22.025 0 0 1-22 22zm0-38a16 16 0 1 0 16 16 16.018 16.018 0 0 0-16-16z" fill="#6c63ff"/><path d="M8.69 567.078A10.276 10.276 0 0 1 .42 554.027l7.788-26.48 11.835 2.218.509 26.967a10.276 10.276 0 0 1-11.86 10.346z" fill="#9f616a"/><path fill="#2f2e41" d="m76.996 736.132-14.793-2.219 3.698-144.235-20.71 62.132-11.095 87.281-14.054-3.698-2.959-90.979 16.273-107.252 81.363 18.492-37.723 180.478z"/><path d="M70.205 771.636a10.873 10.873 0 0 1-10.873-10.873q0-.533.052-1.064l2.297-23.354a3.428 3.428 0 0 1 1.784-2.678c4.36-2.352 8.845-1.782 13.434 1.288a3.407 3.407 0 0 1 1.48 2.429l2.623 22.098a10.873 10.873 0 0 1-10.797 12.154zM9.82 769.898a9.794 9.794 0 0 1-4.754-14.662l15.78-23.457c5.689-4.082 9.12-2.094 10.454 5.534l3.19-7.993 2.404 2.622a7.647 7.647 0 0 1 .404 9.854l-16.368 24.91a9.794 9.794 0 0 1-11.11 3.192z" fill="#2f2e41"/><circle cx="109.542" cy="375.915" r="22.19" fill="#9f616a"/><path d="M123.595 416.596 85.872 409.2c4.86-10.364 8.75-13.87 4.438-25.15h30.327c-2.675 11.683-.09 22.648 2.958 32.546z" fill="#9f616a"/><path d="m119.157 566.009-88.76-25.149c17.78-39.13 21.503-82.09 16.232-127.504a8.217 8.217 0 0 1 7.207-9.116q.15-.017.3-.03c11.578-.953 23.648-2.177 36.174-3.886l14.054 9.615 16.273-3.698c5.221 2.53 10.48 4.636 15.227 6.734a19.038 19.038 0 0 1 10.35 23.414c-14.462 43.677-24.143 86.94-27.057 129.62z" fill="#e6e6e6"/><path d="m22.261 532.724-17.012-2.22L45.55 414.38c2.05-5.907 7.134-9.104 13.323-9.988l5.918.74-4.438 68.788zM155.525 510.524a13.484 13.484 0 0 1-13.487-8.271l-16.224-39.058 13.314-48.078 3.52 2.24q.509.323 1.004.663a31.653 31.653 0 0 1 13.002 31.471l-3.472 20.361 14.323 30.753a13.484 13.484 0 0 1-11.98 9.92z" fill="#e6e6e6"/><path d="M111.248 343.027a23.564 23.564 0 0 1 10.766 3.17c.755.439.406 2.022 1.103 2.539.878.65 2.785.255 3.563 1.017a23.615 23.615 0 0 1 7.022 19.026l-1.238 12.311-2.921-3.194a30.448 30.448 0 0 0-20.305-9.97q-.497-.033-.996-.052l2.247-3.933-3.905 3.906a38.03 38.03 0 0 0-5.321.433l2.988-5.23-5.734 5.734a15.331 15.331 0 0 0-10.713 8.738l-.637 1.411-.713-11.745a23.674 23.674 0 0 1 23.154-24.182q.82-.018 1.64.02z" fill="#2f2e41"/><ellipse cx="131.877" cy="380.205" rx="1.782" ry="4.233" fill="#9f616a"/><ellipse cx="87.319" cy="378.868" rx="1.782" ry="4.233" fill="#9f616a"/><path d="M174.034 435.42a10.276 10.276 0 0 1 7.134 13.706L171.16 474.85l-11.604-3.216 1.782-26.913a10.276 10.276 0 0 1 12.696-9.302z" fill="#9f616a"/><path fill="#e6e6e6" d="m160.579 509.055-18.492-11.835 15.533-31.806 19.231 7.397-9.615 28.107-6.657 8.137z"/><path d="M173.001 409.101V284h-2v125.101a5 5 0 1 0 2 0zm-1 7.899a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3z" fill="#3f3d56"/><path d="M170.435 164.57a4.314 4.314 0 0 1-4.314-4.315v-12.941a4.314 4.314 0 0 1 8.627 0v12.941a4.314 4.314 0 0 1-4.313 4.314zM170.435 263.787a4.314 4.314 0 0 1-4.314-4.313v-12.942a4.314 4.314 0 1 1 8.627 0v12.942a4.314 4.314 0 0 1-4.313 4.313zM200.939 177.203a4.314 4.314 0 0 1-3.05-7.364l9.15-9.15a4.314 4.314 0 0 1 6.1 6.1l-9.15 9.15a4.3 4.3 0 0 1-3.05 1.264zM130.78 247.362a4.314 4.314 0 0 1-3.05-7.364l9.15-9.15a4.314 4.314 0 0 1 6.1 6.1l-9.15 9.151a4.3 4.3 0 0 1-3.05 1.263zM226.514 207.708h-12.941a4.314 4.314 0 0 1 0-8.628h12.941a4.314 4.314 0 1 1 0 8.628zM127.296 207.708h-12.941a4.314 4.314 0 1 1 0-8.628h12.941a4.314 4.314 0 0 1 0 8.628zM210.09 247.362a4.3 4.3 0 0 1-3.05-1.263l-9.152-9.15a4.314 4.314 0 1 1 6.101-6.101l9.15 9.15a4.314 4.314 0 0 1-3.05 7.365zM139.93 177.203a4.3 4.3 0 0 1-3.05-1.263l-9.15-9.15a4.314 4.314 0 0 1 6.1-6.102l9.15 9.151a4.314 4.314 0 0 1-3.05 7.364zM170.435 229.277a25.883 25.883 0 1 1 25.883-25.883 25.912 25.912 0 0 1-25.883 25.883zm0-43.139a17.255 17.255 0 1 0 17.255 17.256 17.275 17.275 0 0 0-17.255-17.256z" fill="#6c63ff"/></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
1
public/icons/undraw_on_the_way.svg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
1
public/icons/undraw_private_data_modified.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="400" height="100" viewBox="0 0 629 160" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M502.973 139.536h68.977L606.438 79.8 571.95 20.064h-68.977L468.484 79.8l34.489 59.736zM56.973 138.577h68.977l34.488-59.736-34.488-59.735H56.973L22.484 78.84l34.489 59.736zM279.973 139.616h68.977l34.488-59.736-34.488-59.735h-68.977L245.484 79.88l34.489 59.736z" fill="#F2F2F2"/><path d="M357.693 159.759h-86.464a5.019 5.019 0 0 1-4.33-2.5l-43.231-74.88a5.009 5.009 0 0 1 0-5L266.899 2.5a5.02 5.02 0 0 1 4.33-2.5h86.464a5.015 5.015 0 0 1 4.33 2.5l43.231 74.879a5.009 5.009 0 0 1 0 5l-43.231 74.88a5.018 5.018 0 0 1-4.33 2.5zM271.229 2a3.008 3.008 0 0 0-2.598 1.5L225.4 78.38a3.003 3.003 0 0 0 0 3l43.231 74.88a3.011 3.011 0 0 0 2.598 1.5h86.464a3.012 3.012 0 0 0 2.597-1.5l43.232-74.88a3.003 3.003 0 0 0 0-3L360.29 3.5a2.999 2.999 0 0 0-2.597-1.5h-86.464zM134.693 159.759H48.229a5.018 5.018 0 0 1-4.33-2.5L.668 82.379a5.015 5.015 0 0 1 0-5L43.899 2.5A5.014 5.014 0 0 1 48.23 0h86.464a5.015 5.015 0 0 1 4.33 2.5l43.231 74.879a5.009 5.009 0 0 1 0 5l-43.231 74.88a5.018 5.018 0 0 1-4.33 2.5zM48.229 2a3.008 3.008 0 0 0-2.598 1.5L2.4 78.379a3.009 3.009 0 0 0 0 3l43.231 74.88a3.011 3.011 0 0 0 2.598 1.5h86.464a3.012 3.012 0 0 0 2.597-1.5l43.232-74.88a3.003 3.003 0 0 0 0-3L137.29 3.5a2.999 2.999 0 0 0-2.597-1.5H48.229zM580.693 159.759H494.23a5.013 5.013 0 0 1-4.331-2.5l-43.231-74.88a5.009 5.009 0 0 1 0-5L489.899 2.5A5.016 5.016 0 0 1 494.23 0h86.463a5.015 5.015 0 0 1 4.33 2.5l43.231 74.879a5.009 5.009 0 0 1 0 5l-43.231 74.88a5.016 5.016 0 0 1-4.33 2.5zM494.23 2a3.01 3.01 0 0 0-2.599 1.5L448.4 78.379a3.003 3.003 0 0 0 0 3l43.231 74.88a3.011 3.011 0 0 0 2.599 1.5h86.463a3.012 3.012 0 0 0 2.598-1.5l43.231-74.88a3.003 3.003 0 0 0 0-3L583.291 3.5a3.009 3.009 0 0 0-2.598-1.5H494.23z" fill="#CCC"/><path d="M91.461 45.863a32.977 32.977 0 1 0 0 65.954 32.977 32.977 0 0 0 0-65.954zm0 9.893a9.893 9.893 0 1 1 0 19.786 9.893 9.893 0 0 1 0-19.786zm0 47.626a24.026 24.026 0 0 1-19.786-10.559c.159-6.595 13.19-10.226 19.786-10.226 6.595 0 19.628 3.631 19.786 10.226a24.062 24.062 0 0 1-19.786 10.559zM314.397 56.907a17.995 17.995 0 0 0-12.604-5.58 18.005 18.005 0 0 0-12.851 31.035l26.067 26.068 25.456-25.456a18.44 18.44 0 0 0-.306-25.761 18.44 18.44 0 0 0-25.762-.306zM558.697 59.821c0 11.728-21.235 37.474-21.235 37.474s-21.235-25.746-21.235-37.474a21.223 21.223 0 0 1 6.207-15.02 21.236 21.236 0 0 1 36.263 14.985v.035z" fill="#5352ED"/><path d="M537.462 68.163a8.342 8.342 0 1 0 0-16.684 8.342 8.342 0 0 0 0 16.684z" fill="#fff"/><path d="M536.702 121.029a8.342 8.342 0 1 0 0-16.685 8.342 8.342 0 0 0 0 16.685z" fill="#5352ED"/></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
1
public/icons/undraw_upgrade_modified.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="1066" height="774" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path opacity=".1" d="M995.19 369.87c.59-6.77.9-13.65.9-20.6 0-108.83-73.84-197.06-164.92-197.06-29 0-56.34 9-80 24.75-64.47-50-148.64-80.21-240.72-80.21-149.83 0-278.71 80.07-335.89 194.9a138.6 138.6 0 0 0-9.6-.34C73.83 291.31 0 379.53 0 488.36c0 40 10 77.23 27.14 108.31 0 97.91 78.45 177.31 175.22 177.31h122l.54-.93c7.17-11.64 24.34-14.55 36.3-7.94 3.16 1.74 6.1 4 9.63 4.8 14.13 3 23.62-19.73 37.92-17.52 7.18 1.11 12.78 8.53 20 7.77 4.79-.51 8.42-4.52 12.92-6.22 7.21-2.74 14.52 1.06 19.57 7.23 2.93-8.43 2.91-18.42 7.65-26.15 7.17-11.64 24.34-14.55 36.3-7.94 3.16 1.74 6.1 4 9.63 4.8 14.13 3 23.62-19.73 37.92-17.52 7.18 1.11 12.78 8.53 20 7.77 4.79-.51 8.42-4.52 12.92-6.22 11.22-4.27 22.71 7.32 25.47 19a81.066 81.066 0 0 1 1.69 15.64c9.68-4.69 17.86-17.88 28.91-16.17 7.18 1.11 12.78 8.53 20 7.77 4.79-.51 8.42-4.52 12.92-6.22 11.22-4.27 22.71 7.32 25.47 19 1.47 6.2 1.57 12.64 1.82 19h202.15c76 0 137.58-62.32 137.58-139.2 15.42-30 24.33-65.28 24.33-103.06 0-67-28-126.2-70.81-161.8z" fill="#6C63FF"/><path d="M114 582.98v-24.3l11.2 11.2 2.8-2.9-16-16-16 16 2.8 2.8 11.2-11.1v24.3h4zM789 299a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM224 283a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM401 435a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM925 534a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM700 617a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM315 687a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM96 444a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM125 380a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM957 492a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM770 716a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM215 612a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM284 337a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM386.5 227.5H371V212h-2v15.5h-15.5v2H369V245h2v-15.5h15.5v-2zM764 513.89h-9.39v-9.39h-1.22v9.39H744v1.22h9.39v9.39h1.22v-9.39H764v-1.22zM707 688.89h-9.39v-9.39h-1.22v9.39H687v1.22h9.39v9.39h1.22v-9.39H707v-1.22zM240 666.89h-9.39v-9.39h-1.22v9.39H220v1.22h9.39v9.39h1.22v-9.39H240v-1.22zM174 490.89h-9.39v-9.39h-1.22v9.39H154v1.22h9.39v9.39h1.22v-9.39H174v-1.22zM940 667v-24.3l11.2 11.2 2.8-2.9-16-16-16 16 2.8 2.8 11.2-11.1V667h4zM747 385a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM816 618a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM807 439a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM909.5 329.5H894V314h-2v15.5h-15.5v2H892V347h2v-15.5h15.5v-2zM697 592.89h-9.39v-9.39h-1.22v9.39H677v1.22h9.39v9.39h1.22v-9.39H697v-1.22z" fill="#fff"/><path d="m258.343 592.735 3.913-4.048-2.432-2.348-3.913 4.048-15.794 8.254 10.535 10.166 7.691-16.072z" fill="#5352ED"/><path d="m268.811 603.045 4.009-4.15-2.49-2.417-4.016 4.158-16.198 8.463 10.812 10.428 7.883-16.482z" fill="#5352ED"/><path d="m279.414 613.083 3.913-4.048-2.432-2.348-3.913 4.048-15.794 8.254 10.543 10.173 7.683-16.079zM354.436 685.727l4.016-4.158-2.497-2.409-4.016 4.157-16.191 8.456 10.805 10.435 7.883-16.481zM365.242 696.161l4.016-4.157-2.498-2.41-4.016 4.158-16.191 8.456 10.805 10.434 7.884-16.481zM376.047 706.596l4.016-4.158-2.498-2.409-4.016 4.157-16.191 8.456 10.805 10.435 7.884-16.481zM305.384 638.366l4.016-4.158-2.497-2.409-4.016 4.157-16.191 8.456 10.805 10.435 7.883-16.481z" fill="#5352ED"/><path d="m316.19 648.8 4.016-4.157-2.49-2.403-4.016 4.158-16.199 8.449 10.805 10.435 7.884-16.482zM326.995 659.235l4.016-4.158-2.49-2.402-4.016 4.157-16.199 8.45 10.812 10.441 7.877-16.488zM575.105 337.375c13.499-14.624 20.646-56.373 11.545-65.16-9.102-8.787-49.749.608-63.888 14.613l-.437-.411-277.74 287.63 52.795 50.972 277.733-287.623-.008-.021zM671.535 430.49c13.499-14.623 20.646-56.373 11.545-65.16-9.102-8.787-49.749.609-63.881 14.621l-.436-.412L341.022 667.17l52.795 50.971L671.55 430.518l-.015-.028z" fill="#5352ED"/><path d="M752.018 249.005c19.93-21.3 38.411-74.794 29.317-83.574-9.094-8.78-61.104 12.332-81.634 32.984l-.429-.405-407.254 421.748 52.781 50.972 407.26-421.755-.041.03z" fill="#363192"/><g opacity=".1" fill="#000"><path opacity=".1" d="M574.87 274.92c7.59-17.71 22.41-36.18 31.63-36.07 12 .14 33.62 31.79 36.07 52l.26-21.71c-.21-19.9-23.65-55.18-36.3-55.33-9.1-.11-23.64 17.88-31.32 35.38l-.34 25.73zM435.41 291.7h.6c.67-19.89 23.81-54.61 36.45-54.46 8.26.1 21.11 15.17 29.1 31.16l1.94-162.36h.59c.78-29.16 24.13-80.2 36.77-80 11.94.14 33.36 46.21 35.78 75.9l.23-20c-.11-29.17-23.36-80.77-36-80.92-12.64-.15-36 50.89-36.77 80.05h-.59l-1.94 162.36c-8-16-20.84-31.06-29.1-31.16-12.64-.15-35.78 34.57-36.45 54.46h-.6l-4.77 399.81h.3l4.46-374.84z"/></g><path d="M540.858 25.386h-.01a6.93 6.93 0 0 0-7.012 6.847l-.192 16.189a6.93 6.93 0 0 0 6.847 7.011h.01a6.93 6.93 0 0 0 7.012-6.847l.192-16.189a6.93 6.93 0 0 0-6.847-7.011z" fill="#ECECF3"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h1066v773.96H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -11,21 +11,17 @@
|
||||
<body>
|
||||
<noscript>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Lexend Deca';
|
||||
src: url('./static/media/lexend-deca-latin-400-normal.35a9aeba.woff2');
|
||||
}
|
||||
|
||||
*, a {
|
||||
font-family: 'Lexend Deca', sans-serif;
|
||||
text-align: center;
|
||||
color: black;
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
*, a {
|
||||
color: white;
|
||||
background: #2f3640;
|
||||
background: #2f3640 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
BIN
public/welcome-images/example1.webp
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
public/welcome-images/example2.webp
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
public/welcome-images/example3.webp
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/welcome-images/example4.webp
Normal file
|
After Width: | Height: | Size: 45 KiB |
14
scripts/updatetranslations.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// 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');
|
||||
|
||||
fs.readdirSync('../src/translations').forEach((file) => {
|
||||
if (file === 'en_GB.json') {
|
||||
return;
|
||||
}
|
||||
|
||||
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');
|
||||
});
|
||||
37
src/App.jsx
@@ -1,37 +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 {
|
||||
componentDidMount() {
|
||||
if (!localStorage.getItem('firstRun')) {
|
||||
SettingsFunctions.setDefaultSettings();
|
||||
window.location.reload();
|
||||
}
|
||||
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
|
||||
if (!localStorage.getItem('order') || !localStorage.getItem('backgroundFilterAmount')) {
|
||||
SettingsFunctions.moveSettings();
|
||||
// the firstRun check was moved here because the old function was useless
|
||||
if (!localStorage.getItem('firstRun') || !localStorage.getItem('stats')) {
|
||||
moveSettings();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
SettingsFunctions.loadSettings();
|
||||
loadSettings();
|
||||
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'other') {
|
||||
SettingsFunctions.loadSettings(true);
|
||||
loadSettings(true);
|
||||
}
|
||||
});
|
||||
|
||||
window.analytics.tabLoad();
|
||||
variables.stats.tabLoad();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,20 +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: [],
|
||||
showList: false,
|
||||
input: ''
|
||||
input: '',
|
||||
autocompleteDisabled: (localStorage.getItem('autocomplete') !== 'true')
|
||||
};
|
||||
this.enabled = (localStorage.getItem('autocomplete') === 'true');
|
||||
}
|
||||
|
||||
onChange = (e) => {
|
||||
if (this.enabled === false) {
|
||||
if (this.state.autocompleteDisabled) {
|
||||
return this.setState({
|
||||
input: e.target.value
|
||||
});
|
||||
@@ -22,7 +23,6 @@ export default class Autocomplete extends React.PureComponent {
|
||||
|
||||
this.setState({
|
||||
filtered: this.props.suggestions.filter((suggestion) => suggestion.toLowerCase().indexOf(e.target.value.toLowerCase()) > -1),
|
||||
showList: true,
|
||||
input: e.target.value
|
||||
});
|
||||
|
||||
@@ -32,42 +32,50 @@ export default class Autocomplete extends React.PureComponent {
|
||||
onClick = (e) => {
|
||||
this.setState({
|
||||
filtered: [],
|
||||
showList: false,
|
||||
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;
|
||||
|
||||
if (this.state.showList && this.state.input) {
|
||||
if (this.state.filtered.length && localStorage.getItem('autocomplete') === 'true') {
|
||||
autocomplete = (
|
||||
<ul className='suggestions'>
|
||||
{this.state.filtered.map((suggestion) => {
|
||||
return (
|
||||
<li key={suggestion} onClick={this.onClick}>
|
||||
{suggestion}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
// length will only be > 0 if enabled
|
||||
if (this.state.filtered.length > 0 && this.state.input.length > 0) {
|
||||
autocomplete = (
|
||||
<ul className='suggestions'>
|
||||
{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}
|
||||
name={this.props.name || 'name'}
|
||||
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
@@ -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
@@ -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.analytics.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,24 +1,28 @@
|
||||
import React from 'react';
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,7 +31,15 @@ export default class Modals extends React.PureComponent {
|
||||
this.setState({
|
||||
welcomeModal: true
|
||||
});
|
||||
window.analytics.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
|
||||
@@ -39,6 +51,19 @@ 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) {
|
||||
@@ -47,26 +72,25 @@ export default class Modals extends React.PureComponent {
|
||||
});
|
||||
|
||||
if (action !== false) {
|
||||
window.analytics.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()}>
|
||||
<Modal closeTimeoutMS={300} onRequestClose={() => this.closeWelcome()} isOpen={this.state.welcomeModal} className='Modal welcomemodal mainModal' overlayClassName='Overlay' ariaHideApp={false}>
|
||||
<Welcome modalClose={() => this.closeWelcome()}/>
|
||||
<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()} 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,50 +1,56 @@
|
||||
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>
|
||||
<div label='' style={{ 'display': 'none' }}></div>
|
||||
<div label='' style={{ display: 'none' }}></div>
|
||||
</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}>
|
||||
<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}>
|
||||
<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}>
|
||||
<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,46 +38,60 @@ 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_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>
|
||||
<br/>
|
||||
{this.props.button}
|
||||
<br/>
|
||||
{updateButton}
|
||||
<br/><br/>
|
||||
{iconsrc ? <img alt='product' draggable='false' src={iconsrc} onClick={() => this.setState({ showLightbox: true })}/> : null}
|
||||
<div className='informationContainer'>
|
||||
<h1 className='overview'>{language.overview}</h1>
|
||||
<p className='description' dangerouslySetInnerHTML={{ __html: this.props.data.description }}></p>
|
||||
<div className='productInformation'>
|
||||
<ul>
|
||||
{/* <li className='header'>{language.last_updated}</li>
|
||||
<li>{this.props.data.updated}</li>
|
||||
<br/>*/}
|
||||
<li className='header'>{language.version}</li>
|
||||
<li>{this.props.data.version}</li>
|
||||
<br/>
|
||||
<li className='header'>{language.author}</li>
|
||||
<li>{this.props.data.author}</li>
|
||||
<div className='side'>
|
||||
<div className='productInformation'>
|
||||
<ul>
|
||||
<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'>{getMessage('modals.main.marketplace.product.author')}</li>
|
||||
<li>{this.props.data.author}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br/>
|
||||
{warningHTML}
|
||||
</div>
|
||||
<br/>
|
||||
{warningHTML}
|
||||
</div>
|
||||
<div className='sidebr'>
|
||||
<br/><br/>
|
||||
</div>
|
||||
<div className='informationContainer'>
|
||||
<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}>
|
||||
<Lightbox modalClose={() => this.setState({ showLightbox: false })} img={iconsrc}/>
|
||||
|
||||
@@ -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_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.analytics.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.analytics.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.analytics.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.analytics.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
@@ -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.analytics.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,16 +87,16 @@ export default class Marketplace extends React.PureComponent {
|
||||
}
|
||||
|
||||
async getItems() {
|
||||
const { data } = await (await fetch(window.constants.MARKETPLACE_URL + '/all', { 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;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
items: data[this.props.type],
|
||||
oldItems: data[this.props.type],
|
||||
items: data,
|
||||
oldItems: data,
|
||||
featured: featured.data,
|
||||
done: true
|
||||
});
|
||||
@@ -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.analytics.postEvent('marketplace-item', `${this.state.item.display_name} ${(type === 'install' ? 'installed': 'uninstalled')}`);
|
||||
window.analytics.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.analytics.postEvent('marketplace', 'Sort');
|
||||
variables.stats.postEvent('marketplace', 'Sort');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,13 +172,26 @@ 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.analytics.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 }}>
|
||||
<div className='featured' style={{ backgroundColor: this.state.featured.colour }}>
|
||||
<p>{this.state.featured.title}</p>
|
||||
<h1>{this.state.featured.name}</h1>
|
||||
<button className='addToMue' onClick={() => openFeatured()}>{this.state.featured.buttonText}</button>
|
||||
@@ -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.analytics.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,234 +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;
|
||||
|
||||
h4 {
|
||||
cursor: initial;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.resetfooter {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
|
||||
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 {
|
||||
@@ -400,7 +118,8 @@ span.modalLink {
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@@ -410,3 +129,13 @@ h3 {
|
||||
h5 {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.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,48 +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);
|
||||
}
|
||||
}
|
||||
|
||||
p.author {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
#item {
|
||||
h1 {
|
||||
font-size: 40px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
#item>h1,
|
||||
#item>.MuiSvgIcon-root {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.description {
|
||||
margin-top: 0px;
|
||||
max-width: 400px;
|
||||
.emptyitems {
|
||||
width: 25vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 90px;
|
||||
}
|
||||
|
||||
.emptyMessage {
|
||||
@@ -119,99 +92,38 @@ p.description {
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 50px;
|
||||
margin-bottom: -20px;
|
||||
}
|
||||
}
|
||||
|
||||
.backArrow {
|
||||
cursor: pointer;
|
||||
width: 2rem !important;
|
||||
height: 2rem !important;
|
||||
|
||||
&:hover {
|
||||
color: grey;
|
||||
}
|
||||
p.author {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.informationContainer {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
|
||||
border: 2px solid #ff4757;
|
||||
color: #ff4757;
|
||||
margin-top: 5px;
|
||||
float: right;
|
||||
margin-top: -10px;
|
||||
|
||||
&:hover {
|
||||
background: #ff4757;
|
||||
@@ -39,10 +40,28 @@
|
||||
|
||||
.addToMue {
|
||||
@extend %storebutton;
|
||||
margin-top: 12px;
|
||||
|
||||
float: right;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.sideload {
|
||||
display: inline;
|
||||
margin-top: 0px;
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
button.round {
|
||||
margin-left: 5px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 3px;
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
.reset {
|
||||
@extend %settingsButton;
|
||||
|
||||
margin-left: 5px;
|
||||
background-color: map-get($button-colours, 'reset');
|
||||
border: 2px solid map-get($button-colours, 'reset');
|
||||
|
||||
@@ -25,7 +24,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.add {
|
||||
.add,
|
||||
.close {
|
||||
@extend %settingsButton;
|
||||
|
||||
background-color: map-get($button-colours, 'other');
|
||||
@@ -38,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,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,38 +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,186 +38,49 @@ 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: 280px;
|
||||
color: var(--modal-text);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 0px;
|
||||
margin: 0;
|
||||
.keybind-table {
|
||||
text-align: left;
|
||||
|
||||
>label {
|
||||
vertical-align: middle;
|
||||
th {
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.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: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.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%;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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,12 +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 SettingsFunctions from '../../../../modules/helpers/settings';
|
||||
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 = {
|
||||
@@ -15,13 +13,18 @@ export default class Checkbox extends React.PureComponent {
|
||||
}
|
||||
|
||||
handleChange = () => {
|
||||
SettingsFunctions.setItem(this.props.name);
|
||||
const value = (this.state.checked === true) ? false : true;
|
||||
localStorage.setItem(this.props.name, value);
|
||||
|
||||
this.setState({
|
||||
checked: (this.state.checked === true) ? false : true
|
||||
checked: value
|
||||
});
|
||||
|
||||
window.analytics.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' 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.analytics.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
@@ -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
@@ -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,14 +19,25 @@ 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.analytics.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)) {
|
||||
document.querySelector('.reminder-info').style.display = 'block';
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
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.analytics.postEvent('setting', 'Reset');
|
||||
SettingsFunctions.setDefaultSettings('reset');
|
||||
variables.stats.postEvent('setting', 'Reset');
|
||||
setDefaultSettings('reset');
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 style={{ 'textAlign': 'center' }}>{language.title}</h3>
|
||||
<h4>{language.question}</h4>
|
||||
<p>{language.information}</p>
|
||||
<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>{variables.language.getMessage(variables.languagecode, 'modals.main.settings.sections.advanced.reset_modal.information')}</span>
|
||||
<div className='resetfooter'>
|
||||
<button className='reset' style={{ 'marginLeft': '0' }} onClick={() => reset()}>{window.language.modals.main.settings.buttons.reset}</button>
|
||||
<button className='import' style={{ 'marginLeft': '5px' }} onClick={props.modalClose}>{language.cancel}</button>
|
||||
<button className='round reset' style={{ marginLeft: 0 }} onClick={() => reset()}>
|
||||
<Delete/>
|
||||
</button>
|
||||
<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,12 +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 SettingsFunctions from '../../../../modules/helpers/settings';
|
||||
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 = {
|
||||
@@ -15,13 +13,14 @@ export default class Switch extends React.PureComponent {
|
||||
}
|
||||
|
||||
handleChange = () => {
|
||||
SettingsFunctions.setItem(this.props.name);
|
||||
const value = (this.state.checked === true) ? false : true;
|
||||
localStorage.setItem(this.props.name, value);
|
||||
|
||||
this.setState({
|
||||
checked: (this.state.checked === true) ? false : true
|
||||
checked: value
|
||||
});
|
||||
|
||||
window.analytics.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 ForumIcon from '@material-ui/icons/Forum';
|
||||
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,39 +26,42 @@ export default class About extends React.PureComponent {
|
||||
let contributors, sponsors, photographers, versionData;
|
||||
|
||||
try {
|
||||
versionData = await (await fetch(window.constants.GITHUB_URL + '/repos/mue/mue/releases', { signal: this.controller.signal })).json();
|
||||
|
||||
contributors = await (await fetch(window.constants.GITHUB_URL + '/repos/mue/mue/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
|
||||
});
|
||||
@@ -69,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;
|
||||
}
|
||||
@@ -86,56 +87,62 @@ export default class About extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<h2>{this.language.title}</h2>
|
||||
<img draggable='false' className='aboutLogo' src='./././icons/logo_horizontal.png' alt='Mue logo'></img>
|
||||
<p>{this.language.copyright} 2018-{new Date().getFullYear()} <a href='https://github.com/mue/mue/graphs/contributors' className='aboutLink' target='_blank' rel='noopener noreferrer'>The Mue Authors</a> (BSD-3 License)</p>
|
||||
<p>{this.language.version.title} {window.constants.VERSION} ({this.state.update})</p>
|
||||
<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.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:hello@muetab.com' className='aboutIcon' target='_blank' rel='noopener noreferrer'><EmailIcon/></a>
|
||||
<a href='https://twitter.com/getmue' className='aboutIcon' target='_blank' rel='noopener noreferrer'><TwitterIcon/></a>
|
||||
<a href='https://instagram.com/mue.tab' className='aboutIcon' target='_blank' rel='noopener noreferrer'><InstagramIcon/></a>
|
||||
<a href='https://facebook.com/muetab' className='aboutIcon' target='_blank' rel='noopener noreferrer'><FacebookIcon/></a>
|
||||
<a href='https://discord.gg/zv8C9F8' className='aboutIcon' target='_blank' rel='noopener noreferrer'><ForumIcon/></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/davidjcralph' className='aboutLink' target='_blank' rel='noopener noreferrer'>GitHub Sponsors</a>
|
||||
• <a href='https://ko-fi.com/davidjcralph' className='aboutLink' target='_blank' rel='noopener noreferrer'>Ko-Fi</a>
|
||||
• <a href='https://patreon.com/davidjcralph' 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={item.profile} 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,58 +1,74 @@
|
||||
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';
|
||||
import Text from '../Text';
|
||||
import Switch from '../Switch';
|
||||
import ResetModal from '../ResetModal';
|
||||
import Dropdown from '../Dropdown';
|
||||
|
||||
import SettingsFunctions from '../../../../../modules/helpers/settings';
|
||||
const time_zones = require('components/widgets/time/timezones.json');
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
settingsImport(e) {
|
||||
const content = JSON.parse(e.target.result);
|
||||
|
||||
Object.keys(content).forEach((key) => {
|
||||
localStorage.setItem(key, content[key]);
|
||||
});
|
||||
|
||||
toast(window.language.toasts.imported);
|
||||
window.analytics.postEvent('tab', 'Settings imported');
|
||||
}
|
||||
|
||||
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' />
|
||||
<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) => (
|
||||
<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) => this.settingsImport(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,4 +1,4 @@
|
||||
import { engineName } from 'react-device-detect';
|
||||
import variables from 'modules/variables';
|
||||
|
||||
import Checkbox from '../Checkbox';
|
||||
import Dropdown from '../Dropdown';
|
||||
@@ -6,60 +6,57 @@ 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} element='.other' />
|
||||
<Checkbox name='refresh' text={appearance.navbar.refresh} element='.other' />
|
||||
|
||||
<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>
|
||||
{(engineName === 'Blink') ?
|
||||
<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);
|
||||
date = date.toLocaleDateString(window.languagecode.replace('_', '-'), {
|
||||
let date = new Date(data.date.split(' ')[0]);
|
||||
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
@@ -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,19 +1,31 @@
|
||||
import variables from 'modules/variables';
|
||||
import { useState } from 'react';
|
||||
import Checkbox from '../Checkbox';
|
||||
import Slider from '../Slider';
|
||||
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>{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' />
|
||||
<Slider title='Debug timeout' name='debugtimeout' min='0' max='5000' default='0' step='100' marks={values('experimental')} element='.other' />
|
||||
<p>Send Event</p>
|
||||
<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>
|
||||
<button className='reset' style={{ marginLeft: '0px' }} onClick={() => localStorage.clear()}>Clear LocalStorage</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,47 +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';
|
||||
|
||||
import DayPickerInput from 'react-day-picker/DayPickerInput';
|
||||
import 'react-day-picker/lib/style.css';
|
||||
|
||||
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 = (data) => {
|
||||
localStorage.setItem('birthday', data);
|
||||
changeDate = (e) => {
|
||||
localStorage.setItem('birthday', e.target.value || new Date());
|
||||
|
||||
this.setState({
|
||||
birthday: data
|
||||
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>
|
||||
<DayPickerInput onDayChange={this.changeDate} value={this.state.birthday}/>
|
||||
<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
@@ -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
@@ -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
@@ -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,68 +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 }) => {
|
||||
@@ -72,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.analytics.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>
|
||||
</>
|
||||
);
|
||||
|
||||